[{"data":1,"prerenderedAt":438},["ShallowReactive",2],{"footer-primary":3,"footer-secondary":93,"footer-description":119,"100-apps-100-hours-virtual-events-platform":121,"100-apps-100-hours-virtual-events-platform-next":169,"sales-reps":186},{"items":4},[5,29,49,69],{"id":6,"title":7,"url":8,"page":8,"children":9},"522e608a-77b0-4333-820d-d4f44be2ade1","Solutions",null,[10,15,20,25],{"id":11,"title":12,"url":8,"page":13},"fcafe85a-a798-4710-9e7a-776fe413aae5","Headless CMS",{"permalink":14},"/solutions/headless-cms",{"id":16,"title":17,"url":8,"page":18},"79972923-93cf-4777-9e32-5c9b0315fc10","Backend-as-a-Service",{"permalink":19},"/solutions/backend-as-a-service",{"id":21,"title":22,"url":8,"page":23},"0fa8d0c1-7b64-4f6f-939d-d7fdb99fc407","Product Information",{"permalink":24},"/solutions/product-information-management",{"id":26,"title":27,"url":28,"page":8},"63946d54-6052-4780-8ff4-91f5a9931dcc","100+ Things to Build","https://directus.io/blog/100-tools-apps-and-platforms-you-can-build-with-directus",{"id":30,"title":31,"url":8,"page":8,"children":32},"8ab4f9b1-f3e2-44d6-919b-011d91fe072f","Resources",[33,37,41,45],{"id":34,"title":35,"url":36,"page":8},"f951fb84-8777-4b84-9e91-996fe9d25483","Documentation","https://docs.directus.io",{"id":38,"title":39,"url":40,"page":8},"366febc7-a538-4c08-a326-e6204957f1e3","Guides","https://docs.directus.io/guides/",{"id":42,"title":43,"url":44,"page":8},"aeb9128e-1c5f-417f-863c-2449416433cd","Community","https://directus.chat",{"id":46,"title":47,"url":48,"page":8},"da1c2ed8-0a77-49b0-a903-49c56cb07de5","Release Notes","https://github.com/directus/directus/releases",{"id":50,"title":51,"url":8,"page":8,"children":52},"d61fae8c-7502-494a-822f-19ecff3d0256","Support",[53,57,61,65],{"id":54,"title":55,"url":56,"page":8},"8c43c781-7ebd-475f-a931-747e293c0a88","Issue Tracker","https://github.com/directus/directus/issues",{"id":58,"title":59,"url":60,"page":8},"d77bb78e-cf7b-4e01-932a-514414ba49d3","Feature Requests","https://github.com/directus/directus/discussions?discussions_q=is:open+sort:top",{"id":62,"title":63,"url":64,"page":8},"4346be2b-2c53-476e-b53b-becacec626a6","Community Chat","https://discord.com/channels/725371605378924594/741317677397704757",{"id":66,"title":67,"url":68,"page":8},"26c115d2-49f7-4edc-935e-d37d427fb89d","Cloud Dashboard","https://directus.cloud",{"id":70,"title":71,"url":8,"page":8,"children":72},"49141403-4f20-44ac-8453-25ace1265812","Organization",[73,78,84,88],{"id":74,"title":75,"url":76,"page":77},"1f36ea92-8a5e-47c8-914c-9822a8b9538a","About","/about",{"permalink":76},{"id":79,"title":80,"url":81,"page":82},"b84bf525-5471-4b14-a93c-225f6c386005","Careers","#",{"permalink":83},"/careers",{"id":85,"title":86,"url":87,"page":8},"86aabc3a-433d-434b-9efa-ad1d34be0a34","Brand Assets","https://drive.google.com/drive/folders/1lBOTba4RaA5ikqOn8Ewo4RYzD0XcymG9?usp=sharing",{"id":89,"title":90,"url":8,"page":91},"8d2fa1e3-198e-4405-81e1-2ceb858bc237","Contact",{"permalink":92},"/contact",{"items":94},[95,101,107,113],{"id":96,"title":97,"url":8,"page":98,"children":100},"8a1b7bfa-429d-4ffc-a650-2a5fdcf356da","Cloud Policies",{"permalink":99},"/cloud-policies",[],{"id":102,"title":103,"url":81,"page":104,"children":106},"bea848ef-828f-4306-8017-6b00ec5d4a0c","License",{"permalink":105},"/bsl",[],{"id":108,"title":109,"url":81,"page":110,"children":112},"4e914f47-4bee-42b7-b445-3119ee4196ef","Terms",{"permalink":111},"/terms",[],{"id":114,"title":115,"url":81,"page":116,"children":118},"ea69eda6-d317-4981-8421-fcabb1826bfd","Privacy",{"permalink":117},"/privacy",[],{"description":120},"\u003Cp>A composable backend to build your Headless CMS, BaaS, and more.&nbsp;\u003C/p>",{"id":122,"slug":123,"vimeo_id":124,"description":125,"tile":126,"length":127,"resources":8,"people":8,"episode_number":128,"published":129,"title":130,"video_transcript_html":131,"video_transcript_text":132,"content":8,"status":133,"episode_people":134,"recommendations":149,"season":150,"seo":167},"b9f1d4cf-f53c-49db-9e87-adf7e3b9ff99","virtual-events-platform","936342561","In this 60 min sprint, Bryant builds a virtual events platform that's similar to webinar services like Zoom, Livestorm, and others. Can he build all the functionality like event scheduling, event registration, separate break out rooms, and an integration to Whereby?","d3bfe6e0-cfaf-409d-bf02-010b8c0aa6aa",64,3,"2024-05-03","Mission: Virtual Events Platform","\u003Cp>Speaker 0: Hi.\u003C/p>\u003Cp>Speaker 1: Welcome back to another episode of 100 apps. 100 hours where we try to build your suggestions or some of your favorite apps in 1 hour or less using Directus or publicly fail, die on the vine trying to build something. If you're new to the show, there are only 2 rules. The first rule is we have 60 minutes to plan and build, no more, no less. The second rule is the anti rule.\u003C/p>\u003Cp>Use whatever you have at your disposal. So then the brakes. Let's dive into today's episode. Right? We're gonna be building an events platform, a virtual events platform.\u003C/p>\u003Cp>Now that sounds to me very very vague, could mean a lot of things to a lot of different folks. But, what kind of products are we talking about? So Zoom comes to mind as far as, like, registering a meeting and performing that meeting. You know, maybe you've got some interactivity like a chat, some registration. You know, you've seen this beautiful, beautiful, beautiful registration page before, I'm I'm sure.\u003C/p>\u003Cp>Not knocking you Zoom, but, like, with all the engineers that you guys have, why is this the best that we get for a registration page? I don't like it. Other softwares that I've looked at, like, DemoHop is an interesting one that, you know, has, like, a science fair where each there's a bunch of different booths, some of the other ones, like, Livestorm. There's a ton of these different webinar platforms every way that you slice it. But what I've discovered and and just in my own experience with these over the years, it's nice to have your own.\u003C/p>\u003Cp>So that's what we're gonna be building today. We're gonna be building our own events platform. Let's run through it. We'll start the clock, pull up 60 minutes, and away we go. Okay.\u003C/p>\u003Cp>So, I am in FigJam here. Let's just kind of mock out our functionality that we like or what we're looking to see out of this particular app. So, as far as our functionality, this is a nice serif font. Maybe we underline that as well. Great.\u003C/p>\u003Cp>Alright. So what do we wanna do? We wanted to set up we can't have all of it underlined. That's not cool. Set up an event.\u003C/p>\u003Cp>You know, allow registrations. You know, have a public landing page, registration page. What else do we want? We wanna have chat during the event. Maybe we wanna have different rooms for the event.\u003C/p>\u003Cp>You know? You think a virtual event, it could just be just, hey. It's a workshop. It's a webinar.\u003C/p>\u003Cp>Speaker 0: Could also be\u003C/p>\u003Cp>Speaker 1: like a week long leap week like we do here at Directus. So maybe we have rooms for that. This this seems like I'm getting way in over my head, so we'll stop there. And then let's let's kinda just flesh out our data model for this a bit. So in my mind, what are we gonna have?\u003C/p>\u003Cp>We're gonna have events. K. And we got that nice purple color. Maybe we'll make this a little bit bigger so we could see it. So we've got events and then we're gonna have, users or attendees.\u003C/p>\u003Cp>You know, that's gonna be our Directus underscore users collection. We'll just pick that up. Directus gives us that out of the box. No reason not to use it. Amazing.\u003C/p>\u003Cp>And then I would imagine there's a junction table here, right, where we have could have multiple events. So these will be, like, registrations, maybe. Naming stuff is always the hardest part of development. So we got registrations, draw some arrows, just connect these, connect these in your mind as well. Great.\u003C/p>\u003Cp>And then what else? You know, if we jive with the rooms, you know, we could have rooms as well. Right? Each room has a different topic, some schedule, etcetera. If we think of, like, our events, they're gonna have a date, start date, duration, or maybe an end date,\u003C/p>\u003Cp>Speaker 0: title, description.\u003C/p>\u003Cp>Speaker 1: What else do we have? Registrations, obviously. Probably an image. Schedule, potentially. Cool.\u003C/p>\u003Cp>Alright. Getting too carried away. Anyway, let's actually start building something. Right? This feels just roughly what we're looking for.\u003C/p>\u003Cp>So let's dive into it. Right? What have I got set up for this? Just gonna close all these other things. I have a Directus instance.\u003C/p>\u003Cp>I've got a Nuxt 3 application, just a starter application set up. It's got a login and register function. I don't even think the register actually works on this thing, but that's what we're working with. Let's go ahead and get logged in to\u003C/p>\u003Cp>Speaker 0: our Directus instance. What\u003C/p>\u003Cp>Speaker 1: did I set the password to? I think it's just password. Real secure. Alright. So we've got a blank instance.\u003C/p>\u003Cp>I've got my FigJam over here on the left. Got Directus on the right, a 125% zoomed in, and let's just dive in. Right? Our first collection, we're going to start here with our events and we'll create a new collection for that. Let's give it a UUID, When it was created, when it was updated, those are all great.\u003C/p>\u003Cp>Let's do status as well for like published events, draft events, completed events. Let's just take a look at that. Right? So we have published, we have draft, we have archived. I could go in and flesh these out further if I wanted to or we could just leave it at that.\u003C/p>\u003Cp>Maybe draft is the default. That's good. Solid. Alright. So here, we're gonna have a name for the event or a title.\u003C/p>\u003Cp>The event title, that's just gonna be a string. A description of this, we want to be the anti zoom here, so we're gonna make this a text field using the WYSIWYG interface for Directus. We're going to call it description. That's our key. And I could adjust our toolbar if I wanted to.\u003C/p>\u003Cp>Maybe I want to add undo and redo. I'm not a big fan of underline, so I'll just remove that. If I need to, I can dive into advanced field creation mode, and specifically with the WYSIWYG editor, you can you've got some formatting options here and options overrides that you can pass to the tiny MCE instance if you need to. Like, you know, maybe you have a special class for buttons or something like that that you want to display inside that WYSIWYG editor. Alright.\u003C/p>\u003Cp>Let's add an image for this. So just a single image. You know, we could potentially have, you know, multiple images, but we'll just keep it simple. That's what we're going to show on a card. As I'm imagining this, on the front end, it's It's just like a running list of events or maybe a grid of cards for the events.\u003C/p>\u003Cp>When you get inside the event, you've got to register. Then once you register, maybe you can see the schedule for the event. Cool. So as far as something like a schedule, right, how should we set this up? If I'm not really sure, like, one of the things that I do a lot, I'm not really sure what this is gonna look like immediately.\u003C/p>\u003Cp>Instead of creating another table, I may use something like the repeater, which is basically a JSON type field. That's what it's going to save as our, inside our SQL database. But let's just call it schedule, and then I could go within this and edit fields to repeat, like a set of fields. So, I don't know. I like a speaker.\u003C/p>\u003Cp>It'll probably be something else. Let's just say the name or label. Okay. We'll do string. That requires a value.\u003C/p>\u003Cp>What's the name of this thing? And the interface would be an input and then we could do something like time, date, time, time. Start time, Start time. Sounds great. That's what we'll roll with.\u003C/p>\u003Cp>Let's make that full width and then we'll use the timestamp format. We'll require value. Maybe we don't require value. May not need to pick a time. Maybe we're just showing scheduling order.\u003C/p>\u003Cp>When does this thing start? Cool. Alright. Sweet. So now we have some events or at least the the start of something for events.\u003C/p>\u003Cp>Let's go in and go back to the data model settings. We're just gonna do registrations and we'll do, like, a date created, user created. Do we even I don't even think we need this. Status. Do we have a status for the registration?\u003C/p>\u003Cp>Maybe it's canceled or something like that. I'm reading too much into it. Right? So here's the date created. Maybe I wanna show this, so I'll click the drop down, show that field.\u003C/p>\u003Cp>Maybe we make it full width, just in case. And for our registrations, we're gonna start digging into our relationships. Right? So when I think of a registration, we've got the event that that registration belongs to, and we've got the user that made the registration. So those are both gonna be basically many to one relationships.\u003C/p>\u003Cp>So we'll go in for the key, we'll add the event, we'll just do events. And for our display template, let's use the title. I can already tell that I forgot to input my start and end date for the event. Amazing. So we'll have to go back and fix that as well, but there's our event.\u003C/p>\u003Cp>Let's go ahead and create our relationship to the users. So we'll say the user that's registered or, you know, this could be like the attendee. Again, naming stuff is hard. Cut me some slack. Alright.\u003C/p>\u003Cp>So I want to pick the directus underscore users collection. I'm just going to dig into the systems drop down here, or system, choose direct us users. I can get fancy with the display on this. Give them the avatar with a nice little thumbnail. We'll do first name, last name, and behind the scenes, this is just mustache syntax.\u003C/p>\u003Cp>So I can edit these raw values if I want, do it that way, or I could just pick from the drop down. That's great. We don't wanna create new users from this. We could potentially select users. Great.\u003C/p>\u003Cp>Okay. So that's our basic data model. Why do we have event? Some type of bug. Alright.\u003C/p>\u003Cp>Regardless, we have events, we have registrations. Let's go in and add our start time for this. How could I forget? So in this case, we wanna save the time zone value, so we're gonna use time stamp. This could be the start date.\u003C/p>\u003Cp>Maybe we have an end date for this or, you know, potentially a duration. I guess you could do either or or both. You know, you could potentially even use flows to calculate that if you wanted to. But this is good. Let's add some icons just to fancy it up a little bit.\u003C/p>\u003Cp>My friend Kevin on the team here will be sweating if you watch me add these icons, but, there's always room for design. Cool. Alright. So we got events. Let's create our first event.\u003C/p>\u003Cp>Right? What are we gonna call this? Let's call it Leap Week number 2 1000. Leap Week 2 1000. We are taking a huge leap forward for Directus.\u003C/p>\u003Cp>Join us as we take a huge leap forward for Directus. Lots of cool stuff. Let's add some formatting to this as well. You know, thing 1, thing 2, thing 3. Cool.\u003C/p>\u003Cp>Alright. And then let's add an image for this. Right? So let's just search Directus Leap Week and see if we can grab, do we have any images? Yeah, we got a couple laying around.\u003C/p>\u003Cp>Cool. So we'll save this image. Is that right? Hope that's the right image. We'll just upload that.\u003C/p>\u003Cp>Everything announced at our 1st Leap Week. And let's set a start date for this. Let's say May 15th is a good date. Maybe this runs 2, 3 days. Yeah.\u003C/p>\u003Cp>Great. So for our schedule, we've got workshop with Bryant. That is the I forget what day we did this now. 17th. Oh, that's it.\u003C/p>\u003Cp>May 17th. Not April 17th, Brent. Alright. Let's say that is at 3. Great.\u003C/p>\u003Cp>Cool. So I'm gonna fill out the schedule workshop with Kevin. Go for 15th at, at 16th at 1 PM. Great. Alright.\u003C/p>\u003Cp>So now we've got our event. Maybe let's just drag this out a little bit, move this around. Okay. One of the other things that I forgot here is probably like a slug. Right?\u003C/p>\u003Cp>I want a nice URL on the front end. So we'll go back into events, we'll just create a new field for it, we're going to use the input, call it slug. The other thing that I'm going to do is go in and make this URL safe, so we could slugify this thing. Now one of the other cool things with the addition of the marketplace, let's look for a slug, Permalink interface to enter URL slugs. Is this what we're looking for?\u003C/p>\u003Cp>No. WP slug interface. Yeah. This is cool. Alright.\u003C/p>\u003Cp>Let's try this out. We'll hit refresh, install this extension. I didn't have this on the first season of 100 apps, 100 hours, But now I can go into our interface and we can choose the slug interface. And our template is going to come from the title and we'll auto generate on create. Let's try that and see what we've got.\u003C/p>\u003Cp>We shrink the title and the slug. Cool. Alright. So now we got a couple settings. We can hit auto generate.\u003C/p>\u003Cp>Generates this nice slug, and it looks a lot like the old days of WordPress. I mean WordPress is still around, obviously, but this is this is nice, I like it. Alright. So we've got events, we've got registrations. What do we do next, right?\u003C/p>\u003Cp>We're gonna look at access control. So this is our settings of who has access to what inside Directus. There's 2 default roles, public and administrator. Let's make the public roles, we want to be able to view the events, We don't want anybody to edit events. And then, for as far as registrations, maybe we want to be able to create registrations and then maybe just read, you know what?\u003C/p>\u003Cp>Let's give full access to that. And, that should be maybe what we want, enough to play around with. Right? So now what are we going to do? Right?\u003C/p>\u003Cp>Let's dive into the front end of this application. Alright. So I'm going to pull this up. Again, I've just got a a Nuxt application here, and let's flesh out a couple of routes. Index dot view.\u003C/p>\u003Cp>We can make this just a list of our events. Alright. So we're gonna go in. I've got Directus configured as a Nuxt plugin here. It's just got some authentication in it.\u003C/p>\u003Cp>We've got real time in case we ever needed it. And then we've just got the REST client. That's the way I prefer it. And we just provide that to the Nuxt application. So we'll call that up like this, const directives equals use Nuxt app, and in this case, we're probably gonna do, like, read items as well.\u003C/p>\u003Cp>So we'll grab that from import items from the atdirectus SDK. And, alright, what's next? Right. So we're gonna do some data. We'll use await, use async data, and we'll do return directus dot request read items.\u003C/p>\u003Cp>And we're reading from the, what, events collection. And then I have a, query for different parameters. Right? We wanna show maybe we only wanna show published events, right, so I could do this. We'll just say status equals published.\u003C/p>\u003Cp>Thank you, GitHub Copilot. Let's see what that gets us. Directors. Cool. Maybe we scrap all of this.\u003C/p>\u003Cp>Would do we even need this to be flexed? Probably not. Alright. Let's see what we got. Data.\u003C/p>\u003Cp>Just wrap this in like a pre tag. See if we get any actual data. Right. So we've set up this filter. We're not getting any data because I'm assuming we did not publish this event.\u003C/p>\u003Cp>So if I go in and publish this, let's see what we have now. Okay. Cool. So we have our event information. What can we do with this, right?\u003C/p>\u003Cp>So I do also have the, like, Nuxt UI library built into this. So if you just go to ui.nuxt, using that, it's got, you know, some components already built in for buttons and cards and things like that. Cool. So without getting too fancy on the UI, let's just mock something up now that we can see we've got some actual data. Right?\u003C/p>\u003Cp>We got a diff. We add some padding. Y 12p y 6. We add a, do we even need an h one? Let's call it h 2.\u003C/p>\u003Cp>This will be font bold. Text, let's make the text large. And let's make it mono, font mono. Alright. So will be events.\u003C/p>\u003Cp>See what we got there. There's our events. And now let's do a grid. Right? So we'll just do a div to wrap this.\u003C/p>\u003Cp>Just grid, maybe grid calls to like a break point, maybe we set the gap to 8, and then we're gonna do a v 4. Right? So, for our events, let's just use this card. I think there's a card component in here. I feel like a nice card component is ucard.\u003C/p>\u003Cp>We've got, like, a header and a footer, or we could just throw it in the middle. So let's do that. Right. V 4. We're gonna do events and data.\u003C/p>\u003Cp>You know? And, usually, I like to destructure those.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. Maybe we call it events.\u003C/p>\u003Cp>Speaker 1: And for the key here, we'll use event ID. That way, we don't have to, use, like, an index or something like that, and then we'll actually do this. Alright. So is there actual I think there's, like, a text component here, so let's just do, like, an h three. Maybe we just take the auto completion here from, GitHub Copilot.\u003C/p>\u003Cp>We'll do a little Nuxt image to optimize these images. Works very well with Directus. There's a provider for it. Then we've got the event dot image. We use the event title.\u003C/p>\u003Cp>That seems pretty good. Let's see what we come up with here. Sweet. Except for our description. Right?\u003C/p>\u003Cp>This kind of sucks because it is not what we want. So in this case, we'll do div. Let's do v h t m l event dot description, and remove that. Great. It has no formatting.\u003C/p>\u003Cp>Maybe we've got, do I have to tell when? We got pros class. Yeah. There we go. Alright.\u003C/p>\u003Cp>So we've got that, and let's add a link for this. Right? Maybe we add a little spacing here, n t 4. Maybe we make that add a little spacing here. MT2, MT8.\u003C/p>\u003Cp>It's great. Cool. So now we've got an event looking really nice. You know, I could duplicate this event if I wanted to. Let's just see what this looks like with a couple of more events.\u003C/p>\u003Cp>We're going to call this Leap Week 3000. Flush this out. And what I'm going to do here, we're just going to open this image up. I'm going to get really fancy on this, guys. And we'll just mark this up.\u003C/p>\u003Cp>Scroll this out. 3000. Oh, so fancy. Alright. For Leap Week 3,000, we're going to close this out, upload a new photo, we can see that's Leap Week 3,000.\u003C/p>\u003Cp>And then what I'm going to do here is click Save and Copy from the 3 dot menu button and now I've got 2 events. We've got Leap Week 2000, Leap Week 3000. There we go. Amazing. Okay.\u003C/p>\u003Cp>Alright. So now we need a route for our actual event. Right? So let's set this up. Let's actually add a folder for it just in case we have a couple of other routes within that.\u003C/p>\u003Cp>And then for our events, we're gonna have a slug dot view. So that's just gonna be a dynamic route. We'll view I think it's init. View init. What does that give me?\u003C/p>\u003Cp>That's not the syntax that I like. It'll do. Trying to make it fancy. I need to get my snippets set back up. I had to scrap Versus code.\u003C/p>\u003Cp>Lost all my snippets. Alright. So we got a slug. We're gonna use the route here. So we've got route equals use routes.\u003C/p>\u003Cp>Cool. And then, on the individual page, right, we want to call the specific data. And, you know, maybe we don't even need this full description on our index page, so we could, you know, potentially leave that off. Maybe we just make it the event start date, but we may wanna use that in here. And let's use just, like, a date function.\u003C/p>\u003Cp>I think it's format from date f n's. Formats\u003C/p>\u003Cp>Speaker 0: oh,\u003C/p>\u003Cp>Speaker 1: not currency. I'm gonna do format. What we got? DDD, month month, DD. Where does that get us?\u003C/p>\u003Cp>Doesn't get you anywhere because it breaks it. Forgot the year year. I guess it's all lowercase. And then okay. Yeah.\u003C/p>\u003Cp>Still still got a ways to go memory wise. So date f and s, just a handy library. We got format. What's the date? What are we looking for?\u003C/p>\u003Cp>Like, month is gonna be m m m. Like that one. So we'll do m m m. And what if we do, like, the ordinal of the dates, day of the month,\u003C/p>\u003Cp>Speaker 0: d o?\u003C/p>\u003Cp>Speaker 1: That's what we want. Now we're looking nice. Okay. And then we wanna do the time. Right?\u003C/p>\u003Cp>So maybe we show the time. Local day of the week, AM PM. What's this gonna\u003C/p>\u003Cp>Speaker 0: be? H. What minute? M\u003C/p>\u003Cp>Speaker 1: m m. And then we've got AM, PM, AAA. Wasting too much time on this. Okay. Alright.\u003C/p>\u003Cp>So we got that. Let's go in and just wrap this card in a NuxLink, or at least the content of the card. We'll do 2. And let's make this dynamic. That'll be /events/ event.slug.\u003C/p>\u003Cp>Uh-oh. Oh, forgot to close that. Cool. Alright. So now we've got the links set up, so we can click on these and and move to the route.\u003C/p>\u003Cp>We don't really have anything inside our route though. Right? So I could take this function here, just copy that in, and here we're still going to be reading items. Right? I'm going to call this event, in addition to status.\u003C/p>\u003Cp>I guess we can set this up where if you call it directly, even if it's a draft, maybe you wanna see it. It. So let's just do this. We'll say slug equals route dot params dot slug. That should get us the event that we want.\u003C/p>\u003Cp>And then, the actual event here, we're gonna do, like, a transform function. The nice little part that's baked into the use async data call, that composable inside Nuxt, where basically this is gonna return an array. I could transform this and just pick up the first item of this array. And now if we do this, just do pre class. Again, I always like to do this just to see what data I'm getting back.\u003C/p>\u003Cp>And now we've got just the specific data object for this item. Alright. What's next? Right? We're gonna dive into building this actual page.\u003C/p>\u003Cp>Right? So let's give it a nice h one tag. We've got font bold, font mono, text let's make this giant. We're gonna do the event dot title. Okay.\u003C/p>\u003Cp>Leap week 3,000. What else do we have? If I dig into the clipboard, I've got that event description. We can make this pros large. We wrap that.\u003C/p>\u003Cp>Let's move down. We'll do another div. Throw this into an image, event\u003C/p>\u003Cp>Speaker 0: dot image,\u003C/p>\u003Cp>Speaker 1: class w 96. Where's our actual image at? Okay. There's our image. We could flex these 2, add a little bit of padding.\u003C/p>\u003Cp>I mean, this is not gonna win any design awards, but, hey, it'll be alright. We'll just take these items from the first one. Let's put this below. Do we have our dates? Oh, gotta actually import that function, don't we?\u003C/p>\u003Cp>Import format from datefns functions, I guess. Alright. So we got our date. Add a little margin to the top. Looking great.\u003C/p>\u003Cp>And then what if we wanted to show that schedule? Right? So, maybe we drop down over here on the left. We have a list. And within the list, take a little GitHub completion.\u003C/p>\u003Cp>That's not really what we're looking for. It's gonna be the item inside the event dot schedule. For the key, we used you the item dot label. And then we have yeah. I was wasting more time using the GitHub suggestions.\u003C/p>\u003Cp>Item dot label. Item. We'll just steal this format, and can we do this as well? Dash hmmaa. What does that give us?\u003C/p>\u003Cp>Does that get us where we want? Okay. So now we can see the schedule. We can drop a little schedule class. List.\u003C/p>\u003Cp>List inside\u003C/p>\u003Cp>Speaker 0: list disk. I guess,\u003C/p>\u003Cp>Speaker 1: is that the\u003C/p>\u003Cp>Speaker 0: list item? Why are we why does that\u003C/p>\u003Cp>Speaker 1: look weird? Not sure. Not gonna bother with it. Let's do font bold, font mono, empty 8. Okay.\u003C/p>\u003Cp>Cool. What else we got? So we got the schedule right. Good. We got a page.\u003C/p>\u003Cp>We could go back to our events. Yeah. Maybe we add a link for that up here somewhere in the top. Next link to equals slash slash I think it's\u003C/p>\u003Cp>Speaker 0: just slash.\u003C/p>\u003Cp>Speaker 1: Back to main list of events. Alright. Cool. We got all this going on, we could go into Leap Week 3000, we could go to Leap Week 2000. How are we doing on time?\u003C/p>\u003Cp>We are cruising on time. Alright, so how do we set up registration? Let's go in and what we're going to\u003C/p>\u003Cp>Speaker 0: do, we're going to need to\u003C/p>\u003Cp>Speaker 1: basically authenticate the user first. So we've got to have a user to register. And I am going to cheat here and use, like, a SSO, like, register with GitHub. I've used this in a past project. So I've just got some code over here.\u003C/p>\u003Cp>We're gonna go in and inside my env or my Docker Compose file, I would set this up. I'm missing the client secret and client ID from GitHub, so go inside my GitHub account. We're gonna go to developers, we'll go to applications, is it authorize no. Developer settings, OAuth apps, we're going to create a new OAuth app. We'll call it Events Platform.\u003C/p>\u003Cp>Super secret. The homepage URL will be HTTPS. We'll just use Directus. Do we need we need this callback. And just from past experience, the structure for this is gonna be my directus instance slash auth /login/provider.\u003C/p>\u003Cp>So we're using GitHub, that's gonna be the provider. We're gonna use that callback. Great. And then I can get my client ID. I can get my client secret.\u003C/p>\u003Cp>We'll just have to use a password, log in, get the secret, they'll steal my secrets, bro. Okay. And then the last piece of the puzzle here is we need a default role for that, for that specific user. So what we're gonna do, we'll go into Directus. Let's create a new role for attendee.\u003C/p>\u003Cp>And we're not going to give them app access, we're just going to save it. I'm going to pick up the actual ID for this role, I can see it over here. That's the primary key. We'll paste that in there. I'm going to save that, and then I'm going to stop this container, Docker Compose up one more time, and the effect I should get if I load this again, now I can see this login with GitHub function.\u003C/p>\u003Cp>So I could actually log in with GitHub, and let's see. Oh, yeah. I'd have to actually do my username. Maybe we swap this out. Alright.\u003C/p>\u003Cp>We get a local host, 8055. Log in. Admin, example, password. Alright. And then I log out here.\u003C/p>\u003Cp>We could test this out. But I could also do this on the front end as well. Right? So let's go back to our page. Alright.\u003C/p>\u003Cp>So I'm gonna stick a big registration button over here somewhere, and I think I've got one in register. Is it the register form or the login form? Do I have it? Yeah. There we go.\u003C/p>\u003Cp>I got this button that I've used previously. We're gonna go to our event page. We'll save this. Uncomment this button. I don't really need the divider label.\u003C/p>\u003Cp>Alright. Signing with GitHub to register. Cool. And here you can see I've got a redirect, just basically to a different page. We're going to make this size excel, giant button, just add a class to it, give it a little more padding.\u003C/p>\u003Cp>Duplicate Attribute. Oh, MT 12. We don't want to duplicate that class. Alright. So what is this going to do?\u003C/p>\u003Cp>This should ask me to log in once accessed to my GitHub account. We authorize, and now I've got, like, the index page. So if I go here, if I actually if I signed in, right, probably getting some errors here because we did not give permissions to those specific accounts or that specific role. Right? So for our attendee role, we want them to view all the events, we want them to create registrations, maybe they can view the registrations where the user equals current underscore user.\u003C/p>\u003Cp>Alright. We're gonna have to run like hell here. Apologies for the curse words. Right? So okay.\u003C/p>\u003Cp>If you I look at my Nuxt state. I've got a use state composable that's actually storing our user data. So I should be able to see that user data. I've got the ID of the user. Do I have any other information about it?\u003C/p>\u003Cp>Maybe that's really all I need. Right? So let's try this. We'll do const user equals use state, user. Will that give us access to the user?\u003C/p>\u003Cp>And if we have a user, we can hide this button And maybe if if no user. Okay. And then maybe we add another button. Register. And on this one, we're gonna add a click handler.\u003C/p>\u003Cp>It's gonna be register for event. Okay. How's that looking? V f user. Cool.\u003C/p>\u003Cp>And now we need a function for that. Right? This is gonna be an async function because we're gonna call directus. We are going to import, we need the create item. Okay.\u003C/p>\u003Cp>Async function, register for events. We're gonna call that. K. We'll do a try. Await direct us request registrations event dot ID.\u003C/p>\u003Cp>That's gonna be an actual reactive value, so we'll just add on that. We can add that around the user too just to be safe. And cool. What else did we do? Response.\u003C/p>\u003Cp>You know, I could just console log this response just to verify. Alright. So let's take a look. If I clear out my network request, we hit register, We've got registrations. Cool.\u003C/p>\u003Cp>Great. So now we're registered for this event. If I open my Directus Instance back up where are you, mister Directus Instance? We have registered for this event. I could see this specific user here.\u003C/p>\u003Cp>Maybe I wanted to send an email for this. Let's see how we're doing on time. Roughly 20 minutes remaining. Oh, we're cruising. So let's send an email event for the registrations.\u003C/p>\u003Cp>Send registration email. Registration email. Great. We'll go in. Event hook, so anytime an event happens, in this case we want the action, so after this action happens, we're not gonna block the thread.\u003C/p>\u003Cp>Anytime the\u003C/p>\u003Cp>Speaker 0: new registration\u003C/p>\u003Cp>Speaker 1: It's great. Let's fetch the actual data. Right? So we're going to read the let's just call it get registration. Find registration, whatever you want to call it.\u003C/p>\u003Cp>From the trigger, we're gonna use full access and collection here. It's gonna be registrations. And basically, what we're going to do here, I'm going to drill through the related fields. So we're gonna do this. We're gonna get all the root level fields of the registration.\u003C/p>\u003Cp>I don't\u003C/p>\u003Cp>Speaker 0: really need to do that, but,\u003C/p>\u003Cp>Speaker 1: we've got the event. I'm gonna do dot dollar symbol or dot asterisk. I'm sorry. And then the user, we're gonna get the user details. User dot k.\u003C/p>\u003Cp>Savvy. Am I forgetting? Oh, Should only be using quotation marks. Alright. Okay.\u003C/p>\u003Cp>So that should give us the fields for the registration and should give us our related data. And now we're gonna actually send this email out. So we'll send the email. Oh, actually, let's back up. We'll save that.\u003C/p>\u003Cp>I do need the key for this. So I need to figure out what IDs we have. That's gonna be dollar sign trigger dot key. I think it's just gonna be trigger dot key. We're using that dollar sign as a special operator just to access the actual trigger data.\u003C/p>\u003Cp>That's the only operation though that we need to append or prepend that dollar sign to to actually access that data. Alright, so we've got the get registration. So this is what our email is gonna be. It's gonna be get underscore registration dot user.email. That's what we'll save that to.\u003C/p>\u003Cp>You are registered for git_registration.event.title. I think that's what it is. And what are we gonna do? Here's the URL to access the event. Alright.\u003C/p>\u003Cp>And then we'll say link, and then we're gonna add the link for this. And for our link, we're gonna have what? HTTP local host 3000/events /getregistration.event.slug. Okay. Alright.\u003C/p>\u003Cp>So we've got this. Let's hit save. We save. And now we cross our fingers. Right?\u003C/p>\u003Cp>I'm gonna go back. We probably need to get our registrations for each user, but I'm just gonna go in and register. We'll see if this flow actually ran. So I can see the flow here. Did we get anything wrong?\u003C/p>\u003Cp>That looks really nice. Let's check my email address. Boom, there's our email. And if I click the link, boom, there's our page. Right?\u003C/p>\u003Cp>Amazing. Alright, so, what do we want to do? We probably need to check registration, but, you know, what happens if they're registered, the time is starting, we want to show this specific event. Alright. So one of the tools that I've used in the past, is called Whereby.\u003C/p>\u003Cp>They have this nice embedded functionality where you can create a video call or a webinar experience on your own site. So that's huge, especially for folks like me that that want to build their own tools that are very design oriented. I refer to myself as a recovering designer. We're gonna go in. I could create a room with no code, but the more interesting bit here is creating this via the API.\u003C/p>\u003Cp>So let's take a look at how we do this really quickly. We want to embed Whereby into our app. They have a web component for this, but we have to generate the the room first. Right? So here is create a transient room.\u003C/p>\u003Cp>I don't know what a transient room means, but, bedding I I guess that's what we want is a transient room. Create some transient room that is available between creation and an hour after the given end date. Alright. So here's the post request, here's the structure of this. We probably need to get an API key to do this as well.\u003C/p>\u003Cp>So let's go into my Whereby application. We'll go to developer guide. No. Go to settings. Members, profiles.\u003C/p>\u003Cp>Where where is my API key? Configure. Generate an API key. Okay. Event platform.\u003C/p>\u003Cp>Okay. There's our API key. We close. We've got that. Alright.\u003C/p>\u003Cp>So let's change up our data model just a little bit where we're gonna add the event. So for our event, we've got a Whereby Room. Actually, let's go back in and we'll just set this up. Let's add our rooms to this. Right?\u003C/p>\u003Cp>Room. Alright. Take created. Just add all of this. Fancy, fancy.\u003C/p>\u003Cp>Alright. Each room is gonna be attached to an event, so we'll have a mini to 1 on the events collection or to the events collection. And if I go into advanced field creation mode, I'm going to go in and add the reverse. Right? So on the events collection, we're adding a rooms field that will have an array of the rooms.\u003C/p>\u003Cp>Cool. Alright. So we got events, and then we're just gonna have, what, a whereby room URL. Alright. Let's see if we can get this done in time.\u003C/p>\u003Cp>We are crunching for time. Alright. Okay. So now we'll say anytime we create a room, let's create a Whereby Room. Create Whereby Room.\u003C/p>\u003Cp>So anytime we create a new room within an event, we're gonna trigger a webhook request. So Action Non blocking, Items Create, Rooms, and we're going to call the API for Whereby. Whereby. We're going to I think it's a post request. Right?\u003C/p>\u003Cp>The header, we're gonna be authorization. Bear. Might include that token that I had. Alright. So where'd we go?\u003C/p>\u003Cp>Create meeting. API\u003C/p>\u003Cp>Speaker 0: meetings,\u003C/p>\u003Cp>Speaker 1: post request, Content type, I'll add that header. Content type, application, JSON. Amazing. We'll go in. The end date is what we need.\u003C/p>\u003Cp>End date is the only thing that is actually required. So we'll do end date. Just copy what they have here. I think I missed 1. 202411.\u003C/p>\u003Cp>I'm just gonna hard code this for now. We'll say 425. Push it a couple weeks out. So the room mode is probably something we're gonna set as well, just because we want it to be a large room. Room mode.\u003C/p>\u003Cp>Group. Okay. And then we've got our request body. That should give us what we want. And then the response is gonna be a room URL.\u003C/p>\u003Cp>Alright. So then we're going to update. Oh, I had it right. We'll update update room. K.\u003C/p>\u003Cp>We got rooms. We got from the let's do full access, and we'll say, what's it gonna be? Trigger dot payload trigger dot key, actually. Should be. Alright.\u003C/p>\u003Cp>And then the payload what did I call this? Whereby room URL. K. And then we'll populate that data, whereby dot data. What's gonna come back from this?\u003C/p>\u003Cp>It'll say room URL. Cool. Let's run it and see. See what we got. So we'll go to this event, Leap Week 2000.\u003C/p>\u003Cp>We'll create a new room. Maybe we need a title for this room as well. Let's add that. Title. Alright.\u003C/p>\u003Cp>So we add a new room under Leap Week 2000. Let's add a new room. This is a test room for Whereby. And we had saved we saved this. Did it update?\u003C/p>\u003Cp>No. It didn't. There's something going on. Create whereby room. What did we get?\u003C/p>\u003Cp>Bad request. Why did we get a bad request? Whereby body, end date room mode. Is there something else that we're missing? Content body dot JSON dot stringify.\u003C/p>\u003Cp>Room mode\u003C/p>\u003Cp>Speaker 0: equals group. Room URL.\u003C/p>\u003Cp>Speaker 1: Whereby embedded? Where is this? Configurable room pages. Directs plus. Where's this developer guide?\u003C/p>\u003Cp>Embedding whereby in an application. The web component is included in our SDK. I'm gonna man, we're just gonna cheat. It's not cheating. Right?\u003C/p>\u003Cp>We'll just create a room. This is gonna be a large room. No cloud recording. We're not gonna record this. We're not gonna do any automatic streaming.\u003C/p>\u003Cp>We'll set this up for the end of April. Room prefix, Leap Week 2,000. Create the room. Alright. There's a participant link for the room.\u003C/p>\u003Cp>Yeah. We could go in and customize this room, but, hey, we're running short on time. Alright. So now we go back. How do we where's the embed?\u003C/p>\u003Cp>Alright. We got this web component. We are going to install. Alright. So we go to node, pmpm@wherebybrowser.\u003C/p>\u003Cp>Ppmi@wherebybrowser. We're cutting this extremely extremely close. We'll wait for that to install. Okay. PMPM dev.\u003C/p>\u003Cp>Alright. So we're gonna go back to our events. We'll do rooms, slug. No. This will be room ID.\u003C/p>\u003Cp>That's what we'll roll with. Room dot ID. Just gonna paste basically all of this. Right? Alright.\u003C/p>\u003Cp>We'll sort this out. We're gonna request the room. We're gonna actually read an item because we're gonna have the ID. Read item. And then it should be the route dot params.id.\u003C/p>\u003Cp>I think that's gonna give us what we want. We don't need to transform that. The room data, And then we're gonna need to import this as well. So we got this imported. Read room.\u003C/p>\u003Cp>Direct to site request. Alright. Let's see if we can actually get this working in time. Proom.title. Nothing fancy.\u003C/p>\u003Cp>And then we have this whereby embed, and we can make this pull in. We've got room dot whereby room URL. Is this actually gonna get us done? And then within the event, we actually need to list those rooms. So class l\u003C/p>\u003Cp>Speaker 0: I v four\u003C/p>\u003Cp>Speaker 1: i.roominevent.roomskey equals room dot ID. Okay. Okay. Hit me. Where we at time wise?\u003C/p>\u003Cp>We're we got, like, 3 minutes left. Let's see if we can actually get a list of the rooms. Where's the rooms? Where are the rooms? We did not set this up.\u003C/p>\u003Cp>We need to view all the rooms. Do we actually see the room? Where did I put that room? Where is it? What event is it under?\u003C/p>\u003Cp>Leap week 2,000? It's under leap week 2,000. Why are we not seeing it? Why are we not seeing you, mister rooms? Slug.\u003C/p>\u003Cp>Event. We don't see the rooms. Okay. Alright then. Again, we'll\u003C/p>\u003Cp>Speaker 0: just improvise. Rooms,\u003C/p>\u003Cp>Speaker 1: rooms slash. Not the right route. Events slash rooms. Document is not defined, it's, client only, isn't it? Whereby room?\u003C/p>\u003Cp>Maybe we wrap this in client only. Is that gonna fix it? Oh, no. What is it? Can you do I think you could do client only pages.\u003C/p>\u003Cp>Let's see what we got. Page not found. Pnpm dev rooms dot ID. Come on, man. We can do this.\u003C/p>\u003Cp>We can do this. Are we gonna get it? I don't think we're gonna get it. Page not found. Why are we not finding the room?\u003C/p>\u003Cp>What am I doing wrong? Rooms. Duh. There it is. Is that right?\u003C/p>\u003Cp>No. Directus dot request dot readitem rooms, route dot params dot ID. Why are we not getting the rooms? What is our request coming back from? Direct to snow match found.\u003C/p>\u003Cp>Events rooms. We are running out of time. We don't have time for this. Document is not defined. Okay.\u003C/p>\u003Cp>So that was working correctly. We're just not finding the room. Return request. Read items rooms.route.params.id. Should be events slash rooms dotid.\u003C/p>\u003Cp>Boom. We ran out of time. Not sure exactly where things have went wrong here. Things happen sometimes. The page\u003C/p>\u003Cp>Speaker 0: not found. We are not\u003C/p>\u003Cp>Speaker 1: finding it. Don't know where we went wrong. Route.params.id should be there. It is not. Sometimes you have to admit defeat.\u003C/p>\u003Cp>So unfortunately, we have not completed the things that we needed to. Let's take a run at this and and just give a recap. Right? So what did we do? We had set up an event, allow registrations, we had a public landing page, we had room support, but we did not get to the actual event platform.\u003C/p>\u003Cp>So this is a pretty neat experience, though. If you wanna check out Whereby, they're really cool for building these things. You can actually, go to this room. Now you can see that I can request permissions from this. And, you know, you can actually serve this inside your application.\u003C/p>\u003Cp>Yeah. Super helpful thing. So that is this episode of 100 Apps, 100 Hours. I hope you enjoyed it. What a fun ride.\u003C/p>\u003Cp>We'll catch you on the next one.\u003C/p>","Hi. Welcome back to another episode of 100 apps. 100 hours where we try to build your suggestions or some of your favorite apps in 1 hour or less using Directus or publicly fail, die on the vine trying to build something. If you're new to the show, there are only 2 rules. The first rule is we have 60 minutes to plan and build, no more, no less. The second rule is the anti rule. Use whatever you have at your disposal. So then the brakes. Let's dive into today's episode. Right? We're gonna be building an events platform, a virtual events platform. Now that sounds to me very very vague, could mean a lot of things to a lot of different folks. But, what kind of products are we talking about? So Zoom comes to mind as far as, like, registering a meeting and performing that meeting. You know, maybe you've got some interactivity like a chat, some registration. You know, you've seen this beautiful, beautiful, beautiful registration page before, I'm I'm sure. Not knocking you Zoom, but, like, with all the engineers that you guys have, why is this the best that we get for a registration page? I don't like it. Other softwares that I've looked at, like, DemoHop is an interesting one that, you know, has, like, a science fair where each there's a bunch of different booths, some of the other ones, like, Livestorm. There's a ton of these different webinar platforms every way that you slice it. But what I've discovered and and just in my own experience with these over the years, it's nice to have your own. So that's what we're gonna be building today. We're gonna be building our own events platform. Let's run through it. We'll start the clock, pull up 60 minutes, and away we go. Okay. So, I am in FigJam here. Let's just kind of mock out our functionality that we like or what we're looking to see out of this particular app. So, as far as our functionality, this is a nice serif font. Maybe we underline that as well. Great. Alright. So what do we wanna do? We wanted to set up we can't have all of it underlined. That's not cool. Set up an event. You know, allow registrations. You know, have a public landing page, registration page. What else do we want? We wanna have chat during the event. Maybe we wanna have different rooms for the event. You know? You think a virtual event, it could just be just, hey. It's a workshop. It's a webinar. Could also be like a week long leap week like we do here at Directus. So maybe we have rooms for that. This this seems like I'm getting way in over my head, so we'll stop there. And then let's let's kinda just flesh out our data model for this a bit. So in my mind, what are we gonna have? We're gonna have events. K. And we got that nice purple color. Maybe we'll make this a little bit bigger so we could see it. So we've got events and then we're gonna have, users or attendees. You know, that's gonna be our Directus underscore users collection. We'll just pick that up. Directus gives us that out of the box. No reason not to use it. Amazing. And then I would imagine there's a junction table here, right, where we have could have multiple events. So these will be, like, registrations, maybe. Naming stuff is always the hardest part of development. So we got registrations, draw some arrows, just connect these, connect these in your mind as well. Great. And then what else? You know, if we jive with the rooms, you know, we could have rooms as well. Right? Each room has a different topic, some schedule, etcetera. If we think of, like, our events, they're gonna have a date, start date, duration, or maybe an end date, title, description. What else do we have? Registrations, obviously. Probably an image. Schedule, potentially. Cool. Alright. Getting too carried away. Anyway, let's actually start building something. Right? This feels just roughly what we're looking for. So let's dive into it. Right? What have I got set up for this? Just gonna close all these other things. I have a Directus instance. I've got a Nuxt 3 application, just a starter application set up. It's got a login and register function. I don't even think the register actually works on this thing, but that's what we're working with. Let's go ahead and get logged in to our Directus instance. What did I set the password to? I think it's just password. Real secure. Alright. So we've got a blank instance. I've got my FigJam over here on the left. Got Directus on the right, a 125% zoomed in, and let's just dive in. Right? Our first collection, we're going to start here with our events and we'll create a new collection for that. Let's give it a UUID, When it was created, when it was updated, those are all great. Let's do status as well for like published events, draft events, completed events. Let's just take a look at that. Right? So we have published, we have draft, we have archived. I could go in and flesh these out further if I wanted to or we could just leave it at that. Maybe draft is the default. That's good. Solid. Alright. So here, we're gonna have a name for the event or a title. The event title, that's just gonna be a string. A description of this, we want to be the anti zoom here, so we're gonna make this a text field using the WYSIWYG interface for Directus. We're going to call it description. That's our key. And I could adjust our toolbar if I wanted to. Maybe I want to add undo and redo. I'm not a big fan of underline, so I'll just remove that. If I need to, I can dive into advanced field creation mode, and specifically with the WYSIWYG editor, you can you've got some formatting options here and options overrides that you can pass to the tiny MCE instance if you need to. Like, you know, maybe you have a special class for buttons or something like that that you want to display inside that WYSIWYG editor. Alright. Let's add an image for this. So just a single image. You know, we could potentially have, you know, multiple images, but we'll just keep it simple. That's what we're going to show on a card. As I'm imagining this, on the front end, it's It's just like a running list of events or maybe a grid of cards for the events. When you get inside the event, you've got to register. Then once you register, maybe you can see the schedule for the event. Cool. So as far as something like a schedule, right, how should we set this up? If I'm not really sure, like, one of the things that I do a lot, I'm not really sure what this is gonna look like immediately. Instead of creating another table, I may use something like the repeater, which is basically a JSON type field. That's what it's going to save as our, inside our SQL database. But let's just call it schedule, and then I could go within this and edit fields to repeat, like a set of fields. So, I don't know. I like a speaker. It'll probably be something else. Let's just say the name or label. Okay. We'll do string. That requires a value. What's the name of this thing? And the interface would be an input and then we could do something like time, date, time, time. Start time, Start time. Sounds great. That's what we'll roll with. Let's make that full width and then we'll use the timestamp format. We'll require value. Maybe we don't require value. May not need to pick a time. Maybe we're just showing scheduling order. When does this thing start? Cool. Alright. Sweet. So now we have some events or at least the the start of something for events. Let's go in and go back to the data model settings. We're just gonna do registrations and we'll do, like, a date created, user created. Do we even I don't even think we need this. Status. Do we have a status for the registration? Maybe it's canceled or something like that. I'm reading too much into it. Right? So here's the date created. Maybe I wanna show this, so I'll click the drop down, show that field. Maybe we make it full width, just in case. And for our registrations, we're gonna start digging into our relationships. Right? So when I think of a registration, we've got the event that that registration belongs to, and we've got the user that made the registration. So those are both gonna be basically many to one relationships. So we'll go in for the key, we'll add the event, we'll just do events. And for our display template, let's use the title. I can already tell that I forgot to input my start and end date for the event. Amazing. So we'll have to go back and fix that as well, but there's our event. Let's go ahead and create our relationship to the users. So we'll say the user that's registered or, you know, this could be like the attendee. Again, naming stuff is hard. Cut me some slack. Alright. So I want to pick the directus underscore users collection. I'm just going to dig into the systems drop down here, or system, choose direct us users. I can get fancy with the display on this. Give them the avatar with a nice little thumbnail. We'll do first name, last name, and behind the scenes, this is just mustache syntax. So I can edit these raw values if I want, do it that way, or I could just pick from the drop down. That's great. We don't wanna create new users from this. We could potentially select users. Great. Okay. So that's our basic data model. Why do we have event? Some type of bug. Alright. Regardless, we have events, we have registrations. Let's go in and add our start time for this. How could I forget? So in this case, we wanna save the time zone value, so we're gonna use time stamp. This could be the start date. Maybe we have an end date for this or, you know, potentially a duration. I guess you could do either or or both. You know, you could potentially even use flows to calculate that if you wanted to. But this is good. Let's add some icons just to fancy it up a little bit. My friend Kevin on the team here will be sweating if you watch me add these icons, but, there's always room for design. Cool. Alright. So we got events. Let's create our first event. Right? What are we gonna call this? Let's call it Leap Week number 2 1000. Leap Week 2 1000. We are taking a huge leap forward for Directus. Join us as we take a huge leap forward for Directus. Lots of cool stuff. Let's add some formatting to this as well. You know, thing 1, thing 2, thing 3. Cool. Alright. And then let's add an image for this. Right? So let's just search Directus Leap Week and see if we can grab, do we have any images? Yeah, we got a couple laying around. Cool. So we'll save this image. Is that right? Hope that's the right image. We'll just upload that. Everything announced at our 1st Leap Week. And let's set a start date for this. Let's say May 15th is a good date. Maybe this runs 2, 3 days. Yeah. Great. So for our schedule, we've got workshop with Bryant. That is the I forget what day we did this now. 17th. Oh, that's it. May 17th. Not April 17th, Brent. Alright. Let's say that is at 3. Great. Cool. So I'm gonna fill out the schedule workshop with Kevin. Go for 15th at, at 16th at 1 PM. Great. Alright. So now we've got our event. Maybe let's just drag this out a little bit, move this around. Okay. One of the other things that I forgot here is probably like a slug. Right? I want a nice URL on the front end. So we'll go back into events, we'll just create a new field for it, we're going to use the input, call it slug. The other thing that I'm going to do is go in and make this URL safe, so we could slugify this thing. Now one of the other cool things with the addition of the marketplace, let's look for a slug, Permalink interface to enter URL slugs. Is this what we're looking for? No. WP slug interface. Yeah. This is cool. Alright. Let's try this out. We'll hit refresh, install this extension. I didn't have this on the first season of 100 apps, 100 hours, But now I can go into our interface and we can choose the slug interface. And our template is going to come from the title and we'll auto generate on create. Let's try that and see what we've got. We shrink the title and the slug. Cool. Alright. So now we got a couple settings. We can hit auto generate. Generates this nice slug, and it looks a lot like the old days of WordPress. I mean WordPress is still around, obviously, but this is this is nice, I like it. Alright. So we've got events, we've got registrations. What do we do next, right? We're gonna look at access control. So this is our settings of who has access to what inside Directus. There's 2 default roles, public and administrator. Let's make the public roles, we want to be able to view the events, We don't want anybody to edit events. And then, for as far as registrations, maybe we want to be able to create registrations and then maybe just read, you know what? Let's give full access to that. And, that should be maybe what we want, enough to play around with. Right? So now what are we going to do? Right? Let's dive into the front end of this application. Alright. So I'm going to pull this up. Again, I've just got a a Nuxt application here, and let's flesh out a couple of routes. Index dot view. We can make this just a list of our events. Alright. So we're gonna go in. I've got Directus configured as a Nuxt plugin here. It's just got some authentication in it. We've got real time in case we ever needed it. And then we've just got the REST client. That's the way I prefer it. And we just provide that to the Nuxt application. So we'll call that up like this, const directives equals use Nuxt app, and in this case, we're probably gonna do, like, read items as well. So we'll grab that from import items from the atdirectus SDK. And, alright, what's next? Right. So we're gonna do some data. We'll use await, use async data, and we'll do return directus dot request read items. And we're reading from the, what, events collection. And then I have a, query for different parameters. Right? We wanna show maybe we only wanna show published events, right, so I could do this. We'll just say status equals published. Thank you, GitHub Copilot. Let's see what that gets us. Directors. Cool. Maybe we scrap all of this. Would do we even need this to be flexed? Probably not. Alright. Let's see what we got. Data. Just wrap this in like a pre tag. See if we get any actual data. Right. So we've set up this filter. We're not getting any data because I'm assuming we did not publish this event. So if I go in and publish this, let's see what we have now. Okay. Cool. So we have our event information. What can we do with this, right? So I do also have the, like, Nuxt UI library built into this. So if you just go to ui.nuxt, using that, it's got, you know, some components already built in for buttons and cards and things like that. Cool. So without getting too fancy on the UI, let's just mock something up now that we can see we've got some actual data. Right? We got a diff. We add some padding. Y 12p y 6. We add a, do we even need an h one? Let's call it h 2. This will be font bold. Text, let's make the text large. And let's make it mono, font mono. Alright. So will be events. See what we got there. There's our events. And now let's do a grid. Right? So we'll just do a div to wrap this. Just grid, maybe grid calls to like a break point, maybe we set the gap to 8, and then we're gonna do a v 4. Right? So, for our events, let's just use this card. I think there's a card component in here. I feel like a nice card component is ucard. We've got, like, a header and a footer, or we could just throw it in the middle. So let's do that. Right. V 4. We're gonna do events and data. You know? And, usually, I like to destructure those. Yeah. Yeah. Maybe we call it events. And for the key here, we'll use event ID. That way, we don't have to, use, like, an index or something like that, and then we'll actually do this. Alright. So is there actual I think there's, like, a text component here, so let's just do, like, an h three. Maybe we just take the auto completion here from, GitHub Copilot. We'll do a little Nuxt image to optimize these images. Works very well with Directus. There's a provider for it. Then we've got the event dot image. We use the event title. That seems pretty good. Let's see what we come up with here. Sweet. Except for our description. Right? This kind of sucks because it is not what we want. So in this case, we'll do div. Let's do v h t m l event dot description, and remove that. Great. It has no formatting. Maybe we've got, do I have to tell when? We got pros class. Yeah. There we go. Alright. So we've got that, and let's add a link for this. Right? Maybe we add a little spacing here, n t 4. Maybe we make that add a little spacing here. MT2, MT8. It's great. Cool. So now we've got an event looking really nice. You know, I could duplicate this event if I wanted to. Let's just see what this looks like with a couple of more events. We're going to call this Leap Week 3000. Flush this out. And what I'm going to do here, we're just going to open this image up. I'm going to get really fancy on this, guys. And we'll just mark this up. Scroll this out. 3000. Oh, so fancy. Alright. For Leap Week 3,000, we're going to close this out, upload a new photo, we can see that's Leap Week 3,000. And then what I'm going to do here is click Save and Copy from the 3 dot menu button and now I've got 2 events. We've got Leap Week 2000, Leap Week 3000. There we go. Amazing. Okay. Alright. So now we need a route for our actual event. Right? So let's set this up. Let's actually add a folder for it just in case we have a couple of other routes within that. And then for our events, we're gonna have a slug dot view. So that's just gonna be a dynamic route. We'll view I think it's init. View init. What does that give me? That's not the syntax that I like. It'll do. Trying to make it fancy. I need to get my snippets set back up. I had to scrap Versus code. Lost all my snippets. Alright. So we got a slug. We're gonna use the route here. So we've got route equals use routes. Cool. And then, on the individual page, right, we want to call the specific data. And, you know, maybe we don't even need this full description on our index page, so we could, you know, potentially leave that off. Maybe we just make it the event start date, but we may wanna use that in here. And let's use just, like, a date function. I think it's format from date f n's. Formats oh, not currency. I'm gonna do format. What we got? DDD, month month, DD. Where does that get us? Doesn't get you anywhere because it breaks it. Forgot the year year. I guess it's all lowercase. And then okay. Yeah. Still still got a ways to go memory wise. So date f and s, just a handy library. We got format. What's the date? What are we looking for? Like, month is gonna be m m m. Like that one. So we'll do m m m. And what if we do, like, the ordinal of the dates, day of the month, d o? That's what we want. Now we're looking nice. Okay. And then we wanna do the time. Right? So maybe we show the time. Local day of the week, AM PM. What's this gonna be? H. What minute? M m m. And then we've got AM, PM, AAA. Wasting too much time on this. Okay. Alright. So we got that. Let's go in and just wrap this card in a NuxLink, or at least the content of the card. We'll do 2. And let's make this dynamic. That'll be /events/ event.slug. Uh-oh. Oh, forgot to close that. Cool. Alright. So now we've got the links set up, so we can click on these and and move to the route. We don't really have anything inside our route though. Right? So I could take this function here, just copy that in, and here we're still going to be reading items. Right? I'm going to call this event, in addition to status. I guess we can set this up where if you call it directly, even if it's a draft, maybe you wanna see it. It. So let's just do this. We'll say slug equals route dot params dot slug. That should get us the event that we want. And then, the actual event here, we're gonna do, like, a transform function. The nice little part that's baked into the use async data call, that composable inside Nuxt, where basically this is gonna return an array. I could transform this and just pick up the first item of this array. And now if we do this, just do pre class. Again, I always like to do this just to see what data I'm getting back. And now we've got just the specific data object for this item. Alright. What's next? Right? We're gonna dive into building this actual page. Right? So let's give it a nice h one tag. We've got font bold, font mono, text let's make this giant. We're gonna do the event dot title. Okay. Leap week 3,000. What else do we have? If I dig into the clipboard, I've got that event description. We can make this pros large. We wrap that. Let's move down. We'll do another div. Throw this into an image, event dot image, class w 96. Where's our actual image at? Okay. There's our image. We could flex these 2, add a little bit of padding. I mean, this is not gonna win any design awards, but, hey, it'll be alright. We'll just take these items from the first one. Let's put this below. Do we have our dates? Oh, gotta actually import that function, don't we? Import format from datefns functions, I guess. Alright. So we got our date. Add a little margin to the top. Looking great. And then what if we wanted to show that schedule? Right? So, maybe we drop down over here on the left. We have a list. And within the list, take a little GitHub completion. That's not really what we're looking for. It's gonna be the item inside the event dot schedule. For the key, we used you the item dot label. And then we have yeah. I was wasting more time using the GitHub suggestions. Item dot label. Item. We'll just steal this format, and can we do this as well? Dash hmmaa. What does that give us? Does that get us where we want? Okay. So now we can see the schedule. We can drop a little schedule class. List. List inside list disk. I guess, is that the list item? Why are we why does that look weird? Not sure. Not gonna bother with it. Let's do font bold, font mono, empty 8. Okay. Cool. What else we got? So we got the schedule right. Good. We got a page. We could go back to our events. Yeah. Maybe we add a link for that up here somewhere in the top. Next link to equals slash slash I think it's just slash. Back to main list of events. Alright. Cool. We got all this going on, we could go into Leap Week 3000, we could go to Leap Week 2000. How are we doing on time? We are cruising on time. Alright, so how do we set up registration? Let's go in and what we're going to do, we're going to need to basically authenticate the user first. So we've got to have a user to register. And I am going to cheat here and use, like, a SSO, like, register with GitHub. I've used this in a past project. So I've just got some code over here. We're gonna go in and inside my env or my Docker Compose file, I would set this up. I'm missing the client secret and client ID from GitHub, so go inside my GitHub account. We're gonna go to developers, we'll go to applications, is it authorize no. Developer settings, OAuth apps, we're going to create a new OAuth app. We'll call it Events Platform. Super secret. The homepage URL will be HTTPS. We'll just use Directus. Do we need we need this callback. And just from past experience, the structure for this is gonna be my directus instance slash auth /login/provider. So we're using GitHub, that's gonna be the provider. We're gonna use that callback. Great. And then I can get my client ID. I can get my client secret. We'll just have to use a password, log in, get the secret, they'll steal my secrets, bro. Okay. And then the last piece of the puzzle here is we need a default role for that, for that specific user. So what we're gonna do, we'll go into Directus. Let's create a new role for attendee. And we're not going to give them app access, we're just going to save it. I'm going to pick up the actual ID for this role, I can see it over here. That's the primary key. We'll paste that in there. I'm going to save that, and then I'm going to stop this container, Docker Compose up one more time, and the effect I should get if I load this again, now I can see this login with GitHub function. So I could actually log in with GitHub, and let's see. Oh, yeah. I'd have to actually do my username. Maybe we swap this out. Alright. We get a local host, 8055. Log in. Admin, example, password. Alright. And then I log out here. We could test this out. But I could also do this on the front end as well. Right? So let's go back to our page. Alright. So I'm gonna stick a big registration button over here somewhere, and I think I've got one in register. Is it the register form or the login form? Do I have it? Yeah. There we go. I got this button that I've used previously. We're gonna go to our event page. We'll save this. Uncomment this button. I don't really need the divider label. Alright. Signing with GitHub to register. Cool. And here you can see I've got a redirect, just basically to a different page. We're going to make this size excel, giant button, just add a class to it, give it a little more padding. Duplicate Attribute. Oh, MT 12. We don't want to duplicate that class. Alright. So what is this going to do? This should ask me to log in once accessed to my GitHub account. We authorize, and now I've got, like, the index page. So if I go here, if I actually if I signed in, right, probably getting some errors here because we did not give permissions to those specific accounts or that specific role. Right? So for our attendee role, we want them to view all the events, we want them to create registrations, maybe they can view the registrations where the user equals current underscore user. Alright. We're gonna have to run like hell here. Apologies for the curse words. Right? So okay. If you I look at my Nuxt state. I've got a use state composable that's actually storing our user data. So I should be able to see that user data. I've got the ID of the user. Do I have any other information about it? Maybe that's really all I need. Right? So let's try this. We'll do const user equals use state, user. Will that give us access to the user? And if we have a user, we can hide this button And maybe if if no user. Okay. And then maybe we add another button. Register. And on this one, we're gonna add a click handler. It's gonna be register for event. Okay. How's that looking? V f user. Cool. And now we need a function for that. Right? This is gonna be an async function because we're gonna call directus. We are going to import, we need the create item. Okay. Async function, register for events. We're gonna call that. K. We'll do a try. Await direct us request registrations event dot ID. That's gonna be an actual reactive value, so we'll just add on that. We can add that around the user too just to be safe. And cool. What else did we do? Response. You know, I could just console log this response just to verify. Alright. So let's take a look. If I clear out my network request, we hit register, We've got registrations. Cool. Great. So now we're registered for this event. If I open my Directus Instance back up where are you, mister Directus Instance? We have registered for this event. I could see this specific user here. Maybe I wanted to send an email for this. Let's see how we're doing on time. Roughly 20 minutes remaining. Oh, we're cruising. So let's send an email event for the registrations. Send registration email. Registration email. Great. We'll go in. Event hook, so anytime an event happens, in this case we want the action, so after this action happens, we're not gonna block the thread. Anytime the new registration It's great. Let's fetch the actual data. Right? So we're going to read the let's just call it get registration. Find registration, whatever you want to call it. From the trigger, we're gonna use full access and collection here. It's gonna be registrations. And basically, what we're going to do here, I'm going to drill through the related fields. So we're gonna do this. We're gonna get all the root level fields of the registration. I don't really need to do that, but, we've got the event. I'm gonna do dot dollar symbol or dot asterisk. I'm sorry. And then the user, we're gonna get the user details. User dot k. Savvy. Am I forgetting? Oh, Should only be using quotation marks. Alright. Okay. So that should give us the fields for the registration and should give us our related data. And now we're gonna actually send this email out. So we'll send the email. Oh, actually, let's back up. We'll save that. I do need the key for this. So I need to figure out what IDs we have. That's gonna be dollar sign trigger dot key. I think it's just gonna be trigger dot key. We're using that dollar sign as a special operator just to access the actual trigger data. That's the only operation though that we need to append or prepend that dollar sign to to actually access that data. Alright, so we've got the get registration. So this is what our email is gonna be. It's gonna be get underscore registration dot user.email. That's what we'll save that to. You are registered for git_registration.event.title. I think that's what it is. And what are we gonna do? Here's the URL to access the event. Alright. And then we'll say link, and then we're gonna add the link for this. And for our link, we're gonna have what? HTTP local host 3000/events /getregistration.event.slug. Okay. Alright. So we've got this. Let's hit save. We save. And now we cross our fingers. Right? I'm gonna go back. We probably need to get our registrations for each user, but I'm just gonna go in and register. We'll see if this flow actually ran. So I can see the flow here. Did we get anything wrong? That looks really nice. Let's check my email address. Boom, there's our email. And if I click the link, boom, there's our page. Right? Amazing. Alright, so, what do we want to do? We probably need to check registration, but, you know, what happens if they're registered, the time is starting, we want to show this specific event. Alright. So one of the tools that I've used in the past, is called Whereby. They have this nice embedded functionality where you can create a video call or a webinar experience on your own site. So that's huge, especially for folks like me that that want to build their own tools that are very design oriented. I refer to myself as a recovering designer. We're gonna go in. I could create a room with no code, but the more interesting bit here is creating this via the API. So let's take a look at how we do this really quickly. We want to embed Whereby into our app. They have a web component for this, but we have to generate the the room first. Right? So here is create a transient room. I don't know what a transient room means, but, bedding I I guess that's what we want is a transient room. Create some transient room that is available between creation and an hour after the given end date. Alright. So here's the post request, here's the structure of this. We probably need to get an API key to do this as well. So let's go into my Whereby application. We'll go to developer guide. No. Go to settings. Members, profiles. Where where is my API key? Configure. Generate an API key. Okay. Event platform. Okay. There's our API key. We close. We've got that. Alright. So let's change up our data model just a little bit where we're gonna add the event. So for our event, we've got a Whereby Room. Actually, let's go back in and we'll just set this up. Let's add our rooms to this. Right? Room. Alright. Take created. Just add all of this. Fancy, fancy. Alright. Each room is gonna be attached to an event, so we'll have a mini to 1 on the events collection or to the events collection. And if I go into advanced field creation mode, I'm going to go in and add the reverse. Right? So on the events collection, we're adding a rooms field that will have an array of the rooms. Cool. Alright. So we got events, and then we're just gonna have, what, a whereby room URL. Alright. Let's see if we can get this done in time. We are crunching for time. Alright. Okay. So now we'll say anytime we create a room, let's create a Whereby Room. Create Whereby Room. So anytime we create a new room within an event, we're gonna trigger a webhook request. So Action Non blocking, Items Create, Rooms, and we're going to call the API for Whereby. Whereby. We're going to I think it's a post request. Right? The header, we're gonna be authorization. Bear. Might include that token that I had. Alright. So where'd we go? Create meeting. API meetings, post request, Content type, I'll add that header. Content type, application, JSON. Amazing. We'll go in. The end date is what we need. End date is the only thing that is actually required. So we'll do end date. Just copy what they have here. I think I missed 1. 202411. I'm just gonna hard code this for now. We'll say 425. Push it a couple weeks out. So the room mode is probably something we're gonna set as well, just because we want it to be a large room. Room mode. Group. Okay. And then we've got our request body. That should give us what we want. And then the response is gonna be a room URL. Alright. So then we're going to update. Oh, I had it right. We'll update update room. K. We got rooms. We got from the let's do full access, and we'll say, what's it gonna be? Trigger dot payload trigger dot key, actually. Should be. Alright. And then the payload what did I call this? Whereby room URL. K. And then we'll populate that data, whereby dot data. What's gonna come back from this? It'll say room URL. Cool. Let's run it and see. See what we got. So we'll go to this event, Leap Week 2000. We'll create a new room. Maybe we need a title for this room as well. Let's add that. Title. Alright. So we add a new room under Leap Week 2000. Let's add a new room. This is a test room for Whereby. And we had saved we saved this. Did it update? No. It didn't. There's something going on. Create whereby room. What did we get? Bad request. Why did we get a bad request? Whereby body, end date room mode. Is there something else that we're missing? Content body dot JSON dot stringify. Room mode equals group. Room URL. Whereby embedded? Where is this? Configurable room pages. Directs plus. Where's this developer guide? Embedding whereby in an application. The web component is included in our SDK. I'm gonna man, we're just gonna cheat. It's not cheating. Right? We'll just create a room. This is gonna be a large room. No cloud recording. We're not gonna record this. We're not gonna do any automatic streaming. We'll set this up for the end of April. Room prefix, Leap Week 2,000. Create the room. Alright. There's a participant link for the room. Yeah. We could go in and customize this room, but, hey, we're running short on time. Alright. So now we go back. How do we where's the embed? Alright. We got this web component. We are going to install. Alright. So we go to node, pmpm@wherebybrowser. Ppmi@wherebybrowser. We're cutting this extremely extremely close. We'll wait for that to install. Okay. PMPM dev. Alright. So we're gonna go back to our events. We'll do rooms, slug. No. This will be room ID. That's what we'll roll with. Room dot ID. Just gonna paste basically all of this. Right? Alright. We'll sort this out. We're gonna request the room. We're gonna actually read an item because we're gonna have the ID. Read item. And then it should be the route dot params.id. I think that's gonna give us what we want. We don't need to transform that. The room data, And then we're gonna need to import this as well. So we got this imported. Read room. Direct to site request. Alright. Let's see if we can actually get this working in time. Proom.title. Nothing fancy. And then we have this whereby embed, and we can make this pull in. We've got room dot whereby room URL. Is this actually gonna get us done? And then within the event, we actually need to list those rooms. So class l I v four i.roominevent.roomskey equals room dot ID. Okay. Okay. Hit me. Where we at time wise? We're we got, like, 3 minutes left. Let's see if we can actually get a list of the rooms. Where's the rooms? Where are the rooms? We did not set this up. We need to view all the rooms. Do we actually see the room? Where did I put that room? Where is it? What event is it under? Leap week 2,000? It's under leap week 2,000. Why are we not seeing it? Why are we not seeing you, mister rooms? Slug. Event. We don't see the rooms. Okay. Alright then. Again, we'll just improvise. Rooms, rooms slash. Not the right route. Events slash rooms. Document is not defined, it's, client only, isn't it? Whereby room? Maybe we wrap this in client only. Is that gonna fix it? Oh, no. What is it? Can you do I think you could do client only pages. Let's see what we got. Page not found. Pnpm dev rooms dot ID. Come on, man. We can do this. We can do this. Are we gonna get it? I don't think we're gonna get it. Page not found. Why are we not finding the room? What am I doing wrong? Rooms. Duh. There it is. Is that right? No. Directus dot request dot readitem rooms, route dot params dot ID. Why are we not getting the rooms? What is our request coming back from? Direct to snow match found. Events rooms. We are running out of time. We don't have time for this. Document is not defined. Okay. So that was working correctly. We're just not finding the room. Return request. Read items rooms.route.params.id. Should be events slash rooms dotid. Boom. We ran out of time. Not sure exactly where things have went wrong here. Things happen sometimes. The page not found. We are not finding it. Don't know where we went wrong. Route.params.id should be there. It is not. Sometimes you have to admit defeat. So unfortunately, we have not completed the things that we needed to. Let's take a run at this and and just give a recap. Right? So what did we do? We had set up an event, allow registrations, we had a public landing page, we had room support, but we did not get to the actual event platform. So this is a pretty neat experience, though. If you wanna check out Whereby, they're really cool for building these things. You can actually, go to this room. Now you can see that I can request permissions from this. And, you know, you can actually serve this inside your application. Yeah. Super helpful thing. So that is this episode of 100 Apps, 100 Hours. I hope you enjoyed it. What a fun ride. We'll catch you on the next one.","published",[135],{"people_id":136},{"id":137,"first_name":138,"last_name":139,"avatar":140,"bio":141,"links":142},"791e1503-1d88-463d-9347-0b9192933576","Bryant","Gillespie","9013afc8-e8d7-4182-9b18-44db08117bb9","Developer Advocate at Directus",[143,146],{"url":144,"service":145},"https://directus.io/team/bryant-gillespie","website",{"service":147,"url":148},"github","https://github.com/bryantgillespie",[],{"id":151,"number":152,"year":153,"episodes":154,"show":164},"14fda5f2-95de-4dbe-a4e2-3fd956c21c19",2,"2024",[155,156,122,157,158,159,160,161,162,163],"9a3a8ffa-a27b-421c-93cf-3da2dcb726e9","d072a935-906e-4208-a5dc-e9b117d0ab29","aad8d674-2b58-4604-8e43-b98f7c6e05cb","6bff0c09-ad87-4d5c-b227-89b8c3c02220","6fb9aa9a-2b59-44b6-b78f-d1831fa657c6","620cf225-a23a-415a-ad95-9ba8e2dec984","b8b36125-7a4a-40e4-85f6-f4fe9138085e","385bdd7d-038d-4f9c-8037-357e5272420a","383c24d5-b6b5-4d66-aba6-6997af5f77b4",{"title":165,"tile":166},"100 Apps In 100 Hours","fb0f9d45-be21-4634-94d4-2ef1cc5146f2",{"title":168,"meta_description":125},"Build a Virtual Events Platform with Nuxt and Whereby Embedded",{"id":170,"slug":171,"season":172,"vimeo_id":173,"description":174,"tile":175,"length":176,"resources":8,"people":8,"episode_number":177,"published":178,"title":179,"video_transcript_html":180,"video_transcript_text":181,"content":8,"seo":182,"status":133,"episode_people":183,"recommendations":185},"ec88bef1-fffd-43eb-9d93-3123dc381b97","ai-letters-to-santa","d6b229fe-38fc-495b-ba0c-c574ebfea38f","1059428648","Bryant builds a holiday-themed app that generates personalized letters from \"Open Source Santa\" based on GitHub profiles. Watch as he creates a system that analyzes developers' repositories, determines whether they're on the open source naughty or nice list, and generates snarky, sarcastic letters from Santa — complete with festive styling and holiday cheer.","6209314e-e6ee-4a2d-9e97-11eedd08595a",59,1,"2025-03-10","Mission: AI Letters to Santa","\u003Cp>Speaker 0: Alright. Alright. Alright. We are back with the Christmas edition of 100 apps, one hundred hours. Today, we are going to be building AI letters from Santa.\u003C/p>\u003Cp>I've got my lumberjack style on today. My wife called this lumberjack Jesus earlier, but I digress. We're back for more. The rules of 100 apps, one hundred hours. If this is your first show, we have sixty minutes to plan and build an application, a website, a portal, whatever.\u003C/p>\u003Cp>Whatever we're building, sixty minutes, no more, no less. And rule number two, the anti rule, use whatever you have at your disposal. And since this is an AI Christmas special, I'm gonna pull out all the stops. So let's dive right in. We're gonna hit the clock here.\u003C/p>\u003Cp>Fire it up. Sixty minutes on the timer. Go. Alright. So AI letters from Santa.\u003C/p>\u003Cp>What do we actually want out of this? So I have to admit, I cheated a little bit because I thought about this with my team, and I knew we wanted to do this. I've seen things in the past where you write a letter to Santa, you get something back in the mail, etcetera, etcetera. With AI, we could take this up a notch. So combining two ideas.\u003C/p>\u003Cp>A while back, I saw a GitHub roast page where you enter in your GitHub profile and it, basically will scrape that and give you a roast of how well you're actually not doing in GitHub. So we're gonna combine that with a letter from Santa. And basically, what we wanna do is, enter a GitHub profile. We're gonna scrape that profile. We're going to send that to AI.\u003C/p>\u003Cp>So we want LLM analysis of the profile. I'm not sure what we're gonna call that. And then I'm going to bucket people on the open source naughty or nice list. So score naughty or nice list. And then we're gonna generate a letter from open source Santa.\u003C/p>\u003Cp>Generate a letter from open source Santa to that GitHub profile to that profile. Alright. So as far as that functionality, this looks pretty good. Right? What are the tools that we're gonna use of the trade today?\u003C/p>\u003Cp>I've got a Directus Docker container up and running locally. Directus is obviously the back end we're using to store all of these things. And if everything works as intended p m p m g. I guess, sometimes things don't work as you intend. I've got a Nuxt application that we are going to try and use here.\u003C/p>\u003Cp>I'm not sure what's going on, but let's hop into the Directus instance. So I'm just gonna pull up Chrome. We'll log in to 8055, and I should be able to pull up my back end. So great. Got Directus running.\u003C/p>\u003Cp>You could see this is a pretty blank instance of Directus. This is just the boilerplate I use now. There are a couple extensions installed that I was testing, just messing around with. But, let's make sure. What are we doing here?\u003C/p>\u003Cp>For MPMI. Sometimes these things never go as planned. Okay. So maybe now we can get this Nuxt application up and running that will be served at local host 3,000, and we'll just basically use it to scaffold out our communications. As far as what I'm using, I've upgraded this boilerplate that I've used for 100 apps to, the Nuxt UI v three alpha, just to play around with Tailwind four and, you know, some of these nice new components that are coming from, like, Radix view.\u003C/p>\u003Cp>So alright. Let's actually model this thing out. Right? What do we need as far as our data models? I think we just need, like, a maybe like a profiles.\u003C/p>\u003Cp>So under profiles, we would have, what, our username, letter from Santa, letter from Santa, list, you know, are you naughty or nice? Great. And what else do we need? Let's let's jam on that. So we'll, just set up the back end for this.\u003C/p>\u003Cp>I'm just gonna create a new collection. We're going to call this profiles is the name of it. And why can't I zoom way in? There we go. That's maybe too far, but all good.\u003C/p>\u003Cp>Let's do created at, updated at. K. Status sort not needed. Who this was created by, I'm not super concerned with. So now we have a profile.\u003C/p>\u003Cp>We're gonna do the username. Great. That's where we'll store the GitHub profile. What else do we need? What else did we have here?\u003C/p>\u003Cp>We've got the letter from Santa. What is that gonna be in letter from let's just call it letter. Great. We'll use the WYSIWYG editor inside Directus so we could just store, I'm assuming, HTML content for that. And then we've got the list.\u003C/p>\u003Cp>So that's basically gonna be a string. We can, you know, make this look nice inside the directus admin. We'll just give it a naughty. Feel naughty just typing that out, and then we have the nice list. Great.\u003C/p>\u003Cp>There we go. And I'm just going to go on record that we're probably as soon as we start typing naughty into the AI stuff, we'll probably get some some things back. Like a I probably set off the content alarms or something like that. So there we go. We've got a username.\u003C/p>\u003Cp>We've got a letter. We've got a list. You know, I could potentially put that in here. What I'm gonna do now, I'm just gonna let's just we're just using Directus to store this at the moment. Right?\u003C/p>\u003Cp>There's a lot of different ways I could go with how to actually generate the application here. But Directus allows me to create custom extensions. What I'm gonna do here is just start, working on this from the Nuxt side of it. So we're gonna input the, actual form here. Let's add a profile.\u003C/p>\u003Cp>What are we gonna call this? Let's just call this letters dot view. We'll get a view component set up. Lang equals TS. Great.\u003C/p>\u003Cp>I need to work on my little macros here. Okay. Alright. The other thing that you'll notice here is that I am using cursor. So cursor I recently started testing this thing out.\u003C/p>\u003Cp>Really enjoying the actual auto completions for this thing. So, I don't have it usually generate like a a giant list of code, but the automations, or the auto completions are are pretty nice for this thing. So let's start with, what, step one. It'd be enter GitHub profile info. GitHub username.\u003C/p>\u003Cp>Alright. So the only thing here, sometimes it gets a little wonky with the okay. So we use the you form from Nuxt UI. Good question, Brian. The new one, the alpha, they changed some of the conventions.\u003C/p>\u003Cp>So I've got a form with a schema. I've got a form field instead of a form group, and then I've got an input. Okay. So we've got the form, new form field, and input GitHub username. Let's just see what that gets us on the front end.\u003C/p>\u003Cp>We're gonna go to this page, which is letters. Okay. Alright. So let's go ahead and just center this up. I think there's actually a container component we can use.\u003C/p>\u003Cp>Great. Cool. Okay. So now we have a GitHub username, and let's add a submit button. New button, click handle submit, and boom.\u003C/p>\u003Cp>We have a GitHub username, blah blah blah. Hit submit. Supposedly does something. What it's gonna do right now? Absolutely nothing.\u003C/p>\u003Cp>Alright. So the next thing that we wanna do, let's kick this thing off. We want to have the form state, we use reactive for that. Great. GitHub username.\u003C/p>\u003Cp>Okay. And then we're gonna write a function to handle submit. Thank you. Yeah. Great.\u003C/p>\u003Cp>We'll just, console log that. Right? Boom. There we go. We can see the GitHub username, yada yada yada.\u003C/p>\u003Cp>Alright. This is actually gonna be an async function. Great. Okay. So now what do we wanna do with this?\u003C/p>\u003Cp>Right? We have to think about our application structure. And what I'm gonna do here is just basically add a Nuxt server route. So if we break this down, in this server route, what we're gonna do, call the GitHub API, call GitHub API. We're gonna wanna grab a couple pieces of information like the user profile, or any other repos, and maybe, like, their their public read me.\u003C/p>\u003Cp>I guess we could loop through the actual repos and, you know, pick up more information there, but, let's see what we can get done with that piece. Alright. So let's just go here. We're gonna set up a new route. Let's call it, roast route.\u003C/p>\u003Cp>We'll do post, and just gonna copy the event handler here. K. So now whenever we hit this route with a post, it should return hello world. We can just check and see if that's actually gonna work. So, we will do the I'm trying to think if that's gonna return.\u003C/p>\u003Cp>Nope. So we got the response. We're gonna do await. We can use the regular fetch or the dollar sign fetch, which is the Nuxt specific version. And just test this out, see what we get back in the console.\u003C/p>\u003Cp>Where are you? Okay. Yeah. So we can see the request going out. We can see hello world coming back.\u003C/p>\u003Cp>Great. Cool. Alright. So now what we're gonna do, right, let's just scaffold this out. We are going to pick up the body.\u003C/p>\u003Cp>There's a wait read body. Great. So that is going to have the GitHub username in there. And, how did we spell that? Yep.\u003C/p>\u003Cp>Great. Alright. So we're gonna say GitHub username, and then we've got, like, this git roast function. I'm not really sure where some of these auto completions are coming from. But, what we're gonna do next, let's call yep.\u003C/p>\u003Cp>There we go. That's a good one. API users, GitHub username. Is that the correct one? Let's just test that.\u003C/p>\u003Cp>All the developers on my team are screaming and crying at the moment, watching all these AI auto completions. So that seems fair. Great. And let's actually use the Nuxt equivalent. Just this is using OFETCH, which does some automated data transformation and should automatically throw errors for you as well, which is nice.\u003C/p>\u003Cp>So this is gonna be the let's call this a profile. Alright. And then if we take a look at the profile, we probably wanna get the actual repos for that user as well. Alright. So we'll get the repos.\u003C/p>\u003Cp>Great. And let's just take a look at the data we're getting from the actual repos. Okay. So what do we actually concern ourself with here? Do we actually want all of this information?\u003C/p>\u003Cp>What do we actually care about from these? So stargazers, watchers counts, maybe those properties. You know what? Let's just jam it all in there and see what comes out of it. Right?\u003C/p>\u003Cp>And then let's get the profile readme. GitHub user content, GitHub username, repos dot name, Repos dot name. No. That's not gonna cut it. I think it's gonna be, what, GitHub username.\u003C/p>\u003Cp>GitHub username. Somebody who's already done this before. Give me the structure. And then main. Let's just see if we can find that.\u003C/p>\u003Cp>Read me will just populate my name. I don't even know if I have a actually have a read me. Yeah. There we go. Okay.\u003C/p>\u003Cp>So that is the structure. Great. That is what we needed to confirm. And now let's just actually return this and see what we'll get back. Alright.\u003C/p>\u003Cp>Brian Gillespie. Now I'm gonna fire this away. Roast. No. Nothing found.\u003C/p>\u003Cp>Well, that's a little concerning. GitHub username equals body. Read the body. We have fetched the user's GitHub username. Let's just console log the username.\u003C/p>\u003Cp>API dot GitHub users. I don't see the actual username coming back. GitHub. That's always fun. Alright.\u003C/p>\u003Cp>What did I do wrong? GitHub underscore username. Okay. Oh, duh. Are we actually passing that in the body of the form?\u003C/p>\u003Cp>Form. Console dot log response. Request payload. API slash roast dot post. What are we getting back here?\u003C/p>\u003Cp>GitHub username form dot GitHub username. Oh, that's right. We are missing a state variable here. So is it actually submitting the form? No?\u003C/p>\u003Cp>No. Okay, friends. What do we do from here? We have handle submit. We're going to use fetch await.\u003C/p>\u003Cp>Wait that fetch request. We should have already got this back. Of course. There it is. What a dumb dumb.\u003C/p>\u003Cp>Forgot to actually fix the v model there. So that's what that is. Sometimes, these are not great to do at the end of the day. But okay. Where we at as far as time?\u003C/p>\u003Cp>We've got forty two minutes remaining. I feel pretty confident on this one. Alright. Now with our roast, we can remove this. We should be able to get that information.\u003C/p>\u003Cp>Now let's make sure that we're getting what we want back from that API. Great. There's the profile. There's the repos, and there's the profile. Read me.\u003C/p>\u003Cp>Great. Alright. So what are we gonna do with these now? Right? The next step in this process would be to, pass the profile to LLM and ask it to summarize for us.\u003C/p>\u003Cp>What do we want this to return? It should return something that looks like this. We want a letter from Santa. Letter from Santa in HTML. And then we're gonna want the the list, naughty or nice.\u003C/p>\u003Cp>And that should be all we really need to return. Alright. LLM returns JSON. Cool. So what is the LLM we're gonna use?\u003C/p>\u003Cp>You know, typically, I use OpenAI for a lot of the stuff that I do here at Directus. I've been messing around a lot with, Claude locally. So we're just going to try this out. Santa letters. So we're gonna use anthropic.\u003C/p>\u003Cp>There's my API key. We're going to drop that in our ENV file, if I can actually get there. Jeez. There we go. I'm just gonna call it Claude ABI key.\u003C/p>\u003Cp>Okay. Great. And by the time you've watched this, hopefully, I've disabled that key. So, don't stop the video and try to figure that out. Alright.\u003C/p>\u003Cp>Let's pull up our docs for the API. We need to get the API reference. And let's define this prompt. Prompt. You are a letter writing AI.\u003C/p>\u003Cp>Alright. Analyze the following GitHub profile. You are the open source Santa Claus. You determine whose open source contributions are naughty or nice, analyze the following GitHub profile, Return a JSON object with the following fields, a letter from Santa and HTML. Set a really high bar for the nice list.\u003C/p>\u003Cp>What else do we need as far as a prompt? And, yeah, here is the data, profile, readme, json, stringify. Wonder why it's doing that. But okay. Nevertheless, there we go.\u003C/p>\u003Cp>Turn a JSON object instead of really write the letter in a snarky sarcastic tone. Cool. Alright. And now we're going to send that to Anthropic. Alright.\u003C/p>\u003Cp>So if we look at their oh, looks like we could just use their JavaScript SDK. That's great. Let's go ahead and open this up. We'll fire that up, install this thing. Import anthropic.\u003C/p>\u003Cp>Great. And then we're going to create that message. Alright. Constant AI response equals anthropic messages dot create Claude Sonnet. Okay.\u003C/p>\u003Cp>Messages user role, content prompt. Do we wanna set, like, max tokens? What is the what's the default for max tokens? Where do we actually pass this API key? Getting started authentication, x API key.\u003C/p>\u003Cp>API key equals process e n v. And, again, like, you could start to see why I really like using cursor because it has, like, this sixth sense for a lot of this stuff that I'm actually trying to do. Sometimes it gets that wrong, but a lot of times it gets it right. So alright, AI response messages. Do we wanna set a max tokens?\u003C/p>\u003Cp>Body messages, max tokens required. Let's give some more parameters. Write a short letter in a short in a snarky sarcastic tone. That is 500 words or less. And then for the tokens, if we look at Sonnet, we've got like a context window of like 200,000, so maybe a hundred thousand tokens.\u003C/p>\u003Cp>Oh, no. That's the output. Max output is eight nine one two. That's fine. Max tokens.\u003C/p>\u003Cp>Great. And let's return. Actually, what we're gonna do next is save that to the Directus database. Right? So we've got this collection for our profile.\u003C/p>\u003Cp>What I've also done, I've got a utility set up here. This is just using the Directus SDK. And one of the nice things about Nuxt, I say that a lot, is, the ability to it will auto import this for me. So I don't have to import it. I should just be able to call Directus server right here.\u003C/p>\u003Cp>So let's call it Directus response equals await directus server dot request create item. That's going to be in the profile. And we'll do the GitHub username. That's actually going to be username. Letter response, content dot text.\u003C/p>\u003Cp>I don't actually know what we're gonna get back directly. Return only a JSON object. And maybe we wanna add something like this for let's just do code. We'll set this up. And I'm just gonna add a field for, let's call it metadata or something where I'm just gonna store the entire response.\u003C/p>\u003Cp>And honestly, let's just do that to begin with. Metadata, AI response, content dot text. So if we take a look at the API reference, we go back to messages here. I'm kinda curious as to what we're gonna get back. The content text.\u003C/p>\u003Cp>Okay. Type text something. We'll get back something from the system. Let's just even do it this way. We'll say content direct us response, and then we're going to return direct us response.\u003C/p>\u003Cp>See what that gives us. Now let's go in. Where's our app? We'll switch back to Chrome. I do like Arc.\u003C/p>\u003Cp>I've found it to be lacking for development because it's just not super fast. Alright. So fingers crossed that this actually does what it should do. And, let's make this even nicer. And we'll add, like, a loading state, constant loading, ref equals false.\u003C/p>\u003Cp>We'll add loading dot value equals true. Loading dot value equals false. Great. And what else do we want to do? Is there a loading state on the actual form?\u003C/p>\u003Cp>Let's take a look. So Nuxt UI state, there is not a loading state on that. There should be on the button though. So just update that. Okay.\u003C/p>\u003Cp>And let's test this bad way out. Submit. Alright. We're waiting. We're waiting.\u003C/p>\u003Cp>We're waiting. We're waiting. We're waiting. This could take a minute. So, you know, we might even want to, like, potentially set up a oh, okay.\u003C/p>\u003Cp>So we're not getting anything back. We see a request error. So let's go into our roast, and we should probably do some error handling. Alright. Catch error, console error.\u003C/p>\u003Cp>Return, or we could just throw the error. What do we got here? Format. Alright. Let's refresh.\u003C/p>\u003Cp>I'll try this again and see what kind of error we're getting and why. Pending. Invalid user credentials for Directus. Okay. Great.\u003C/p>\u003Cp>So, just wasting tokens there, throwing them into the void. One of the things that you'll notice, I do have this direct as URL set up, but, my server token is probably a % not correct. So I'm gonna go in and create a token for this. We'll just create a new token. We'll call this the server token.\u003C/p>\u003Cp>And I wanna make sure in my utility that I have that set as server token, direct us URL. Okay. Let's try this thing again. PPM dev. I will restart the dev server, pull in that new ENV, though I think Nuxt may automatically update that for us.\u003C/p>\u003Cp>How we doing on time? We got twenty nine minutes left, so I'm feeling pretty confident that we can get something out of this. Let's go ahead and try it again. Bryant Gillespie. Submit.\u003C/p>\u003Cp>K. K. Roast. You do not have permission to access this. Okay.\u003C/p>\u003Cp>Can anybody spot the error? It is because I left off a s. We have profiles, and this is profile. So again, if I I don't know if I you can actually see the logs for anthropic. Okay.\u003C/p>\u003Cp>Yeah. We could see here's the actual logs. It's probably not showing what we've got there. But anyway alright. We'll try this one more time.\u003C/p>\u003Cp>Let's just clean this up a bit. And away we go. Dun dun dun. I don't like the looks of this, actually. Let's just reset.\u003C/p>\u003Cp>Try this again. AI response. We got the prompt. Got the profile. Dun dun dun.\u003C/p>\u003Cp>The moment of truth. Are we actually gonna be able to get this thing to work? Brig Gillespie. Submit. Obviously, this would probably be better as, like, a background job or something like that.\u003C/p>\u003Cp>Alright. So we refresh, and we have something here. Okay. Yeah. So we're getting some text back.\u003C/p>\u003Cp>It looks like we need to parse the JSON. The letter is going to be text parsed response, text, parse response dot list, and then we get metadata, which would just be the parse response, I'm assuming. Alright. We're gonna delete this out. Let's run this again.\u003C/p>\u003Cp>And hopefully I'm not burning through all these credits that I loaded up. Okay. So now we're looking great. Okay. So we have our username.\u003C/p>\u003Cp>We've got our letter. Ho ho ho. What do we have here? Another developer thinking they can impress Santa with a few measly repositories. I've seen l's with more impressive profiles.\u003C/p>\u003Cp>I got made it to the naughty list. Great. Amazing. Alright. So now that's working as intended.\u003C/p>\u003Cp>Let's let's make this pretty. Right? The form, we're going to do max width. Maybe we set this to Excel. Move that form to the somewhat in the center of the page.\u003C/p>\u003Cp>And let's just lean on AI here. Right? This is already pretty cool. One of the other things I wanna do is maybe we set up a route where we actually surface this letter. Right?\u003C/p>\u003Cp>So if we do let's do letters as a directory inside pages. And we're gonna do the username in brackets. So just take this username, make that in brackets, and then I'm gonna put letters inside here, and we'll change the name of this to the index route. Alright. So let's just clean this up a bit, wrap this up, and console the error loading.\u003C/p>\u003Cp>Actually, we could do that in finally. Great. And what we're gonna do, if the response is good, we could navigate to the username page. Cool. And that way, you know, basically, like, this could get very expensive if if you made this thing public.\u003C/p>\u003Cp>Right? You don't want people generating like 35 letters to Santa. So we can add a check to the database if we've already got that GitHub username and just return the letter that we we already have. Right? Okay.\u003C/p>\u003Cp>So on the response, as long as there's no error, we're going to navigate dot to form. Github username. And this would be await. Navigate to. Cool.\u003C/p>\u003Cp>Alright. Now let's just lean on AI and see what we could do. Add some Christmas theming to this. Let's see what this actually will do. Add some Christmas thinging, ho ho ho.\u003C/p>\u003Cp>Looks like it's generating some random messages. Code review letters to Santa, random message, decorative elements. Great. Love decorative elements. Now, with cursor, I'm just gonna click apply here.\u003C/p>\u003Cp>It should go through and run through this actual code. I can close this out and see, you know, in kind of a preview way what it's gonna change. And if we hit reload oh, what we got going on here? Letters index. Is that because I changed the route?\u003C/p>\u003Cp>Okay. Yeah. Now we're looking very festive here. This looks this looks great. AI, what can you do?\u003C/p>\u003Cp>Alright. The other thing I see, maybe we want this to be block. Will that get it done? Block. Class.\u003C/p>\u003Cp>Let's just make the width full. Width full. Okay. And then let's shrink this actual form a bit. Yeah.\u003C/p>\u003Cp>MD. There we go. Alright. We're deep in the Christmas cheer now. And, while we wait, let's well, not while we wait.\u003C/p>\u003Cp>Let's actually go in and now we're gonna work on this letter. Alright. So, this does have a Nux plug in. This is just my boilerplate where I can go in and actually request the information from Directus on the client side, or, you know, I could set up a route for this on the server side in Nuxt. Both of those ways are are totally valid depending on your application.\u003C/p>\u003Cp>Obviously, totally up to you. We will just, let's let's keep it the same theme. We're going to, like, fetch roast, or we could do, like, a roast.git.ts. And what are we gonna pass? Do we want to pass the username as a param, or we'll just pass it as a query parameter?\u003C/p>\u003Cp>Okay. So in this one, what we're gonna do, we will call the profiles endpoint inside Directus. So we'll just go const, response equals await Directus server. And, you know, sometimes you wanna make requests on the server side. That's why I've got this set up, this way.\u003C/p>\u003Cp>We're gonna do read item, and I gave this a UUID. We could've used the actual profile as the primary key. But, what we're gonna do, read profiles, and we're gonna set up a query for this. So we'll do a filter parameter, the username. So that's the field.\u003C/p>\u003Cp>We're gonna drop down again. This will be equal to the username. So first we're gonna have to get the username equals get router param. Nope. We're gonna do git query, and that would just be the query.\u003C/p>\u003Cp>Great. Username. We could destructure this if we wanted to. Return username equals username, and we're gonna return that response. Great.\u003C/p>\u003Cp>Cool. So now we do this. And on this one, what we can do is use the use fetch composable from Nuxt. So this will be we've got some data. We're gonna use fetch, and we're gonna call API slash roast.\u003C/p>\u003Cp>And the is it params? I believe. See what we got. And let's add the so the same festivities, I guess. Festiveness.\u003C/p>\u003Cp>Perfect. Alright. Decorative elements, blah blah blah, random messages. We're gonna put that up here in the script. Okay.\u003C/p>\u003Cp>Code review letters to Santa. And instead of the form, right, we're gonna replace this with data. Alright. So now if I do this, what's gonna happen? Route is not defined.\u003C/p>\u003Cp>Okay. So we just need to call use route to fetch that route. And do we actually get the stuff that we need here? We could test this API as well. Letters API roast username equals Brian Gillespie.\u003C/p>\u003Cp>Okay. Yeah. So that's getting us what we want from direct us except is it query? What is the use fetch? This is where, like, Nuxt documentation comes in handy.\u003C/p>\u003Cp>Use fetch. Where we at? We got sixteen minutes remaining. We got data use fetch. What are the URL query?\u003C/p>\u003Cp>Okay. Alias for query. That's what I thought. Root params username. Oh, data.\u003C/p>\u003Cp>Are we actually let's jump into the view dev tools. We'll hit the username route. And I see the data here. Here's the issue. Right?\u003C/p>\u003Cp>It is returning an array. So inside our routes, we could, you know, do something like this where we're just picking off the first item. I could also do that transform that on the the Nuxt side if I wanted to. Here's our letter from Santa. Cool.\u003C/p>\u003Cp>Code review letters from Santa. What I'm gonna do, let's use the pros class from Tailwind to get styling for this. We'll make the text dark green. Great. That's fine.\u003C/p>\u003Cp>And then the interior of this, we're just going to use v HTML. So we get this. Do I not have Tailwind typography into this? At plug in Tailwind typography. Okay.\u003C/p>\u003Cp>Yeah. So there we go. Now we've got the letter from Santa Claus. This is looking really nice. Perfect.\u003C/p>\u003Cp>Let's add like a cursive font. Right? Font family cursive. And this is Tailwind four, where all the config is basically CSS variables. So, really enjoying that Nuxt module, playing around with it.\u003C/p>\u003Cp>Let's find a handwritten font. Okay. Caveat. Looks nice. Nuxt has also added a a like this font amazing thing where you just throw your fonts in the CSS and it will actually download these things for you.\u003C/p>\u003Cp>So let's take a look at this. Right? I'm just gonna change this to font cursive and bada bing bada boom, we get what we want. So let's put, like, pros XL to XL. And there we go.\u003C/p>\u003Cp>So dear Bryant, what do we have here? Blah blah blah, etcetera. We have got thirteen minutes left on the clock. What can we do for fun? Let's go back and actually test this thing out.\u003C/p>\u003Cp>I'm just gonna refresh. There we go. I'm gonna do our fearless leader here at Directus, mister Ben Haines. We're gonna send this to Santa, and something bad happened. We could not find okay.\u003C/p>\u003Cp>So it looks like this one is not finding Ben's profile. Haynes, Maine. And Haynes Haynes Haynes Haynes Haynes. Would that be at, like, Master branch maybe? Where's our roast?\u003C/p>\u003Cp>Roast.post, profile read me. Try. I bet it's at master. I'm just gonna do this the quick and dirty way. Alright.\u003C/p>\u003Cp>So we go back. Let's try this again. Mister Ben Haines, we're about to roast you, sir. Alright. So we're checking the list twice.\u003C/p>\u003Cp>And eleven minutes on the clock. We've got the letter to Ben Haines from Ben Haines. Why are we not seeing the actual letter? There it is. I'm dreaming of a Nuxt application that actually works.\u003C/p>\u003Cp>What is going on with this? Letters, username, data dot name. I'm assuming because there is no name. Username. I'm running this on a sour note here.\u003C/p>\u003Cp>Ben Haines. That's kinda weird. Ben Haines. Why is it doing that? API roast username.\u003C/p>\u003Cp>What is going on here? Get async data. API roast. Why does it work for me and not for mister Haynes? What are we actually doing wrong here?\u003C/p>\u003Cp>Did I spell the name wrong? GitHub username. Alright. E pipe. Use fetch roast.\u003C/p>\u003Cp>Can't find the username? Profiles get username, get query. Is it read query? No. It's get query.\u003C/p>\u003Cp>Return query. API API roast. Ben Haines. So why aren't we why isn't this working? So it's not actually finding the username for that, which is odd because I have the username right there.\u003C/p>\u003Cp>Filter contains. Okay. I don't understand it, but we're gonna roll with it. Great. Some type of encoding or something maybe.\u003C/p>\u003Cp>Not sure. Booyah. Ben, I'm gonna read this to you. Dear Ben, ho ho ho. Well, isn't this embarrassing?\u003C/p>\u003Cp>I've been reviewing your GitHub profile, and I must say I'm thoroughly underwhelmed. 20 whole repositories, you must have been super busy this century. Meanwhile, Santa's got billions of believers worldwide. Look, I'm not saying you're on the naughty list because your contributions are lackluster. I'm saying if you were open source for coal, you barely have enough to heat a dollhouse.\u003C/p>\u003Cp>That is brutal. So let's call that a win. This is AI letters with Santa. Do we wanna do one more just for fun? Just for giggles?\u003C/p>\u003Cp>Let's let's test this out. Directus Directus. I forget Reich's actual, GitHub profile. There it is. Okay.\u003C/p>\u003Cp>So we're gonna throw mister Reich Van Zanten in there, our CTO, see what comes out of this thing. Hopefully, we got everything we need. It will do its thing. And and, that's not gonna pick on Wrike. Yeah.\u003C/p>\u003Cp>I don't know what's going on with this thing. Potentially some kind of caching issue. Don't know. Anyway, response zero. Down to the wire, five minutes, four minutes, three minutes, two minutes, no minutes.\u003C/p>\u003Cp>Is the server running? Use async data. And what if we just use fetch? Response. It's gonna be response dot letter.\u003C/p>\u003Cp>Oh, boy. Response fetch, browse, params, username. Come on. Failed to stringify the server logs. What is going on?\u003C/p>\u003Cp>This feels like a crappy way to end this one. It should be, like, festive with holiday cheer. I don't understand what is going on with Nox. Oh, duh, dummy. You have to wait the promise.\u003C/p>\u003Cp>Is that getting us what we need? Still not getting us what we need. Data is not defined on the instance. Where else am I getting the data at? Fested messages, data.\u003C/p>\u003Cp>Oh, if response. We're not even getting a response. SSR, undefined, undefined. Hey. That's the way the cookie crumbles sometimes.\u003C/p>\u003Cp>I'm not sure what I am doing wrong with this. I'm sure it'll come to me right after I get done with this. Is it like a key? Cash. Cash, no cash.\u003C/p>\u003Cp>Cache. No cache. No cache. Well, at least Ben's works. At least mine works.\u003C/p>\u003Cp>Not entirely sure what's going on with this little API that I've written, why it is caching this. But, hey, that's AI letters to Santa. That's the way it goes. This has been a hundred apps, hundred hours. Thanks for joining me.\u003C/p>\u003Cp>We'll catch you on the next episode. See you.\u003C/p>","Alright. Alright. Alright. We are back with the Christmas edition of 100 apps, one hundred hours. Today, we are going to be building AI letters from Santa. I've got my lumberjack style on today. My wife called this lumberjack Jesus earlier, but I digress. We're back for more. The rules of 100 apps, one hundred hours. If this is your first show, we have sixty minutes to plan and build an application, a website, a portal, whatever. Whatever we're building, sixty minutes, no more, no less. And rule number two, the anti rule, use whatever you have at your disposal. And since this is an AI Christmas special, I'm gonna pull out all the stops. So let's dive right in. We're gonna hit the clock here. Fire it up. Sixty minutes on the timer. Go. Alright. So AI letters from Santa. What do we actually want out of this? So I have to admit, I cheated a little bit because I thought about this with my team, and I knew we wanted to do this. I've seen things in the past where you write a letter to Santa, you get something back in the mail, etcetera, etcetera. With AI, we could take this up a notch. So combining two ideas. A while back, I saw a GitHub roast page where you enter in your GitHub profile and it, basically will scrape that and give you a roast of how well you're actually not doing in GitHub. So we're gonna combine that with a letter from Santa. And basically, what we wanna do is, enter a GitHub profile. We're gonna scrape that profile. We're going to send that to AI. So we want LLM analysis of the profile. I'm not sure what we're gonna call that. And then I'm going to bucket people on the open source naughty or nice list. So score naughty or nice list. And then we're gonna generate a letter from open source Santa. Generate a letter from open source Santa to that GitHub profile to that profile. Alright. So as far as that functionality, this looks pretty good. Right? What are the tools that we're gonna use of the trade today? I've got a Directus Docker container up and running locally. Directus is obviously the back end we're using to store all of these things. And if everything works as intended p m p m g. I guess, sometimes things don't work as you intend. I've got a Nuxt application that we are going to try and use here. I'm not sure what's going on, but let's hop into the Directus instance. So I'm just gonna pull up Chrome. We'll log in to 8055, and I should be able to pull up my back end. So great. Got Directus running. You could see this is a pretty blank instance of Directus. This is just the boilerplate I use now. There are a couple extensions installed that I was testing, just messing around with. But, let's make sure. What are we doing here? For MPMI. Sometimes these things never go as planned. Okay. So maybe now we can get this Nuxt application up and running that will be served at local host 3,000, and we'll just basically use it to scaffold out our communications. As far as what I'm using, I've upgraded this boilerplate that I've used for 100 apps to, the Nuxt UI v three alpha, just to play around with Tailwind four and, you know, some of these nice new components that are coming from, like, Radix view. So alright. Let's actually model this thing out. Right? What do we need as far as our data models? I think we just need, like, a maybe like a profiles. So under profiles, we would have, what, our username, letter from Santa, letter from Santa, list, you know, are you naughty or nice? Great. And what else do we need? Let's let's jam on that. So we'll, just set up the back end for this. I'm just gonna create a new collection. We're going to call this profiles is the name of it. And why can't I zoom way in? There we go. That's maybe too far, but all good. Let's do created at, updated at. K. Status sort not needed. Who this was created by, I'm not super concerned with. So now we have a profile. We're gonna do the username. Great. That's where we'll store the GitHub profile. What else do we need? What else did we have here? We've got the letter from Santa. What is that gonna be in letter from let's just call it letter. Great. We'll use the WYSIWYG editor inside Directus so we could just store, I'm assuming, HTML content for that. And then we've got the list. So that's basically gonna be a string. We can, you know, make this look nice inside the directus admin. We'll just give it a naughty. Feel naughty just typing that out, and then we have the nice list. Great. There we go. And I'm just going to go on record that we're probably as soon as we start typing naughty into the AI stuff, we'll probably get some some things back. Like a I probably set off the content alarms or something like that. So there we go. We've got a username. We've got a letter. We've got a list. You know, I could potentially put that in here. What I'm gonna do now, I'm just gonna let's just we're just using Directus to store this at the moment. Right? There's a lot of different ways I could go with how to actually generate the application here. But Directus allows me to create custom extensions. What I'm gonna do here is just start, working on this from the Nuxt side of it. So we're gonna input the, actual form here. Let's add a profile. What are we gonna call this? Let's just call this letters dot view. We'll get a view component set up. Lang equals TS. Great. I need to work on my little macros here. Okay. Alright. The other thing that you'll notice here is that I am using cursor. So cursor I recently started testing this thing out. Really enjoying the actual auto completions for this thing. So, I don't have it usually generate like a a giant list of code, but the automations, or the auto completions are are pretty nice for this thing. So let's start with, what, step one. It'd be enter GitHub profile info. GitHub username. Alright. So the only thing here, sometimes it gets a little wonky with the okay. So we use the you form from Nuxt UI. Good question, Brian. The new one, the alpha, they changed some of the conventions. So I've got a form with a schema. I've got a form field instead of a form group, and then I've got an input. Okay. So we've got the form, new form field, and input GitHub username. Let's just see what that gets us on the front end. We're gonna go to this page, which is letters. Okay. Alright. So let's go ahead and just center this up. I think there's actually a container component we can use. Great. Cool. Okay. So now we have a GitHub username, and let's add a submit button. New button, click handle submit, and boom. We have a GitHub username, blah blah blah. Hit submit. Supposedly does something. What it's gonna do right now? Absolutely nothing. Alright. So the next thing that we wanna do, let's kick this thing off. We want to have the form state, we use reactive for that. Great. GitHub username. Okay. And then we're gonna write a function to handle submit. Thank you. Yeah. Great. We'll just, console log that. Right? Boom. There we go. We can see the GitHub username, yada yada yada. Alright. This is actually gonna be an async function. Great. Okay. So now what do we wanna do with this? Right? We have to think about our application structure. And what I'm gonna do here is just basically add a Nuxt server route. So if we break this down, in this server route, what we're gonna do, call the GitHub API, call GitHub API. We're gonna wanna grab a couple pieces of information like the user profile, or any other repos, and maybe, like, their their public read me. I guess we could loop through the actual repos and, you know, pick up more information there, but, let's see what we can get done with that piece. Alright. So let's just go here. We're gonna set up a new route. Let's call it, roast route. We'll do post, and just gonna copy the event handler here. K. So now whenever we hit this route with a post, it should return hello world. We can just check and see if that's actually gonna work. So, we will do the I'm trying to think if that's gonna return. Nope. So we got the response. We're gonna do await. We can use the regular fetch or the dollar sign fetch, which is the Nuxt specific version. And just test this out, see what we get back in the console. Where are you? Okay. Yeah. So we can see the request going out. We can see hello world coming back. Great. Cool. Alright. So now what we're gonna do, right, let's just scaffold this out. We are going to pick up the body. There's a wait read body. Great. So that is going to have the GitHub username in there. And, how did we spell that? Yep. Great. Alright. So we're gonna say GitHub username, and then we've got, like, this git roast function. I'm not really sure where some of these auto completions are coming from. But, what we're gonna do next, let's call yep. There we go. That's a good one. API users, GitHub username. Is that the correct one? Let's just test that. All the developers on my team are screaming and crying at the moment, watching all these AI auto completions. So that seems fair. Great. And let's actually use the Nuxt equivalent. Just this is using OFETCH, which does some automated data transformation and should automatically throw errors for you as well, which is nice. So this is gonna be the let's call this a profile. Alright. And then if we take a look at the profile, we probably wanna get the actual repos for that user as well. Alright. So we'll get the repos. Great. And let's just take a look at the data we're getting from the actual repos. Okay. So what do we actually concern ourself with here? Do we actually want all of this information? What do we actually care about from these? So stargazers, watchers counts, maybe those properties. You know what? Let's just jam it all in there and see what comes out of it. Right? And then let's get the profile readme. GitHub user content, GitHub username, repos dot name, Repos dot name. No. That's not gonna cut it. I think it's gonna be, what, GitHub username. GitHub username. Somebody who's already done this before. Give me the structure. And then main. Let's just see if we can find that. Read me will just populate my name. I don't even know if I have a actually have a read me. Yeah. There we go. Okay. So that is the structure. Great. That is what we needed to confirm. And now let's just actually return this and see what we'll get back. Alright. Brian Gillespie. Now I'm gonna fire this away. Roast. No. Nothing found. Well, that's a little concerning. GitHub username equals body. Read the body. We have fetched the user's GitHub username. Let's just console log the username. API dot GitHub users. I don't see the actual username coming back. GitHub. That's always fun. Alright. What did I do wrong? GitHub underscore username. Okay. Oh, duh. Are we actually passing that in the body of the form? Form. Console dot log response. Request payload. API slash roast dot post. What are we getting back here? GitHub username form dot GitHub username. Oh, that's right. We are missing a state variable here. So is it actually submitting the form? No? No. Okay, friends. What do we do from here? We have handle submit. We're going to use fetch await. Wait that fetch request. We should have already got this back. Of course. There it is. What a dumb dumb. Forgot to actually fix the v model there. So that's what that is. Sometimes, these are not great to do at the end of the day. But okay. Where we at as far as time? We've got forty two minutes remaining. I feel pretty confident on this one. Alright. Now with our roast, we can remove this. We should be able to get that information. Now let's make sure that we're getting what we want back from that API. Great. There's the profile. There's the repos, and there's the profile. Read me. Great. Alright. So what are we gonna do with these now? Right? The next step in this process would be to, pass the profile to LLM and ask it to summarize for us. What do we want this to return? It should return something that looks like this. We want a letter from Santa. Letter from Santa in HTML. And then we're gonna want the the list, naughty or nice. And that should be all we really need to return. Alright. LLM returns JSON. Cool. So what is the LLM we're gonna use? You know, typically, I use OpenAI for a lot of the stuff that I do here at Directus. I've been messing around a lot with, Claude locally. So we're just going to try this out. Santa letters. So we're gonna use anthropic. There's my API key. We're going to drop that in our ENV file, if I can actually get there. Jeez. There we go. I'm just gonna call it Claude ABI key. Okay. Great. And by the time you've watched this, hopefully, I've disabled that key. So, don't stop the video and try to figure that out. Alright. Let's pull up our docs for the API. We need to get the API reference. And let's define this prompt. Prompt. You are a letter writing AI. Alright. Analyze the following GitHub profile. You are the open source Santa Claus. You determine whose open source contributions are naughty or nice, analyze the following GitHub profile, Return a JSON object with the following fields, a letter from Santa and HTML. Set a really high bar for the nice list. What else do we need as far as a prompt? And, yeah, here is the data, profile, readme, json, stringify. Wonder why it's doing that. But okay. Nevertheless, there we go. Turn a JSON object instead of really write the letter in a snarky sarcastic tone. Cool. Alright. And now we're going to send that to Anthropic. Alright. So if we look at their oh, looks like we could just use their JavaScript SDK. That's great. Let's go ahead and open this up. We'll fire that up, install this thing. Import anthropic. Great. And then we're going to create that message. Alright. Constant AI response equals anthropic messages dot create Claude Sonnet. Okay. Messages user role, content prompt. Do we wanna set, like, max tokens? What is the what's the default for max tokens? Where do we actually pass this API key? Getting started authentication, x API key. API key equals process e n v. And, again, like, you could start to see why I really like using cursor because it has, like, this sixth sense for a lot of this stuff that I'm actually trying to do. Sometimes it gets that wrong, but a lot of times it gets it right. So alright, AI response messages. Do we wanna set a max tokens? Body messages, max tokens required. Let's give some more parameters. Write a short letter in a short in a snarky sarcastic tone. That is 500 words or less. And then for the tokens, if we look at Sonnet, we've got like a context window of like 200,000, so maybe a hundred thousand tokens. Oh, no. That's the output. Max output is eight nine one two. That's fine. Max tokens. Great. And let's return. Actually, what we're gonna do next is save that to the Directus database. Right? So we've got this collection for our profile. What I've also done, I've got a utility set up here. This is just using the Directus SDK. And one of the nice things about Nuxt, I say that a lot, is, the ability to it will auto import this for me. So I don't have to import it. I should just be able to call Directus server right here. So let's call it Directus response equals await directus server dot request create item. That's going to be in the profile. And we'll do the GitHub username. That's actually going to be username. Letter response, content dot text. I don't actually know what we're gonna get back directly. Return only a JSON object. And maybe we wanna add something like this for let's just do code. We'll set this up. And I'm just gonna add a field for, let's call it metadata or something where I'm just gonna store the entire response. And honestly, let's just do that to begin with. Metadata, AI response, content dot text. So if we take a look at the API reference, we go back to messages here. I'm kinda curious as to what we're gonna get back. The content text. Okay. Type text something. We'll get back something from the system. Let's just even do it this way. We'll say content direct us response, and then we're going to return direct us response. See what that gives us. Now let's go in. Where's our app? We'll switch back to Chrome. I do like Arc. I've found it to be lacking for development because it's just not super fast. Alright. So fingers crossed that this actually does what it should do. And, let's make this even nicer. And we'll add, like, a loading state, constant loading, ref equals false. We'll add loading dot value equals true. Loading dot value equals false. Great. And what else do we want to do? Is there a loading state on the actual form? Let's take a look. So Nuxt UI state, there is not a loading state on that. There should be on the button though. So just update that. Okay. And let's test this bad way out. Submit. Alright. We're waiting. We're waiting. We're waiting. We're waiting. We're waiting. This could take a minute. So, you know, we might even want to, like, potentially set up a oh, okay. So we're not getting anything back. We see a request error. So let's go into our roast, and we should probably do some error handling. Alright. Catch error, console error. Return, or we could just throw the error. What do we got here? Format. Alright. Let's refresh. I'll try this again and see what kind of error we're getting and why. Pending. Invalid user credentials for Directus. Okay. Great. So, just wasting tokens there, throwing them into the void. One of the things that you'll notice, I do have this direct as URL set up, but, my server token is probably a % not correct. So I'm gonna go in and create a token for this. We'll just create a new token. We'll call this the server token. And I wanna make sure in my utility that I have that set as server token, direct us URL. Okay. Let's try this thing again. PPM dev. I will restart the dev server, pull in that new ENV, though I think Nuxt may automatically update that for us. How we doing on time? We got twenty nine minutes left, so I'm feeling pretty confident that we can get something out of this. Let's go ahead and try it again. Bryant Gillespie. Submit. K. K. Roast. You do not have permission to access this. Okay. Can anybody spot the error? It is because I left off a s. We have profiles, and this is profile. So again, if I I don't know if I you can actually see the logs for anthropic. Okay. Yeah. We could see here's the actual logs. It's probably not showing what we've got there. But anyway alright. We'll try this one more time. Let's just clean this up a bit. And away we go. Dun dun dun. I don't like the looks of this, actually. Let's just reset. Try this again. AI response. We got the prompt. Got the profile. Dun dun dun. The moment of truth. Are we actually gonna be able to get this thing to work? Brig Gillespie. Submit. Obviously, this would probably be better as, like, a background job or something like that. Alright. So we refresh, and we have something here. Okay. Yeah. So we're getting some text back. It looks like we need to parse the JSON. The letter is going to be text parsed response, text, parse response dot list, and then we get metadata, which would just be the parse response, I'm assuming. Alright. We're gonna delete this out. Let's run this again. And hopefully I'm not burning through all these credits that I loaded up. Okay. So now we're looking great. Okay. So we have our username. We've got our letter. Ho ho ho. What do we have here? Another developer thinking they can impress Santa with a few measly repositories. I've seen l's with more impressive profiles. I got made it to the naughty list. Great. Amazing. Alright. So now that's working as intended. Let's let's make this pretty. Right? The form, we're going to do max width. Maybe we set this to Excel. Move that form to the somewhat in the center of the page. And let's just lean on AI here. Right? This is already pretty cool. One of the other things I wanna do is maybe we set up a route where we actually surface this letter. Right? So if we do let's do letters as a directory inside pages. And we're gonna do the username in brackets. So just take this username, make that in brackets, and then I'm gonna put letters inside here, and we'll change the name of this to the index route. Alright. So let's just clean this up a bit, wrap this up, and console the error loading. Actually, we could do that in finally. Great. And what we're gonna do, if the response is good, we could navigate to the username page. Cool. And that way, you know, basically, like, this could get very expensive if if you made this thing public. Right? You don't want people generating like 35 letters to Santa. So we can add a check to the database if we've already got that GitHub username and just return the letter that we we already have. Right? Okay. So on the response, as long as there's no error, we're going to navigate dot to form. Github username. And this would be await. Navigate to. Cool. Alright. Now let's just lean on AI and see what we could do. Add some Christmas theming to this. Let's see what this actually will do. Add some Christmas thinging, ho ho ho. Looks like it's generating some random messages. Code review letters to Santa, random message, decorative elements. Great. Love decorative elements. Now, with cursor, I'm just gonna click apply here. It should go through and run through this actual code. I can close this out and see, you know, in kind of a preview way what it's gonna change. And if we hit reload oh, what we got going on here? Letters index. Is that because I changed the route? Okay. Yeah. Now we're looking very festive here. This looks this looks great. AI, what can you do? Alright. The other thing I see, maybe we want this to be block. Will that get it done? Block. Class. Let's just make the width full. Width full. Okay. And then let's shrink this actual form a bit. Yeah. MD. There we go. Alright. We're deep in the Christmas cheer now. And, while we wait, let's well, not while we wait. Let's actually go in and now we're gonna work on this letter. Alright. So, this does have a Nux plug in. This is just my boilerplate where I can go in and actually request the information from Directus on the client side, or, you know, I could set up a route for this on the server side in Nuxt. Both of those ways are are totally valid depending on your application. Obviously, totally up to you. We will just, let's let's keep it the same theme. We're going to, like, fetch roast, or we could do, like, a roast.git.ts. And what are we gonna pass? Do we want to pass the username as a param, or we'll just pass it as a query parameter? Okay. So in this one, what we're gonna do, we will call the profiles endpoint inside Directus. So we'll just go const, response equals await Directus server. And, you know, sometimes you wanna make requests on the server side. That's why I've got this set up, this way. We're gonna do read item, and I gave this a UUID. We could've used the actual profile as the primary key. But, what we're gonna do, read profiles, and we're gonna set up a query for this. So we'll do a filter parameter, the username. So that's the field. We're gonna drop down again. This will be equal to the username. So first we're gonna have to get the username equals get router param. Nope. We're gonna do git query, and that would just be the query. Great. Username. We could destructure this if we wanted to. Return username equals username, and we're gonna return that response. Great. Cool. So now we do this. And on this one, what we can do is use the use fetch composable from Nuxt. So this will be we've got some data. We're gonna use fetch, and we're gonna call API slash roast. And the is it params? I believe. See what we got. And let's add the so the same festivities, I guess. Festiveness. Perfect. Alright. Decorative elements, blah blah blah, random messages. We're gonna put that up here in the script. Okay. Code review letters to Santa. And instead of the form, right, we're gonna replace this with data. Alright. So now if I do this, what's gonna happen? Route is not defined. Okay. So we just need to call use route to fetch that route. And do we actually get the stuff that we need here? We could test this API as well. Letters API roast username equals Brian Gillespie. Okay. Yeah. So that's getting us what we want from direct us except is it query? What is the use fetch? This is where, like, Nuxt documentation comes in handy. Use fetch. Where we at? We got sixteen minutes remaining. We got data use fetch. What are the URL query? Okay. Alias for query. That's what I thought. Root params username. Oh, data. Are we actually let's jump into the view dev tools. We'll hit the username route. And I see the data here. Here's the issue. Right? It is returning an array. So inside our routes, we could, you know, do something like this where we're just picking off the first item. I could also do that transform that on the the Nuxt side if I wanted to. Here's our letter from Santa. Cool. Code review letters from Santa. What I'm gonna do, let's use the pros class from Tailwind to get styling for this. We'll make the text dark green. Great. That's fine. And then the interior of this, we're just going to use v HTML. So we get this. Do I not have Tailwind typography into this? At plug in Tailwind typography. Okay. Yeah. So there we go. Now we've got the letter from Santa Claus. This is looking really nice. Perfect. Let's add like a cursive font. Right? Font family cursive. And this is Tailwind four, where all the config is basically CSS variables. So, really enjoying that Nuxt module, playing around with it. Let's find a handwritten font. Okay. Caveat. Looks nice. Nuxt has also added a a like this font amazing thing where you just throw your fonts in the CSS and it will actually download these things for you. So let's take a look at this. Right? I'm just gonna change this to font cursive and bada bing bada boom, we get what we want. So let's put, like, pros XL to XL. And there we go. So dear Bryant, what do we have here? Blah blah blah, etcetera. We have got thirteen minutes left on the clock. What can we do for fun? Let's go back and actually test this thing out. I'm just gonna refresh. There we go. I'm gonna do our fearless leader here at Directus, mister Ben Haines. We're gonna send this to Santa, and something bad happened. We could not find okay. So it looks like this one is not finding Ben's profile. Haynes, Maine. And Haynes Haynes Haynes Haynes Haynes. Would that be at, like, Master branch maybe? Where's our roast? Roast.post, profile read me. Try. I bet it's at master. I'm just gonna do this the quick and dirty way. Alright. So we go back. Let's try this again. Mister Ben Haines, we're about to roast you, sir. Alright. So we're checking the list twice. And eleven minutes on the clock. We've got the letter to Ben Haines from Ben Haines. Why are we not seeing the actual letter? There it is. I'm dreaming of a Nuxt application that actually works. What is going on with this? Letters, username, data dot name. I'm assuming because there is no name. Username. I'm running this on a sour note here. Ben Haines. That's kinda weird. Ben Haines. Why is it doing that? API roast username. What is going on here? Get async data. API roast. Why does it work for me and not for mister Haynes? What are we actually doing wrong here? Did I spell the name wrong? GitHub username. Alright. E pipe. Use fetch roast. Can't find the username? Profiles get username, get query. Is it read query? No. It's get query. Return query. API API roast. Ben Haines. So why aren't we why isn't this working? So it's not actually finding the username for that, which is odd because I have the username right there. Filter contains. Okay. I don't understand it, but we're gonna roll with it. Great. Some type of encoding or something maybe. Not sure. Booyah. Ben, I'm gonna read this to you. Dear Ben, ho ho ho. Well, isn't this embarrassing? I've been reviewing your GitHub profile, and I must say I'm thoroughly underwhelmed. 20 whole repositories, you must have been super busy this century. Meanwhile, Santa's got billions of believers worldwide. Look, I'm not saying you're on the naughty list because your contributions are lackluster. I'm saying if you were open source for coal, you barely have enough to heat a dollhouse. That is brutal. So let's call that a win. This is AI letters with Santa. Do we wanna do one more just for fun? Just for giggles? Let's let's test this out. Directus Directus. I forget Reich's actual, GitHub profile. There it is. Okay. So we're gonna throw mister Reich Van Zanten in there, our CTO, see what comes out of this thing. Hopefully, we got everything we need. It will do its thing. And and, that's not gonna pick on Wrike. Yeah. I don't know what's going on with this thing. Potentially some kind of caching issue. Don't know. Anyway, response zero. Down to the wire, five minutes, four minutes, three minutes, two minutes, no minutes. Is the server running? Use async data. And what if we just use fetch? Response. It's gonna be response dot letter. Oh, boy. Response fetch, browse, params, username. Come on. Failed to stringify the server logs. What is going on? This feels like a crappy way to end this one. It should be, like, festive with holiday cheer. I don't understand what is going on with Nox. Oh, duh, dummy. You have to wait the promise. Is that getting us what we need? Still not getting us what we need. Data is not defined on the instance. Where else am I getting the data at? Fested messages, data. Oh, if response. We're not even getting a response. SSR, undefined, undefined. Hey. That's the way the cookie crumbles sometimes. I'm not sure what I am doing wrong with this. I'm sure it'll come to me right after I get done with this. Is it like a key? Cash. Cash, no cash. Cache. No cache. No cache. Well, at least Ben's works. At least mine works. Not entirely sure what's going on with this little API that I've written, why it is caching this. But, hey, that's AI letters to Santa. That's the way it goes. This has been a hundred apps, hundred hours. Thanks for joining me. We'll catch you on the next episode. See you.","ec46771b-85fb-4967-9a7e-81102f96cf74",[184],"cc9262db-1ae2-4bc1-a1f9-7d2fc51a396d",[],{"reps":187},[188,244],{"name":189,"sdr":8,"link":190,"countries":191,"states":193},"John Daniels","https://meet.directus.io/meetings/john2144/john-contact-form-meeting",[192],"United States",[194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243],"Michigan","Indiana","Ohio","West Virginia","Kentucky","Virginia","Tennessee","North Carolina","South Carolina","Georgia","Florida","Alabama","Mississippi","New York","MI","IN","OH","WV","KY","VA","TN","NC","SC","GA","FL","AL","MS","NY","Connecticut","CT","Delaware","DE","Maine","ME","Maryland","MD","Massachusetts","MA","New Hampshire","NH","New Jersey","NJ","Pennsylvania","PA","Rhode Island","RI","Vermont","VT","Washington DC","DC",{"name":245,"link":246,"countries":247},"Michelle Riber","https://meetings.hubspot.com/mriber",[248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,225,436,437],"Albania","ALB","Algeria","DZA","Andorra","AND","Angola","AGO","Austria","AUT","Belgium","BEL","Benin","BEN","Bosnia and Herzegovina","BIH","Botswana","BWA","Bulgaria","BGR","Burkina Faso","BFA","Burundi","BDI","Cameroon","CMR","Cape Verde","CPV","Central African Republic","CAF","Chad","TCD","Comoros","COM","Côte d'Ivoire","CIV","Croatia","HRV","Czech Republic","CZE","Democratic Republic of Congo","COD","Denmark","DNK","Djibouti","DJI","Egypt","EGY","Equatorial Guinea","GNQ","Eritrea","ERI","Estonia","EST","Eswatini","SWZ","Ethiopia","ETH","Finland","FIN","France","FRA","Gabon","GAB","Gambia","GMB","Ghana","GHA","Greece","GRC","Guinea","GIN","Guinea-Bissau","GNB","Hungary","HUN","Iceland","ISL","Ireland","IRL","Italy","ITA","Kenya","KEN","Latvia","LVA","Lesotho","LSO","Liberia","LBR","Libya","LBY","Liechtenstein","LIE","Lithuania","LTU","Luxembourg","LUX","Madagascar","MDG","Malawi","MWI","Mali","MLI","Malta","MLT","Mauritania","MRT","Mauritius","MUS","Moldova","MDA","Monaco","MCO","Montenegro","MNE","Morocco","MAR","Mozambique","MOZ","Namibia","NAM","Niger","NER","Nigeria","NGA","North Macedonia","MKD","Norway","NOR","Poland","POL","Portugal","PRT","Republic of Congo","COG","Romania","ROU","Rwanda","RWA","San Marino","SMR","São Tomé and Príncipe","STP","Senegal","SEN","Serbia","SRB","Seychelles","SYC","Sierra Leone","SLE","Slovakia","SVK","Slovenia","SVN","Somalia","SOM","South Africa","ZAF","South Sudan","SSD","Spain","ESP","Sudan","SDN","Sweden","SWE","Tanzania","TZA","Togo","TGO","Tunisia","TUN","Uganda","UGA","United Kingdom","GBR","Vatican City","VAT","Zambia","ZMB","Zimbabwe","ZWE","UK","Germany","Netherlands","Switzerland","CH","NL",1773850436338]