I built my Course Platform in Sveltekit. Here's how!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi I'm Brad cyer and I recently soft launched courses. Brad cci.com it's an education platform a course platform where you can take online courses uh built by yours truly and I built the entire thing in spel kit we're going to talk about how I built it today okay so first things first let's get into the website so you can just kind of see what we're working with so it's courses. brad.com I'm not a designer it's not the prettiest thing I'm more focused on the content so uh you land on this page if you're not signed in so let's say I'm not signed in um if you you land on the homepage uh you can see information about the courses these are all Customs spel kit pages so one per course I'll walk you through the source code in just a second um and then I'm using flowy uh it's a component library for SP um to help with some of the niceties that you see here uh there's an outline on what you can expect to learn um this is the only only course I have listed right now it's completely free and it's focused on teaching people who have very little experience or none ideally uh enough HTML CSS and JavaScript to get a web presence established so you would build your own um web page and get it deployed through netlify for free um if you are an experienced programmer this is probably not your cup of tea but if it's you know if you have someone that is interested in deploying their own web page maybe send it to them it's free to take um and as you can see down here take this course entirely for free so if you click this it'll prompt you to sign in if you've already signed in uh you will be taken to my courses um but if you've already signed in on the courses homepage here and you hit take this course entirely for free it will take you to the course so there's a couple different things going on here uh there's this sidebar that popped up um you now see that there's a my courses section now that we're logged in and we pull a little bit of information from GitHub um right now github's the only off provider but that may change and I'll show you how I set up off here in just a minute as well so now that you're here you have one of three different options you can land on video content text content or quiz content so the type of contents right here little tool tip that tells you a little bit more about it you can play the video right here all of my free courses are the videos are hosted on YouTube so they're also available on YouTube um but you get extra things like this notes section and quizzes if you sign up to take them on course Brad cipher.com okay so there's some notes these are markdown um I write these and put them directly in mongodb and then I pull those from mongod DV and render it to the HTML uh to the Dom sorry um through a markdown renderer in JavaScript okay there are these little navigation bar items that help you migrate from lesson to lesson um you can also click on them over here to change and then it's somewhat responsive that's maybe not the best uh example but um yeah a little bit to clean up there I guess you have a quiz uh so for example under Which element would you put a title tag let's just say the head tag hey that's right uh HTML tag is commonly has the two children elements what are they left and right ah not quite those don't actually exist so the whole point here is that you get some rapid feedback to help you learn you're not blocked on quizzes you're not graded on quiz is you don't have to take them you can skip right past them but the idea here is to kind of help reinforce what you have learned so far um and then more video content for this one there's not actually any pure text content but if it was it would look like this uh except you wouldn't have this video block right here okay um there's some information here this is just a static page this actually links out to Brad cyber.com my blog and then pricing information free courses paid courses uh there are some paid courses in the work they are not available yet the plan is that those will integrate with stripe um so that's halfway cooked at this point so I probably won't walk through that today but I'll show you the rest oh and then also courses have tag so you can filter absolute beginners this one's an absolute beginners one so that's available if you search for Zig there are no courses that match your search filters that's basically it so let's walk through the code okay okay so I mentioned this is a spelt kit application let's make this a little bigger and let's start with package Json because there's a couple things I pull in that we want to make sure we go ahead and talk about uh let me make this a little bigger for you okay so we have our dependencies um this I just scaffolded out a new spel kit project this uses spelt 4 excuse me with spelt kit 2 and um yeah most of these come from scaffolding out that spel kit application uh flowy is the component library that I mentioned flowy spelt are spelt bindings for it um spelt spelt check TS lib v v test uh dependencies I pull in a mongod DB adapter for OJs I use OJs for spelt kit I pull in a new font um I pull in some icons from flow BTE marked is the markdown renderer that I mentioned so it takes in markdown and renders it to H HTML mongodb is the connector to mongodb and then stripe for stripe management um we won't get into the stripe stuff but we'll touch on everything else really quick your spelt kit application starts with a couple different things uh it starts with um app HTML app CSS and app. D.S uh I haven't actually made any modifications of this at all which means that this doesn't have a favicon it has the default spelt favicon so I need to replace that at some point and I will next we have our server hooks um I hope I can yeah GitHub client GitHub secret okay nothing should be hardcoded good good good uh okay so we pull in spel kit off and this is off JS for spel Kit um I have another video on this I will link it uh if I can with a card right here if not I'll post a link in the video uh in the comments or the chat below the video description that's what it's called um it walks you through setting up spel kit off for GitHub so I won't go into that here I'll just mention that I've set up spel kit off the providers or GitHub and I'm using a mongodb adapter so this will take my connection that we'll get to in a minute and wrap it in an adapter pass that to spelt kit off and then that will manage my users in mongod DV session strategy database this is needed to uh manage your users in and they sessions in otherwise it' be Json web token um I have an event that I have so whenever a user signs in I create a course account for them and and then I also ensure that the settings exist for that user um so settings is another table I just I make sure that that data structure exists uh there are some callbacks uh and a es lint disable which is great um this session just adds a user object onto the session with the user details so I have access to that and then there's a redirect here so whenever you sign in I redirect you to slm uh which is my courses takes you to slm so these are the ones that you have access to okay that's the off part in a nutshell um in the lib I've got a couple components there's like a courses list component with some hard-coded tags uh some conditionals this is basic spelt there's not a ton going on here um but it this component is this list right here so the cards that you see um and then I have a store that I have with this is used for quizzes um and part of the reason I chose a store was that this was really nice and easy to manage in the quiz component itself but in the lesson component when you change so for example you're here you hit a couple things you go next and you go back I wanted these to clear so to clear them when you navigate away from this page I clear the store so that's why I chose a store there it's a little a little bit of a weird Edge case um but it felt like a cleaner way to manage that so I just pulled everything out to a store uh so here's the quiz that I was talking about you can see that it pulls in the quiz store um it just checks to see if the answers are correct uh a little bit of managing that here uh I have an error spell component that I have um so if I catch an error for whatever reason from the server and that makes it to the user I give you a nice little emoji that indicates something went wrong uh for 401 403 404 500 they get custom ones otherwise you get this embarrassed face because that's how I feel now I've got a markdown block uh this is really cool I was really thrilled with how marked Works um so you pull in the renderer and you pull in marked you technically can use a renderer that they ship with I created a custom one because I'm using tailwind and I want to add Tailwind classes to the rendered markdown so for example I have overridden The Heading method here um so if it's an H1 it gets text 5 L if it's an H2 it gets text 4L and so on and so forth and you can kind of see how that's managed here paragraphs get margin bottom text large list items get certain list formatting uh same with sorry lists get certain list formatting list items get certain list formatting um links get formatted a certain way namely links I wanted to make sure that the target is always blank uh so that's how we take care of that uh and then you create that courses renderer and then you use that with marked right here so um we use this felt directive here uh and this is um H directive might not be the right term for that now that I think about it uh but I'm going to call it a directive because I've got angular uh angular experience a long time ago that says this is a directive um so yeah I use this HTML directive uh and then I pass in the um result from calling marked on that markdown string from the database and I say from the database it gets passed in to the component um I try to make these not aware that there is a database okay so we have a couple different things uh so I have every thing that I write to the database I've got three layers for or three pieces for I should say there's a model it's usually an interface um in fact it's in this application it's always an interface but this is a very common pattern that I use I have a d this is a data access layer that takes those models or takes data and uh writes the model to the database or takes data and reads a model from the database so in our case we have get course account which takes in ID of a string and it returns a promise of a course account or a null um and this just uses uh so this is really basic so we say course account collection find one I pass in the object ID uh save account replace the object ID it upserts were available and then lastly I've got a service so the service is what the rest of my application uses to facilitate communicating with that so just really quick one more time this just models the data and might have some functions that help uh access certain pieces of data the D is what actually interfaces with a database and then the service is what makes those database interactions meaningful to my application so this is where like you would do business Logic for example I have a method called ensure course account exists for user so I get the course account and if it's null sorry if it's not null I return it otherwise I create the course account and then I fetch it um this basically is just to ensure that a user always has a cour course account since that's where their licenses for courses are added and then you can see down here I have a method called register license for course account so some of the methods here are passrs like get course account just calls db. getour account um but some of them are a little more business uh problem focused business logic focused okay uh I do the same thing for courses here's what they look like um these are a little bit more involved they have a little bit more of a complex type uh mainly because a course item um well let's see video content course here we go so a course has content which is a course item and a course item can be video content quiz content or text content like we already mentioned so with this in mind we have to model text content quiz content and then you have quiz questions so you have the text the correct answer and then the answer choice you have the text for the answer choice and the reason that that one is or is not correct uh type Union here for a BC or D string literals to for the answers and then video content which is a video link and text content uh this one might change a little bit when I look at hosting um non-public videos uh but for now with public videos this works really well okay uh same thing with course service um this is really light there's a couple things get all courses get courses for user uh get course for user um and then the course d uh which is again just um working with cursors here um and getting getting the course by an ID uh not a ton there uh really basic I pull in this should be dynamic um right why is that I'll sort that out later um so this client uh I create a new client here with a URL I connect to it and then the default DB that I export is my courses database uh licenses it's the same thing I'm going to skip over the rest of these um settings stripe yeah there's nothing else to go into there stripe has a little bit of interesting things going on but I don't want to show that at this point in time so I'm going to skip that especially since we don't have paid courses um I do have a Services file I generally just knew these up and Export them here it just makes it a little easier to manage uh using the same instance um this isn't maybe the cleanest thing but it works so it's been sufficient for my needs uh I guess one thing to add is like this is um this building this in spelt kit took like a couple days which is fantastic it's one of the best selling points in my opinion okay we have a couple different things going on uh let's start with the window I declare the interface of window I add gtag because I'm using Google analytics uh so I have to declare this for typescript that's it uh if we look at the layout we can see a couple different things so the layout has this navbar uh with Brad cyber.com if you have a session we go ahead and show the Avatar component and the hamburger um and this is from mobile uh and then you can see the drop- down headers here um and then the nav UL so in our case we have all courses if you're logged in you see my courses about the blog pricing um not a ton going on there and then a little bit of uh extra extra steps I guess to get GTM going so that I could add my Google analytics to the applications so you have to use spelt head and there's like a whole uh if browser check up here I also have a video on this I'll try to post a link in the description to that if you're interested on setting up Google analytics for your spelt video it'll show you exactly how to set it up okay uh I do a little bit of uh server layout stuff mainly I call event locals get session so whenever I try to load the layout I have access to the session object um that gets returned on the page uh it just makes it easier to do things things like this and that's the whole point of that um and hopefully you're familiar at this point but if you're not this layout spelt file just this is the spelt file that determines how this lays out it runs on the server for SSR and it runs on the client this dos server. TS file only runs on the server fairly straightforward but worth calling out uh here's that error page that we saw at the beginning we can trigger it again really easily because if you go to my courses and then you hit sign out and you hit yes please log in to view this content that's it that's what you're seeing um if you don't provided an error message it says something went wrong please try again just a backup just in case and then I render that error code Emoji based on the page status so you can see here uh we have what a 401 so we're getting the lock in key oops not one note that is not what I wanted to click okay uh the p page so a little bit of stuff going on here uh a to-do of course because no project is complete without a to-do and I just fetch the courses from the course service and then I return them so you can see courses. map um there's one weird odity that I had to do that I didn't find a great solution for I need to revisit this this looks really weird uh so this ID is a ID and I can't serialize that and anything you return in this load function has to be ser serialized and you can't serialize a ID so um what I ended up doing was to preserve my types I just undefine this one and then I added a new ID property uh that is two stringing the ID which returns it as a string instead of a object ID little weird there's probably a better way to do this I need to look into that a little bit more and then page spell so this is the stuff that goes in the layout so this layout will be present on every page since it's a a root level layout but this file will only be when it matches so how do we get a match um in our case this is the root level route so that would be this one this right here renders what you see on the screen and a good chunk of that is via the course list component that we've already talked about okay now I've got a couple different components uh pages in components so I've got page. spelt for the about page this is just about information um there's nothing special going on here uh you can see a couple different things there's some stripe stuff here this is a function I don't think I can show this right now because it's a work in progress and some values that are there shouldn't be there um this is a server side function so this is going to be uh like uh whenever you do a post for example for web hooks uh to stripe um like that's how that's handled uh so build your own web page is the one we are looking at there's two parts here um so let's click on this so this page right here this is what you see right here uh a lot of Tailwind uh some flow bite stuff and some markup it's there's not a lot going on here um that's it now the server side is a little bit more complicated this is a hardcoded ID uh this will probably change um but for now I only have one course so it felt fine this is the course ID in so what I do here is I take that course ID and instead of needing to pass it from the client I just have access to it here I get the session uh if it's undefined I send you to sign in we saw that happen um and then uh I get your ID I ensure or sorry I get your course account we've already insured it at this point um and then I check if your course account has a license so if it has a license for this course ID I redirect you to the course I go ahead and just take you right into it if you don't have a license we generated a new license for a course uh the price is free for this one um and then I uh register the license for that course using the course account service and then I redirect you to the course so ultimately you end up there either way uh okay the pricing page it's literally just static content it's tailwind and HTML nothing special going on there I skipped over my we'll come back to my in just a second um register is this used am I using this I'm not sure where I would use this from no this is throwaway I need to delete this I'll get back to that okay and then there are two other courses that I'm working on at the moment so here's the spoiler for that I'm working on a SP fundamentals course and then a spelt micro blogging course um these are Pages these courses are set to unpublished in my database so they won't show up um but you can look forward to those in the future okay the one we skipped over was my so my is the authenticated experience there's a little bit going on here so I'm going to sign back in with GitHub um if you haven't signed in before this will take you to GitHub and ask you to authorize it since I have signed in it's it's a very easy authorization step excuse me okay so now that we're here uh you have access to your courses um that is this folder right here so there's a little bit going on here here's that page that we just saw again the course list does most the heavy lifting here uh and then there's a little bit of a server side component too this checks to see whether you are logged in uh if you're not logged in we give you a 401 um there's a way to do this with hooks that I've done on another project uh so basically offg guarding an entire set of routes um I need to do that because the way this is set up each page offg guards itself individually and it's it's just duplicate code that isn't needed um where did my go okay so we offard we checked to make sure you're logged in if you're not logged in you have to uh go log in so I send you a 401 error uh this gets caught by spel kit and takes you to the error page that we've already seen uh that uses the custom error Emoji widget that I wrote or component that I wrote um and then we get the courses that are available for the user and then we return those and again we do a little bit of uh massaging the data I guess is a a nice way to put it uh to remove the object ID from the return type uh there's a settings page we actually didn't talk about this mainly because the settings page is Tiny so let's go take a look at it this is it send me emails about new courses yes no submit about a being uh so yeah it looks like this um I do use forms here so there's a form result so I guess on that note let me go submit one so you can see what that looks like and I'll talk about how that works there you go okay so how does that work so we have a form we have two exports to This Server TS we have the load method so this loads the settings for a user uh or tells them to log in so hey if you're not logged in you got to log in um otherwise we return the settings very basic now we have actions so these are the form actions this one's a default so I don't have to set anything up on my page uh right here so if I was working with a form like this I would have to to set an a custom action if I was using one instead I'm using the default action so I don't have to set anything up there um spel has really great form support I would just urge you to either a read the documentation or B uh come back in maybe a couple weeks and take my spelt fundamentals course where we will cover that okay or spelt kit sorry that would be the spell kit course I guess anyways uh so I've got a default Handler here I get a request event I get the form data that gets passed in I get the session and then I get the user ID I now create a new settings object with allow emails for new courses and then I get the allow email for new courses is equal to on uh this is the data that gets passed since it's a checkbox so if it is on then we're good go ahead and save that and then return the settings um I should mention that we're not returning just the settings exactly we're returning result settings so whatever you return from this form will be available on the page on the subsequent reload of that page so you can see here if form. result and form is action data then we show this green box so we saw the green box if I refresh green box is gone um that's how you would handle that with spell kit okay so that's settings we talked about the my stuff course ID uh this is the most complicated I guess and it's really not that bad again we check to make sure you're logged in if you're not you have to log in I get the course for the user if the course does not come back you don't have access you don't have a license for this course so you need one this was this is maybe a little bit of a slightly weird behavior for free courses but I am going to follow this pattern um because this is what I want to do for paid stuff um basically having a license for it and then finally we return the course and we uh mung the ID the ID again um because we can't serialize it okay there is a lot going on on the spelt side for this we pull in our page from App Stores we pull in our page data we pull in some stuff from flowy uh we don't need this type anymore which is great uh we pull in our course item is qu quiz course item is text course item is video These are functions that we have already lightly covered um and then these are just type guards and typescript so this basically if I pass in a course item and this is true it means that course item is video content and and if I pass in a course item and this is true it's quiz content um you'll see how that gets used in just a minute it's just a helper Flow by icons just some iconography uh specifically these guys down here so these two arrows and the bars which are used right here for toggling the course outline okay uh course quiz is a spelt component so this is a a component that we use to render quizzes the quiz store that I mentioned previously um and uh I'll show you how we clear that in just a second after navigate which is a navigation hook that we have access to which we'll use to clear the quiz store that we just talked about and then uh MD block so this is a component a spel component that I wrote that um we've talked about already this is the one with a custom renderer that renders our markdown for us export let page data um this is the data that gets passed in to the page from the server sorry that might not have been clear so I wanted to clarify that and then uh we have some reactive code here so we have a page pram um we basically so we can talk more about this in spell again if you haven't done the spelt document or the tutorial yet it's fantastic if you want to learn spelt I highly recommend it um this is reactive uh so this is this is letting our um I'm sorry this isn't reactive this is derived which I guess in theory also means it's reactive so this value page peram is derived by the contents over here so if these contents change then this needs to be recalculated um that's what this means so in our case we have an active URL if this stuff changes we need to update this and then spel itself is reactive so if these der values change it will rerender the pieces that need to be rendered for us down here okay so we have this after navigate function uh this again this is pulled in from spelt navigation it's a life cycle function that runs the supplied callback when the current component mounts and also when we navigate to a new URL okay so in our case we want to clear the quiz store simple enough get rid of all of the existing answers so it's clean for the next Quiz and then uh we also turn the side bar off if it's uh open now that I think about it this probably needs to change slightly um and then we default to letting sidebar open equal to false okay there's a lot going on here this is the sidebar um most of this is just doing flow bite components the way that they're intended to be used so I'm going to skim over these it's some Tailwind some flow bite flow bite has decent documentation if you want to learn how to use flowy components with felt so I would maybe recommend checking that out um but yeah we render each of the sidebar items that you see over here that's that okay we have a section some Tailwind here this is the course name this is always up there on every single page and then you have the active content name so you can think of this as kind of like the active lesson um and then I do a uh if active content is not equal to null so basically we check to make sure this active content is not null I don't think there's a scenario where it ever could be so maybe I could tighten up the types here um and make that a little clearer but if it's a video then we give you a badge with a tool tip and an iframe if it is text content then we render the text content there's also no badge for this so I probably need to add one and if it's a quiz we give you a badge and a tool to tip oh I'm sorry I see now why there's not a badge there I miss R part of this sorry if it's a video we give you the video badge the tool tip and then we iframe in the YouTube video videos can also have text content if they have text content we render that as well else if it's a quiz we have a badge and a tool tip that tells you about quizzes and then finally we have the quiz itself else if it's text we give you a badge that explains uh with a tool chip that explains that this is a literature type lesson um and then we give you the content there's a bottom nav here the bottom nav is a little wonky uh and you might say why so there's two items here if we shrink it down we get three so that was a little little strange um so the short and sweet here is we have our bottom nav we have our bottom nav items previous lesson there's one of our icons uh we have another bottom nav that's set to large Hidden so if you're on a large screen this is hidden this is the course outline button um and the reason why it's hidden is because the course outlines already over here it doesn't make sense to have it a button to trigger it over here but on mobile we're get a little tighter it becomes nice and then finally we have the uh bottom uh nav item that is the next lesson so it's the arrow pointing to the right there's an empty style tag that is obviously not necessary so we can get rid of that and I think that about covers it uh probably the most interesting thing that took me a while with that nav was I realizing that I could use Tailwind here to set the columns to two when this one is no longer visible uh and set it to three when it is visible uh really small thing but I I struggled to figure that out at first and I actually had two bottom navs and one showed when it was a certain size and one sh was another and it was just um not great but this actually ended up being a better solution okay I think that covers it this is about all the code that is used uh to show off my course plat form so hopefully two things one if you have thought about building something in spell and you've been holding off hopefully now you see how easy it is and how little code it takes to build something fairly substantial and then two you have just seen all the code for everything in my course site except for stripe integration you could build this you could literally go through the video and type out word for word exactly what I have okay again if you found this video helpful uh I always appreciate it if you subscribe so you can be notified of future videos and if you know someone who's learning is felt share this video with them I'd appreciate it they'd appreciate it you'd be everybody's hero I think that's it thanks and have a fantastic weekend
Info
Channel: Brad Cypert
Views: 1,885
Rating: undefined out of 5
Keywords:
Id: buDXNdpLt4A
Channel Id: undefined
Length: 35min 14sec (2114 seconds)
Published: Sun Mar 03 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.