[{"data":1,"prerenderedAt":475},["ShallowReactive",2],{"footer-primary":3,"footer-secondary":93,"footer-description":119,"100-apps-100-hours-slack":121,"100-apps-100-hours-slack-next":207,"sales-reps":223},{"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":128,"episode_number":141,"published":142,"title":143,"video_transcript_html":144,"video_transcript_text":145,"content":8,"status":146,"episode_people":147,"recommendations":188,"season":189,"seo":8},"c067c012-5fe1-4d70-8946-8bfc8e1b22c0","slack","918839097","In this special edition live episode, Bryant is joined by Kevin, Alex, and Matt to build a clone on Slack in just one hour with Directus Realtime and Nuxt. Join the absolute chaos as friends and colleagues try to \"help\" against the clock. ","08d6a2f3-8c06-4d00-b7c0-cec62b2144cb",66,[129,132,135,138],{"name":130,"url":131},"Bryant Gillespie","https://directus.io/team/bryant-gillespie",{"name":133,"url":134},"Alex van der Valk","https://directus.io/team/alex-van-der-valk",{"name":136,"url":137},"Kevin Lewis","https://directus.io/team/kevin-lewis",{"name":139,"url":140},"Matt Minor","https://directus.io/team/matt-minor",11,"2024-03-06","Mission: Slack (Live Episode)","\u003Cp>Speaker 0: Build in an extra 15 minutes for chaos. I'm gonna be here for the first 20. Then for the 1st presales engineers, Alex, will be here for 20, and then Matt will be here for 20. With that in mind, Bryant, I'm gonna start a timer. No stress, but stress.\u003C/p>\u003Cp>Take it away.\u003C/p>\u003Cp>Speaker 1: Yeah. Let's start the timer. What are we building?\u003C/p>\u003Cp>Speaker 0: You tell me.\u003C/p>\u003Cp>Speaker 1: Yeah. So you you let me know ahead of time, maybe, like, just a hour ago that we were gonna be building. Any guesses in the chat. Right? This might be fun to do live.\u003C/p>\u003Cp>Right? What are the guesses? No guesses. Alright. I don't see him coming through.\u003C/p>\u003Cp>We are going to be building a Slack clone. Build direct to us.\u003C/p>\u003Cp>Speaker 0: In an hour.\u003C/p>\u003Cp>Speaker 1: No. In an hour. Alright. So, Slack. Right?\u003C/p>\u003Cp>Yeah. Everybody uses it. You either love it, hate it. Xbox Live. Yeah.\u003C/p>\u003Cp>We'll do Xbox Live next time. Alright. So let's sketch this thing out. Right? What do we need out of a Slack clone?\u003C/p>\u003Cp>What feels good as far as functionality for this? How are we gonna set it up in direct us? Facebook, they need some help with their APIs. Yep. Certainly.\u003C/p>\u003Cp>Yeah. Experienced\u003C/p>\u003Cp>Speaker 0: that. We all saw that today. What is needed for Slack? I suppose the lightest version is gonna be you have channels, and inside of channels, you have users, and then you have channels. And then inside the channels, you have messages with, like, real time.\u003C/p>\u003Cp>And I think the that's the slimmest version. Right?\u003C/p>\u003Cp>Speaker 1: Yeah. So let's put, like, channels and messages at the top. We would call, like, threads a stretch goal. Yeah. Thread threading feels like, probably more than an hour.\u003C/p>\u003Cp>Alright. So we got channels. We got messages. We're gonna get the user authentication from direct to its users. And as far as functionality, what do we wanna do?\u003C/p>\u003Cp>We wanna have a channel. We want to submit messages in the channel. We want that all to update in real time. Notifications? Do we want notifications?\u003C/p>\u003Cp>We'll\u003C/p>\u003Cp>Speaker 0: I never wanna be notified when people send me messages ever. There is literally no world where I want that feature. So\u003C/p>\u003Cp>Speaker 1: so how many Slack groups are you a part of where, like, you just got the whole thing muted, Kevin? That would be a good question.\u003C/p>\u003Cp>Speaker 0: So let's take a look here. Let me let me open Slack for the first time in a in a hot minute. Okay. I'm in 123456789101112 Slacks. 12 Slacks.\u003C/p>\u003Cp>And I look at 2 of them regularly. 3 of them regularly. Most of the others are muted.\u003C/p>\u003Cp>Speaker 1: Alright. Yep. That's how it goes. Same thing for me. Alright.\u003C/p>\u003Cp>So I've got a brand new instance. Please don't use this password.\u003C/p>\u003Cp>Speaker 0: Oh my lord. I okay. That's fine. Everything's fine.\u003C/p>\u003Cp>Speaker 1: Probably need to change that before we get some surprises. Right? Change that real quick. Okay. Alright.\u003C/p>\u003Cp>So we're inside Directus. How are we gonna map this out? Right? We get the Directus users already out of the box. We get the authentication.\u003C/p>\u003Cp>We should get real time out of the box as well. So we just need these components. Right? Channels and messages should be fairly straightforward. We'll just call it a channel.\u003C/p>\u003Cp>Speaker 0: Why would you say that? That's like the beginning of the it should be it should be straightforward. Keep it simple, and it won't be complicated.\u003C/p>\u003Cp>Speaker 1: Keep it simple, and it won't be complicated. Yeah. Alright. What do we need for a channel? Do we yeah.\u003C/p>\u003Cp>Yeah. We don't even really need any of this. Right? I don't care when it was created, who it was created by. Just need a name for the channel.\u003C/p>\u003Cp>That'll be an input. We're going to go to the advanced settings. And because I want this to be URL safe, right, we'll use the slugify option inside the interface, and boom. There's channels. That was really complicated.\u003C/p>\u003Cp>I I saw Ben was\u003C/p>\u003Cp>Speaker 0: He begins now.\u003C/p>\u003Cp>Speaker 1: Let's let's do, like, the sahash. Oh, egg.\u003C/p>\u003Cp>Speaker 0: Oh, god. We don't have time for this, Brian. Dude God, I'm stressed already, and we're, like, 4 minutes in. Everything.\u003C/p>\u003Cp>Speaker 1: Yep. Alright. So then we've got messages. Alright. So as far as messages, we want when that message was created.\u003C/p>\u003Cp>So let's just call that timestamp for simplicity. Fun fact.\u003C/p>\u003Cp>Speaker 0: I didn't know you could rename those. I literally didn't that is the first time I've ever seen that. Damn.\u003C/p>\u003Cp>Speaker 1: Yeah. You could change it. You know, like, some people prefer updated at, versus date created or update updated. Do whatever you want with it. Alright.\u003C/p>\u003Cp>So we've got some messages within the messages. What do we have? We have what do we wanna do? Just text text area. We could go to markdown to be super complicated, but we'll just call it content, text, message.\u003C/p>\u003Cp>What do you Yeah.\u003C/p>\u003Cp>Speaker 0: I literally I literally don't care. Oh my god. No. I I wouldn't do message because if you're, like, looping through messages, you might use message as the singular, and then, yeah, text feels good.\u003C/p>\u003Cp>Speaker 1: Text. Alright. Let's unhide this just so we could see.\u003C/p>\u003Cp>Speaker 0: My leg's literally doing its nervous shake. I'm so glad this is your series and not mine. Oh my gosh. I'm sweating. Is it is it\u003C/p>\u003Cp>Speaker 1: just the technical difficulties at the start of it? Or or what?\u003C/p>\u003Cp>Speaker 0: No. It's having an hour on the clock. It's having an hour on the clock, Brian. It's the very format.\u003C/p>\u003Cp>Speaker 1: Yeah. No worries. Alright. So we got some messages. We got some channels.\u003C/p>\u003Cp>What else do we really need here? Oh, we'll come back to threads later. Right? We've already got our users. I'm gonna go ahead and, let's add you as a user, Kevin.\u003C/p>\u003Cp>Kevin, add example. And I'm not gonna tell you guys what the password is here. Alright. Now let's go into\u003C/p>\u003Cp>Speaker 0: I did actually think reactions would be a nice sorry. That was me interacting with, Ben in the chat. I did actually think reactions would be a nice a nice one as well. It's quite common.\u003C/p>\u003Cp>Speaker 1: Yeah. Cool. It'd be an interesting one to add. Yeah. We'll come back to it for sure.\u003C/p>\u003Cp>Alright. So we're gonna create a role for users so we can give access to messages for channels, etcetera. We're just gonna go ahead and give all access to this to start with. And the other thing that I'm gonna do, let's give public access to create we'll come back to, like, registering users.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. Yeah. No. That's later.\u003C/p>\u003Cp>Speaker 1: Yeah. Okay. Alright. So we've given access. Now we can access any as long as we're in this user role, we'll be able to access channels, messages, all of that.\u003C/p>\u003Cp>Let's actually start to build something. Right? If I wanted to, I could give Kevin access to this right now. He could log in. Let's just do that.\u003C/p>\u003Cp>Speaker 0: You're just seeing my stress face here. Okay.\u003C/p>\u003Cp>Speaker 1: I just see your stress face. Kevin at example.\u003C/p>\u003Cp>Speaker 0: And Kevin Oh, that is a lowbrow password.\u003C/p>\u003Cp>Speaker 1: Is it lowbrow?\u003C/p>\u003Cp>Speaker 0: Wow. I mean Yeah.\u003C/p>\u003Cp>Speaker 1: So let's go in. I'm gonna just create a couple\u003C/p>\u003Cp>Speaker 2: of channels.\u003C/p>\u003Cp>Speaker 0: Tests, though.\u003C/p>\u003Cp>Speaker 1: Oh, yeah. That's right. Let me just go ahead and give that. Because eventually, you're gonna be interacting through this Nuxt front end. But Yeah.\u003C/p>\u003Cp>Yeah. Cool. Alright. So we create a couple channels. Right?\u003C/p>\u003Cp>What do we like, guys? Throw some suggestions in the chat. We'll add a few of these channels. Let's start with Random? Evan is crazy nervous channel.\u003C/p>\u003Cp>Speaker 0: My leg's shaking.\u003C/p>\u003Cp>Speaker 1: General, memes. Okay. Sounds great. Got some memes. Alright.\u003C/p>\u003Cp>And now as far as messages, right, we got the user. We got the timestamp. We've got when it was updated. In case we change it, we got the text. But the thing that we don't have is how these messages belong to a channel.\u003C/p>\u003Cp>So inside Directus, we'll just go in. Again, creating these relationships, super easy. I don't even have to touch SQL. We'll go in, create a mini to 1 relationship for this. We'll call it channel, and we'll use our channels collection.\u003C/p>\u003Cp>And\u003C/p>\u003Cp>Speaker 2: what else do we need?\u003C/p>\u003Cp>Speaker 1: Maybe display template. We'll use name. Great. Cool. And now I could populate a message into a specific channel.\u003C/p>\u003Cp>Hey, Kev. I'll be nervous.\u003C/p>\u003Cp>Speaker 0: I have the most faith in you while having absolutely no faith in you at the same time. By the way, that user you created for me still doesn't have app access. You might need to go into the user settings and allow that. I thought doing the role would be sufficient, but, oh, I'm not I'm not in that role. I'm not in that role.\u003C/p>\u003Cp>Users in that role was 0.\u003C/p>\u003Cp>Speaker 1: Yeah. Gotcha. Yep. Try it now. Okay.\u003C/p>\u003Cp>Speaker 0: I'll tell you in a moment.\u003C/p>\u003Cp>Speaker 1: Tell me in a moment. Alright. So we've got the basic logic here. Like, we could go and fetch the data from this API if we wanted to just by going to messages or item slash messages. We're gonna get a forbidden because I'm not logged in, but that's okay.\u003C/p>\u003Cp>We'll take care of that.\u003C/p>\u003Cp>Speaker 0: Oh, we're 10 minutes in. This? Sorry. No stress, but we're 10 minutes in. That's half my time here.\u003C/p>\u003Cp>I'm I'm halfway to freedom because I am sweating so hard.\u003C/p>\u003Cp>Speaker 1: Don't sweat, man. Somehow I got logged out. So chaos rains, my friend. Oh my gosh. Glass work.\u003C/p>\u003Cp>Okay. Alright. I don't know how I got logged out, but okay. So we've got channels. We've got messages.\u003C/p>\u003Cp>Let's actually do something. I low key hate this, he says. I can still see the chat in the other window, Kevin. So you're not you're not giving me much confidence.\u003C/p>\u003Cp>Speaker 0: I'm so sorry.\u003C/p>\u003Cp>Speaker 1: Alright. Yeah. No worries, man. No worries. Alright.\u003C/p>\u003Cp>So this is my NUC starter from all the other episodes that we've got. I've switched it up a little bit just so you have a simple plug in for the sake of this live instance because the the other starter that had had a Nuxt module. It's got a lot going on. This is really simple. Right?\u003C/p>\u003Cp>So we've got a direct us plug in, that is in the plug ins directory inside Nuxt, which will automatically register this. And really simple. Right? Inside Nuxt, we've got a runtime config that we're calling to get the directus URL. And my Nuxt config looks like this.\u003C/p>\u003Cp>Right? We've got a direct us URL. Right now, that is pointing to, like, a local host, so we do need to switch this over. We'll just go to our URL here. It is switched over.\u003C/p>\u003Cp>Great. As far as the server token, I don't think we even actually will be using this. I don't think I've got it anywhere else in my config either. Cool. So we'll save that.\u003C/p>\u003Cp>And now we want to, like, start fetching some data. Right? There's a couple of routes that I already have set up in this, like a login and a register route. Drink a red bar too.\u003C/p>\u003Cp>Speaker 0: I've got all my caffeine.\u003C/p>\u003Cp>Speaker 1: More I've\u003C/p>\u003Cp>Speaker 0: got my caffeine right here. Don't you worry.\u003C/p>\u003Cp>Speaker 1: Give him some more energy drinks, please.\u003C/p>\u003Cp>Speaker 0: I'm gonna go get one. I'll be right back. Woof. Woof. I am actually doing it.\u003C/p>\u003Cp>Speaker 1: Alright. So let's fire up the dev server for Nux here. As far as the stuff that I've already got, like I said, I've got a a login route, a register route that we'll try to mess with. Looks like this. We'll go in and log in, where we can register.\u003C/p>\u003Cp>Very simple. And then we just have a index page, and then, I had some testing that I was doing here. Alright. So as far as layout, what do we want? You know, a lot of times, I it's like I I love Tailwind.\u003C/p>\u003Cp>Tailwind UI is totally worth the money, especially for, like, quick prototypes like this. I usually like, if it's just something I'm hacking on personally, I'll start with, like, the application shells. Like, sidebar layout. Right? This feels pretty good for, like, a Slack type of setup.\u003C/p>\u003Cp>Speaker 0: Yeah. That looks good.\u003C/p>\u003Cp>Speaker 1: Yep. So let's just go in. We've got that set to view. Where are we gonna run into issues at? Probably with the icons because I don't have those set, but no matter.\u003C/p>\u003Cp>Alright. So as far as our pages, let's do what are we gonna do here? Let's just do, like, an app directory. Within that, we'll probably have, like, some channels.\u003C/p>\u003Cp>Speaker 0: Yeah. That makes sense. Just just And then the dynamic route inside of that. Yeah.\u003C/p>\u003Cp>Speaker 1: Yep. So we'll do, like, channel dot view. And then also, what I can do is set up, like, a a parent root for the app. So if I go here inside the pages directory and I do app dot view, I paste this in.\u003C/p>\u003Cp>Speaker 0: Is this for, like, the sidebar? Like, the sidebar will be held in this?\u003C/p>\u003Cp>Speaker 1: Yeah. Exactly. Nice. Alright. So we'll save that.\u003C/p>\u003Cp>Let's see what we get.\u003C/p>\u003Cp>Speaker 0: That is a major indentation. Yikes.\u003C/p>\u003Cp>Speaker 1: K. So these are Rike's rules. I'm not sure if he's on here, but, alright.\u003C/p>\u003Cp>Speaker 0: He quit out. He hit the wrong keyboard shortcut. That is absolutely what happened. He got arced. That's what our team say when you accidentally, like, quit the whole window.\u003C/p>\u003Cp>I'm sure he'll be back in a moment. Until then, you got me, stressed as heck. Got my energy drink, waiting for the waiting for the stress to return. This is my brief moment of respite here. That was a major indentation, right?\u003C/p>\u003Cp>Is it worth the time of pause? Is it worth the time of pause?\u003C/p>\u003Cp>Speaker 1: Did you get arced? I I don't know what happened. Yeah. I guess I did.\u003C/p>\u003Cp>Speaker 0: We're back. Sorry. You don't need to be sorry and you don't get that time back.\u003C/p>\u003Cp>Speaker 1: Oh, yeah. Yeah. It's still running. I don't know what happened, man. I really don't.\u003C/p>\u003Cp>Speaker 0: Weird.\u003C/p>\u003Cp>Speaker 1: All right. So getting some errors here. We get console wise. Home icon. Navigation.\u003C/p>\u003Cp>Yeah. This is the only bad part. I'm gonna zoom out just a little bit here so I can find\u003C/p>\u003Cp>Speaker 0: Just to actually navigate this, healthscape of tabs. Yeah.\u003C/p>\u003Cp>Speaker 1: Just delete a bunch of these icons. Gosh. They put so many icons in this.\u003C/p>\u003Cp>Speaker 0: Everyone that direct us has a personal choice between Mac and Windows, by the way. So, you know, some folks use use either. I would say with the folks I work with directly, I think we mostly use Max, but that's definitely not the case across the board. Tim is my resident, brain slug. Tim is is my resident Windows user.\u003C/p>\u003Cp>Oh, let's see. It's not about getting to use Linux. It's about wanting to use Linux. So folks wanna use it, whatever. Most of the stuff we use is in the cloud, so it doesn't really\u003C/p>\u003Cp>Speaker 1: matter. Yeah. Okay. Well, at least we got something now. Right?\u003C/p>\u003Cp>Alright. We're gonna ditch teams here at the bottom. Where are you? Your teams. Great.\u003C/p>\u003Cp>Just ditch that list of stuff.\u003C/p>\u003Cp>Speaker 0: Can you zoom back in? I'm like, I need my spectacles to see it over there.\u003C/p>\u003Cp>Speaker 1: Alright. Teams is gone. Right? So now we want to fetch the list of channels here and display those. So how do we do that, Kev?\u003C/p>\u003Cp>Speaker 2: Do you have the\u003C/p>\u003Cp>Speaker 0: Directus SDK in this app?\u003C/p>\u003Cp>Speaker 1: We do have the Directus SDK in this app.\u003C/p>\u003Cp>Speaker 0: Lovely stuff. Well, have you got it set up as, like, a as, like, a plug in, or do we need to do that now?\u003C/p>\u003Cp>Speaker 1: As a Nuxt plug in. So we're gonna Very good. We'll use dollar sign direct us, use Nuxt app. The only thing that I don't have is, like, the auto imports from the SDK. That was one of the nice things about the the other module I had.\u003C/p>\u003Cp>So we're gonna need to do, what, read items? Yeah. From atdirectus SDK.\u003C/p>\u003Cp>Speaker 0: I think that's it.\u003C/p>\u003Cp>Speaker 1: Speaking of this.\u003C/p>\u003Cp>Speaker 0: What's it complaining about? White space, I think. Yeah.\u003C/p>\u003Cp>Speaker 1: Yeah. These are, again, these are Rikes, ESLint, and\u003C/p>\u003Cp>Speaker 0: Which are very legit for building actually serious things unlike this.\u003C/p>\u003Cp>Speaker 1: They're very, very legit. Right? Alright. So let's do what do we want? We want channels.\u003C/p>\u003Cp>There's a ref for channels, and then we're going to well, actually, we should just be able to use Nuxt async data. Right? Nuxt data error. We get an error. We're gonna go, wait, use async data.\u003C/p>\u003Cp>We'll give this a key. Let's just call it channels. And then we just return something from Directus, right? So Oh,\u003C/p>\u003Cp>Speaker 0: look at that interesting code pilot code using old SDK methods. Fascinating.\u003C/p>\u003Cp>Speaker 1: Yeah. Not right. Right? That's a oh, what are we doing there? Auto completion or something.\u003C/p>\u003Cp>So we'll do read items. We've got channels. Rates. Oh, man. GitHub Copilot is getting on the way this time.\u003C/p>\u003Cp>Speaker 0: You can turn it off if you want.\u003C/p>\u003Cp>Speaker 1: We do only have an hour. We'll see. Alright. So, basically, we're gonna read all the items from channels. We wanna get the fields.\u003C/p>\u003Cp>We'll just use the wild card. It's basically just name and ID. Does that do it? It should do it. Let's just destructure this and oh.\u003C/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\u003Cp>Speaker 1: Are you are you\u003C/p>\u003Cp>Speaker 0: As as channels.\u003C/p>\u003Cp>Speaker 1: Needs to be channels, and we'll just call this channels. And hopefully\u003C/p>\u003Cp>Speaker 0: And now now usually use channels.\u003C/p>\u003Cp>Speaker 1: Yep. We just go down here. Item in channels.\u003C/p>\u003Cp>Speaker 0: Very nice. And I think we could it is name. The key is name. Yeah.\u003C/p>\u003Cp>Speaker 1: Yeah. Item dot name. We're just gonna swap this for, like, Nuxt link. One of the interesting things, like, Nuxt link will take a h ref or, like, a 2 property. So that's one of the nice things that I like about it just because it's you know, you're used to using h ref on the regular side when you're creating a regular link.\u003C/p>\u003Cp>Right. Do you wanna do\u003C/p>\u003Cp>Speaker 0: you wanna know what's happened? We've, we've hit the 20 minute mark, and, thank gosh. I don't have to be here like, I can sweat from the audience. At this point, I'm gonna I'm gonna hand off to the amazing Alex who I'm gonna bring in. Hang on.\u003C/p>\u003Cp>Let let me add him in. Alex, would you, would you like to say hello?\u003C/p>\u003Cp>Speaker 2: Good evening, everyone. Can you hear me alright?\u003C/p>\u003Cp>Speaker 0: I can hear you grand. Lovely. Your your lovely tones. Excellent. I've\u003C/p>\u003Cp>Speaker 2: I've, enjoyed watching you sweat, Jacob. It's been great.\u003C/p>\u003Cp>Speaker 0: Oh, I'm sorry. It's coming it's coming for you too, mate.\u003C/p>\u003Cp>Speaker 2: Yeah. Yeah. Yeah. I'm I'm learning a lot of you right now as well. So\u003C/p>\u003Cp>Speaker 1: Hey, Viv. Hey. We failed to resolve next link. What is that all about?\u003C/p>\u003Cp>Speaker 0: I'm gonna peace out everyone. I'm gonna put you in Alex's very capable hands for 20 minutes, and then, he will be handing off to Matt. Bye for now, folks. Enjoy the rest. Yeah.\u003C/p>\u003Cp>Speaker 1: I thought we were doing, like, Royal Rumble rules. Everybody just hangs on. Be fun. Oh.\u003C/p>\u003Cp>Speaker 2: You just call me now, Brian.\u003C/p>\u003Cp>Speaker 1: I that's alright, my friend. How are you, dude? It's been a while since we've, like like paired up for something.\u003C/p>\u003Cp>Speaker 2: Yeah. Yeah. Yeah. This is this is cool, man. I, always enjoy, seeing all this view code, which I wish I understood better.\u003C/p>\u003Cp>Speaker 1: Yeah. Most of it is is tailwind at this point. But, yeah. So we having an error right now. Right?\u003C/p>\u003Cp>We're not having permission to access the channels. So we just need to log in. Right? So I'm gonna go to my login form components. Let's redirect to slash app.\u003C/p>\u003Cp>And we're gonna try to log in to this thing as well. I guess I need to add a user for you. Yep. I have one for Alex. Alright.\u003C/p>\u003Cp>Oh, don't forget the role. Alright. So I'm just gonna try to log in as Alex now. So we're gonna log in. Oops.\u003C/p>\u003Cp>Nope. That's not right. It's auth/login. I already used the link on the home page. That would have worked.\u003C/p>\u003Cp>So we got Alex at example, and the password is super secure. Okay. Alright. So now we can see at least we have our channels up here. Right?\u003C/p>\u003Cp>That looks good.\u003C/p>\u003Cp>Speaker 2: Yep. Yeah. Nice.\u003C/p>\u003Cp>Speaker 1: Alright. How are you feeling, Alex? Are you nervous as as Kevin? Like, feeling good? I\u003C/p>\u003Cp>Speaker 2: I've got a feeling you got the power of Bry Ross, under the hood there. So Ross. Big. I've got confidence, man. I got confidence.\u003C/p>\u003Cp>Speaker 1: Okay. Right. Alright. So now we need to fix, like, the h refs here. Like, hey.\u003C/p>\u003Cp>We wanna navigate to one of these channels. Right? Let's just test if this is gonna work. I'm a script at the top guy. Not sure if we've got any other view fans or not.\u003C/p>\u003Cp>I like seeing what's going on with the JavaScript first, and we're going to use the routes. So we'll do routes. Let's use routes. And I'm just gonna do this. I'm just gonna log this out.\u003C/p>\u003Cp>Right? I wanna look at routes a params.channel. K. Good. The other thing that we're gonna need to do somewhere in here where's the main dev?\u003C/p>\u003Cp>Main content. We're gonna plug in this Nux page here. So this should render the child page, for all my nested routes. Yeah. Alex is a boss.\u003C/p>\u003Cp>So, I'm not sure if that's John Daniels or not, but, I will say, like, if you've used the template CLI tool at all, Alex is is that guy. He's the guy that put that together originally.\u003C/p>\u003Cp>Speaker 2: Yeah. Thanks thanks to directors having good APIs. It was, yeah, difficult, but we we got there, and it's pretty cool right now.\u003C/p>\u003Cp>Speaker 1: Yeah. Alright. So we got the next page. I save. 2 2 what's going on?\u003C/p>\u003Cp>Oh, yeah. I forgot to fix the login again. Yeah. Apparently.\u003C/p>\u003Cp>Speaker 2: Yeah.\u003C/p>\u003Cp>Speaker 1: You need to persist that. Alright. So what else do we need to do? We need to fix those links. Channels.\u003C/p>\u003Cp>Where are you? Alright. So this will be channel or item dot name.\u003C/p>\u003Cp>Speaker 2: Item dot name. Yeah.\u003C/p>\u003Cp>Speaker 1: And then we'll do /channels /item.name. Should get it. We'll do a Nuxt link. Oh, duh. It's app dot channels.\u003C/p>\u003Cp>Speaker 2: Nice.\u003C/p>\u003Cp>Speaker 1: So now we have our channels working. Right? What are we gonna do inside the channels? Right? Great call.\u003C/p>\u003Cp>What do we do inside the channels? We are going to here is this is where we're actually gonna fetch, like, real time data. Right?\u003C/p>\u003Cp>Speaker 2: Yeah. You think? Yeah. Yeah. I'm looking forward to this part, the the, real time.\u003C/p>\u003Cp>Speaker 1: Okay. Alright. So I'm gonna cheat a little bit. We're just gonna go into our documentation. We do have this nice guide, if you haven't checked it out, on real time multi user chat.\u003C/p>\u003Cp>There's a vanilla JavaScript version. There's React. There's Vue. Let's just take a look at this. Right?\u003C/p>\u003Cp>Alright. How are we going to authenticate with real time? So we've got, the authentication composable. We've added the real time composable. All of those are in my plug in here.\u003C/p>\u003Cp>So it should be as simple as, by calling connect. That'd be right. Let's see. Kevin's got a I think it was Kevin that he's got. We've got a subscribe function here.\u003C/p>\u003Cp>So we're gonna subscribe to the messages, client dot connect. Yep. Let's see here.\u003C/p>\u003Cp>Speaker 2: This is usually where I run into some SSR issues, but I'll we'll see how you manage this one, Brian.\u003C/p>\u003Cp>Speaker 1: Let me show you. SSR equals false.\u003C/p>\u003Cp>Speaker 2: That's That's the solution there?\u003C/p>\u003Cp>Speaker 1: That's the easiest way. Yeah. Yeah. Absolutely. Alright.\u003C/p>\u003Cp>Alright. So let's just, is this cheating? Yeah. This probably is. Use whatever you have at your disposal.\u003C/p>\u003Cp>Right? So the subscription client dot subscribe messages, that's correct. Our fields here that we want, we're grabbing the all the fields on the root level. I guess I could zoom in a little bit and make that easier for everybody. Then we have user.\u003C/p>\u003Cp>So let's just grab all the fields from the user. We've got message of subscription, and then it looks like there's a receive message function that goes along with this.\u003C/p>\u003Cp>Speaker 2: Yeah. You yeah, you might need to filter on the channel as well, but maybe we can do that in the next step.\u003C/p>\u003Cp>Speaker 1: Yeah. So we've got data type equals subscription. This is just like the init. Right? Let's see if we are getting any.\u003C/p>\u003Cp>No. Yeah. This is gonna be interesting. I should have set up better authentication. So persisted on this.\u003C/p>\u003Cp>Alright. Do we see anything from, see some more issues from Tailwind? X mark icon. Where are you? Note to self, on the next one, do not import any of the icons from.\u003C/p>\u003Cp>So this should be, like, we definitely wanna redirect on this. Right? Let's add app. Let's add some page meta. Define oh, actually, it's not a const.\u003C/p>\u003Cp>We'll just do define page meta. And if we give this a middleware of auth, it should automatically redirect. And then I'm gonna go into where's my login form? I'm gonna make this really easy for myself, Alex.\u003C/p>\u003Cp>Speaker 2: Oh, good idea.\u003C/p>\u003Cp>Speaker 1: And password, super secure. We'll learn a little bit about everything here. Okay. So it's redirected. Sign in.\u003C/p>\u003Cp>This should take us back to the app, where we could get all the channels. What am I not seeing here? Right. Do we have to do we need to connect to the client, I guess? Let's plug in why we're not receiving\u003C/p>\u003Cp>Speaker 2: Yeah. Client dot connect. Right? Isn't that it?\u003C/p>\u003Cp>Speaker 1: Okay.\u003C/p>\u003Cp>Speaker 2: Yeah.\u003C/p>\u003Cp>Speaker 1: Yeah. So this is on mounted, but we're not using SSR. This should just work. Right? Where are we gonna stick that?\u003C/p>\u003Cp>On? Oh, what if we just call subscribe? Set up all these functions, forgot to call them. Alright. Client is not defined.\u003C/p>\u003Cp>Duh. Because it's not the client, it should be directus dot subscribe. So, again, we're gonna grab that directus plugin that we set up from use Nuxt app. It's error data is not defined. Oh, we gotta pass the data to that.\u003C/p>\u003Cp>Depcription started. Okay. Alright. So what do we wanna do now? Within the channel, we wanna have all of our messages.\u003C/p>\u003Cp>Right? I'm assuming that will be an array.\u003C/p>\u003Cp>Speaker 2: Sounds good to me.\u003C/p>\u003Cp>Speaker 1: In our received messages Actually, let's just do this to start with, see what type of data we're getting back. Received message. Alright. So now if we go in, I got, like, 35 tabs here, don't I?\u003C/p>\u003Cp>Speaker 2: Don't we all?\u003C/p>\u003Cp>Speaker 1: For apps. For hours. Live.\u003C/p>\u003Cp>Speaker 0: Or I\u003C/p>\u003Cp>Speaker 1: forgot the URL. It's just 100 apps dotdirectus.app. And I've logged myself out again. Let's keep that.\u003C/p>\u003Cp>Speaker 2: I can't help you here, Brian. Okay.\u003C/p>\u003Cp>Speaker 1: Alright. So now how do I split these out? That's the only thing I get hung up on on our half the time. It's like, hey. How do we make this, go split screen?\u003C/p>\u003Cp>Alright. So couple things should be happening here. Right? We've got our subscription. Let's make this larger.\u003C/p>\u003Cp>Right. So the subscription has started. We're in the Kevin is crazy crazy nervous. We'll probably have to filter that on our front end anyway, but, testing 1, 2, 3. We'll just add the channel to begin with.\u003C/p>\u003Cp>And Oh. Cool. So we we see the real time is connecting. We're getting our data. There's the actual messages.\u003C/p>\u003Cp>Great. Right. We've got the test. We've got the user is null.\u003C/p>\u003Cp>Speaker 2: Probably permissions.\u003C/p>\u003Cp>Speaker 1: Correct. That is correct. Alright. We need to make sure that the user role can access all the users. So right now, it's just by default, like, just a one user.\u003C/p>\u003Cp>We'll just make it so we can read all the users, and let's send a new one. Test 567. Save. Get our message. Okay.\u003C/p>\u003Cp>Now do we get the user data? Okay. Cool. That's a lot more user data than we actually wanted. So let's go in and trim that down.\u003C/p>\u003Cp>We'll just do first name, last name, avatar. We probably want the ID too.\u003C/p>\u003Cp>Speaker 2: And the\u003C/p>\u003Cp>Speaker 1: dot ID.\u003C/p>\u003Cp>Speaker 2: And the first name, not the first name.\u003C/p>\u003Cp>Speaker 1: First name.\u003C/p>\u003Cp>Speaker 2: Not the\u003C/p>\u003Cp>Speaker 1: catch. Good catch. Alright. Cool. And then here, we need to what?\u003C/p>\u003Cp>We want to see the message. Where's that message at? Alright. Subscription, create data. We want to filter that.\u003C/p>\u003Cp>Right? What are we gonna call this? For each message of subscription, We need to populate that data. Right?\u003C/p>\u003Cp>Speaker 2: Do do you wanna filter it at the at the WebSocket level or just at the whenever a new message is received? You could do both, I guess. But\u003C/p>\u003Cp>Speaker 1: Yeah. I I'm trying to think of, like, in Slack, you'd probably have, like, some kinda indicator up here that's, like, hey. Yeah. Yeah. Even if a message in a different channel popped up, you would probably filter it out on the the front end.\u003C/p>\u003Cp>Right? Yeah. On on the specific channel Because you probably have, like, some type of yeah. Alright. So receive message here.\u003C/p>\u003Cp>What do we does it show the oh, let's get the channel name as well. Query Oh, yeah. That's that'd be a useful one. Dotidchannel.name. So here, if the data type equals subscription.\u003C/p>\u003Cp>Create. And data event equals create. We will push those messages into our array of messages. And then instead of this, we should be showing a some messages. Alright.\u003C/p>\u003Cp>What we doing on time? I've totally lost the window here.\u003C/p>\u003Cp>Speaker 2: Yeah. Me too. Me too. It's about quarter to the hour on my clock. Let's\u003C/p>\u003Cp>Speaker 1: just take a look. 22 minutes left. Okay. Alright. Cool.\u003C/p>\u003Cp>Fun. Fun. Fun. Alright. GitHub Copilot for the win.\u003C/p>\u003Cp>Do we how confident are we feeling? I can't even see the chat at this point.\u003C/p>\u003Cp>Speaker 2: But Not confident in that one.\u003C/p>\u003Cp>Speaker 1: So we'll do, like, a list for all the message. Yeah. Again, hey. Like, you gotta take it with a grain of salt. Message dot content.\u003C/p>\u003Cp>You know, I I think this would get better if we had typed this out, but Yeah. Key message dot ID, we should have that for sure. We'll just close that list, see what we get. Alright. Do we have any messages?\u003C/p>\u003Cp>The other thing is, like, do we fetch the messages for this channel on\u003C/p>\u003Cp>Speaker 2: Initially. Right?\u003C/p>\u003Cp>Speaker 1: On yeah. And, like, do you have you messed with the the real time and, like, the chat stuff at all, Alex, or no?\u003C/p>\u003Cp>Speaker 2: Not not a not as much as I should have, but, this is an interesting one because you might not even need to use the real time in this in this example.\u003C/p>\u003Cp>Speaker 0: Just Hello, everyone. It's the disembodied voice of Kevin here. Hello. I'm watching, I've been screaming into the void in chat. I know this isn't I know this is an illegal move to throw you a bone here, but the init payload comes with the initial items.\u003C/p>\u003Cp>Up to a 100, and you can set limit minus 1 to get more than a 100. Okay. Peace out. Bye. What was that, Alex?\u003C/p>\u003Cp>You wanna miss off the clock, mate.\u003C/p>\u003Cp>Speaker 2: Okay. Thanks, Gabe. I think that's probably just, just we you can start logging out the the all all the messages that come through from the website. Right? So it's probably a yeah.\u003C/p>\u003Cp>So it would be one of the messages received maybe, with the initial with a different type. I don't know if I've seen that in your logs to be fair. But\u003C/p>\u003Cp>Speaker 1: Yeah. Let's back up. Subscription started. Received message. No.\u003C/p>\u003Cp>Matt's gonna save the day on this.\u003C/p>\u003Cp>Speaker 2: We should know this. I should know this, Bryant.\u003C/p>\u003Cp>Speaker 1: That's alright. So, like, we we'd also do it this way as well. Right? Where we go in and read items. Oh, nope.\u003C/p>\u003Cp>That's gonna be coming from the SDK. Read items from directus s e k. Let's just call this\u003C/p>\u003Cp>Speaker 2: Oh, I think no. I'm starting to sweat. Thanks, Kevin.\u003C/p>\u003Cp>Speaker 1: Yeah. No worries. God. What am I doing? Constant populate messages equals async.\u003C/p>\u003Cp>Yeah. There we go. This is wrong. Totally wrong. Oh, no.\u003C/p>\u003Cp>That's not that bad, actually. Filter channel equals route dot params.channel. Equal route dot value. No. Should just be route dot is it route dot value?\u003C/p>\u003Cp>Is route\u003C/p>\u003Cp>Speaker 0: reactive? Hello. Hello, everyone. It's me again. It's Kevin again.\u003C/p>\u003Cp>I was I was having the chat with Matt, and in the nature of chaos, we've decided to Royal Rumble this. So it is my pleasure to introduce mister Matt Minor. Hello, Matt.\u003C/p>\u003Cp>Speaker 3: Hey, everyone. I, will be the most useless person on this call because I'm not\u003C/p>\u003Cp>Speaker 0: Oh, hardly.\u003C/p>\u003Cp>Speaker 3: I'll help you pick the colors. And I was actually thinking, maybe I could just sabotage Bryant here at the end.\u003C/p>\u003Cp>Speaker 1: Just Sabotage.\u003C/p>\u003Cp>Speaker 3: The headlines of the day or something. Do it,\u003C/p>\u003Cp>Speaker 0: man. Oh, no. Don't don't do that. Can you imagine?\u003C/p>\u003Cp>Speaker 3: What do we have? We have we have 15 minutes.\u003C/p>\u003Cp>Speaker 0: No. There's there's 20 left. We got just just 20 minutes left. We're 2 thirds of the way in. No pressure.\u003C/p>\u003Cp>But it it doesn't look like Slack yet, does it? Where are the messages? Yeah. Alex, is it is this what you did with your 20 minutes, mate?\u003C/p>\u003Cp>Speaker 2: Mate, we were we were work we were working hard. Yeah. We were working hard. All hardly working.\u003C/p>\u003Cp>Speaker 1: Yeah. Yeah. This was\u003C/p>\u003Cp>Speaker 3: where are we stuck?\u003C/p>\u003Cp>Speaker 1: What are\u003C/p>\u003Cp>Speaker 3: we, what are we doing?\u003C/p>\u003Cp>Speaker 1: Just gonna load the initial messages.\u003C/p>\u003Cp>Speaker 0: So so I know from experience that when you I don't know what's up with maybe how you're including real time here, but that init payload that init message does come. It definitely comes with all of the mess with all of the items in the collection, but I have also observed it's not happening here. So I think that would be the easiest fix is, like, why isn't that working? Because then you can just use the real time interface.\u003C/p>\u003Cp>Speaker 1: Right. Why isn't that working\u003C/p>\u003Cp>Speaker 2: Yeah. Kevin?\u003C/p>\u003Cp>Speaker 0: Yeah. Bloody great question, mate.\u003C/p>\u003Cp>Speaker 1: So I log in. We go to Kevin is crazy nervous. We start this subscription.\u003C/p>\u003Cp>Speaker 0: Yeah. But where is the where's the payload? Where's that init payload? So in oh, oh, I think maybe it Oh,\u003C/p>\u003Cp>Speaker 1: is it is it here?\u003C/p>\u003Cp>Speaker 0: Is it is it because you're just subscribing to the single event? If you remove the event from, like, inside of the, if you get rid of that and get every everything over that subscription, does that help? There it is.\u003C/p>\u003Cp>Speaker 1: Data. Oh, okay. Yep. Okay. So just use that.\u003C/p>\u003Cp>Nice. Okay. There we go. My man.\u003C/p>\u003Cp>Speaker 3: That's what I was gonna say too. But, Kevin\u003C/p>\u003Cp>Speaker 0: I know. Sorry. I felt I felt emanating from your your corner of the ring. How are you doing today, Matt?\u003C/p>\u003Cp>Speaker 1: Is I'm good. I'm doing good.\u003C/p>\u003Cp>Speaker 3: I'm excited to be\u003C/p>\u003Cp>Speaker 0: here. Good. News headlines for today?\u003C/p>\u003Cp>Speaker 3: Facebook and Instagram. No. Struggling.\u003C/p>\u003Cp>Speaker 0: I I sorry. I was too busy working, so I didn't know that.\u003C/p>\u003Cp>Speaker 1: Oh, yeah.\u003C/p>\u003Cp>Speaker 3: Sorry. I was too I, it just came up\u003C/p>\u003Cp>Speaker 2: when\u003C/p>\u003Cp>Speaker 3: I was logging in to work today. So, how's the chat doing? Is anybody Let's see.\u003C/p>\u003Cp>Speaker 2: You you might have to spread that one, Bryant. I think.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. You will. You will.\u003C/p>\u003Cp>Speaker 3: Just enjoying the chaos. Yeah. But for a net\u003C/p>\u003Cp>Speaker 0: but for a net, dude, for a net, don't push it into the array. Just replace the array because\u003C/p>\u003Cp>Speaker 1: Oh, okay.\u003C/p>\u003Cp>Speaker 0: There's gonna be nothing in it at the start.\u003C/p>\u003Cp>Speaker 1: Data dot data.\u003C/p>\u003Cp>Speaker 0: Joshua, just enjoying the cast. Honestly, so are we. As I said, this is our first kind of live event of this kind, and I don't know. I'm having a great time. It is chaotic, and we're just making it up.\u003C/p>\u003Cp>We changed the format. Who bloody cares? Love it.\u003C/p>\u003Cp>Speaker 1: Yeah. Nope. Alright. So now we got some messages. Right?\u003C/p>\u003Cp>Let's, let's mess with this a bit. Alright. So we're gonna have a div. We're gonna show the avatar, image source,\u003C/p>\u003Cp>Speaker 0: message. Joshua, it's so funny. You're like, yeah. You know, I'm struggling, like, getting this, you know, flow automation thing to work while listening to you in the background. And the funny bit is some of the people who would be there to help you are currently here.\u003C/p>\u003Cp>So it's like, have fun struggling, but you're struggling on your own for the next, 15. Nah. Jonathan's there. Funny. And\u003C/p>\u003Cp>Speaker 3: you think you're struggling\u003C/p>\u003Cp>Speaker 1: to get, like,\u003C/p>\u003Cp>Speaker 3: 4 people watching you work at the they were all just staring over Brian Schuler right now.\u003C/p>\u003Cp>Speaker 1: I love it, man. Thank you. This is this is this is more fun for me. I think I feel\u003C/p>\u003Cp>Speaker 3: like I go, when I go shopping with my wife and, like, just follow her around the store. That's what I feel like right now. I know value just in the way.\u003C/p>\u003Cp>Speaker 0: Alright. So far.\u003C/p>\u003Cp>Speaker 1: Let's add some padding for these. What else?\u003C/p>\u003Cp>Speaker 0: We have time for the padding. We have time for the padding. Dude. I'm good. God.\u003C/p>\u003Cp>I'm sweating again.\u003C/p>\u003Cp>Speaker 1: All things, man. All things. You you gotta have time for it. Alright. So we got some messages.\u003C/p>\u003Cp>Great. Now we need what? We need a form at the bottom of this one. Right? So we got VText.\u003C/p>\u003Cp>What does this thing do? This is, just text. Okay. Great. We don't need that.\u003C/p>\u003Cp>So we just got an input. Text input, b model, new message. Right? So we're gonna populate. We can just scrap all this shit.\u003C/p>\u003Cp>Oh, were we allowed to curse on this one or no?\u003C/p>\u003Cp>Speaker 0: Well, you've done it now. So yeah. Sure. Anything anything goes.\u003C/p>\u003Cp>Speaker 1: Anything goes. Alright. So we got a new message. We're just gonna add a ref for that. We will remodel new message at key up, enter, send message.\u003C/p>\u003Cp>Yeah. I don't like that, but, let's just wrap it. We'll give it a button. Oh, actually, I forgot I got the Nux UI library included in this. So we should be able to get something nice just by doing new input.\u003C/p>\u003Cp>Did that work? No. Nope. Did not work.\u003C/p>\u003Cp>Speaker 2: No pressure, Brian. That's\u003C/p>\u003Cp>Speaker 1: the beauty of this. No pressure.\u003C/p>\u003Cp>Speaker 0: Yeah. We've all gone quiet. Just letting you work\u003C/p>\u003Cp>Speaker 1: for a moment. New button. Just add send message. What are we doing now? Nope.\u003C/p>\u003Cp>Gotta close that guy. Alright. So we got this. Right? We need to add a handler for this.\u003C/p>\u003Cp>Just click send oh, let me just remove that. Send message.\u003C/p>\u003Cp>Speaker 0: Nice.\u003C/p>\u003Cp>Speaker 1: And then we've got should have an async function for send message. And this is not right at all. Right? So I think\u003C/p>\u003Cp>Speaker 0: I don't know. How are you gonna tap into the because you you've gotta you've got to send it over the subscription, don't you?\u003C/p>\u003Cp>Speaker 1: Yeah. That's that's where I was going back to your wonderful guy.\u003C/p>\u003Cp>Speaker 0: Oh, no. No. No. I think it's fine. No.\u003C/p>\u003Cp>No. No. Ignore me. It's totally fine. You can do it.\u003C/p>\u003Cp>You can do it at that level where you've written send message because received message is at that level too. Right? So Actually Actually\u003C/p>\u003Cp>Speaker 1: client.send message. Right? Should be possible or no?\u003C/p>\u003Cp>Speaker 0: We'll find out in a minute. I think there's gonna be a weird scoping thing here. Let's find out.\u003C/p>\u003Cp>Speaker 2: Well, I think if you just use the create item function with the new SDK,\u003C/p>\u003Cp>Speaker 0: that should work. Do that,\u003C/p>\u003Cp>Speaker 1: but you could also\u003C/p>\u003Cp>Speaker 0: send it over you could send it over the WebSocket connection, and then you're just using that one connection use make a make a HTTP request, honestly.\u003C/p>\u003Cp>Speaker 1: Test. Oh, yeah. That's why you don't trust this. Right?\u003C/p>\u003Cp>Speaker 0: Yes.\u003C/p>\u003Cp>Speaker 1: Request. Yeah. Create item.\u003C/p>\u003Cp>Speaker 2: Yeah.\u003C/p>\u003Cp>Speaker 1: Did you get it? Test. Send message. Alright. What do we get?\u003C/p>\u003Cp>Unexpected error occurred.\u003C/p>\u003Cp>Speaker 0: Good. Good. Good. Helpful. Helpful.\u003C/p>\u003Cp>Helpful.\u003C/p>\u003Cp>Speaker 2: That's the channel that's the channel name. That should be the ID. Right?\u003C/p>\u003Cp>Speaker 1: Yes. Correct.\u003C/p>\u003Cp>Speaker 2: Oh, did we get the channel ID?\u003C/p>\u003Cp>Speaker 0: No. That's right. Because, it's in the it's coming from the URL.\u003C/p>\u003Cp>Speaker 2: Yeah. But it's\u003C/p>\u003Cp>Speaker 0: That value there is coming from the URL. Yeah. Yeah. The dynamic part.\u003C/p>\u003Cp>Speaker 1: But the channel itself\u003C/p>\u003Cp>Speaker 0: Oh, wait. Right. Right. It's saying here, ding, ding, Alex. Got it.\u003C/p>\u003Cp>So hang on. I lower my confidence.\u003C/p>\u003Cp>Speaker 1: There you go. Yeah. Yeah. So we gotta get the channel ID. Where do we have the data?\u003C/p>\u003Cp>Right?\u003C/p>\u003Cp>Speaker 0: Oh, I understand now. I understand what you mean the ID. Got it. Got it. Got it.\u003C/p>\u003Cp>So I I think you'll need to do that. That initial load. Yeah. I think I think you do. Yeah.\u003C/p>\u003Cp>Just to fetch that ID.\u003C/p>\u003Cp>Speaker 1: I really don't wanna do that. But\u003C/p>\u003Cp>Speaker 0: Mate, you've got you've got 10 9 minutes. Oh, we got left? You're doing that.\u003C/p>\u003Cp>Speaker 1: We're doing it. Yeah. Alright. So read items, channels, filter ID equals no. That's our ID.\u003C/p>\u003Cp>Speaker 2: What's the channel name? Right? Or\u003C/p>\u003Cp>Speaker 1: Channel name. This will be wrapped. We'll have what equals. Alright. So you can have the channel.\u003C/p>\u003Cp>There's the ID. Good.\u003C/p>\u003Cp>Speaker 2: And it's an array out, which is a rookie error that I often make.\u003C/p>\u003Cp>Speaker 1: Woah. Yep. That is. Yeah. So this will be\u003C/p>\u003Cp>Speaker 0: How are you doing over there, Matt? Having a good time?\u003C/p>\u003Cp>Speaker 3: Watching all along. Yeah. I'm waiting for us to get to the colors. I think that's where I'll have my most, impact.\u003C/p>\u003Cp>Speaker 0: Well, we've got we've got a whole 8 minutes, and I think we're about to be finished, I guess. So, you you get a pick of the next function now or 2.\u003C/p>\u003Cp>Speaker 1: If I use the, what, If we use async data, and you can do, like, some transforms on this as well. Using data. Channel. Turn up paste. Is this guy out?\u003C/p>\u003Cp>Speaker 2: I think I think Greg's suggestion was the best to just use the channel ID in the in the URL.\u003C/p>\u003Cp>Speaker 1: Probably. 100%. Requests from that. This needs to be here. I like the pretty URLs, though.\u003C/p>\u003Cp>Alright. I will just refresh. Go to crazy nervous. Okay. So now we've got the channel.\u003C/p>\u003Cp>Alright. So we got channel. This should just be the channel dot ID. Best. Okay.\u003C/p>\u003Cp>So we're okay. It seems that, like, the message was sent, unless I'm wrong. Test. Okay. So it did populate the message.\u003C/p>\u003Cp>We just don't see it show up here. Alright. So what are we doing wrong there?\u003C/p>\u003Cp>Speaker 2: In that payload, shouldn't it also have the the channel ID?\u003C/p>\u003Cp>Speaker 1: Yep. It does.\u003C/p>\u003Cp>Speaker 2: But it does, but it's not showing in the console there.\u003C/p>\u003Cp>Speaker 1: Test. Okay. There's the test. Send messages. Yeah.\u003C/p>\u003Cp>That's kinda odd. Why is it not showing that in the payload? Alright. Regardless, we should be getting a message back. Right?\u003C/p>\u003Cp>Reading undefined dot avatar. This is a simple v f. Right? Yes. Message.\u003C/p>\u003Cp>Speaker 0: Just get rid of it. No one needs an image. No one needs an image. We're using Slack compact mode. Ditch it off.\u003C/p>\u003Cp>Get rid of it. Just saying that's 5 that's 5 and a half minutes.\u003C/p>\u003Cp>Speaker 1: Yeah. No worries, man. Alright. Test. Alright.\u003C/p>\u003Cp>So first name. Why is this not populating? User but we gotta have the username. Right? It cannot read first name.\u003C/p>\u003Cp>Speaker 0: It's not user. It's user created or whatever you called it. Right? Or did you rename it to user?\u003C/p>\u003Cp>Speaker 1: No. It should be user created. Yeah. Channel messages dotuser.firstname. This is used, like, a v f message.\u003C/p>\u003Cp>A user? You're trying to get something out of this. Oh, shoot. Blah blah blah. Kevin is crazy nervous.\u003C/p>\u003Cp>Still crazy nervous.\u003C/p>\u003Cp>Speaker 0: I'm literally rocking back and forward in my chair here by feeling\u003C/p>\u003Cp>Speaker 1: the heat. So, like, the the messages are coming in, but we're just not populating. We missed something. Messages dot value dot push data dot data. Alright.\u003C/p>\u003Cp>So if we're receiving data, we're getting data. It's it's\u003C/p>\u003Cp>Speaker 2: a it's another array in there.\u003C/p>\u003Cp>Speaker 1: Data is an array. Yeah. Okay. So, we'll just do this.\u003C/p>\u003Cp>Speaker 0: Someone else who has the cute baby in the background, and I'm using a hardware mute, but, I think that's that's me. And they're they're they're hungry. They're not.\u003C/p>\u003Cp>Speaker 2: Sorry. That might be my fault.\u003C/p>\u003Cp>Speaker 0: Oh, do you I haven't heard I haven't heard yours in the background.\u003C/p>\u003Cp>Speaker 2: Okay. Was it yours? Alright.\u003C/p>\u003Cp>Speaker 0: Could be.\u003C/p>\u003Cp>Speaker 2: They came\u003C/p>\u003Cp>Speaker 0: home, but they're not sounding cute. They're not sounding cute. They're sounding angry.\u003C/p>\u003Cp>Speaker 1: Alright. So now what is the Nuxt command? Right? I think there's one where I can let you guys tunnel into this. Right?\u003C/p>\u003Cp>Nuxt dev tunnel? Where is this guy?\u003C/p>\u003Cp>Speaker 0: That is brand that must be brand new because I didn't know that was there. But it's built into Visual Studio Code. If you bring up your status bar at the bottom, I thought it was baked in now. No way. That is sick.\u003C/p>\u003Cp>Alright. So\u003C/p>\u003Cp>Speaker 1: does this actually work?\u003C/p>\u003Cp>Speaker 0: There there's the tunnel right there, the Cloudflare URL. Womp womp.\u003C/p>\u003Cp>Speaker 1: Why Why doesn't it work?\u003C/p>\u003Cp>Speaker 0: You better work it out because you got 2 minutes.\u003C/p>\u003Cp>Speaker 1: At this point, I don't know. Do you guys just, do me a favor and then log in to the direct us instance in this firehouse message?\u003C/p>\u003Cp>Speaker 0: I got you.\u003C/p>\u003Cp>Speaker 1: P p m. Yeah. Nux. Dev. Oh, p m p m.\u003C/p>\u003Cp>It would be, like, running the let's see. I don't know if that's it or not. I'm stressed. Yeah. So there we go.\u003C/p>\u003Cp>Yeah.\u003C/p>\u003Cp>Speaker 0: That was me.\u003C/p>\u003Cp>Speaker 1: It's Does it work\u003C/p>\u003Cp>Speaker 0: does it work if you type it in here as well? Yes.\u003C/p>\u003Cp>Speaker 1: I I'm logged in as Alex. I'm logged in as Alex. Alright. Send send one more just so we can say something.\u003C/p>\u003Cp>Speaker 0: Alright.\u003C/p>\u003Cp>Speaker 1: Here? Less stressed. Yay.\u003C/p>\u003Cp>Speaker 0: Yeah. Rock\u003C/p>\u003Cp>Speaker 1: on. Some concept.\u003C/p>\u003Cp>Speaker 0: That's basically feature complete.\u003C/p>\u003Cp>Speaker 3: I was shitting. Hit right at the buzzer. Is Let's go.\u003C/p>\u003Cp>Speaker 1: Because this is a winner. Yeah?\u003C/p>\u003Cp>Speaker 0: Yay. Holy heck. My heart rate. I'm I'm gonna need to take a shower. I'm sweating.\u003C/p>\u003Cp>Oof. Look at that.\u003C/p>\u003Cp>Speaker 1: Yeah. Wow.\u003C/p>\u003Cp>Speaker 0: Stop the timer.\u003C/p>\u003Cp>Speaker 1: Yeah. Leave it for Stop. Yeah. Series a already. I've watched them, man.\u003C/p>\u003Cp>Speaker 0: And don't forget, there's more in your stressing and dressing in 100 hours. A whole season now available on Directus TV with more coming in April.\u003C/p>\u003Cp>Speaker 1: I I look forward to the other episodes where you guys are are it may be not on there. I I've enjoyed this a lot, though. This is fun. It definitely distracted a little bit.\u003C/p>\u003Cp>Speaker 0: Yeah. I did feel like, should this be, like, 1 app in a 100 hours, but with guests? Oh, wow.\u003C/p>\u003Cp>Speaker 1: Oh, so takeaways from this. Right? What did we what did we learn?\u003C/p>\u003Cp>Speaker 0: I'm not coming back to the next one.\u003C/p>\u003Cp>Speaker 3: Spend more time with Patty.\u003C/p>\u003Cp>Speaker 1: More time with that. Dude, I'm sorry we didn't get to the, like, colors. Like, do you wanna what what color are you feeling, Matt? It's the only reason I'm so sorry. Pick the color.\u003C/p>\u003Cp>I'm looking for the color.\u003C/p>\u003Cp>Speaker 3: I can't think of the hex code\u003C/p>\u003Cp>Speaker 1: for\u003C/p>\u003Cp>Speaker 3: I didn't 466, double f.\u003C/p>\u003Cp>Speaker 1: What do you want? 4466\u003C/p>\u003Cp>Speaker 3: f f.\u003C/p>\u003Cp>Speaker 0: No. It's 6644 f f.\u003C/p>\u003Cp>Speaker 3: Knew it. Hopefully, Ben's not watching.\u003C/p>\u003Cp>Speaker 0: What is it? Oh, he's watching. 6:6:8. He's watching.\u003C/p>\u003Cp>Speaker 1: 44 f f. Where are we? Is it not updating? Is that already the indigo oh, there it is. Okay.\u003C/p>\u003Cp>No?\u003C/p>\u003Cp>Speaker 0: Maybe it wasn't on it here. Is that the sidebar?\u003C/p>\u003Cp>Speaker 1: Yeah. Okay. Sidebar. Yeah. There you go.\u003C/p>\u003Cp>BG you're gonna find me one more time. 6644 f f? Yep.\u003C/p>\u003Cp>Speaker 0: That's the one.\u003C/p>\u003Cp>Speaker 1: That's it. There it is.\u003C/p>\u003Cp>Speaker 0: Subtleship. Boom. Feature comp New product. Ship it. Thanks everyone for joining us.\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: I'm not I'm not kidding. We probably won't do another one now. We probably will, but I won't be here. Gosh. Literally, 30 seconds to go.\u003C/p>\u003Cp>30 seconds to go. Congratulations, Brian.\u003C/p>\u003Cp>Speaker 1: Yeah. I feel like we started off on the wrong foot with the 5 minutes of technical difficulties. The for the nodes. Selecting 5 minutes of technical difficulties at the the start of this thing. That's what everybody did not see on the front\u003C/p>\u003Cp>Speaker 0: end. We got there. We got there.\u003C/p>\u003Cp>Speaker 1: Now we're now we're ready to code for, like, another 2 hours. And if you're\u003C/p>\u003Cp>Speaker 0: watching this live, this whole episode will be packaged up as a special at the end of season 1 of a 100 and a 100 hours on director's TV. We'll pop that out tomorrow. And then there are new episodes of a 100 apps and a 100 hours coming in April.\u003C/p>\u003Cp>Speaker 1: Amazing. Excellent. Yeah. Well, thanks for joining us, to the circus episode of 100 Hours 100 Hours. I'm your host, Brian Gillispie.\u003C/p>\u003Cp>Thanks to my special guests, mister Kevin Lewis, mister Avdv, and Awesome. With the crazy colors coming in.\u003C/p>\u003Cp>Speaker 0: Thank you, everyone. Bye for now.\u003C/p>\u003Cp>Speaker 2: Cheers. Bye. Cheers.\u003C/p>","Build in an extra 15 minutes for chaos. I'm gonna be here for the first 20. Then for the 1st presales engineers, Alex, will be here for 20, and then Matt will be here for 20. With that in mind, Bryant, I'm gonna start a timer. No stress, but stress. Take it away. Yeah. Let's start the timer. What are we building? You tell me. Yeah. So you you let me know ahead of time, maybe, like, just a hour ago that we were gonna be building. Any guesses in the chat. Right? This might be fun to do live. Right? What are the guesses? No guesses. Alright. I don't see him coming through. We are going to be building a Slack clone. Build direct to us. In an hour. No. In an hour. Alright. So, Slack. Right? Yeah. Everybody uses it. You either love it, hate it. Xbox Live. Yeah. We'll do Xbox Live next time. Alright. So let's sketch this thing out. Right? What do we need out of a Slack clone? What feels good as far as functionality for this? How are we gonna set it up in direct us? Facebook, they need some help with their APIs. Yep. Certainly. Yeah. Experienced that. We all saw that today. What is needed for Slack? I suppose the lightest version is gonna be you have channels, and inside of channels, you have users, and then you have channels. And then inside the channels, you have messages with, like, real time. And I think the that's the slimmest version. Right? Yeah. So let's put, like, channels and messages at the top. We would call, like, threads a stretch goal. Yeah. Thread threading feels like, probably more than an hour. Alright. So we got channels. We got messages. We're gonna get the user authentication from direct to its users. And as far as functionality, what do we wanna do? We wanna have a channel. We want to submit messages in the channel. We want that all to update in real time. Notifications? Do we want notifications? We'll I never wanna be notified when people send me messages ever. There is literally no world where I want that feature. So so how many Slack groups are you a part of where, like, you just got the whole thing muted, Kevin? That would be a good question. So let's take a look here. Let me let me open Slack for the first time in a in a hot minute. Okay. I'm in 123456789101112 Slacks. 12 Slacks. And I look at 2 of them regularly. 3 of them regularly. Most of the others are muted. Alright. Yep. That's how it goes. Same thing for me. Alright. So I've got a brand new instance. Please don't use this password. Oh my lord. I okay. That's fine. Everything's fine. Probably need to change that before we get some surprises. Right? Change that real quick. Okay. Alright. So we're inside Directus. How are we gonna map this out? Right? We get the Directus users already out of the box. We get the authentication. We should get real time out of the box as well. So we just need these components. Right? Channels and messages should be fairly straightforward. We'll just call it a channel. Why would you say that? That's like the beginning of the it should be it should be straightforward. Keep it simple, and it won't be complicated. Keep it simple, and it won't be complicated. Yeah. Alright. What do we need for a channel? Do we yeah. Yeah. We don't even really need any of this. Right? I don't care when it was created, who it was created by. Just need a name for the channel. That'll be an input. We're going to go to the advanced settings. And because I want this to be URL safe, right, we'll use the slugify option inside the interface, and boom. There's channels. That was really complicated. I I saw Ben was He begins now. Let's let's do, like, the sahash. Oh, egg. Oh, god. We don't have time for this, Brian. Dude God, I'm stressed already, and we're, like, 4 minutes in. Everything. Yep. Alright. So then we've got messages. Alright. So as far as messages, we want when that message was created. So let's just call that timestamp for simplicity. Fun fact. I didn't know you could rename those. I literally didn't that is the first time I've ever seen that. Damn. Yeah. You could change it. You know, like, some people prefer updated at, versus date created or update updated. Do whatever you want with it. Alright. So we've got some messages within the messages. What do we have? We have what do we wanna do? Just text text area. We could go to markdown to be super complicated, but we'll just call it content, text, message. What do you Yeah. I literally I literally don't care. Oh my god. No. I I wouldn't do message because if you're, like, looping through messages, you might use message as the singular, and then, yeah, text feels good. Text. Alright. Let's unhide this just so we could see. My leg's literally doing its nervous shake. I'm so glad this is your series and not mine. Oh my gosh. I'm sweating. Is it is it just the technical difficulties at the start of it? Or or what? No. It's having an hour on the clock. It's having an hour on the clock, Brian. It's the very format. Yeah. No worries. Alright. So we got some messages. We got some channels. What else do we really need here? Oh, we'll come back to threads later. Right? We've already got our users. I'm gonna go ahead and, let's add you as a user, Kevin. Kevin, add example. And I'm not gonna tell you guys what the password is here. Alright. Now let's go into I did actually think reactions would be a nice sorry. That was me interacting with, Ben in the chat. I did actually think reactions would be a nice a nice one as well. It's quite common. Yeah. Cool. It'd be an interesting one to add. Yeah. We'll come back to it for sure. Alright. So we're gonna create a role for users so we can give access to messages for channels, etcetera. We're just gonna go ahead and give all access to this to start with. And the other thing that I'm gonna do, let's give public access to create we'll come back to, like, registering users. Yeah. Yeah. Yeah. No. That's later. Yeah. Okay. Alright. So we've given access. Now we can access any as long as we're in this user role, we'll be able to access channels, messages, all of that. Let's actually start to build something. Right? If I wanted to, I could give Kevin access to this right now. He could log in. Let's just do that. You're just seeing my stress face here. Okay. I just see your stress face. Kevin at example. And Kevin Oh, that is a lowbrow password. Is it lowbrow? Wow. I mean Yeah. So let's go in. I'm gonna just create a couple of channels. Tests, though. Oh, yeah. That's right. Let me just go ahead and give that. Because eventually, you're gonna be interacting through this Nuxt front end. But Yeah. Yeah. Cool. Alright. So we create a couple channels. Right? What do we like, guys? Throw some suggestions in the chat. We'll add a few of these channels. Let's start with Random? Evan is crazy nervous channel. My leg's shaking. General, memes. Okay. Sounds great. Got some memes. Alright. And now as far as messages, right, we got the user. We got the timestamp. We've got when it was updated. In case we change it, we got the text. But the thing that we don't have is how these messages belong to a channel. So inside Directus, we'll just go in. Again, creating these relationships, super easy. I don't even have to touch SQL. We'll go in, create a mini to 1 relationship for this. We'll call it channel, and we'll use our channels collection. And what else do we need? Maybe display template. We'll use name. Great. Cool. And now I could populate a message into a specific channel. Hey, Kev. I'll be nervous. I have the most faith in you while having absolutely no faith in you at the same time. By the way, that user you created for me still doesn't have app access. You might need to go into the user settings and allow that. I thought doing the role would be sufficient, but, oh, I'm not I'm not in that role. I'm not in that role. Users in that role was 0. Yeah. Gotcha. Yep. Try it now. Okay. I'll tell you in a moment. Tell me in a moment. Alright. So we've got the basic logic here. Like, we could go and fetch the data from this API if we wanted to just by going to messages or item slash messages. We're gonna get a forbidden because I'm not logged in, but that's okay. We'll take care of that. Oh, we're 10 minutes in. This? Sorry. No stress, but we're 10 minutes in. That's half my time here. I'm I'm halfway to freedom because I am sweating so hard. Don't sweat, man. Somehow I got logged out. So chaos rains, my friend. Oh my gosh. Glass work. Okay. Alright. I don't know how I got logged out, but okay. So we've got channels. We've got messages. Let's actually do something. I low key hate this, he says. I can still see the chat in the other window, Kevin. So you're not you're not giving me much confidence. I'm so sorry. Alright. Yeah. No worries, man. No worries. Alright. So this is my NUC starter from all the other episodes that we've got. I've switched it up a little bit just so you have a simple plug in for the sake of this live instance because the the other starter that had had a Nuxt module. It's got a lot going on. This is really simple. Right? So we've got a direct us plug in, that is in the plug ins directory inside Nuxt, which will automatically register this. And really simple. Right? Inside Nuxt, we've got a runtime config that we're calling to get the directus URL. And my Nuxt config looks like this. Right? We've got a direct us URL. Right now, that is pointing to, like, a local host, so we do need to switch this over. We'll just go to our URL here. It is switched over. Great. As far as the server token, I don't think we even actually will be using this. I don't think I've got it anywhere else in my config either. Cool. So we'll save that. And now we want to, like, start fetching some data. Right? There's a couple of routes that I already have set up in this, like a login and a register route. Drink a red bar too. I've got all my caffeine. More I've got my caffeine right here. Don't you worry. Give him some more energy drinks, please. I'm gonna go get one. I'll be right back. Woof. Woof. I am actually doing it. Alright. So let's fire up the dev server for Nux here. As far as the stuff that I've already got, like I said, I've got a a login route, a register route that we'll try to mess with. Looks like this. We'll go in and log in, where we can register. Very simple. And then we just have a index page, and then, I had some testing that I was doing here. Alright. So as far as layout, what do we want? You know, a lot of times, I it's like I I love Tailwind. Tailwind UI is totally worth the money, especially for, like, quick prototypes like this. I usually like, if it's just something I'm hacking on personally, I'll start with, like, the application shells. Like, sidebar layout. Right? This feels pretty good for, like, a Slack type of setup. Yeah. That looks good. Yep. So let's just go in. We've got that set to view. Where are we gonna run into issues at? Probably with the icons because I don't have those set, but no matter. Alright. So as far as our pages, let's do what are we gonna do here? Let's just do, like, an app directory. Within that, we'll probably have, like, some channels. Yeah. That makes sense. Just just And then the dynamic route inside of that. Yeah. Yep. So we'll do, like, channel dot view. And then also, what I can do is set up, like, a a parent root for the app. So if I go here inside the pages directory and I do app dot view, I paste this in. Is this for, like, the sidebar? Like, the sidebar will be held in this? Yeah. Exactly. Nice. Alright. So we'll save that. Let's see what we get. That is a major indentation. Yikes. K. So these are Rike's rules. I'm not sure if he's on here, but, alright. He quit out. He hit the wrong keyboard shortcut. That is absolutely what happened. He got arced. That's what our team say when you accidentally, like, quit the whole window. I'm sure he'll be back in a moment. Until then, you got me, stressed as heck. Got my energy drink, waiting for the waiting for the stress to return. This is my brief moment of respite here. That was a major indentation, right? Is it worth the time of pause? Is it worth the time of pause? Did you get arced? I I don't know what happened. Yeah. I guess I did. We're back. Sorry. You don't need to be sorry and you don't get that time back. Oh, yeah. Yeah. It's still running. I don't know what happened, man. I really don't. Weird. All right. So getting some errors here. We get console wise. Home icon. Navigation. Yeah. This is the only bad part. I'm gonna zoom out just a little bit here so I can find Just to actually navigate this, healthscape of tabs. Yeah. Just delete a bunch of these icons. Gosh. They put so many icons in this. Everyone that direct us has a personal choice between Mac and Windows, by the way. So, you know, some folks use use either. I would say with the folks I work with directly, I think we mostly use Max, but that's definitely not the case across the board. Tim is my resident, brain slug. Tim is is my resident Windows user. Oh, let's see. It's not about getting to use Linux. It's about wanting to use Linux. So folks wanna use it, whatever. Most of the stuff we use is in the cloud, so it doesn't really matter. Yeah. Okay. Well, at least we got something now. Right? Alright. We're gonna ditch teams here at the bottom. Where are you? Your teams. Great. Just ditch that list of stuff. Can you zoom back in? I'm like, I need my spectacles to see it over there. Alright. Teams is gone. Right? So now we want to fetch the list of channels here and display those. So how do we do that, Kev? Do you have the Directus SDK in this app? We do have the Directus SDK in this app. Lovely stuff. Well, have you got it set up as, like, a as, like, a plug in, or do we need to do that now? As a Nuxt plug in. So we're gonna Very good. We'll use dollar sign direct us, use Nuxt app. The only thing that I don't have is, like, the auto imports from the SDK. That was one of the nice things about the the other module I had. So we're gonna need to do, what, read items? Yeah. From atdirectus SDK. I think that's it. Speaking of this. What's it complaining about? White space, I think. Yeah. Yeah. These are, again, these are Rikes, ESLint, and Which are very legit for building actually serious things unlike this. They're very, very legit. Right? Alright. So let's do what do we want? We want channels. There's a ref for channels, and then we're going to well, actually, we should just be able to use Nuxt async data. Right? Nuxt data error. We get an error. We're gonna go, wait, use async data. We'll give this a key. Let's just call it channels. And then we just return something from Directus, right? So Oh, look at that interesting code pilot code using old SDK methods. Fascinating. Yeah. Not right. Right? That's a oh, what are we doing there? Auto completion or something. So we'll do read items. We've got channels. Rates. Oh, man. GitHub Copilot is getting on the way this time. You can turn it off if you want. We do only have an hour. We'll see. Alright. So, basically, we're gonna read all the items from channels. We wanna get the fields. We'll just use the wild card. It's basically just name and ID. Does that do it? It should do it. Let's just destructure this and oh. Yeah. Are you are you As as channels. Needs to be channels, and we'll just call this channels. And hopefully And now now usually use channels. Yep. We just go down here. Item in channels. Very nice. And I think we could it is name. The key is name. Yeah. Yeah. Item dot name. We're just gonna swap this for, like, Nuxt link. One of the interesting things, like, Nuxt link will take a h ref or, like, a 2 property. So that's one of the nice things that I like about it just because it's you know, you're used to using h ref on the regular side when you're creating a regular link. Right. Do you wanna do you wanna know what's happened? We've, we've hit the 20 minute mark, and, thank gosh. I don't have to be here like, I can sweat from the audience. At this point, I'm gonna I'm gonna hand off to the amazing Alex who I'm gonna bring in. Hang on. Let let me add him in. Alex, would you, would you like to say hello? Good evening, everyone. Can you hear me alright? I can hear you grand. Lovely. Your your lovely tones. Excellent. I've I've, enjoyed watching you sweat, Jacob. It's been great. Oh, I'm sorry. It's coming it's coming for you too, mate. Yeah. Yeah. Yeah. I'm I'm learning a lot of you right now as well. So Hey, Viv. Hey. We failed to resolve next link. What is that all about? I'm gonna peace out everyone. I'm gonna put you in Alex's very capable hands for 20 minutes, and then, he will be handing off to Matt. Bye for now, folks. Enjoy the rest. Yeah. I thought we were doing, like, Royal Rumble rules. Everybody just hangs on. Be fun. Oh. You just call me now, Brian. I that's alright, my friend. How are you, dude? It's been a while since we've, like like paired up for something. Yeah. Yeah. Yeah. This is this is cool, man. I, always enjoy, seeing all this view code, which I wish I understood better. Yeah. Most of it is is tailwind at this point. But, yeah. So we having an error right now. Right? We're not having permission to access the channels. So we just need to log in. Right? So I'm gonna go to my login form components. Let's redirect to slash app. And we're gonna try to log in to this thing as well. I guess I need to add a user for you. Yep. I have one for Alex. Alright. Oh, don't forget the role. Alright. So I'm just gonna try to log in as Alex now. So we're gonna log in. Oops. Nope. That's not right. It's auth/login. I already used the link on the home page. That would have worked. So we got Alex at example, and the password is super secure. Okay. Alright. So now we can see at least we have our channels up here. Right? That looks good. Yep. Yeah. Nice. Alright. How are you feeling, Alex? Are you nervous as as Kevin? Like, feeling good? I I've got a feeling you got the power of Bry Ross, under the hood there. So Ross. Big. I've got confidence, man. I got confidence. Okay. Right. Alright. So now we need to fix, like, the h refs here. Like, hey. We wanna navigate to one of these channels. Right? Let's just test if this is gonna work. I'm a script at the top guy. Not sure if we've got any other view fans or not. I like seeing what's going on with the JavaScript first, and we're going to use the routes. So we'll do routes. Let's use routes. And I'm just gonna do this. I'm just gonna log this out. Right? I wanna look at routes a params.channel. K. Good. The other thing that we're gonna need to do somewhere in here where's the main dev? Main content. We're gonna plug in this Nux page here. So this should render the child page, for all my nested routes. Yeah. Alex is a boss. So, I'm not sure if that's John Daniels or not, but, I will say, like, if you've used the template CLI tool at all, Alex is is that guy. He's the guy that put that together originally. Yeah. Thanks thanks to directors having good APIs. It was, yeah, difficult, but we we got there, and it's pretty cool right now. Yeah. Alright. So we got the next page. I save. 2 2 what's going on? Oh, yeah. I forgot to fix the login again. Yeah. Apparently. Yeah. You need to persist that. Alright. So what else do we need to do? We need to fix those links. Channels. Where are you? Alright. So this will be channel or item dot name. Item dot name. Yeah. And then we'll do /channels /item.name. Should get it. We'll do a Nuxt link. Oh, duh. It's app dot channels. Nice. So now we have our channels working. Right? What are we gonna do inside the channels? Right? Great call. What do we do inside the channels? We are going to here is this is where we're actually gonna fetch, like, real time data. Right? Yeah. You think? Yeah. Yeah. I'm looking forward to this part, the the, real time. Okay. Alright. So I'm gonna cheat a little bit. We're just gonna go into our documentation. We do have this nice guide, if you haven't checked it out, on real time multi user chat. There's a vanilla JavaScript version. There's React. There's Vue. Let's just take a look at this. Right? Alright. How are we going to authenticate with real time? So we've got, the authentication composable. We've added the real time composable. All of those are in my plug in here. So it should be as simple as, by calling connect. That'd be right. Let's see. Kevin's got a I think it was Kevin that he's got. We've got a subscribe function here. So we're gonna subscribe to the messages, client dot connect. Yep. Let's see here. This is usually where I run into some SSR issues, but I'll we'll see how you manage this one, Brian. Let me show you. SSR equals false. That's That's the solution there? That's the easiest way. Yeah. Yeah. Absolutely. Alright. Alright. So let's just, is this cheating? Yeah. This probably is. Use whatever you have at your disposal. Right? So the subscription client dot subscribe messages, that's correct. Our fields here that we want, we're grabbing the all the fields on the root level. I guess I could zoom in a little bit and make that easier for everybody. Then we have user. So let's just grab all the fields from the user. We've got message of subscription, and then it looks like there's a receive message function that goes along with this. Yeah. You yeah, you might need to filter on the channel as well, but maybe we can do that in the next step. Yeah. So we've got data type equals subscription. This is just like the init. Right? Let's see if we are getting any. No. Yeah. This is gonna be interesting. I should have set up better authentication. So persisted on this. Alright. Do we see anything from, see some more issues from Tailwind? X mark icon. Where are you? Note to self, on the next one, do not import any of the icons from. So this should be, like, we definitely wanna redirect on this. Right? Let's add app. Let's add some page meta. Define oh, actually, it's not a const. We'll just do define page meta. And if we give this a middleware of auth, it should automatically redirect. And then I'm gonna go into where's my login form? I'm gonna make this really easy for myself, Alex. Oh, good idea. And password, super secure. We'll learn a little bit about everything here. Okay. So it's redirected. Sign in. This should take us back to the app, where we could get all the channels. What am I not seeing here? Right. Do we have to do we need to connect to the client, I guess? Let's plug in why we're not receiving Yeah. Client dot connect. Right? Isn't that it? Okay. Yeah. Yeah. So this is on mounted, but we're not using SSR. This should just work. Right? Where are we gonna stick that? On? Oh, what if we just call subscribe? Set up all these functions, forgot to call them. Alright. Client is not defined. Duh. Because it's not the client, it should be directus dot subscribe. So, again, we're gonna grab that directus plugin that we set up from use Nuxt app. It's error data is not defined. Oh, we gotta pass the data to that. Depcription started. Okay. Alright. So what do we wanna do now? Within the channel, we wanna have all of our messages. Right? I'm assuming that will be an array. Sounds good to me. In our received messages Actually, let's just do this to start with, see what type of data we're getting back. Received message. Alright. So now if we go in, I got, like, 35 tabs here, don't I? Don't we all? For apps. For hours. Live. Or I forgot the URL. It's just 100 apps dotdirectus.app. And I've logged myself out again. Let's keep that. I can't help you here, Brian. Okay. Alright. So now how do I split these out? That's the only thing I get hung up on on our half the time. It's like, hey. How do we make this, go split screen? Alright. So couple things should be happening here. Right? We've got our subscription. Let's make this larger. Right. So the subscription has started. We're in the Kevin is crazy crazy nervous. We'll probably have to filter that on our front end anyway, but, testing 1, 2, 3. We'll just add the channel to begin with. And Oh. Cool. So we we see the real time is connecting. We're getting our data. There's the actual messages. Great. Right. We've got the test. We've got the user is null. Probably permissions. Correct. That is correct. Alright. We need to make sure that the user role can access all the users. So right now, it's just by default, like, just a one user. We'll just make it so we can read all the users, and let's send a new one. Test 567. Save. Get our message. Okay. Now do we get the user data? Okay. Cool. That's a lot more user data than we actually wanted. So let's go in and trim that down. We'll just do first name, last name, avatar. We probably want the ID too. And the dot ID. And the first name, not the first name. First name. Not the catch. Good catch. Alright. Cool. And then here, we need to what? We want to see the message. Where's that message at? Alright. Subscription, create data. We want to filter that. Right? What are we gonna call this? For each message of subscription, We need to populate that data. Right? Do do you wanna filter it at the at the WebSocket level or just at the whenever a new message is received? You could do both, I guess. But Yeah. I I'm trying to think of, like, in Slack, you'd probably have, like, some kinda indicator up here that's, like, hey. Yeah. Yeah. Even if a message in a different channel popped up, you would probably filter it out on the the front end. Right? Yeah. On on the specific channel Because you probably have, like, some type of yeah. Alright. So receive message here. What do we does it show the oh, let's get the channel name as well. Query Oh, yeah. That's that'd be a useful one. Dotidchannel.name. So here, if the data type equals subscription. Create. And data event equals create. We will push those messages into our array of messages. And then instead of this, we should be showing a some messages. Alright. What we doing on time? I've totally lost the window here. Yeah. Me too. Me too. It's about quarter to the hour on my clock. Let's just take a look. 22 minutes left. Okay. Alright. Cool. Fun. Fun. Fun. Alright. GitHub Copilot for the win. Do we how confident are we feeling? I can't even see the chat at this point. But Not confident in that one. So we'll do, like, a list for all the message. Yeah. Again, hey. Like, you gotta take it with a grain of salt. Message dot content. You know, I I think this would get better if we had typed this out, but Yeah. Key message dot ID, we should have that for sure. We'll just close that list, see what we get. Alright. Do we have any messages? The other thing is, like, do we fetch the messages for this channel on Initially. Right? On yeah. And, like, do you have you messed with the the real time and, like, the chat stuff at all, Alex, or no? Not not a not as much as I should have, but, this is an interesting one because you might not even need to use the real time in this in this example. Just Hello, everyone. It's the disembodied voice of Kevin here. Hello. I'm watching, I've been screaming into the void in chat. I know this isn't I know this is an illegal move to throw you a bone here, but the init payload comes with the initial items. Up to a 100, and you can set limit minus 1 to get more than a 100. Okay. Peace out. Bye. What was that, Alex? You wanna miss off the clock, mate. Okay. Thanks, Gabe. I think that's probably just, just we you can start logging out the the all all the messages that come through from the website. Right? So it's probably a yeah. So it would be one of the messages received maybe, with the initial with a different type. I don't know if I've seen that in your logs to be fair. But Yeah. Let's back up. Subscription started. Received message. No. Matt's gonna save the day on this. We should know this. I should know this, Bryant. That's alright. So, like, we we'd also do it this way as well. Right? Where we go in and read items. Oh, nope. That's gonna be coming from the SDK. Read items from directus s e k. Let's just call this Oh, I think no. I'm starting to sweat. Thanks, Kevin. Yeah. No worries. God. What am I doing? Constant populate messages equals async. Yeah. There we go. This is wrong. Totally wrong. Oh, no. That's not that bad, actually. Filter channel equals route dot params.channel. Equal route dot value. No. Should just be route dot is it route dot value? Is route reactive? Hello. Hello, everyone. It's me again. It's Kevin again. I was I was having the chat with Matt, and in the nature of chaos, we've decided to Royal Rumble this. So it is my pleasure to introduce mister Matt Minor. Hello, Matt. Hey, everyone. I, will be the most useless person on this call because I'm not Oh, hardly. I'll help you pick the colors. And I was actually thinking, maybe I could just sabotage Bryant here at the end. Just Sabotage. The headlines of the day or something. Do it, man. Oh, no. Don't don't do that. Can you imagine? What do we have? We have we have 15 minutes. No. There's there's 20 left. We got just just 20 minutes left. We're 2 thirds of the way in. No pressure. But it it doesn't look like Slack yet, does it? Where are the messages? Yeah. Alex, is it is this what you did with your 20 minutes, mate? Mate, we were we were work we were working hard. Yeah. We were working hard. All hardly working. Yeah. Yeah. This was where are we stuck? What are we, what are we doing? Just gonna load the initial messages. So so I know from experience that when you I don't know what's up with maybe how you're including real time here, but that init payload that init message does come. It definitely comes with all of the mess with all of the items in the collection, but I have also observed it's not happening here. So I think that would be the easiest fix is, like, why isn't that working? Because then you can just use the real time interface. Right. Why isn't that working Yeah. Kevin? Yeah. Bloody great question, mate. So I log in. We go to Kevin is crazy nervous. We start this subscription. Yeah. But where is the where's the payload? Where's that init payload? So in oh, oh, I think maybe it Oh, is it is it here? Is it is it because you're just subscribing to the single event? If you remove the event from, like, inside of the, if you get rid of that and get every everything over that subscription, does that help? There it is. Data. Oh, okay. Yep. Okay. So just use that. Nice. Okay. There we go. My man. That's what I was gonna say too. But, Kevin I know. Sorry. I felt I felt emanating from your your corner of the ring. How are you doing today, Matt? Is I'm good. I'm doing good. I'm excited to be here. Good. News headlines for today? Facebook and Instagram. No. Struggling. I I sorry. I was too busy working, so I didn't know that. Oh, yeah. Sorry. I was too I, it just came up when I was logging in to work today. So, how's the chat doing? Is anybody Let's see. You you might have to spread that one, Bryant. I think. Yeah. Yeah. You will. You will. Just enjoying the chaos. Yeah. But for a net but for a net, dude, for a net, don't push it into the array. Just replace the array because Oh, okay. There's gonna be nothing in it at the start. Data dot data. Joshua, just enjoying the cast. Honestly, so are we. As I said, this is our first kind of live event of this kind, and I don't know. I'm having a great time. It is chaotic, and we're just making it up. We changed the format. Who bloody cares? Love it. Yeah. Nope. Alright. So now we got some messages. Right? Let's, let's mess with this a bit. Alright. So we're gonna have a div. We're gonna show the avatar, image source, message. Joshua, it's so funny. You're like, yeah. You know, I'm struggling, like, getting this, you know, flow automation thing to work while listening to you in the background. And the funny bit is some of the people who would be there to help you are currently here. So it's like, have fun struggling, but you're struggling on your own for the next, 15. Nah. Jonathan's there. Funny. And you think you're struggling to get, like, 4 people watching you work at the they were all just staring over Brian Schuler right now. I love it, man. Thank you. This is this is this is more fun for me. I think I feel like I go, when I go shopping with my wife and, like, just follow her around the store. That's what I feel like right now. I know value just in the way. Alright. So far. Let's add some padding for these. What else? We have time for the padding. We have time for the padding. Dude. I'm good. God. I'm sweating again. All things, man. All things. You you gotta have time for it. Alright. So we got some messages. Great. Now we need what? We need a form at the bottom of this one. Right? So we got VText. What does this thing do? This is, just text. Okay. Great. We don't need that. So we just got an input. Text input, b model, new message. Right? So we're gonna populate. We can just scrap all this shit. Oh, were we allowed to curse on this one or no? Well, you've done it now. So yeah. Sure. Anything anything goes. Anything goes. Alright. So we got a new message. We're just gonna add a ref for that. We will remodel new message at key up, enter, send message. Yeah. I don't like that, but, let's just wrap it. We'll give it a button. Oh, actually, I forgot I got the Nux UI library included in this. So we should be able to get something nice just by doing new input. Did that work? No. Nope. Did not work. No pressure, Brian. That's the beauty of this. No pressure. Yeah. We've all gone quiet. Just letting you work for a moment. New button. Just add send message. What are we doing now? Nope. Gotta close that guy. Alright. So we got this. Right? We need to add a handler for this. Just click send oh, let me just remove that. Send message. Nice. And then we've got should have an async function for send message. And this is not right at all. Right? So I think I don't know. How are you gonna tap into the because you you've gotta you've got to send it over the subscription, don't you? Yeah. That's that's where I was going back to your wonderful guy. Oh, no. No. No. I think it's fine. No. No. No. Ignore me. It's totally fine. You can do it. You can do it at that level where you've written send message because received message is at that level too. Right? So Actually Actually client.send message. Right? Should be possible or no? We'll find out in a minute. I think there's gonna be a weird scoping thing here. Let's find out. Well, I think if you just use the create item function with the new SDK, that should work. Do that, but you could also send it over you could send it over the WebSocket connection, and then you're just using that one connection use make a make a HTTP request, honestly. Test. Oh, yeah. That's why you don't trust this. Right? Yes. Request. Yeah. Create item. Yeah. Did you get it? Test. Send message. Alright. What do we get? Unexpected error occurred. Good. Good. Good. Helpful. Helpful. Helpful. That's the channel that's the channel name. That should be the ID. Right? Yes. Correct. Oh, did we get the channel ID? No. That's right. Because, it's in the it's coming from the URL. Yeah. But it's That value there is coming from the URL. Yeah. Yeah. The dynamic part. But the channel itself Oh, wait. Right. Right. It's saying here, ding, ding, Alex. Got it. So hang on. I lower my confidence. There you go. Yeah. Yeah. So we gotta get the channel ID. Where do we have the data? Right? Oh, I understand now. I understand what you mean the ID. Got it. Got it. Got it. So I I think you'll need to do that. That initial load. Yeah. I think I think you do. Yeah. Just to fetch that ID. I really don't wanna do that. But Mate, you've got you've got 10 9 minutes. Oh, we got left? You're doing that. We're doing it. Yeah. Alright. So read items, channels, filter ID equals no. That's our ID. What's the channel name? Right? Or Channel name. This will be wrapped. We'll have what equals. Alright. So you can have the channel. There's the ID. Good. And it's an array out, which is a rookie error that I often make. Woah. Yep. That is. Yeah. So this will be How are you doing over there, Matt? Having a good time? Watching all along. Yeah. I'm waiting for us to get to the colors. I think that's where I'll have my most, impact. Well, we've got we've got a whole 8 minutes, and I think we're about to be finished, I guess. So, you you get a pick of the next function now or 2. If I use the, what, If we use async data, and you can do, like, some transforms on this as well. Using data. Channel. Turn up paste. Is this guy out? I think I think Greg's suggestion was the best to just use the channel ID in the in the URL. Probably. 100%. Requests from that. This needs to be here. I like the pretty URLs, though. Alright. I will just refresh. Go to crazy nervous. Okay. So now we've got the channel. Alright. So we got channel. This should just be the channel dot ID. Best. Okay. So we're okay. It seems that, like, the message was sent, unless I'm wrong. Test. Okay. So it did populate the message. We just don't see it show up here. Alright. So what are we doing wrong there? In that payload, shouldn't it also have the the channel ID? Yep. It does. But it does, but it's not showing in the console there. Test. Okay. There's the test. Send messages. Yeah. That's kinda odd. Why is it not showing that in the payload? Alright. Regardless, we should be getting a message back. Right? Reading undefined dot avatar. This is a simple v f. Right? Yes. Message. Just get rid of it. No one needs an image. No one needs an image. We're using Slack compact mode. Ditch it off. Get rid of it. Just saying that's 5 that's 5 and a half minutes. Yeah. No worries, man. Alright. Test. Alright. So first name. Why is this not populating? User but we gotta have the username. Right? It cannot read first name. It's not user. It's user created or whatever you called it. Right? Or did you rename it to user? No. It should be user created. Yeah. Channel messages dotuser.firstname. This is used, like, a v f message. A user? You're trying to get something out of this. Oh, shoot. Blah blah blah. Kevin is crazy nervous. Still crazy nervous. I'm literally rocking back and forward in my chair here by feeling the heat. So, like, the the messages are coming in, but we're just not populating. We missed something. Messages dot value dot push data dot data. Alright. So if we're receiving data, we're getting data. It's it's a it's another array in there. Data is an array. Yeah. Okay. So, we'll just do this. Someone else who has the cute baby in the background, and I'm using a hardware mute, but, I think that's that's me. And they're they're they're hungry. They're not. Sorry. That might be my fault. Oh, do you I haven't heard I haven't heard yours in the background. Okay. Was it yours? Alright. Could be. They came home, but they're not sounding cute. They're not sounding cute. They're sounding angry. Alright. So now what is the Nuxt command? Right? I think there's one where I can let you guys tunnel into this. Right? Nuxt dev tunnel? Where is this guy? That is brand that must be brand new because I didn't know that was there. But it's built into Visual Studio Code. If you bring up your status bar at the bottom, I thought it was baked in now. No way. That is sick. Alright. So does this actually work? There there's the tunnel right there, the Cloudflare URL. Womp womp. Why Why doesn't it work? You better work it out because you got 2 minutes. At this point, I don't know. Do you guys just, do me a favor and then log in to the direct us instance in this firehouse message? I got you. P p m. Yeah. Nux. Dev. Oh, p m p m. It would be, like, running the let's see. I don't know if that's it or not. I'm stressed. Yeah. So there we go. Yeah. That was me. It's Does it work does it work if you type it in here as well? Yes. I I'm logged in as Alex. I'm logged in as Alex. Alright. Send send one more just so we can say something. Alright. Here? Less stressed. Yay. Yeah. Rock on. Some concept. That's basically feature complete. I was shitting. Hit right at the buzzer. Is Let's go. Because this is a winner. Yeah? Yay. Holy heck. My heart rate. I'm I'm gonna need to take a shower. I'm sweating. Oof. Look at that. Yeah. Wow. Stop the timer. Yeah. Leave it for Stop. Yeah. Series a already. I've watched them, man. And don't forget, there's more in your stressing and dressing in 100 hours. A whole season now available on Directus TV with more coming in April. I I look forward to the other episodes where you guys are are it may be not on there. I I've enjoyed this a lot, though. This is fun. It definitely distracted a little bit. Yeah. I did feel like, should this be, like, 1 app in a 100 hours, but with guests? Oh, wow. Oh, so takeaways from this. Right? What did we what did we learn? I'm not coming back to the next one. Spend more time with Patty. More time with that. Dude, I'm sorry we didn't get to the, like, colors. Like, do you wanna what what color are you feeling, Matt? It's the only reason I'm so sorry. Pick the color. I'm looking for the color. I can't think of the hex code for I didn't 466, double f. What do you want? 4466 f f. No. It's 6644 f f. Knew it. Hopefully, Ben's not watching. What is it? Oh, he's watching. 6:6:8. He's watching. 44 f f. Where are we? Is it not updating? Is that already the indigo oh, there it is. Okay. No? Maybe it wasn't on it here. Is that the sidebar? Yeah. Okay. Sidebar. Yeah. There you go. BG you're gonna find me one more time. 6644 f f? Yep. That's the one. That's it. There it is. Subtleship. Boom. Feature comp New product. Ship it. Thanks everyone for joining us. Yeah. I'm not I'm not kidding. We probably won't do another one now. We probably will, but I won't be here. Gosh. Literally, 30 seconds to go. 30 seconds to go. Congratulations, Brian. Yeah. I feel like we started off on the wrong foot with the 5 minutes of technical difficulties. The for the nodes. Selecting 5 minutes of technical difficulties at the the start of this thing. That's what everybody did not see on the front end. We got there. We got there. Now we're now we're ready to code for, like, another 2 hours. And if you're watching this live, this whole episode will be packaged up as a special at the end of season 1 of a 100 and a 100 hours on director's TV. We'll pop that out tomorrow. And then there are new episodes of a 100 apps and a 100 hours coming in April. Amazing. Excellent. Yeah. Well, thanks for joining us, to the circus episode of 100 Hours 100 Hours. I'm your host, Brian Gillispie. Thanks to my special guests, mister Kevin Lewis, mister Avdv, and Awesome. With the crazy colors coming in. Thank you, everyone. Bye for now. Cheers. Bye. Cheers.","published",[148,158,167,179],{"people_id":149},{"id":150,"first_name":151,"last_name":152,"avatar":153,"bio":154,"links":155},"82b3f7e5-637b-4890-93b2-378b497d5dc6","Kevin","Lewis","a662f91b-1ee9-4277-8c9d-3ac1878e44ad","Director of Developer Experience at Directus",[156],{"url":137,"service":157},"website",{"people_id":159},{"id":160,"first_name":161,"last_name":162,"avatar":163,"bio":164,"links":165},"45ccc96f-caa7-4e6c-931a-5f209c70a25a","Alex","van der Valk","d8221565-7c60-4745-87b3-f54094123f7e","Sales Engineer at Directus",[166],{"url":134,"service":157},{"people_id":168},{"id":169,"first_name":170,"last_name":171,"avatar":172,"bio":173,"links":174},"791e1503-1d88-463d-9347-0b9192933576","Bryant","Gillespie","9013afc8-e8d7-4182-9b18-44db08117bb9","Developer Advocate at Directus",[175,176],{"url":131,"service":157},{"service":177,"url":178},"github","https://github.com/bryantgillespie",{"people_id":180},{"id":181,"first_name":182,"last_name":183,"avatar":184,"bio":185,"links":186},"ca1ac688-ecac-4f25-a4e9-7daf52c8235a","Matt","Minor","b4402ab0-41e4-4fc6-8bf0-769bf39ff114","Director of Demand Generation at Directus",[187],{"url":140,"service":157},[],{"id":190,"number":191,"year":192,"episodes":193,"show":204},"56dda5ff-2c3a-41ce-ae3a-580d6101026b",1,"2023",[194,195,196,197,198,199,200,201,202,203,122],"cb4e067f-9507-4e18-ab9a-435565f9e653","8434838a-8e4f-489a-8da2-fbf10de5de6a","c997b25e-400c-4350-bba4-f63853d844f7","1109be0d-8ab5-479b-a052-8ad30d9ffb1c","dcb952e0-7d13-43ea-9b1c-f2ca63efd07d","0aae287d-3916-4d91-9310-25828998e562","8fed0eee-43f6-4767-8b77-79da7a059821","a311b57d-34e7-4073-9cf3-6a8c6c0f8b85","9271a4fc-addf-4400-9591-f2f2ec07bd79","30c48566-52cd-4728-a633-ca9675acc959",{"title":205,"tile":206},"100 Apps In 100 Hours","fb0f9d45-be21-4634-94d4-2ef1cc5146f2",{"id":208,"slug":209,"season":210,"vimeo_id":211,"description":212,"tile":213,"length":214,"resources":8,"people":8,"episode_number":191,"published":215,"title":216,"video_transcript_html":217,"video_transcript_text":218,"content":8,"seo":219,"status":146,"episode_people":220,"recommendations":222},"9a3a8ffa-a27b-421c-93cf-3da2dcb726e9","crm","14fda5f2-95de-4dbe-a4e2-3fd956c21c19","936325383","In this intense one-hour challenge, watch as Bryant incredibly builds a full-featured custom CRM from the ground up using Directus. He builds contacts, organizations, deal pipelines, activities, and more.","f6b880a0-5cd2-45ad-beff-3117f0a78581",56,"2024-04-19","Mission: Customer Relationship Manager","\u003Cp>Speaker 0: Hi. Welcome back to another episode of 100 apps. 100 hours where we build some of your favorite apps or try to rebuild some of your favorite apps in 1 hour or less, or get publicly humiliated trying. Alright. Super excited to be back for another season.\u003C/p>\u003Cp>If you are new to 100 apps 100 hours, there are 2 basic rules. Number 1, you have 60 minutes to build and plan and build, no more, no less. So when that clock strikes 0, that's it. And then the second rule is there are no other rules. Use whatever you have at your disposal to complete the functionality.\u003C/p>\u003Cp>That's it. Let's dive into this episode. So today, I've got a custom CRM. Tools like Salesforce, Pipedrive, HubSpot, they really need no introduction. Everybody needs a CRM.\u003C/p>\u003Cp>A lot of them, depending on your purposes, may be way too much for what you need or they may not be specific enough for your industry. So we are going to build our own custom CRM in 1 hour or less. Let's do it baby. Alright. So, let's start the clock and away we go.\u003C/p>\u003Cp>So, when we plan our CRM, what do we need out of a CRM? Right? What kind of functionality do we want to see inside our CRM? So basically, we want to manage all of our contacts. We want to manage all the different organizations those contacts belong to.\u003C/p>\u003Cp>What else? We wanna manage our sales pipeline. Manage sales pipeline. That's gonna be deals or activities. We want to be able to track activities and follow-up.\u003C/p>\u003Cp>So this seems like a a pretty good set of functionality for a basic CRM. I'm sure we might embellish this a little bit depending on how far we get, but let's dive into actually fleshing out what the data model just might look like for something like this. So we'll drag a nice square up here. We're gonna have contacts. We could just call those people.\u003C/p>\u003Cp>I'm a big fan of that. We're gonna have organizations. There's definitely gonna be a relationship between those 2, but I I feel like this could be a many to many relationship because one contact could belong to multiple organizations, and and some CRMs make that a little more difficult to do than others. What else do we have? We're gonna have deals or opportunities.\u003C/p>\u003Cp>Deals is kind of a a standard nomenclature. We're gonna have activities that are attached to what, those are attached to organizations, they're attached to contacts, they're probably also gonna be attached to deals. What else are we going to have? We're probably going to have some sales reps, those are going to be our users inside our accounts. This looks pretty good for a a base set of functionality.\u003C/p>\u003Cp>Let's dive in and actually start building something. So, I'm just gonna pull up my Directus instance, that's what we're using on the back end. Totally blank instance, we'll zoom in on this, and let's start building. Right? Let's create our first collection.\u003C/p>\u003Cp>Let's start with contacts. So we're gonna give this a contacts as the name. For the primary key, let's use generated UUID. What kind of fields do we wanna add for our contacts? Right?\u003C/p>\u003Cp>When we think of contacts, we're gonna need name, email, but we may also wanna track when this was updated, when it was created. Directus makes that super easy. And one tip that you may not have realized if you've used Directus before, is I can go in and change these to be whatever I want. So if I wanted this to be created at and created by, I could go in and update those, change them to be whatever I want. Updated at, updated by.\u003C/p>\u003Cp>That's the the nomenclature that you're used to. Do we actually need a sort for our contacts? I don't think. Let's just go ahead and save this. Alright.\u003C/p>\u003Cp>So for our contacts, we're gonna add a first name. Great. We'll make that a string. Let's do half width for that. I can go in and click the three little dots here and just quickly duplicate this field to save myself some time.\u003C/p>\u003Cp>And, of course, as I am plotting away on this, Directus underneath is mirroring all these changes to my database schema. So we've got our first name, we've got our last name, we're gonna need a email or an email. Great. Let's require this value. And, you know, I could even go as far as, like, making this unique if I I wanted to have some type of unique identifier to match these up with.\u003C/p>\u003Cp>Alright. As far as an interface, you know, I could get fancy with this and we could add a little email icon like an at symbol. That's great. I think everything else is good. You know, one of the other nice things that's built into Directus is I can go in and add my own custom reg x validation.\u003C/p>\u003Cp>So, you know, I can put in some type of reg x that matches email addresses. I don't have one of those handy here, but we'll go ahead and keep marching along here. So we got first name, we got last name, we got email. We'll probably have a phone number at some point, but let's let's go for, like, a job title, probably. I'm trying to think of all the standard fields.\u003C/p>\u003Cp>Maybe we have some notes. We could set that to be a text area field. I don't really need formatting for those. Great. Job title.\u003C/p>\u003Cp>And maybe honestly, let let's strip this out because I'm I'm thinking about that many to many relationship with organizations, and we may have, different titles at different organizations. So I'll show you how we can handle that coming up. Alright. So we got first name, we got email. We could go ahead and add phone number as a string.\u003C/p>\u003Cp>Phone or phone number. Let's just keep it short. We'll say phone. Let's give it a phone icon. Great.\u003C/p>\u003Cp>K. Phone, email. We'll put email above phone. Cool. Looks nice.\u003C/p>\u003Cp>Alright. Let's just take a look. Right? We've got first name. Go ahead.\u003C/p>\u003Cp>Oh, I was gonna add my wife here, but she's gonna get mad at me if I misspell it. Right? Ashley atexample.com. I had a phone number, 555-5555. Here's some nice notes for our contact.\u003C/p>\u003Cp>These are looking great. Right? We've got our contact in here. I could potentially sort and filter this a a hundred different ways, maybe change the layout. But let's dive into the next collection that we wanna set up, our organizations.\u003C/p>\u003Cp>So we'll have organizations, again, for the primary key, I'm gonna use ID and just use generated UUID as the type. And, again, I could change created at I could change this structure for these system fields to be whatever I want. Created by, date updated, that's gonna be updated at, if I can actually spell. And we use this one as well and call it updated by. Great.\u003C/p>\u003Cp>Okay. So now we've got an organization. For our organization, what are we gonna have? We're gonna have a name of the organization. Right?\u003C/p>\u003Cp>Probably, some addresses for that organization. Right? We could have multiple. So that's where we might reach into an another bag of just a different table. What else are we gonna have for an organization?\u003C/p>\u003Cp>You know, let's just do something like country in case we want to, even, like, automatically route new organizations to a specific sales rep. Potentially, we've got a name. What are we gonna have? We're gonna have a website. You know, we may have a a logo if we wanna track that for a company.\u003C/p>\u003Cp>So let's do an image file. Let's call it logo. You know, you might even add things like brand color, things like that if if you're really in tracking that. And then we'll just add another section for notes. This is gonna be a text area field with the type text, and we'll hit save.\u003C/p>\u003Cp>So we've got our organizations. We've got our contacts. Let's make a link between the 2. Right? So how do we go about that?\u003C/p>\u003Cp>Depends on how you want the data to actually be structured. Right? In an application, depending on the setup, maybe a user can only belong to one specific organization. But often inside of CRM and maybe I am working with the local little league. I am a member of the board there, and then I'm also a developer advocate here at Directus or, you know, a a founder at Better Side Shop.\u003C/p>\u003Cp>So a lot of different relationships that is gonna be modeled with the mini to mini relationship inside Directus. Here's how we set that up. It's gonna be pretty easy. We'll just go to create field. We'll look for a many to many relationship.\u003C/p>\u003Cp>And because we're on organizations, our key here is gonna be contact for the or contacts, I should say. Our related collection is gonna be contacts and then we'll just go through and paint by numbers here. Do we want to show these in a list or a table? I'm good either way. We definitely want to show a link to that item so we can get to that specific contact, but I'm gonna pop open this advanced field creation mode just to show you that you do have control over the naming of the junction collection and the individual fields within that.\u003C/p>\u003Cp>So, the default setup here is just going to take this table name and this table name or this collection name and this collection name and marry the 2 together and we end up with organizations underscore contacts. Works for me. We'll leave that autofill set up. And then on the reverse, I'm gonna add that many to many field to the contacts as well. We're gonna keep that as the organizations.\u003C/p>\u003Cp>Alright? In this case, we may have a sort field. So we wanna control the the sorting for contacts, like who's 1st, 2nd, 3rd, that could be helpful in, like, a primary contact situation. And then we have some of our relational triggers. Right?\u003C/p>\u003Cp>On deselect of organizations' contacts, what do we want to do here? Yeah. Maybe I want to delete that association. So I'll set these to cascade. Great.\u003C/p>\u003Cp>So now that back out, we can see we've got our contacts field here. If we go into, yep, looks like we've got, an extra contact table created. I might have typed something wrong. But, so we can see contacts there in the organizations. And then on our contact, I should see organizations here.\u003C/p>\u003Cp>Let's just add an organization. Right? So let's say my wife worked at Tesla. That's in the United States. And, you know, we could add the website, logo, notes.\u003C/p>\u003Cp>I could choose existing contacts. But I'm just going to go ahead and say 1. Right? So now, I could see I've got that organization here, but it's showing an ID, which is not super helpful. Right?\u003C/p>\u003Cp>So I can go into the organization itself and control the display template where I have a name. I could even potentially add the logo to that if I wanted. So now if we check it again, we can see, okay, here's the organizations that they're a part of. Right? Now, you also have the ability to manage the data inside that junction collection.\u003C/p>\u003Cp>Right? So if I go in and and like I said, I may have a different job title at all these different organizations. So I could go in and add a new field here inside this junction collection for job title. Great. Alright.\u003C/p>\u003Cp>So, now if we go into Tesla, we can see the job title is down here at the bottom, but Directus also gives me an easy way to control where that displays. So if I go to our many to many relationship inside our contacts, I can go to maybe we wanna add a sort field for this just to make sure. And then if I go to the interface, I can control where my junction fields are located. So I can put this at the top, which should be great. Okay.\u003C/p>\u003Cp>So now if we just check that out one more time. Right. Now I could see director of DirectUs. So now I could give a specific job title to this specific person within that organization. I could go in and add a new organization as well, like, hey, the little league.\u003C/p>\u003Cp>We can say a board member. Right? Little League Baseball is the name of the organization. Great. Okay.\u003C/p>\u003Cp>So now there my wife is a part of 2 organizations, different job titles for each. Right? Alright. How are we looking on time? We're looking great.\u003C/p>\u003Cp>Let's move on to our next collection that we want to set up. That's gonna be what? Our deals, our activities, what do we want to set up next? Let's go for deals or opportunities. Deals is probably the standard naming for this.\u003C/p>\u003Cp>So that's what we'll stick with. Could be opportunities. Could be something else. That's fine. We'll do created at.\u003C/p>\u003Cp>Just to keep the same structure, we'll do created by. And by adding these, whenever this record is created, it's going to populate a timestamp and a user. The same thing for Updated, whenever it gets updated or who updates it, it's gonna populate that info for us. Updated by. Great.\u003C/p>\u003Cp>Okay. So our deals what we're gonna have for our deal? We'll probably have a name or a title of the deal. Deal name sounds great. It could be good to prefix some of these sometimes if you're dealing with a lot of nested relational data.\u003C/p>\u003Cp>So you get used to seeing name in there a 100 times. Could be confusing whether that's the actual deal name or the organization name or the contact name. So we'll just save the deal name. What else are we gonna have on this deal? Right?\u003C/p>\u003Cp>We'll probably have a average dollar amount or a potential dollar amount. Let's set that to be, decimal. Deal value. We can add a nice little dollar sign to the icon to make this look nice and pretty. And then we are going to have some related fields.\u003C/p>\u003Cp>But before we do that, let's just add, like, some deal notes or something like that. Sounds great. Okay. So we got a deal name, we got a deal value, you know, maybe these are side by side, not a big deal. Let's go in, and now we want to add a relationship to the organization and probably to our primary contact for this deal.\u003C/p>\u003Cp>So those are both gonna be many to one relationships. And we'll set a key of organization here. So the related collection, we'll set that to be organizations. Great. And I could control the display template here.\u003C/p>\u003Cp>We'll just use the name and we hit save. So now we've got an organization that we're gonna tie to the deal. I'll make that half width. And then we also want to add a primary contact. So we'll just say primary contact.\u003C/p>\u003Cp>The related collection here is going to be the Contacts collection. And for the Display template, let's use first name, last name, and let's use this format, which I think is typically how it displays inside an email client where you have the email address inside these less than or greater than SQL or symbols. So let's save that. We've got our primary contact. We've got our notes.\u003C/p>\u003Cp>We've got our deal. What do we need next? Right? We need to track where that deal is at. So we could use, like, a a status field as, like, a drop down for that potentially.\u003C/p>\u003Cp>Right? What's the status of this particular deal? Blah blah blah. Or we could do something else where we have a relationship to a deal stage or our individual pipeline. Right?\u003C/p>\u003Cp>Because then we may want to, potentially have separate pipelines or we we wanna give more control back to our users, so the the sales reps or the sales manager to control that actual pipeline without getting into the admin section. So how can we do that? Right? Looks like I've got a little extra couple of fields that I've been creating here inadvertently. Alright, so now let's add a new collection.\u003C/p>\u003Cp>Let's just call it Deal Stages. We want to manage what are the stages of a particular deal. As far as the optional fields, maybe I don't really not super concerned with these, deal stages. We're gonna call this the name of this particular stage. Maybe we wanna give it a color so we can add some color to it.\u003C/p>\u003Cp>And, you know, I guess we could even give it an icon if we wanted to. We could play around with that and see what that looks like. Deal stages. Let's make both of those half width. And, now, let's just go through and map some of these particular stages.\u003C/p>\u003Cp>Right? And I and I, you know, I've got this s on here. One of the other things that you could do inside Directus, you can't control the translations for all of these. So even if you're working in English, on the the back end, you may want to use prefix tables to keep things nice and organized as a developer. This is a great way to control what displays via the interface to your actual users within the application.\u003C/p>\u003Cp>So I could just call this deal stages or pipeline stages, whatever makes a lot of sense here, and hit save. Alright. So it still shows me deals_stages here, but when I start looking inside the actual app, there we go, we can see our pipeline stages. So, let's create our first one. Right?\u003C/p>\u003Cp>Let's just say, New, this is inbound. Do we have a symbol? We do have a symbol for this. Right? So let's say red.\u003C/p>\u003Cp>This is we need to take action on this. What's next? Right? Qualified, maybe? Contacted?\u003C/p>\u003Cp>Or, let's say assigned. Right? We forgot to assign those to a sales rep. Assign assignment. That's a nice looking icon for that.\u003C/p>\u003Cp>Then we'll say qualified. You know, if we're doing software sales, like, a check mark. Okay. What else? Demo.\u003C/p>\u003Cp>Let's save green. Oh, let's make this orange. Do we have like a demo? Maybe a TV? What have we got?\u003C/p>\u003Cp>Yeah, there we go. That works for a demo as far as the icons. And then we are going to set, like, a last stage, like, proposal. Great. Let's just go gray.\u003C/p>\u003Cp>Cool. Document. Awesome. Alright. So I can control the way that these are displayed through my actual settings, here.\u003C/p>\u003Cp>So I can go into each specific one. And on the display tab, let's display a colored dot for the color. And then for the icon, we're just gonna display the actual icon. Alright. Great.\u003C/p>\u003Cp>If we take a look now, I can see what these actually look like. Cool. And then for my display template for the deal stages, I may even go in and just mirror that same structure where I have a color, then I have an icon with a name. Okay. So, now, what we've done, we've effectively given control over these, and we probably actually need a sort on these as well, so so we can control that value.\u003C/p>\u003Cp>So, let's go in and quickly add a sort. That's going to be an integer value. And because this is coming after the fact, after we've created this actual table, I'm just gonna scroll down a little bit, and I will have a sort field here. So we'll choose sort just to make sure we manage that. And now I should be able to drag and drop these, in whatever order that I like.\u003C/p>\u003Cp>Again, we're giving control back to the users of our custom CRM. We're definitely gonna prevent them from getting inside the admin so they don't mess with the data model. But here, we give them the ability to control what those pipeline stages are gonna be. So now we need to add those to our actual deals. Right?\u003C/p>\u003Cp>So we're gonna go into our deals. We're going to create a many to one relationship. And, you know, this is going to be the deal stage or just stage. And we're gonna use deals underscore stages as the related collection. We can open this up and and just see what's going on.\u003C/p>\u003Cp>I I don't really need to create that inverse relationship. I could. Doesn't make a ton of sense, though. I'm not sure. Alright.\u003C/p>\u003Cp>Display related values, validation. Let's keep it very simple, and then we're gonna use our deal stage. Alright. So now if I go into our deals Why is this oh, it goes in the machine. I don't know what's going on on this particular example, but it keeps creating additional fields that we don't need.\u003C/p>\u003Cp>Alright. So let's create a new deal. Alright. This is gonna be a new deal. We can see, hey, there's our stage.\u003C/p>\u003Cp>That looks great. The deal name is 100 apps, 100 hours contract. It's worth $1,000,000. So we'll add that. How many zeros do we have there?\u003C/p>\u003Cp>1, 2, 3, 4, 5, 6. Alright. Let's make this for the Little League team. Great. My wife is the primary contact.\u003C/p>\u003Cp>Here's some notes on this deal. Great. Did we save it? Numeric value and deals is out of range. Uh-oh.\u003C/p>\u003Cp>Wouldn't be a 100 apps, a 100 hours without, some type of issue. Right? So let's take a look at this. The deal value, what did I set? Precision and scale.\u003C/p>\u003Cp>Maybe we bumped this up way up. Probably could have gone with integers for this as well. Do I have a minimum and a maximum? I I don't have that set. So not entirely sure what's going on.\u003C/p>\u003Cp>Let's just try this again. 100 apps, 100 hours contract, 1 100,000, 1,000,000. Try it again. Save. Okay.\u003C/p>\u003Cp>Alright. So now, we've got this particular deal set up. This is kind of an underwhelming view. Right? And, again, I might control how these things display.\u003C/p>\u003Cp>So if I just go into our deals, we go to the deal stage. If I wanna control how it displays, I can go in and set this. So let's do the color, do the icon, do the name, just because, I'm a I'm a very visual person, and I suspect, a lot of your end users may be as well. We still don't see the organization there. So, again, we'll go back.\u003C/p>\u003Cp>We'll adjust our organization. We wanna display related values. We want to show the name of that specific organization. And let's get even fancier and maybe we wanna show that logo. I'm gonna click on this and then you'll see this one that has a little magic beside it that's a thumbnail.\u003C/p>\u003Cp>That's just a shortcut for, like, a nice thumbnail sized image instead of loading the actual image size. So I donit have a logo for this company, but I could quickly find 1. Alright. So, we'll just copy this image address, go back to our instance, let's load up that specific company, they who shall not be named, Hit import. Oh, that's a data URL.\u003C/p>\u003Cp>Let's find an actual URL that we can just copy. Great. Let's try this again. We can import via URL. Cool.\u003C/p>\u003Cp>And now when I'm looking at my deals, I can see the logo of that organization. That makes it really handy, when I'm working on a deal just to have that extra extra visual reinforcement. Now, when I'm looking at deals, I may want to set up like a traditional pipeline type of view. So I could do that really easily just by switching the layout here inside Directus. So we'll change this to a Kanban layout.\u003C/p>\u003Cp>Let's group by the deal stage and the group title is gonna be the name. So I can see new assigned qualified demo. We have that Kanban view that we're used to. As far as the text, what do we wanna look at? We want to control, let's say, when this was created.\u003C/p>\u003Cp>I'm not really concerned about that. The tags, what do we want to set that to be? Do we want a card image? Honestly, this looks okay. Do we need an actual user on here?\u003C/p>\u003Cp>Probably not. Cool. Alright. We'll just keep it as is and let's let's start fleshing this out a little more as well. Right?\u003C/p>\u003Cp>So within a deal, when I'm working this deal, we're going to have activities assigned to this as it moves across the pipeline. And, for that, let's just go in and add a new table, a new collection, we're going to call it activities. Activities, I think that's the correct spelling. We use generated UID, we'll do created at, createdby, create up, updatedat, updated by. Great.\u003C/p>\u003Cp>And, in this case, maybe we do give a status for this particular activity. Right. Status, great. Let's give a name of the activity, probably a type of activity. Makes sense.\u003C/p>\u003Cp>We use a drop down for that. It seems pretty straightforward or, you know, maybe they're not gonna need to change the activity types on a a very frequent basis. So we'll call it the activity type, and let's add a couple choices to this. So this is a relatively new addition to Directus within the drop down, the ability to have an icon and a color. So let's say text is a phone call, value is a phone.\u003C/p>\u003Cp>One of the other cool things if you want to translate this value, you could use this phone t, or this dollar sign t for a translatable string. And then anytime you have users who are using the app within a different language, inside the settings, you can control all those custom translations. We'll take a look at that in a moment. Let's call this what? Phone call.\u003C/p>\u003Cp>The value could just be phone call, depending on how I wanna store this. Right? It may have an underscore within it as well. It could just be the same thing. Either way.\u003C/p>\u003Cp>Look for that phone. Great. And I could even add a color for that if needed. Maybe I just wanna keep these all the same. Phone call, we'll call it a meeting.\u003C/p>\u003Cp>And for this, do we have a meeting icon? Right? We calendar looks nice. Maybe we want to track demos separate from meetings. Alright.\u003C/p>\u003Cp>So we drag a demo. That was the TV icon that we had. Great. What else are we gonna need? Like, an email?\u003C/p>\u003Cp>Not sure I would Yeah. Maybe, like, a follow-up email. We wanna reschedule that. And then we'll do an email. Great.\u003C/p>\u003Cp>Demo, phone call. I think I'm gonna set this to be underscore value. Cool. Alright. Looking good.\u003C/p>\u003Cp>Activity type. Okay. What else are we going to need for this? We need a due date for this activity and the status, we can use as as is. It's published draft, archived.\u003C/p>\u003Cp>What do I really care about this activity? It probably isn't completed or not. Right? So maybe we scrap status, and maybe we just go for a toggle instead. Right?\u003C/p>\u003Cp>Hey. Is this completed? The default value, great. For our label, maybe we change it to completed and give it a color of color on. Color off is red, just to show that it has not been completed.\u003C/p>\u003Cp>Great. And let's just clean up this form a little bit, making sure everything looks nice. Okay. We are going to add a date for this, and let's set a specific date and time that this thing occurred. Call it due date, end date.\u003C/p>\u003Cp>Due date seems reasonable, but when do we actually need to complete this specific task? Okay. So now we've got an activity. We want to, link this to our actual deal so we can track those. Right?\u003C/p>\u003Cp>So, what I'm gonna do in this case is create a mini to one relationship because this activity could only belong to a single deal. Right? We're gonna use the key of deal. The related collection, we'll set that to deals. We'll show the name of that deal.\u003C/p>\u003Cp>Maybe we show the actual organization as well so I can actually dig into the related collections and and show values from that? Excuse me one second. Sorry about that. Cool. Alright.\u003C/p>\u003Cp>So now, what I forgot to do is create that inverse relationship. You can actually set that up via direct us when you're creating that relationship. But now I can also just go into our deals, not deal. We'll go into our deals, and now we're gonna create a one to many relationship back to those activities. So we'll call this key of activities.\u003C/p>\u003Cp>We've got activities. The foreign key will be deal. That already exists. Maybe we wanna show these in a table. We'll choose the columns, due date, Name, Due Date, Activity Type.\u003C/p>\u003Cp>Seems pretty savvy. And I can even filter these, right? So if I wanted to see just activities where they were not completed. We can enable search and filtering and show a link to these. We'll take a look at what all these look like in just a moment.\u003C/p>\u003Cp>But we've forgotten one important step through this whole process is, hey. We need somebody to assign these deals to. Right? So, let's add a many to one relationship. We'll call that the deal owner or, you know, you potentially say who this is assigned to.\u003C/p>\u003Cp>Like, the deal owners, again, kind of standard naming in these scenarios. And for the related collection here, we're gonna use directus underscore users. So these are gonna be actual users of the application that we're assigning this to. Invalid payload collections can't start with direct us users. Oh, deal owner.\u003C/p>\u003Cp>Let's go to our related collection, and let's get direct us underscore users. And, in this case, we're gonna show the first name, last name. We may back up and do an avatar as well. So just the the thumbnail, the avatar, I could move these around just by using edit raw value. You'll see these are just, the standard mustache syntax that you see throughout Directus as well.\u003C/p>\u003Cp>Excuse me. Let me get this a drink of water. I'm actually dying. That's a turn of fate. Okay.\u003C/p>\u003Cp>Alright. So we're gonna save this. Deal owner already exists in deals. Okay. Alright.\u003C/p>\u003Cp>Great. Let's move this around. Maybe we make deal stage half width. We slide deal owner up there. Let's actually take a look at this now.\u003C/p>\u003Cp>I should be able to assign folks. So let's create a new user. We'll just call it sales rep. Salesrep@example.com. And maybe we give them a nice avatar.\u003C/p>\u003Cp>Right? Sales rep, avatar. Let's just see what Google comes back with. John's inside sales rep. Yeah.\u003C/p>\u003Cp>This looks this is perfect. This is my guy right here. Alright. There's his avatar. We'll just save that.\u003C/p>\u003Cp>And now we can see who we've assigned this particular deal to. And now maybe within the deal card user here, I wanna show who that deal owner is. Great. Mister sales rep. Looking good.\u003C/p>\u003Cp>Alright. One of the other things that we need to do on our activities, we probably got a an owner of that activity or assigned to. It's been assigned to somebody on the team. Again, that's going to be assigned to a direct us user. We'll save that.\u003C/p>\u003Cp>And lots going on here. Just some type of weird glitch. I could see a couple of extra collections. I'll just remove these. Alright.\u003C/p>\u003Cp>Cool. So now weive got a deal. Weive got a table full of activities. I can go ahead and add these, like, follow-up on proposal. This is assigned to Mr.\u003C/p>\u003Cp>Sales Rep. We've got the activity type. We can see that's going to be, just a quick phone call. This is completed. We can see that conditional, conditional formatting for that.\u003C/p>\u003Cp>And we can add a due date of, let's say, the next Friday. Great. Save that. Keep editing. Cool.\u003C/p>\u003Cp>Alright. So, now, we've got the basic inner workings of a CRM. Right? We've got our deals, we've got contacts, we've got organizations, we've got our different pipeline stages. Right?\u003C/p>\u003Cp>If I wanted to organize these things a bit, we could go in and add different icons for each of these. So, you know, maybe we set some people icons for our contacts. We've got our organizations. Do we have an organization option? Let's look and see.\u003C/p>\u003Cp>Business. Is there a business? There you go. That looks somewhat like a business. We've got activities.\u003C/p>\u003Cp>Maybe this will be like a checklist. Cool. We've got our deals. Let's make those the money. Dollar signs, that's great.\u003C/p>\u003Cp>And then, deal stages, to me this is like a settings. Right? So I could create a new folder, let's just call it settings folder. You don't necessarily have to add this, but maybe we just do to keep it clean. And we look for, like, a settings icon just to use here.\u003C/p>\u003Cp>That's great. This one looks magical. Settings suggest. Right? And, again, I can change the name of this to just say settings.\u003C/p>\u003Cp>So, it still creates this collection, but, we can call it whatever we want. So we'll drag this up within settings folder. We'll drag deal stages and, this will be, what, like the Kanban view. There we go. Awesome.\u003C/p>\u003Cp>Okay. So now we get a little more organization to this. One of the other things that you might do and, you know, that you use all the time within ACRM are saved views. Right? So Directus gives you that ability with bookmarks.\u003C/p>\u003Cp>We'll just go in to the top here and maybe I want to sort by a specific sales rep. Right? Like, the deal owner is, a specific person and specific name is sales rep. That's the only one at this point. But I could go in and create a bookmark for this, and we could call it, deals sales rep Man.\u003C/p>\u003Cp>And we can change this up, give it a color. Now, within that collection, now we can see we've got our deals for Sales Rep Van, and I could save that bookmark. So even if I go into the main deals view, and maybe we change this back to a table view, which could be easier for, you know, maybe a sales manager or something who's controlling this. So I had deal owner back to this as well. Now if I go back, deal sales rep man view, boom, there it is.\u003C/p>\u003Cp>It's saved. I could go in and update this if I wanted to as well. So now we've got our pipeline. When we go into each one of the deal, we have our name, we've got our organization, we've got the notes, we've got our activities. You know, we can mark these activities off as completed.\u003C/p>\u003Cp>That seems like a great CRM baseline. Let's let's take a look at where we're at. Right? We got, like, 16 minutes on the clock. This feels like a win.\u003C/p>\u003Cp>I don't I don't know if we wanna run that one, let's discuss where we could go from here, right, maybe we want to automatically send some emails when it hits a certain stage in the pipeline. So let's just call these things done. Right? We can manage all of our contacts. We can manage all of our organizations.\u003C/p>\u003Cp>We can manage our sales pipelines. We can track our activities and follow-up. So let's say, you know, we get a new deal inbound. Maybe we want to automatically assign that to a a particular person, or we want to send a notification to our sales rep when that assignment happens. Let's figure out how to do that.\u003C/p>\u003Cp>Right? So I'm gonna go in. Let's just create a new deal. We'll say actually, let's wait a moment. Let's go into our flows.\u003C/p>\u003Cp>This is a good example. Right? Whenever a a new deal comes inbound, we want to send an email notification to our sales rep to to let them know. Alright. So we're gonna create a new flow.\u003C/p>\u003Cp>We'll just call it new inbound deal. Pretty straightforward. We could change this to the new symbol if we want to and just do a trigger setup. So what are we going to choose here? Directus gives you a ton of different options, as far as what to use when you're creating a flow.\u003C/p>\u003Cp>In this case, we're going to use the event hook. So when a certain event happens inside the platform, we wanna trigger an automation. The type that we're gonna choose here is action non blocking because we don't, the the filter allows you to basically either adjust the payload when a new deal gets created or a new event happens. Action non blocking, again, that runs after a create or update action. So in this case, let's do the items dot create.\u003C/p>\u003Cp>Whenever we're gonna trigger this based on the deals collection. And cool. Alright, so now I'm just going to save this, right? I'm going to go in and let's create a new deal inside the system. It's in the new stage.\u003C/p>\u003Cp>We're gonna assign this to mister sales rep. New deal automation. And we add, let's set this one to be for the little league. Again, we choose a specific contact, add some notes, and, maybe, the deal value, we'll just ballpark it at $5,000. Great.\u003C/p>\u003Cp>If I go back, now I can see that in my logs, I've got this flow. Here's the payload of this particular flow, and we could see who the deal owner is on this particular deal. How do we send an email notification to that specific owner? Right? I'm just gonna take this, copy it, and I open up just my Versus code editor where I've got my 100 apps, just, Docker Compose file here set up to run this locally.\u003C/p>\u003Cp>I'm just going to save that in case I need it. And, next, let's flesh this out a little bit. Right? So you can see the data we're getting back here. We're probably gonna need to look up that specific user.\u003C/p>\u003Cp>We can find them there. Those are the deal owner. We could send them a notification inside the app or we could send them a notification via email. Right? There's 2 options there.\u003C/p>\u003Cp>We've got notification. In this case, we've got the UUID of the user that we're going to send that to. So, you know, potentially, you want to send that in app. App. In this case, if there's a new deal, they're probably gonna be in their inbox.\u003C/p>\u003Cp>We're going to send that new deal to them. But let's actually find that email address first, though. Alright. So we're gonna read data from a specific collection. Let's call it Find User is the actual step we're gonna do here.\u003C/p>\u003Cp>For the permissions, let's just give full access. And, for the collection, you can see I don't have the Directus Users collection here, but I can go in and edit my raw value and just use Directus underscore users. And for our IDs, right, what are we gonna put here? So if I open this back up, we're gonna use the trigger. Payload.\u003C/p>\u003Cp>Dealowner. So we'll do this, we'll do trigger dot payload dot deal underscore owner. And I'm gonna wrap this in mustache syntax. And let's try this again. So we'll read the user.\u003C/p>\u003Cp>Let's go ahead and maybe just add this send email as well. So we'll send the email, and this is gonna be the read underscore user. We're using the key that we set of the previous operation within that flow. Readuser. Email should be.\u003C/p>\u003Cp>We'll input that. New deal assigned to you. And hey. Read_user.firstname. We've assigned a new deal to you.\u003C/p>\u003Cp>And then I can even go through and add those different variables if I wanted to for things like the deal name or the deal ID and and add a link back to that. So let's just try trigger. Payload.deal_name. Great. We'll save it.\u003C/p>\u003Cp>And now let's just test this out. I'm not actually sure if I've got emails set up here locally on this particular instance though, so that could be fun. But we should be able to actually see if this runs. In light of that bit of news, because it just looking at my configuration here, I do not have email configured here locally. So let's let's detach that one, and let's just test the notifications.\u003C/p>\u003Cp>Send notification. Cool. Send notification. The find_user.id. Cool.\u003C/p>\u003Cp>Full access. And it was a new deal assigned. We'll just add the key here. So that'll be trigger dot payload. Or no.\u003C/p>\u003Cp>Actually, it may be something like this where we have key. Alright. Let's just take a look just to make sure. Alright. Within our payload, we can see the key there.\u003C/p>\u003Cp>That's good. In that case, we probably didn't need to get the actual user there, but that's okay. I'm just gonna copy this message that we set here. Just paste that. And let's take a look at where this gets us.\u003C/p>\u003Cp>Alright. So we've got a new inbound deal. We're going to read the data of the user that we've assigned that to and we're going to send a notification to that specific person. Alright. Now we've got a new deal, deal stage.\u003C/p>\u003Cp>Let's just set it to new. Deal owner. I'm gonna choose myself here so we could test this notification. Say deal name. Test deal.\u003C/p>\u003Cp>It's worth $6,000,000. Very nice deal. We got a primary contact. Here's some notes. We'll just save that and let's see what happens.\u003C/p>\u003Cp>Right? We go to our Flow, we get a new inbound deal, we can see our logs, send notification, recipient is so and so. Hey, undefined. We've sent you a new test deal. Underscore first name.\u003C/p>\u003Cp>Why did that not come back? Read underscore oh, that's why I used the wrong key. Sometimes that happens. Find user dot first name. Great.\u003C/p>\u003Cp>So now, if I look and I check my activity log, I can see that I've got this new deal signed. Here's the notification for that. And if I click on it where it says view content, it should take me to that specific test deal. Great. Awesome.\u003C/p>\u003Cp>Let's call that a win. Right? We've got our custom CRM built out. We've set up some automation for this. We could go further and flesh this out a a ton of different ways.\u003C/p>\u003Cp>Right? If we talk about it, like, we could go through and send emails to our actual primary contact when it reaches a certain stage, that would be fairly easy to do using direct dis flows. You know, we could maybe even go as far as, like, a future future iteration of this could be setting up an inbox to parse incoming emails like a BCC functionality and add these to those specific deals as well. But this this feels really good. I'm I'm calling this a win.\u003C/p>\u003Cp>That's our custom CRM. Thanks for joining me on this episode of 100 apps, 100 hours. We'll catch you on the next one.\u003C/p>","Hi. Welcome back to another episode of 100 apps. 100 hours where we build some of your favorite apps or try to rebuild some of your favorite apps in 1 hour or less, or get publicly humiliated trying. Alright. Super excited to be back for another season. If you are new to 100 apps 100 hours, there are 2 basic rules. Number 1, you have 60 minutes to build and plan and build, no more, no less. So when that clock strikes 0, that's it. And then the second rule is there are no other rules. Use whatever you have at your disposal to complete the functionality. That's it. Let's dive into this episode. So today, I've got a custom CRM. Tools like Salesforce, Pipedrive, HubSpot, they really need no introduction. Everybody needs a CRM. A lot of them, depending on your purposes, may be way too much for what you need or they may not be specific enough for your industry. So we are going to build our own custom CRM in 1 hour or less. Let's do it baby. Alright. So, let's start the clock and away we go. So, when we plan our CRM, what do we need out of a CRM? Right? What kind of functionality do we want to see inside our CRM? So basically, we want to manage all of our contacts. We want to manage all the different organizations those contacts belong to. What else? We wanna manage our sales pipeline. Manage sales pipeline. That's gonna be deals or activities. We want to be able to track activities and follow-up. So this seems like a a pretty good set of functionality for a basic CRM. I'm sure we might embellish this a little bit depending on how far we get, but let's dive into actually fleshing out what the data model just might look like for something like this. So we'll drag a nice square up here. We're gonna have contacts. We could just call those people. I'm a big fan of that. We're gonna have organizations. There's definitely gonna be a relationship between those 2, but I I feel like this could be a many to many relationship because one contact could belong to multiple organizations, and and some CRMs make that a little more difficult to do than others. What else do we have? We're gonna have deals or opportunities. Deals is kind of a a standard nomenclature. We're gonna have activities that are attached to what, those are attached to organizations, they're attached to contacts, they're probably also gonna be attached to deals. What else are we going to have? We're probably going to have some sales reps, those are going to be our users inside our accounts. This looks pretty good for a a base set of functionality. Let's dive in and actually start building something. So, I'm just gonna pull up my Directus instance, that's what we're using on the back end. Totally blank instance, we'll zoom in on this, and let's start building. Right? Let's create our first collection. Let's start with contacts. So we're gonna give this a contacts as the name. For the primary key, let's use generated UUID. What kind of fields do we wanna add for our contacts? Right? When we think of contacts, we're gonna need name, email, but we may also wanna track when this was updated, when it was created. Directus makes that super easy. And one tip that you may not have realized if you've used Directus before, is I can go in and change these to be whatever I want. So if I wanted this to be created at and created by, I could go in and update those, change them to be whatever I want. Updated at, updated by. That's the the nomenclature that you're used to. Do we actually need a sort for our contacts? I don't think. Let's just go ahead and save this. Alright. So for our contacts, we're gonna add a first name. Great. We'll make that a string. Let's do half width for that. I can go in and click the three little dots here and just quickly duplicate this field to save myself some time. And, of course, as I am plotting away on this, Directus underneath is mirroring all these changes to my database schema. So we've got our first name, we've got our last name, we're gonna need a email or an email. Great. Let's require this value. And, you know, I could even go as far as, like, making this unique if I I wanted to have some type of unique identifier to match these up with. Alright. As far as an interface, you know, I could get fancy with this and we could add a little email icon like an at symbol. That's great. I think everything else is good. You know, one of the other nice things that's built into Directus is I can go in and add my own custom reg x validation. So, you know, I can put in some type of reg x that matches email addresses. I don't have one of those handy here, but we'll go ahead and keep marching along here. So we got first name, we got last name, we got email. We'll probably have a phone number at some point, but let's let's go for, like, a job title, probably. I'm trying to think of all the standard fields. Maybe we have some notes. We could set that to be a text area field. I don't really need formatting for those. Great. Job title. And maybe honestly, let let's strip this out because I'm I'm thinking about that many to many relationship with organizations, and we may have, different titles at different organizations. So I'll show you how we can handle that coming up. Alright. So we got first name, we got email. We could go ahead and add phone number as a string. Phone or phone number. Let's just keep it short. We'll say phone. Let's give it a phone icon. Great. K. Phone, email. We'll put email above phone. Cool. Looks nice. Alright. Let's just take a look. Right? We've got first name. Go ahead. Oh, I was gonna add my wife here, but she's gonna get mad at me if I misspell it. Right? Ashley atexample.com. I had a phone number, 555-5555. Here's some nice notes for our contact. These are looking great. Right? We've got our contact in here. I could potentially sort and filter this a a hundred different ways, maybe change the layout. But let's dive into the next collection that we wanna set up, our organizations. So we'll have organizations, again, for the primary key, I'm gonna use ID and just use generated UUID as the type. And, again, I could change created at I could change this structure for these system fields to be whatever I want. Created by, date updated, that's gonna be updated at, if I can actually spell. And we use this one as well and call it updated by. Great. Okay. So now we've got an organization. For our organization, what are we gonna have? We're gonna have a name of the organization. Right? Probably, some addresses for that organization. Right? We could have multiple. So that's where we might reach into an another bag of just a different table. What else are we gonna have for an organization? You know, let's just do something like country in case we want to, even, like, automatically route new organizations to a specific sales rep. Potentially, we've got a name. What are we gonna have? We're gonna have a website. You know, we may have a a logo if we wanna track that for a company. So let's do an image file. Let's call it logo. You know, you might even add things like brand color, things like that if if you're really in tracking that. And then we'll just add another section for notes. This is gonna be a text area field with the type text, and we'll hit save. So we've got our organizations. We've got our contacts. Let's make a link between the 2. Right? So how do we go about that? Depends on how you want the data to actually be structured. Right? In an application, depending on the setup, maybe a user can only belong to one specific organization. But often inside of CRM and maybe I am working with the local little league. I am a member of the board there, and then I'm also a developer advocate here at Directus or, you know, a a founder at Better Side Shop. So a lot of different relationships that is gonna be modeled with the mini to mini relationship inside Directus. Here's how we set that up. It's gonna be pretty easy. We'll just go to create field. We'll look for a many to many relationship. And because we're on organizations, our key here is gonna be contact for the or contacts, I should say. Our related collection is gonna be contacts and then we'll just go through and paint by numbers here. Do we want to show these in a list or a table? I'm good either way. We definitely want to show a link to that item so we can get to that specific contact, but I'm gonna pop open this advanced field creation mode just to show you that you do have control over the naming of the junction collection and the individual fields within that. So, the default setup here is just going to take this table name and this table name or this collection name and this collection name and marry the 2 together and we end up with organizations underscore contacts. Works for me. We'll leave that autofill set up. And then on the reverse, I'm gonna add that many to many field to the contacts as well. We're gonna keep that as the organizations. Alright? In this case, we may have a sort field. So we wanna control the the sorting for contacts, like who's 1st, 2nd, 3rd, that could be helpful in, like, a primary contact situation. And then we have some of our relational triggers. Right? On deselect of organizations' contacts, what do we want to do here? Yeah. Maybe I want to delete that association. So I'll set these to cascade. Great. So now that back out, we can see we've got our contacts field here. If we go into, yep, looks like we've got, an extra contact table created. I might have typed something wrong. But, so we can see contacts there in the organizations. And then on our contact, I should see organizations here. Let's just add an organization. Right? So let's say my wife worked at Tesla. That's in the United States. And, you know, we could add the website, logo, notes. I could choose existing contacts. But I'm just going to go ahead and say 1. Right? So now, I could see I've got that organization here, but it's showing an ID, which is not super helpful. Right? So I can go into the organization itself and control the display template where I have a name. I could even potentially add the logo to that if I wanted. So now if we check it again, we can see, okay, here's the organizations that they're a part of. Right? Now, you also have the ability to manage the data inside that junction collection. Right? So if I go in and and like I said, I may have a different job title at all these different organizations. So I could go in and add a new field here inside this junction collection for job title. Great. Alright. So, now if we go into Tesla, we can see the job title is down here at the bottom, but Directus also gives me an easy way to control where that displays. So if I go to our many to many relationship inside our contacts, I can go to maybe we wanna add a sort field for this just to make sure. And then if I go to the interface, I can control where my junction fields are located. So I can put this at the top, which should be great. Okay. So now if we just check that out one more time. Right. Now I could see director of DirectUs. So now I could give a specific job title to this specific person within that organization. I could go in and add a new organization as well, like, hey, the little league. We can say a board member. Right? Little League Baseball is the name of the organization. Great. Okay. So now there my wife is a part of 2 organizations, different job titles for each. Right? Alright. How are we looking on time? We're looking great. Let's move on to our next collection that we want to set up. That's gonna be what? Our deals, our activities, what do we want to set up next? Let's go for deals or opportunities. Deals is probably the standard naming for this. So that's what we'll stick with. Could be opportunities. Could be something else. That's fine. We'll do created at. Just to keep the same structure, we'll do created by. And by adding these, whenever this record is created, it's going to populate a timestamp and a user. The same thing for Updated, whenever it gets updated or who updates it, it's gonna populate that info for us. Updated by. Great. Okay. So our deals what we're gonna have for our deal? We'll probably have a name or a title of the deal. Deal name sounds great. It could be good to prefix some of these sometimes if you're dealing with a lot of nested relational data. So you get used to seeing name in there a 100 times. Could be confusing whether that's the actual deal name or the organization name or the contact name. So we'll just save the deal name. What else are we gonna have on this deal? Right? We'll probably have a average dollar amount or a potential dollar amount. Let's set that to be, decimal. Deal value. We can add a nice little dollar sign to the icon to make this look nice and pretty. And then we are going to have some related fields. But before we do that, let's just add, like, some deal notes or something like that. Sounds great. Okay. So we got a deal name, we got a deal value, you know, maybe these are side by side, not a big deal. Let's go in, and now we want to add a relationship to the organization and probably to our primary contact for this deal. So those are both gonna be many to one relationships. And we'll set a key of organization here. So the related collection, we'll set that to be organizations. Great. And I could control the display template here. We'll just use the name and we hit save. So now we've got an organization that we're gonna tie to the deal. I'll make that half width. And then we also want to add a primary contact. So we'll just say primary contact. The related collection here is going to be the Contacts collection. And for the Display template, let's use first name, last name, and let's use this format, which I think is typically how it displays inside an email client where you have the email address inside these less than or greater than SQL or symbols. So let's save that. We've got our primary contact. We've got our notes. We've got our deal. What do we need next? Right? We need to track where that deal is at. So we could use, like, a a status field as, like, a drop down for that potentially. Right? What's the status of this particular deal? Blah blah blah. Or we could do something else where we have a relationship to a deal stage or our individual pipeline. Right? Because then we may want to, potentially have separate pipelines or we we wanna give more control back to our users, so the the sales reps or the sales manager to control that actual pipeline without getting into the admin section. So how can we do that? Right? Looks like I've got a little extra couple of fields that I've been creating here inadvertently. Alright, so now let's add a new collection. Let's just call it Deal Stages. We want to manage what are the stages of a particular deal. As far as the optional fields, maybe I don't really not super concerned with these, deal stages. We're gonna call this the name of this particular stage. Maybe we wanna give it a color so we can add some color to it. And, you know, I guess we could even give it an icon if we wanted to. We could play around with that and see what that looks like. Deal stages. Let's make both of those half width. And, now, let's just go through and map some of these particular stages. Right? And I and I, you know, I've got this s on here. One of the other things that you could do inside Directus, you can't control the translations for all of these. So even if you're working in English, on the the back end, you may want to use prefix tables to keep things nice and organized as a developer. This is a great way to control what displays via the interface to your actual users within the application. So I could just call this deal stages or pipeline stages, whatever makes a lot of sense here, and hit save. Alright. So it still shows me deals_stages here, but when I start looking inside the actual app, there we go, we can see our pipeline stages. So, let's create our first one. Right? Let's just say, New, this is inbound. Do we have a symbol? We do have a symbol for this. Right? So let's say red. This is we need to take action on this. What's next? Right? Qualified, maybe? Contacted? Or, let's say assigned. Right? We forgot to assign those to a sales rep. Assign assignment. That's a nice looking icon for that. Then we'll say qualified. You know, if we're doing software sales, like, a check mark. Okay. What else? Demo. Let's save green. Oh, let's make this orange. Do we have like a demo? Maybe a TV? What have we got? Yeah, there we go. That works for a demo as far as the icons. And then we are going to set, like, a last stage, like, proposal. Great. Let's just go gray. Cool. Document. Awesome. Alright. So I can control the way that these are displayed through my actual settings, here. So I can go into each specific one. And on the display tab, let's display a colored dot for the color. And then for the icon, we're just gonna display the actual icon. Alright. Great. If we take a look now, I can see what these actually look like. Cool. And then for my display template for the deal stages, I may even go in and just mirror that same structure where I have a color, then I have an icon with a name. Okay. So, now, what we've done, we've effectively given control over these, and we probably actually need a sort on these as well, so so we can control that value. So, let's go in and quickly add a sort. That's going to be an integer value. And because this is coming after the fact, after we've created this actual table, I'm just gonna scroll down a little bit, and I will have a sort field here. So we'll choose sort just to make sure we manage that. And now I should be able to drag and drop these, in whatever order that I like. Again, we're giving control back to the users of our custom CRM. We're definitely gonna prevent them from getting inside the admin so they don't mess with the data model. But here, we give them the ability to control what those pipeline stages are gonna be. So now we need to add those to our actual deals. Right? So we're gonna go into our deals. We're going to create a many to one relationship. And, you know, this is going to be the deal stage or just stage. And we're gonna use deals underscore stages as the related collection. We can open this up and and just see what's going on. I I don't really need to create that inverse relationship. I could. Doesn't make a ton of sense, though. I'm not sure. Alright. Display related values, validation. Let's keep it very simple, and then we're gonna use our deal stage. Alright. So now if I go into our deals Why is this oh, it goes in the machine. I don't know what's going on on this particular example, but it keeps creating additional fields that we don't need. Alright. So let's create a new deal. Alright. This is gonna be a new deal. We can see, hey, there's our stage. That looks great. The deal name is 100 apps, 100 hours contract. It's worth $1,000,000. So we'll add that. How many zeros do we have there? 1, 2, 3, 4, 5, 6. Alright. Let's make this for the Little League team. Great. My wife is the primary contact. Here's some notes on this deal. Great. Did we save it? Numeric value and deals is out of range. Uh-oh. Wouldn't be a 100 apps, a 100 hours without, some type of issue. Right? So let's take a look at this. The deal value, what did I set? Precision and scale. Maybe we bumped this up way up. Probably could have gone with integers for this as well. Do I have a minimum and a maximum? I I don't have that set. So not entirely sure what's going on. Let's just try this again. 100 apps, 100 hours contract, 1 100,000, 1,000,000. Try it again. Save. Okay. Alright. So now, we've got this particular deal set up. This is kind of an underwhelming view. Right? And, again, I might control how these things display. So if I just go into our deals, we go to the deal stage. If I wanna control how it displays, I can go in and set this. So let's do the color, do the icon, do the name, just because, I'm a I'm a very visual person, and I suspect, a lot of your end users may be as well. We still don't see the organization there. So, again, we'll go back. We'll adjust our organization. We wanna display related values. We want to show the name of that specific organization. And let's get even fancier and maybe we wanna show that logo. I'm gonna click on this and then you'll see this one that has a little magic beside it that's a thumbnail. That's just a shortcut for, like, a nice thumbnail sized image instead of loading the actual image size. So I donit have a logo for this company, but I could quickly find 1. Alright. So, we'll just copy this image address, go back to our instance, let's load up that specific company, they who shall not be named, Hit import. Oh, that's a data URL. Let's find an actual URL that we can just copy. Great. Let's try this again. We can import via URL. Cool. And now when I'm looking at my deals, I can see the logo of that organization. That makes it really handy, when I'm working on a deal just to have that extra extra visual reinforcement. Now, when I'm looking at deals, I may want to set up like a traditional pipeline type of view. So I could do that really easily just by switching the layout here inside Directus. So we'll change this to a Kanban layout. Let's group by the deal stage and the group title is gonna be the name. So I can see new assigned qualified demo. We have that Kanban view that we're used to. As far as the text, what do we wanna look at? We want to control, let's say, when this was created. I'm not really concerned about that. The tags, what do we want to set that to be? Do we want a card image? Honestly, this looks okay. Do we need an actual user on here? Probably not. Cool. Alright. We'll just keep it as is and let's let's start fleshing this out a little more as well. Right? So within a deal, when I'm working this deal, we're going to have activities assigned to this as it moves across the pipeline. And, for that, let's just go in and add a new table, a new collection, we're going to call it activities. Activities, I think that's the correct spelling. We use generated UID, we'll do created at, createdby, create up, updatedat, updated by. Great. And, in this case, maybe we do give a status for this particular activity. Right. Status, great. Let's give a name of the activity, probably a type of activity. Makes sense. We use a drop down for that. It seems pretty straightforward or, you know, maybe they're not gonna need to change the activity types on a a very frequent basis. So we'll call it the activity type, and let's add a couple choices to this. So this is a relatively new addition to Directus within the drop down, the ability to have an icon and a color. So let's say text is a phone call, value is a phone. One of the other cool things if you want to translate this value, you could use this phone t, or this dollar sign t for a translatable string. And then anytime you have users who are using the app within a different language, inside the settings, you can control all those custom translations. We'll take a look at that in a moment. Let's call this what? Phone call. The value could just be phone call, depending on how I wanna store this. Right? It may have an underscore within it as well. It could just be the same thing. Either way. Look for that phone. Great. And I could even add a color for that if needed. Maybe I just wanna keep these all the same. Phone call, we'll call it a meeting. And for this, do we have a meeting icon? Right? We calendar looks nice. Maybe we want to track demos separate from meetings. Alright. So we drag a demo. That was the TV icon that we had. Great. What else are we gonna need? Like, an email? Not sure I would Yeah. Maybe, like, a follow-up email. We wanna reschedule that. And then we'll do an email. Great. Demo, phone call. I think I'm gonna set this to be underscore value. Cool. Alright. Looking good. Activity type. Okay. What else are we going to need for this? We need a due date for this activity and the status, we can use as as is. It's published draft, archived. What do I really care about this activity? It probably isn't completed or not. Right? So maybe we scrap status, and maybe we just go for a toggle instead. Right? Hey. Is this completed? The default value, great. For our label, maybe we change it to completed and give it a color of color on. Color off is red, just to show that it has not been completed. Great. And let's just clean up this form a little bit, making sure everything looks nice. Okay. We are going to add a date for this, and let's set a specific date and time that this thing occurred. Call it due date, end date. Due date seems reasonable, but when do we actually need to complete this specific task? Okay. So now we've got an activity. We want to, link this to our actual deal so we can track those. Right? So, what I'm gonna do in this case is create a mini to one relationship because this activity could only belong to a single deal. Right? We're gonna use the key of deal. The related collection, we'll set that to deals. We'll show the name of that deal. Maybe we show the actual organization as well so I can actually dig into the related collections and and show values from that? Excuse me one second. Sorry about that. Cool. Alright. So now, what I forgot to do is create that inverse relationship. You can actually set that up via direct us when you're creating that relationship. But now I can also just go into our deals, not deal. We'll go into our deals, and now we're gonna create a one to many relationship back to those activities. So we'll call this key of activities. We've got activities. The foreign key will be deal. That already exists. Maybe we wanna show these in a table. We'll choose the columns, due date, Name, Due Date, Activity Type. Seems pretty savvy. And I can even filter these, right? So if I wanted to see just activities where they were not completed. We can enable search and filtering and show a link to these. We'll take a look at what all these look like in just a moment. But we've forgotten one important step through this whole process is, hey. We need somebody to assign these deals to. Right? So, let's add a many to one relationship. We'll call that the deal owner or, you know, you potentially say who this is assigned to. Like, the deal owners, again, kind of standard naming in these scenarios. And for the related collection here, we're gonna use directus underscore users. So these are gonna be actual users of the application that we're assigning this to. Invalid payload collections can't start with direct us users. Oh, deal owner. Let's go to our related collection, and let's get direct us underscore users. And, in this case, we're gonna show the first name, last name. We may back up and do an avatar as well. So just the the thumbnail, the avatar, I could move these around just by using edit raw value. You'll see these are just, the standard mustache syntax that you see throughout Directus as well. Excuse me. Let me get this a drink of water. I'm actually dying. That's a turn of fate. Okay. Alright. So we're gonna save this. Deal owner already exists in deals. Okay. Alright. Great. Let's move this around. Maybe we make deal stage half width. We slide deal owner up there. Let's actually take a look at this now. I should be able to assign folks. So let's create a new user. We'll just call it sales rep. Salesrep@example.com. And maybe we give them a nice avatar. Right? Sales rep, avatar. Let's just see what Google comes back with. John's inside sales rep. Yeah. This looks this is perfect. This is my guy right here. Alright. There's his avatar. We'll just save that. And now we can see who we've assigned this particular deal to. And now maybe within the deal card user here, I wanna show who that deal owner is. Great. Mister sales rep. Looking good. Alright. One of the other things that we need to do on our activities, we probably got a an owner of that activity or assigned to. It's been assigned to somebody on the team. Again, that's going to be assigned to a direct us user. We'll save that. And lots going on here. Just some type of weird glitch. I could see a couple of extra collections. I'll just remove these. Alright. Cool. So now weive got a deal. Weive got a table full of activities. I can go ahead and add these, like, follow-up on proposal. This is assigned to Mr. Sales Rep. We've got the activity type. We can see that's going to be, just a quick phone call. This is completed. We can see that conditional, conditional formatting for that. And we can add a due date of, let's say, the next Friday. Great. Save that. Keep editing. Cool. Alright. So, now, we've got the basic inner workings of a CRM. Right? We've got our deals, we've got contacts, we've got organizations, we've got our different pipeline stages. Right? If I wanted to organize these things a bit, we could go in and add different icons for each of these. So, you know, maybe we set some people icons for our contacts. We've got our organizations. Do we have an organization option? Let's look and see. Business. Is there a business? There you go. That looks somewhat like a business. We've got activities. Maybe this will be like a checklist. Cool. We've got our deals. Let's make those the money. Dollar signs, that's great. And then, deal stages, to me this is like a settings. Right? So I could create a new folder, let's just call it settings folder. You don't necessarily have to add this, but maybe we just do to keep it clean. And we look for, like, a settings icon just to use here. That's great. This one looks magical. Settings suggest. Right? And, again, I can change the name of this to just say settings. So, it still creates this collection, but, we can call it whatever we want. So we'll drag this up within settings folder. We'll drag deal stages and, this will be, what, like the Kanban view. There we go. Awesome. Okay. So now we get a little more organization to this. One of the other things that you might do and, you know, that you use all the time within ACRM are saved views. Right? So Directus gives you that ability with bookmarks. We'll just go in to the top here and maybe I want to sort by a specific sales rep. Right? Like, the deal owner is, a specific person and specific name is sales rep. That's the only one at this point. But I could go in and create a bookmark for this, and we could call it, deals sales rep Man. And we can change this up, give it a color. Now, within that collection, now we can see we've got our deals for Sales Rep Van, and I could save that bookmark. So even if I go into the main deals view, and maybe we change this back to a table view, which could be easier for, you know, maybe a sales manager or something who's controlling this. So I had deal owner back to this as well. Now if I go back, deal sales rep man view, boom, there it is. It's saved. I could go in and update this if I wanted to as well. So now we've got our pipeline. When we go into each one of the deal, we have our name, we've got our organization, we've got the notes, we've got our activities. You know, we can mark these activities off as completed. That seems like a great CRM baseline. Let's let's take a look at where we're at. Right? We got, like, 16 minutes on the clock. This feels like a win. I don't I don't know if we wanna run that one, let's discuss where we could go from here, right, maybe we want to automatically send some emails when it hits a certain stage in the pipeline. So let's just call these things done. Right? We can manage all of our contacts. We can manage all of our organizations. We can manage our sales pipelines. We can track our activities and follow-up. So let's say, you know, we get a new deal inbound. Maybe we want to automatically assign that to a a particular person, or we want to send a notification to our sales rep when that assignment happens. Let's figure out how to do that. Right? So I'm gonna go in. Let's just create a new deal. We'll say actually, let's wait a moment. Let's go into our flows. This is a good example. Right? Whenever a a new deal comes inbound, we want to send an email notification to our sales rep to to let them know. Alright. So we're gonna create a new flow. We'll just call it new inbound deal. Pretty straightforward. We could change this to the new symbol if we want to and just do a trigger setup. So what are we going to choose here? Directus gives you a ton of different options, as far as what to use when you're creating a flow. In this case, we're going to use the event hook. So when a certain event happens inside the platform, we wanna trigger an automation. The type that we're gonna choose here is action non blocking because we don't, the the filter allows you to basically either adjust the payload when a new deal gets created or a new event happens. Action non blocking, again, that runs after a create or update action. So in this case, let's do the items dot create. Whenever we're gonna trigger this based on the deals collection. And cool. Alright, so now I'm just going to save this, right? I'm going to go in and let's create a new deal inside the system. It's in the new stage. We're gonna assign this to mister sales rep. New deal automation. And we add, let's set this one to be for the little league. Again, we choose a specific contact, add some notes, and, maybe, the deal value, we'll just ballpark it at $5,000. Great. If I go back, now I can see that in my logs, I've got this flow. Here's the payload of this particular flow, and we could see who the deal owner is on this particular deal. How do we send an email notification to that specific owner? Right? I'm just gonna take this, copy it, and I open up just my Versus code editor where I've got my 100 apps, just, Docker Compose file here set up to run this locally. I'm just going to save that in case I need it. And, next, let's flesh this out a little bit. Right? So you can see the data we're getting back here. We're probably gonna need to look up that specific user. We can find them there. Those are the deal owner. We could send them a notification inside the app or we could send them a notification via email. Right? There's 2 options there. We've got notification. In this case, we've got the UUID of the user that we're going to send that to. So, you know, potentially, you want to send that in app. App. In this case, if there's a new deal, they're probably gonna be in their inbox. We're going to send that new deal to them. But let's actually find that email address first, though. Alright. So we're gonna read data from a specific collection. Let's call it Find User is the actual step we're gonna do here. For the permissions, let's just give full access. And, for the collection, you can see I don't have the Directus Users collection here, but I can go in and edit my raw value and just use Directus underscore users. And for our IDs, right, what are we gonna put here? So if I open this back up, we're gonna use the trigger. Payload. Dealowner. So we'll do this, we'll do trigger dot payload dot deal underscore owner. And I'm gonna wrap this in mustache syntax. And let's try this again. So we'll read the user. Let's go ahead and maybe just add this send email as well. So we'll send the email, and this is gonna be the read underscore user. We're using the key that we set of the previous operation within that flow. Readuser. Email should be. We'll input that. New deal assigned to you. And hey. Read_user.firstname. We've assigned a new deal to you. And then I can even go through and add those different variables if I wanted to for things like the deal name or the deal ID and and add a link back to that. So let's just try trigger. Payload.deal_name. Great. We'll save it. And now let's just test this out. I'm not actually sure if I've got emails set up here locally on this particular instance though, so that could be fun. But we should be able to actually see if this runs. In light of that bit of news, because it just looking at my configuration here, I do not have email configured here locally. So let's let's detach that one, and let's just test the notifications. Send notification. Cool. Send notification. The find_user.id. Cool. Full access. And it was a new deal assigned. We'll just add the key here. So that'll be trigger dot payload. Or no. Actually, it may be something like this where we have key. Alright. Let's just take a look just to make sure. Alright. Within our payload, we can see the key there. That's good. In that case, we probably didn't need to get the actual user there, but that's okay. I'm just gonna copy this message that we set here. Just paste that. And let's take a look at where this gets us. Alright. So we've got a new inbound deal. We're going to read the data of the user that we've assigned that to and we're going to send a notification to that specific person. Alright. Now we've got a new deal, deal stage. Let's just set it to new. Deal owner. I'm gonna choose myself here so we could test this notification. Say deal name. Test deal. It's worth $6,000,000. Very nice deal. We got a primary contact. Here's some notes. We'll just save that and let's see what happens. Right? We go to our Flow, we get a new inbound deal, we can see our logs, send notification, recipient is so and so. Hey, undefined. We've sent you a new test deal. Underscore first name. Why did that not come back? Read underscore oh, that's why I used the wrong key. Sometimes that happens. Find user dot first name. Great. So now, if I look and I check my activity log, I can see that I've got this new deal signed. Here's the notification for that. And if I click on it where it says view content, it should take me to that specific test deal. Great. Awesome. Let's call that a win. Right? We've got our custom CRM built out. We've set up some automation for this. We could go further and flesh this out a a ton of different ways. Right? If we talk about it, like, we could go through and send emails to our actual primary contact when it reaches a certain stage, that would be fairly easy to do using direct dis flows. You know, we could maybe even go as far as, like, a future future iteration of this could be setting up an inbox to parse incoming emails like a BCC functionality and add these to those specific deals as well. But this this feels really good. I'm I'm calling this a win. That's our custom CRM. Thanks for joining me on this episode of 100 apps, 100 hours. We'll catch you on the next one.","592c22ad-1cb2-4d65-9bd7-e0d7c98fe4f2",[221],"e9e66fa8-0650-4e37-ae8b-74755fdd5dca",[],{"reps":224},[225,281],{"name":226,"sdr":8,"link":227,"countries":228,"states":230},"John Daniels","https://meet.directus.io/meetings/john2144/john-contact-form-meeting",[229],"United States",[231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,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],"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":282,"link":283,"countries":284},"Michelle Riber","https://meetings.hubspot.com/mriber",[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,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,262,473,474],"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",1773850453808]