From creating and organizing course content, managing instructors and students, and tracking enrollments and completions, Bryant has sixty minutes on the clock.
Speaker 0: Welcome back to the next episode of 100 apps, 100 hours, where we try to recreate and build your favorite apps in 1 hour or less, or die trying. Hopefully not dying. I'm your host Brian Gillespie, developer advocate at Directus. Super nice to have you. Today we've got a app that is near and dear to my heart.
I've created a lot of courses in the past, so we are going to be building a learning management system, an LMS. There's a few examples that I took a look at in the the research for this prior to. I've got my proper dad attire on and we are going to dive in and build some apps. So let's just add a little bit of context for you guys before we start the clock. If you take a look at sites like Kajabe or Podia, these are online learning management systems.
Most of these are software as a service where you can sign up for, $159 a month, it looks like, or $199 a month and sell your courses. The interface for these, if we can maybe navigate to one of, Podia's websites here. We've got like a main navigation on the left. We've got, you know, the courses that show up on the right. If we take a look at Udemy, that's a kind of a different case where you're on their platform, you're not white labeling, but you can create your own courses and load those up and sell those to their specific audience.
So I think we'll land somewhere in between all of these different tools today. Should be very fun, very interesting. So with that, let's, let's dive into this. I'm gonna start my little mouse pose thing so I can highlight things on screen as we go through and build. So, I want to kind of show you what I am working with just so there is no, you're aware, there's no cheating behind the scenes here.
I do have a Directus instance already started. I haven't logged in yet, but that is a blank Instance ready to go. That's what we'll use for the back end of the app. We also have a Nuxt 3 starter application. I do have like a Directus SDK already preconfigured and a couple of utilities just to make building a little bit easier.
But aside from that we can see this is just a blank slate. There's a login form, text component and an upload component, just to make this a little easier. A lot of this is boilerplate functionality, I don't want to bore you guys with that. So that's the setup. Alright.
Let's dive into building the app and we will start the clock on this. So we now have 60 minutes to develop this out, But before we actually do any code, I want to like sketch this out. And I I love using Figma for this. So the first thing we want to flesh out is our data model. What's our schema?
We're going to have courses. So within each course, we have some lessons. We probably have some modules. Alright. Maybe these are not huge per se.
We'll just make them smaller. What else are we gonna have? We're gonna have users. We'll probably have some instructors. Right?
We want to be able to track the instructors on the course. This is probably a a pretty good starting point. Now as far as the functionality that we need, so let's just call this our data model. Cool. We'll go over here.
Inspect this out. Functionality. Alright. Let's go through and flesh this out a little bit. We want to view a list of courses, view an individual course, and this is gonna be on the front end.
View individual lessons, we probably need to sign up for our course. We just walk it through the entire process. View individual lessons. We want some authentication, so we wanna be able to log in. And, what else?
We wanna be able to track progress. Right? And, to that end we probably need like a collection to track enrollments. So who are our users, what courses are they enrolled in, what is the completion percentage for those. Alright.
So this seems like a pretty good start. We'll continually come back and refer to this and refine it as we go through. But the first thing that I wanna do is start into our back end. So I'm gonna pull up Directus in this case, and let's see if we can zoom in just a little bit to make it easier for everybody. Now I've got this running on Docker locally, but the easiest way to run Directus is using Directus Cloud.
And I need to make sure I've got the correct password here. Alright. So we've got a blank Directus instance. We're going to flesh out this data model in no time at all. So let's create our 1st collection, we'll call it Courses, and we will use the generated UUID as the primary key field.
We'll just go in and for now we'll just select all of these defaults for tracking date created, user created, etcetera, handy to have. And for our courses we probably want a name for the course, that's great. We want to have a description for the course, so that will be a WYSIWYG editor. That way we could support HTML and rich text formatting. What else do we need as as far as our course data?
We've probably got an image for the course as well. So we could call this image. We could have multiple images, but let's just stick with a single featured image, and we'll use that. Great. So now if we back up, we've got the start to our courses data model.
Let's go in and flesh out the other parts of our course. So each course will be made up of several different modules. So the modules, again, we can add all these fields. We may not need those. I typically just add these because it has some preset functionality.
Obviously the user created it will store the UUID for that particular user, updated date and times, those are all handy to have. So the modules, we will have a name for the module, and, you know, we could have like a short description for the module. Let's just stick with name, we'll keep it really simple. The other thing that we may want to have on the courses, this just reminds me, is maybe something like a slug. So just a pretty URL for that specific course.
So we'll go in and set that up. One of the other things I could do really quickly in Directus is make sure this is URL safe. So there's just a, a field to set that up for us to automatically format that. Next, let's go in and add our lessons table, or lessons collection. We'll do the same, so what's the status, is this published or is it draft?
And now in the lessons we'll have a name for the lesson. A lot of names here. We'll have the, let's call it Lesson Content, and that could be rich text. And because these are primarily videos, let's do a video URL. We can host those on Vimeo, YouTube, Bloom, however you wanted to do that.
You could also just actually upload these into Directus as well. Alright, so now we've got kind of this side of the equation figured out. Directus already gives us users out of the box, which is great. We can go in and add these other collections. We'll have an instructor's collection.
Instructor's, I hope that's how you spell it. So our course instructors, that will be we'll have a name for them. Let's have a bio, so that'll be a WYSIWYG field. And one of the things that I love most about Directus is just how easy it is to map out this data model and get a complete back end with an AI or API ready to query for my front end. So instead of working with dummy data, I can actually prototype live with real data.
So we got our name, we got our bio, let's do an image, or we could call it avatar. Great. Cool. And last but not least, we've got enrollments. So we'll just, create all these separate tables, all these different collections within Directus.
And now we can go back and flesh out the relationships between these, because that's where the tricky part comes in. Or not necessarily tricky with Directus, but, we wanna make sure that data model works correctly on the front end. So the when we start mapping these out we have our courses. Each course could have multiple modules, and each module could have multiple lessons. So those are one too many relationships inside Directus.
So if we start fleshing this out, we'll go into the Courses collection and we'll use the relational fields inside Directus. So behind the scenes Directus will set up these relationships inside your SQL database and make sure everything plays nice with each other. So we're going to call this modules. So that's going to be our key. The related collection here is going to be modules.
And then the foreign key, so our key inside the modules collection is going to be Course. We could choose whether we want a list or table layout, that's fine, and we'll just use the name of the module and we'll show a link to that module. So now our modules are set up and if I were to go to the actual module section we can see we've got the reverse relationship back to the course. And maybe we drag and drop this, give these some icons to make them look nice. So we've got a Course, maybe that is a folder looking object.
It's great. Let's use black as the color. So we'll customize this a bit and make it look nice. The module, you know, let's look for a list, maybe. Looks great.
And now let's add a lesson, we'll just use like a video icon for that. Perfect. Okay. So now if we go back to our modules, again we're going to create a one to many relationship, but this time we're going to do that on our lessons. So the key here will be called lessons and the related collection will be lessons.
Our foreign key will be module. So just the singular because each lesson can only be in a single module. And we'll use the name of that lesson. Great. We'll show a link to the item.
And now we've got that structure set up. Great. How do the other items relate? Right? We've got instructors.
Instructors could have many different courses inside our platform, and a course could have multiple instructors. So that's a good use case for the many to many relationships inside Directus. And what we'll do is go to either courses or instructors, doesn't really matter which one. And instead of a one to many or a many to 1, we're going to use the many to many relationship. So here we'll call this instructors, that's gonna be our key.
For our related collection, we're gonna use instructors. And we don't want to allow duplicates, we want to show a link to the item. If I wanted more control over what Directus creates, or uses for the name of the junction tables, I can go into the advanced field mode. So we can see in our junction collection Directus is going to create this inside our SQL database. Courses underscore instructors, and there's gonna be a Courses ID and an Instructor's ID.
I can also add this relationship to the instructors field and we'll give this a sort just in case we want to have a head instructor and, you know, prioritize who is the primary instructor on the course. So we'll hit save, Directus will do some magic behind the scenes, and if I go in now I will see a hidden collection in our data model for courses underscore instructors. Savvy? Everything's looking good so far? Great.
Alright, now we also want to flesh out our enrollments. So if we think of enrollments, enrollments are going to be the users that are enrolled in the individual courses and this setup will be a little bit different in that we go to Enrollments, there's gonna be a many to one relationship. So each enrollment can only have one course. So what are the course that this person enrolled in? Great.
And then we're also going to have a many to one relationship with the Directus user. In this case we could just call it user as well. But the related collection here is going to be the Directus underscore users. That's our system collection for users within directus that, gives us all the authentication and user access and permissions. We could also keep track of progress or lessons completed, but for now let's just roll with this.
Okay, a little OCD over things like icons and colors, so I will add some icons and colors here. We've got instructors. These will be people. Let's see what we've got. Nice friendly guy waving, that's perfect.
Let's make that purple as well. Alright, so now if we look at our data model, this is looking pretty good. We will go into the front end and we can see that we've got courses, we've got modules, we've got lessons. Let's just create a course. Let's do published.
And we'll call this 100 Apps in 100 hours, great. Watch to follow along as we build an LMS. While building an LMS? I don't know. It's kinda meta.
No worries. Alright so we can, let's go to Unsplash and find a nice looking image. One of the nice things about Directus, and you'll hear me say that multiple times throughout this series, is the ability to import from a URL. So I can just copy that URL there, import that image, it will store it in my file library for me. And we'll give this a slug.
So you'll see if I press space bar it will automatically format this for me so that it becomes URL safe. And let's go in and create a module. So we'll call this 1st module. We'll create a second module. Great.
And we'll add an instructor. The dude building stuff and videos. Cool. I've got let's upload a photo. Great.
Good. Solid. Okay. So now we've got our first course. Since we've set up these collections I could start querying this on the front end using the Directus APIs.
So So if I just open this up in a new tab, I'm going to change the URL a little bit. We'll go to items/courses and boom, we get this permission access error. So I haven't enabled permissions. We've created this actual, collection. We've created several of these collections, but for the general public all these collections are access is forbidden by default.
So I could open this up and you know, just allow public access for now to all of these. Once we went to production, obviously we want to restrict this per user. But if I just refresh you can see over here on the left, excuse me, we've got our data coming from the LMS, or the back end of our LMS, our direct us instance. Great. So we are about 15 minutes in.
We've got our first bit of data modeling done. Let's dive into kind of the front end and start building, right? We will, I'm just gonna move this out of the way for now, and we'll get to a blank slate inside our code editor. We've got our direct assistance up here, but let's pull this up. Now within the pages directory of my Nuxt application, maybe we create a new folder for courses.
This will give us a route slash courses on the front end. And we'll have an index route. That's great. We'll set up a default view component here and now we can start querying that data from Directus. One of the things that I do have in my little Directus module is a composable to actually query the front end of or query the Directus back end.
So we can do something like this where we say const courses equals useDirectus. And this is using the Directus SDK on the the back end. We're gonna read items, courses, And I could go in and add some options here as well. You know, Copilot is showing me like a filter for published. But for now, because we haven't published anything, let's just take a look at this.
We'll, log this out to the front end. And if we navigate to /courses, it looks like we have an object promise, so we wanna do await use directus, and boom, now we've got our data on the front end. Cool. So now we probably want to do a little bit of formatting, you know, add this, add maybe a card for this. One of the things that I am using or I've got set up in this Nuxt instance or this Nuxt template app is the Nuxt UI library, which is just a UI library put out by the Nuxt team.
It helps you build apps that look great faster. It works perfectly for stuff like this. On a lot of other projects, I may use, you know, my own custom coded components. But for now let's do a card. We'll do v4 courses and courses because that is an array, course ID.
Let's actually just do something like this where we've got v text. I do also have a text component, we'll call it course. Name. And we can use the Nuxt image component, course. Image.
See how far that gets us. Okay. So we've got a card. We probably want to give a little bit of styling to the container. What is the I guess the muxu container It constrains the width.
Okay. And then maybe we've got let's just go back. We'll do the keep this really simple. I also, use tailwind a lot. We'll do like a max width of 4 XL for now.
Make this in x auto. And maybe we set these up in a grid with 3 columns on, like a large size. Okay. So we've got this actual course card. We're not rendering the actual image, which, is concerning me.
You don't have permission to access this. So we've hit our first kind of snag here. Somehow I've managed to log myself out of Directus as well. So let's log back in. Directus.
Alright. And now we will go in and we forgot to set permissions for our system collection. So all the Directus files, we're just going to make those public for now so we can take a look at that file. I could now see that and it should show up within our card as well. Great.
You know, maybe we make this font bold, 2 x l. Make it a bit bigger. Let's make it giant, right, 4 x l. I need to take a look at my Vtex component actually. I think I've already got sizes on here, so we could just use the size prop instead.
2XL. Great. Okay. Alright. So now we've got kind of an index page.
This is a course listing. Let's wrap this with, another div. I'll go in and paste that. So now we've got a course listing, we've got a course card, How do we get to this actual course detail? So I will go in and, now we'll go into our courses folder within the pages directory And let's create actually let's create a new folder.
And we'll use the brackets for this. This will give us a dynamic route. And we will use like slug or I could call this course or ID. We're using slug as the property for the course on the directive side, if I remember correctly. So we've got a slug for each of the courses.
That's what we'll query by. Sounds great. And we will go in and now we will add like an index page for this course. So again, we'll build a detailed page here, just a simple index page, and let's do course, wait, use direct us, read item. We wanna read a single item.
This is using our SDK context. And we'll use route params slug. That's not gonna cut it. Actually, we want to read multiple items. And the reason why is because we're giving each course an ID, a a UUID.
And the Directus SDK, when you read a single item, you can look up by that primary key, in this case the ID field. But where we want to look up by the slug, we're going to use read items and then we're going to construct a filter. So it'll be like this where slug is equal to route dot params.slug. We're gonna use route. So we'll pick up the route from view.
We use route dot params.slug. And again, this is why I love building this way with the back end first because I can use actual data to flesh this out. So now on our course listing page we want to add a link to that. So if we go back to the index page, we can wrap, both of these in a Nuxt link component. And for the to prop, let's just add, courses slash course ID or no, course dot slug.
Alright, so now we should get a clickable link. We can see at the bottom of the screen it says 100 Apps, 100 Hours. And when I click on it it will give me, an array of the data in this case. But I would rather have just a, I'd rather have the actual image. So let's do something like this where constant course equals courses dot I or core the first item of the array.
Maybe this could be a computer prop as well just in case that changes. Courses dot value cannot read properties of undefined. What is going on here? Let's back up and let's just keep it simple. Right.
Course equals courses. Okay, there we go. So we've got our course, and now we can start fleshing this out. So if I go in and I look at the Nuxt UI Library, we've got some components here, we've got like a skeleton. We've got a container.
We've got a card. What do some of these other apps look like? How do we actually get to their courses? Not a lot of great examples on the website. If we look at Udemy, we could see we've got like a course name, we've got a preview of the course, we've got, some of the lessons here.
And then when you get into the actual, enrollment of that specific course, they've got like a kind of a sidebar layout. So let's create a new section. We'll do the course title, course description, follow along as we build. Okay. 100 apps, 100 hours.
So the course description, we're gonna use v html for that. Course dot description. Okay. And now we've got that. We could apply some nice styling for this.
Let's do the font. Black text 4 x l. We'll do a, let's try that U container again. And this is coming from the Nuxt UI library. Alright.
So we got that. We've got, the description. Maybe we wanna add like a small bit of text to label that. This description class text extra small, text gray 500, something like that. And we can space these out a bit.
So again, this is a bit crude. We're going for speed over beauty in this case. But we want to flesh out this functionality. Where did you go? Okay, so we got our course name, we got a description.
Let's show our instructors. Alright. So this is gonna be our pclasstext. Let's do instructors. Okay.
So we've got that. We'll add a bit of margin to it. Space it apart. We'll pull up the instructors. How are we doing on time?
Quick time check. Got 30 minutes remaining to flesh this out. Gonna be a definite challenge here. So we've got the instructors. We'll do, should be course dot instructors, right?
Let's just log that out and see what we get. Course dot instructors. So you can see Directus is giving me back an array of the different ID's for those instructors. I've said this multiple times, but this is one of the reasons why I really love Directus. Because I can go in and for, this is using the REST API.
I can actually go in and do something like this where I say, hey, I want all the root level fields. And then for instructors, I want, I want a list of all the root level fields for the instructor. So I could get all the related fields in a single API call. Now where this is a many to many relationship, you can see I need to drill in one more level. So I have to go in and do one more level where I have instructors underscore ID and grab all those fields.
So now I can see the instructor and we can set up like a, just a v four loop here. V four instructor in course dot instructors. And it looks like Copilot has added a lot of code that I did not mean to add here. So let's just pull that back. Be careful how many times you tab, right?
So now let's take a look at instructor within that loop and see what we have. Are we even getting anything? Alright. We've got instructors. We've got the instructor ID.
K. So we could do something like this. We could actually destructure that. Or can we? Instructor instructor's ID.
And let's see what we get back with that. We're breaking stuff. Okay. Yeah. So we'll just run with this for now.
Alright. So we've got our instructor, we've got an instructor ID. So we'll have instructor dot instructorsid.name. Let's wrap that P TECH. We'll do, Nuxt Image.
And let's do, yeah. Maybe width 24 sounds good. 24 height, rounded full to make this a circle. And then the source is gonna be instructor dot instructorsid.avatar. And then we can do a divvhtml for the bio equals instructor dot instructorsid.bio.
Okay. Cool. So now we've got a few things going on here. Let's actually look at the lessons. Right?
We'll go into our Directus instance. If we go back to our specific course here, we didn't add any lessons to this specific module. So I'm just gonna pull up the Directus YouTube account. Our YouTube studio here will get to the Directus channel and take a look at our content. So I'm just gonna copy a few of these down.
We're gonna add a couple of lessons within each one of these. Just gonna actually take these wholesale from the YouTube channel. Here's some content. Great. Alright.
How do we do, we'll copy some of this. How to manage different versions. Blah blah blah, copy that video URL. Oh, that's actually a short. Not sure what that'll do.
Let's, let's take an actual video. Great. So we've got our first module. Let's go into the second module and we will add one of my videos. Getting started with Agency OS.
My wife tends to make fun of, my sausage fingers all the time. So lots of typos here. So we'll go in. Let's just copy one more of these so we've got something to look at on the front end. Project templates.
Great. Okay. So So save this. Now we've got our course, we go back to our course detail page in this instance and, you know, let's add a new section over here where we've got our section. Looks like it get hub copilot for the win.
We've got our lessons. Give that some margin. Give some space. And, so if we log each lesson, let's just take a look, we'll probably get the same, let's just close this out real quick. We'll do course dot lessons just to see what we're getting back from the API, which it doesn't look like anything, right?
So if I open up the dev tools, we take a look at the network request, we dive into the data, the lessons are actually going to come through the different modules. Alright, so again, we can go back up to our fields section and we could do something like this where we go to modules, then we, drill down again into, our lessons. And we want to grab the module name. And within our lessons, again, we could just grab all the root level fields within that. And now we can see that second API call that was made.
We've got our modules. We've got our lessons within that. And again, I only have to make a single API call. You know, we can certainly cache this if this data doesn't change, but it keeps the entire app very snappy and prevents me from having to make 35 different calls to get all the related data. Also kind of it follows a very GraphQL like structure as well where I can request just the data that I need.
I don't necessarily have to go in and use these wildcards to grab those root level fields. I could go in and say something like name, description, that way I can prevent over fetching and larger network requests than I actually need. So we've got, now we've got our data coming in correctly. Let's go through and we're going to loop through the modules. So we'll do v 4 module in course dot modules, and then inside that we'll have another loop for the lessons.
So we've got our second module. Let's style these a bit. So maybe we wrap this, do something like divide y and divide y, divide gray 300 maybe? Okay. So now we've got a divider between our different modules.
And maybe we give each module some padding. That's probably a bit so we'll do p y 4. Let's style the module a bit And font, let's just give it a little fancier font. I think I've got this set up as maybe Poppins inside my specific account. Font display.
Great. Div 4. Okay. We got the we don't really need the lesson content. And then let's style each one of the lessons.
You know, we'll likely change these to a NUCs link and then the 2. Now we want to dive into each individual lesson. So our URL structure is going to be courses, we've got course, so we use the dollar sign, so so we get the template literals here. We've got course dot ID and then we've got lessons and then maybe lesson dot ID or it we could use slugs on the lessons as well if we wanted to. Let me fix this.
We've got slugdot ID. Okay. Unterminated template literal. Okay. Alright.
So now we've got a URL that should take us to this specific lesson. I'm gonna go back into our Directus instance, which keeps logging me out, probably a cookie or some type of setup issue on my end when I configured Docker, but we'll work around it for now. Let's go in and give a slug for the individual lessons as well. So, just to maintain parity. We'll go into the interface, we'll make slugify checked, and now we've got a slug for the lessons.
Let's go into each one of those lessons and set a slug. Project templates, managing versions. Alright. Getting started. And custom operations.
Okay. So now each one of those have a slug. We can change this from ID to slug. We'll still use that ID as the the key for looping over these. But now we're starting to get something that kind of looks like, some courses.
You know, maybe we wrap these in a go back. Maybe we wrap these in a card component. And a lot of times as I go along, I would definitely be refactoring these as we went along into components that made sense. So we've got the padding, p y 4. Let's do some space between each card.
So we've got a little bit of space between each card. That's great. There's our different modules. I don't know why the first module is showing, below the second module. That could be the way that we've got our sorting on the course.
So if we go in, we don't have sorting enabled. So what I can do is go into our Courses section, our Courses Data Model, we'll go to the relationship field, or the relationship tab, and for our sort field we're just going to add that sort property, or that sort field on that. And that should take care of the sorting for us. So now we've got our first module, that's great. We've got our second module, starting to look like something.
Let's just take one moment and make a, like a nice little header for this. We've got image course dot image, object cover, And we'll give this a little bit of padding as well. MT 8. Maybe we want to round the corners. Again, very crude but, definitely paints the picture.
So we've got about 18 minutes left. Let's dive into the actual lessons, Right? So a pretty common set up when we go to the lessons, and right now we're getting a Page Error Not Found, is to have a list of the other lessons over here on the left hand side, and then on the right hand side we've got the actual course content. So let's figure out what that would actually look like inside here. So one of the nice things about Nuxt is like the nested routes.
So we go to their documentation, we go to, it's like child routes. Child route keys, nested routes. So I can set up, like parent and child collections, or pages, within these nested routes so that, it's a nice way to do routing, where I can have a child page show up in the parent view. So let's go in and we've got our slug here. So this is our actual course.
Within the course, maybe we want to show let's do a slug. I don't know if this will be it or not, slug dot view, So we'll create a new folder for lessons and then we're going to create a dynamic route for the lesson. So we'll call that lesson dot view v comp ts. We're going to use the route, so route equals use route. And here I'm just gonna log the params to make sure we're on the correct spot.
Alright. So we can't find the slug dot view. That's great. Let's add a component for that. Courses, 100 apps, lessons, managing versions, slug.
Let's just say this is the course parent. What does that get us? Alright. So we've got managing versions. We don't see the course parent part of it here.
Okay. So let's call this lessons. And now all we should see is course parents. But if I go in, and I can't remember the exact syntax, so let's go back to nuxt.com. We'll do the child routes, nested routes.
It is what? NUXT page. So we just throw this NUXT page in here And now we see this where we have Course Parent, and then we have our NUCs page which gets rendered inside that. So why are we doing it this way? Because we can actually fetch the lessons within this parent component and then, that will not re render, but then we can navigate within these individual lessons and render those over here on the right.
So if we do something like this where, let's flesh this out, we've got a, let's do a div. Alright. We could do, like, an aside maybe. This is the list of lessons. Then we've got another div.
We'll render that, and we'll just flex these. Flex. We'll add, let's just maybe give this a fixed width of like 56 or 64 wide. Alright. And just to illustrate this, we'll give it like a background, a bggray100.
So we've got our list of lessons there. You Gonna add some padding for that, p 4. Then our next page, we probably got a new container. I need to actually look up order the the settings for that. Let's take a look at it.
You container, constrain the width of your content. Max width 7 x l. Okay. Yeah. Works fine for me.
Alright. And this could be probably height full. We want it to be the full height or height, screen. That'll get us. Cool.
Alright. So now we can go in and render the list of lessons here. So we will grab our lessons. So let's do const lessons equals await. You got GitHub Copilot for the win.
We're gonna read the items from the lessons. The filter is gonna be where the course dot slug equals route dot params.slug. So we're gonna pick up the we can pick up the course from the course slug. And let's actually see what we get here. I refresh the page.
We're breaking some stuff. Course, slug, route. Oh, we actually have to grab the route, don't we? Forbidden. So I can't get this information.
Wonder why that is. Let's take a look. We'll just erase that filter, see what data we're getting back, in this list of lessons. So I'm gonna quickly wrap that and, just output the lessons. Okay.
So there's our individual lessons. Looks like those are actually coming through. Okay. But if we go to course dot slug is equal to, Let's just take a look at our network request. Alright?
This is being fetched on the server side. You don't have a permission to access this, which makes me think something is wrong. Alright. So let's just look through this really quickly to see if we can find our module. Duh.
That's because our modules are not, our lessons are not actually linked to our course, they are with our modules. So the the courses belong or the lessons belong to the individual modules. So, fun debugging issue here, but let's just go into our original index where we get the course, we get our lessons, and we can actually copy this code. And within the lesson route, her lessons where's our parent component? We could do this.
Right. And here we could probably adjust it where the slug or the course, slug. So we get all the modules instead of the lessons, which is a little bit counterintuitive. But we still wanna display all those modules within that bar anyway. Alright.
So we get our modules. We'll change this to modules. And we'll make sure that we grab the lessons as well. Let's just clean this up and refresh. Something is breaking.
We don't get our modules. Route is not defined. Again, silly me, silly rabbit, you have to use the route. Alright. So now we can loop through these modules.
And what does GitHub Copilot got for us? Alright. So we've got our different modules here, how to manage different versions, how operations can be completed. Okay, great. Nuxt link courses, route planarams.
Slug. And now if I am navigating between these over here on the right you can see that this is actually changing. You can see that we're logging the slug for that specific lesson. So if we go into the lesson, we've got route params. Lesson.
Now within this specific component we will call the lesson that we want. But what we'll do is make sure that we update this a little bit. And we're gonna use the lesson equals lessons if we just log lesson over here. Alright. Boom.
We can see that. If I go back, I wanted to make sure this doesn't shrink. Select shrink. 0. Alright.
And now on our individual lesson, let's actually set this up. So we got the lesson name. We got the lesson content. It's great. And then we will have a where it'll be an iframe?
For the YouTube Embed. And I actually think do I have do I have Do I have a video string for this? I don't. Generate lesson and video. What is the YouTube embed?
Alright. So we're getting the URL from YouTube. So if I log in to the lessons here, how do we transform this? Closing in on what? We're gonna cut this one down to the wire.
Right? We've got 6 minutes and 55 seconds here. How do we actually render out this lesson content? We are going to do, let's rely on chat GPT or get up copilot. Right?
Function to generate YouTube embed URL. Okay. So now we're gonna get youtubeurllesson.video. Let's see if this actually works. Cannot read properties of undefined split.
So we'll do if, oh, I know what it is, we've used video URL instead of video. A lot of these issues can be avoided by using TypeScript, but lesson let's do a v f. Cannot read properties index of undefined. YouTube video ID. So we need to take a look at Directus.
It's what's fun about doing these things live. You never know exactly what's gonna go wrong. And when you're under the gun, the pressure is mounting. Don't necessarily know. Can't even get the password right now.
Hallelujah. There we go. Alright. So in this case, we want to, accounting for YouTube URL variations. Let's see what this thing comes up with.
And you to that be Will this actually work? Hey. There we go. Alright. We probably want to do, like, an aspect ratio, aspect video.
Okay. And so now we have our content. We could do a v if, so there is no lesson content here. And boom. So now if I click through these, we should be loading our different courses.
So at this point we are at 3 minutes and 44 seconds. So we did not get as far along as I thought we would get on the front end of this. But, going in and extending this further, one of the things that I would do next is probably taking a look at building this out and adding enrollments to this. So we could set up a like authentication for this so that, let's just say on the access control side of it, instead of allowing all the courses to be seen we could certainly let people view all the courses, see the instructors. But as far as the actual lessons, maybe we didn't want to give them access to that until they were enrolled.
But for anybody that was enrolled, they could see all of those courses. So now if I refresh the page, we're gonna see like an error that says, hey, this is forbidden because we cannot see this actual course. But if I were to go into my authentication page, let's just say auth/login and use my Directus URL or my Directus username and password. Go back a couple pages. Where are you?
Courses. Alright. We go into that and we can actually see those. And that's because this user is that user is now authenticated as the administrator and they have full rights. But we could set up a public and then a user specific role where we've got courses, we've got instructors, you can see all of that, where we want to see only the, like, courses that that person has access to.
So, let's just go with no access, like modules, that's fine. But as far as the lessons, you would have to set up like a a role here or, like a custom permission for this specific user. Taking that a step further, you know, we could get into like, making it work with Stripe, setting up some of the different, payment options. Like if we took a look at Udemy, there are, you know, trial options. You could pay individually for courses.
You could set up subscriptions for this. But, clearly we we built the back end of our LMS, but, really a little disappointed on how far we made it through the front end, considering I've actually built several of these systems myself. But we've definitely learned a lot. So if we take a look at our list of functionality, how do we do? We got to view a list of courses, we got to the individual courses, we got the individual lessons, We did have the authentication and login, but as far as tracking progress, and enrolling for a course, I give myself, what, 4 out of 6 or 4 out of 7.
Not a stellar effort here. So that is this lesson of 100 Apps, 100 Hours. I hope it's been a great example of how quickly you can build functionality like an LMS inside direct us and with front end tools. But there is a reason these other products exist like Podia, Kajabe, Udemy, Teachable, all of those. Rome wasn't built in 1 hour.
So hope to catch you on the next episode. That's all I've got for you on this one.