[{"data":1,"prerenderedAt":439},["ShallowReactive",2],{"footer-primary":3,"footer-secondary":93,"footer-description":119,"100-apps-100-hours-website-personalization-engine":121,"100-apps-100-hours-website-personalization-engine-next":168,"sales-reps":187},{"items":4},[5,29,49,69],{"id":6,"title":7,"url":8,"page":8,"children":9},"522e608a-77b0-4333-820d-d4f44be2ade1","Solutions",null,[10,15,20,25],{"id":11,"title":12,"url":8,"page":13},"fcafe85a-a798-4710-9e7a-776fe413aae5","Headless CMS",{"permalink":14},"/solutions/headless-cms",{"id":16,"title":17,"url":8,"page":18},"79972923-93cf-4777-9e32-5c9b0315fc10","Backend-as-a-Service",{"permalink":19},"/solutions/backend-as-a-service",{"id":21,"title":22,"url":8,"page":23},"0fa8d0c1-7b64-4f6f-939d-d7fdb99fc407","Product Information",{"permalink":24},"/solutions/product-information-management",{"id":26,"title":27,"url":28,"page":8},"63946d54-6052-4780-8ff4-91f5a9931dcc","100+ Things to Build","https://directus.io/blog/100-tools-apps-and-platforms-you-can-build-with-directus",{"id":30,"title":31,"url":8,"page":8,"children":32},"8ab4f9b1-f3e2-44d6-919b-011d91fe072f","Resources",[33,37,41,45],{"id":34,"title":35,"url":36,"page":8},"f951fb84-8777-4b84-9e91-996fe9d25483","Documentation","https://docs.directus.io",{"id":38,"title":39,"url":40,"page":8},"366febc7-a538-4c08-a326-e6204957f1e3","Guides","https://docs.directus.io/guides/",{"id":42,"title":43,"url":44,"page":8},"aeb9128e-1c5f-417f-863c-2449416433cd","Community","https://directus.chat",{"id":46,"title":47,"url":48,"page":8},"da1c2ed8-0a77-49b0-a903-49c56cb07de5","Release Notes","https://github.com/directus/directus/releases",{"id":50,"title":51,"url":8,"page":8,"children":52},"d61fae8c-7502-494a-822f-19ecff3d0256","Support",[53,57,61,65],{"id":54,"title":55,"url":56,"page":8},"8c43c781-7ebd-475f-a931-747e293c0a88","Issue Tracker","https://github.com/directus/directus/issues",{"id":58,"title":59,"url":60,"page":8},"d77bb78e-cf7b-4e01-932a-514414ba49d3","Feature Requests","https://github.com/directus/directus/discussions?discussions_q=is:open+sort:top",{"id":62,"title":63,"url":64,"page":8},"4346be2b-2c53-476e-b53b-becacec626a6","Community Chat","https://discord.com/channels/725371605378924594/741317677397704757",{"id":66,"title":67,"url":68,"page":8},"26c115d2-49f7-4edc-935e-d37d427fb89d","Cloud Dashboard","https://directus.cloud",{"id":70,"title":71,"url":8,"page":8,"children":72},"49141403-4f20-44ac-8453-25ace1265812","Organization",[73,78,84,88],{"id":74,"title":75,"url":76,"page":77},"1f36ea92-8a5e-47c8-914c-9822a8b9538a","About","/about",{"permalink":76},{"id":79,"title":80,"url":81,"page":82},"b84bf525-5471-4b14-a93c-225f6c386005","Careers","#",{"permalink":83},"/careers",{"id":85,"title":86,"url":87,"page":8},"86aabc3a-433d-434b-9efa-ad1d34be0a34","Brand Assets","https://drive.google.com/drive/folders/1lBOTba4RaA5ikqOn8Ewo4RYzD0XcymG9?usp=sharing",{"id":89,"title":90,"url":8,"page":91},"8d2fa1e3-198e-4405-81e1-2ceb858bc237","Contact",{"permalink":92},"/contact",{"items":94},[95,101,107,113],{"id":96,"title":97,"url":8,"page":98,"children":100},"8a1b7bfa-429d-4ffc-a650-2a5fdcf356da","Cloud Policies",{"permalink":99},"/cloud-policies",[],{"id":102,"title":103,"url":81,"page":104,"children":106},"bea848ef-828f-4306-8017-6b00ec5d4a0c","License",{"permalink":105},"/bsl",[],{"id":108,"title":109,"url":81,"page":110,"children":112},"4e914f47-4bee-42b7-b445-3119ee4196ef","Terms",{"permalink":111},"/terms",[],{"id":114,"title":115,"url":81,"page":116,"children":118},"ea69eda6-d317-4981-8421-fcabb1826bfd","Privacy",{"permalink":117},"/privacy",[],{"description":120},"\u003Cp>A composable backend to build your Headless CMS, BaaS, and more.&nbsp;\u003C/p>",{"id":122,"slug":123,"vimeo_id":124,"description":125,"tile":126,"length":127,"resources":8,"people":8,"episode_number":128,"published":129,"title":130,"video_transcript_html":131,"video_transcript_text":132,"content":8,"status":133,"episode_people":134,"recommendations":149,"season":150,"seo":167},"54789ca9-ca1d-4c25-a4d7-31c32de53d5d","website-personalization-engine","1059436796","Bryant builds a content personalization system that shows different variations of web content based on visitor segments. Watch as he uses Directus to create a flexible backend for managing segment-specific content variations, then implements client-side detection to serve the right content to the right visitors.","40c50d05-f056-49ff-a4e8-cb7db67cbdc1",61,7,"2025-03-10","Mission: Website Personalization Engine","\u003Cp>Speaker 0: Welcome back to another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie, here from Directus. And today should be well, I think it's gonna be an exciting show. My confidence level on this one, just to put that out there, is fairly lower than normal. But today, we are going to be building a website personalization engine.\u003C/p>\u003Cp>I went through several different titles for this one. I don't know if that's actually gonna stick when this goes on Directus TV or not. But, what are we trying to do? Well, first, let's explain the rules. If you're new to 100 apps, one hundred hours, the rules are simple.\u003C/p>\u003Cp>Basically, we have sixty minutes to plan and build a clone, an application, a website. So that is plan and build, nothing more, nothing less. And then the only other rule, which is the I call it the anti rule, is use whatever you have at your disposal, which I will probably be leveraging, some AI today because I am wading into uncharted territory for me. So website personalization engine, sixty minutes. Let's do this together.\u003C/p>\u003Cp>Or it'll, like, publicly laugh at Bryant. Try. Alright. So we're gonna put sixty minutes on the clock, and let's cover this. Alright.\u003C/p>\u003Cp>So website personalization, you know, originally, this idea was for, like, personalized landing pages. Like, I know a specific company or target account that I want. Let's create a specific landing page for them. But in the light research that I did before this, which is basically, like, ten minutes of Google, basically, when it comes to personalization, it's more of a segment based approach. So that's what we're gonna sketch out here.\u003C/p>\u003Cp>You know, we wanna bucket visitors into a segment and then display certain content for that spec segment to personalize it. So, let's plan out our kinda data model. And, you know, I could leverage, like, some of our direct to CMS starters here. We're just gonna keep this stupid simple super simple. I tell my kids not to say stupid, so I probably shouldn't say that either.\u003C/p>\u003Cp>But alright. So as far as our data model, let's flesh out the application. Maybe we do just wanna, like, flesh out what are the actual features that we want out of this specific application and make that less huge. Alright. So we want to have a piece of content.\u003C/p>\u003Cp>Great. Directus will manage that for us pretty easily. And then we want to show variations of that content based on traits of a visitor or segments segments that a visitor belongs to. Now, something like this or or even when you get into, like, AB testing, you know, it's fairly easy. I won't well, I won't say fairly easy, but it's easy enough to set up, like, an AB testing code.\u003C/p>\u003Cp>If this, do that. If this if else, do that. But, like, the intersection of, like, handing this off to a content or a marketing team and setting it up in code and, like, managing all of that to work together for front end, back end, and making it cohesive, really difficult task. Right? So this is really all I'm trying to achieve today.\u003C/p>\u003Cp>We could make this fancier maybe down the the line, but let's let's discuss the data model for this. What are we gonna set up? So I could just call this content. Maybe it's, let's just have a pages collection. You know, this could be like a post or something like that.\u003C/p>\u003Cp>And then we're going to have what else? We're probably gonna have some segments. Like, here's the different segments that we want. And, you know, if you were doing, like, a proper system, you'd probably want something like events. Not super concerned with that.\u003C/p>\u003Cp>You know, the segments probably have, like, some rules for each segment or yeah. What are the rules? This sort of thing. I don't even know if we'll get this far. Let's just try to wade through this and and show something on the page.\u003C/p>\u003Cp>Alright. So let's get into what I've got set up. This is, just a Nuxt application. This is my standard starter for, you know, like, if I was just YOLO ing into a hackathon, this would be my starter. You could see a bunch of commented out code.\u003C/p>\u003Cp>But basically, I've got a blank Directus instance, if I can actually get logged into this bad boy. Directus is gonna be our CMS. It's gonna handle all of the back end for us. Dear lord. Why can I not get logged into this thing?\u003C/p>\u003Cp>Boy. Boy, oh, boy. Admin example. Password is the password. It's not even up, but that would be why that would be why I couldn't get into the application.\u003C/p>\u003Cp>Alright. So I've got Directus set up, in a Docker Compose, just a standard local setup kind of format. And like I said, Directus is gonna be our back end, our CMS. Okay. Great.\u003C/p>\u003Cp>Blank slate. Nothing fancy here. We do have templates available for stuff like CMS if if that's something that you're into. And then we have a Nuxt website. Alright.\u003C/p>\u003Cp>So let's start building some functionality. And inside the Nuxt website, we've got, like, an index page. So that's what we're staring at here. Alright, so let's add some pages content. Great.\u003C/p>\u003Cp>Go in, we'll create a new collection inside pages. For those of you who are new to Directus, what's happening behind the scenes here is Directus is connected to my Postgres database and it is basically gonna mirror whatever changes I make here to that SQL database. And I could connect like a an existing SQL database to this as well. So we have some pages. Great.\u003C/p>\u003Cp>Got a collection that's gonna show up here. When I hit new, there's nothing shows up. So we need to add some fields to our pages. And again, like, we're modeling our data here. We're generating an API and we're building a form for our content editors all in one place.\u003C/p>\u003Cp>So page needs a title. Great. A page needs a slug. So we're just gonna create a new field called slug. I'm gonna go into the interface settings and make sure this is URL safe.\u003C/p>\u003Cp>Do I have my little pointer? Yeah. I do. That's a cool little tool. Occasionally, I get questions on it.\u003C/p>\u003Cp>It is called mouse pose, Mac only, I think. Alright. So what else are we gonna have on our pages? We got a title. We got a slug.\u003C/p>\u003Cp>Maybe we'll keep this simple. Right? We've got a headline. That could be some text. Cool.\u003C/p>\u003Cp>And because I am totally gonna make it easy on myself, we're just gonna call this content. One big rich HTML rich text content block. This probably looks more akin to, like, a blog post than a page. Directus does have that amazing many to any like page builder scenario that's covered in some of our templates but we gotta crawl before we can walk. Alright so we got our pages, lets go ahead and let's just create a page.\u003C/p>\u003Cp>Right? This is Brian's page. Brian's page. Cool. Hey, yo.\u003C/p>\u003Cp>Why am I talking to myself? Great. Alright. So we got some content. Now if I want to query that via the API, I can go to the directus URL, go to items, pages, and boom.\u003C/p>\u003Cp>I could see I have a page here. There's my content. API is ready to go. This is already I've I've got the Nuxt application wired up with, like, a simple Directus SDK client. So we're creating Directus, we're using REST, and then we're providing that to the Nuxt application.\u003C/p>\u003Cp>So So if I go in and I do something like this, where we're just gonna create a new dynamic route inside Nuxt and do I have, like, a v t s? Yep. Script set up. Really need to work on my snippets to save time here. And my script at the top guy, when I'm working with Nuxt review.\u003C/p>\u003Cp>Alright. So now we want to fetch some of this page content. Right? Because what happens now, I've got this. If I go to Bryant's page, nothing shows.\u003C/p>\u003Cp>Yeah. That sucks. We need to resolve that. Alright. So what we're gonna do, we will do const can't type today.\u003C/p>\u003Cp>You'll notice I am using cursor here. Directus equals use Nuxt app. And, okay, we've got some auto completions that are not necessarily gonna give us what we want. Alright, so we're gonna get the data. Nuxt has the use async data function or the composable that we're going to use.\u003C/p>\u003Cp>If I can actually type that, great. We're gonna give this a key. So, the key, if we grab the routes, is going to be the slug, route. Params. Slug page.\u003C/p>\u003Cp>Let's just tag this as a literal. There we go. Thank you. Alright. And then we are going to return directus dot request, we're gonna do read items pages.\u003C/p>\u003Cp>Pages. K. And we're gonna filter where the slug, that's right, is equal to route dot params dot slug. K. So we have direct us there.\u003C/p>\u003Cp>We're gonna import read items from our SDK. You know, I could potentially, like, import that here and provide that as well if I wanted to, but we'll just do it this way, keep it nice and easy. And then let's see if we can just actually get the data. It'd been a minute since I messed with this. Is this actually going to work?\u003C/p>\u003Cp>Are we making a network call? Oh, wait. Use async data. There's that. There's the call that we're returning.\u003C/p>\u003Cp>Read items pages. Let's check our network requests. Are we seeing any actual network requests? It is server rendering these. So we are not seeing that.\u003C/p>\u003Cp>Probably want to add some error handling. So we'll just add that. If there's an error dot value, we are going to console dot error, error dot value. Great. Do I have any formatting set on this?\u003C/p>\u003Cp>Got a little bit of formatting. Alright. Can we see the error? H three error create error. What is going on there?\u003C/p>\u003Cp>There's an error. Error dot value dot message. What's the format for this? Create error. We're throwing an error.\u003C/p>\u003Cp>Hey. I'm guessing though, just from past experience, this is because we have not enabled any access to that page's collection. Right? So when I do this, I am still using the session token from Directus. But if I open this up in a new browser, we could see, hey.\u003C/p>\u003Cp>I don't have any permissions. So I'm just gonna enable permissions for pages. Let's do read permissions, see what we got. Boom. We can actually get some data and wrap this in a nice pre tag to see what we've got.\u003C/p>\u003Cp>Okay. Cool. Now let's take this data that we have and we'll add an h one. This will be the page dot headline. And then we are going to check and see what all I do have in this application.\u003C/p>\u003Cp>I don't have, like, a pros component. So we'll just do something like this where we have VHtml content. Now if I refresh if I refresh if I refresh, why is it that actually showing? Where's my content? Page dot headline.\u003C/p>\u003Cp>How is it that I could see the page content? Oh, I do have it here. Page dot headline. Why isn't that actually showing on the page, though? Interesting question.\u003C/p>\u003Cp>Interesting. Definitely. Let's just center this up. Maybe let's max auto, max width, four x up. Add some padding.\u003C/p>\u003Cp>Why isn't my content displaying via page? Do we need to wait on the page? Am I really not smart enough to figure this part of it out? Is there some type of error that we're getting paged out headline? I could clearly see oh, yes.\u003C/p>\u003Cp>That's why, Brian. You are getting an array here, so we need to transform that. So async data. I think it's here that we can do a transform. Data.\u003C/p>\u003Cp>Data. Just the first item. There we go. Alright. There we go.\u003C/p>\u003Cp>Hey, yo. Why am I talking to myself? Make sure that you understand what your data looks like before you start trying to mess around with it. We're gonna make that giant. And if we slap a pros tag onto this, pros l g.\u003C/p>\u003Cp>I think I've got Telen typography included here so we could see this data. Okay. So we're rendering something out to the page. Great. Now, this is super fancy.\u003C/p>\u003Cp>Right? Let's just grab something from the Directus blog to use as content. Here's a post that I wrote. I'm just gonna copy this entire thing. We're gonna go in and throw that here.\u003C/p>\u003Cp>Great. Just to get something on the page. Okay. We'll call this context switching sucks for devs. Cool.\u003C/p>\u003Cp>Alright. So now we are showing a basic thing on the page. Hallelujah this is amazing this is the coolest thing we've ever built. How are we doing on time? We got about forty five minutes left.\u003C/p>\u003Cp>All right so we have a piece of content now we want to show variations of that content for an actual visitor. So how are we going to achieve this sort of thing? And, you know, if we boil this down to the basics, we've got a page. We're going to have variations of page content. Yeah.\u003C/p>\u003Cp>We could call this personalizations or page variations. That's gonna be linked to our segments, and this is not right. I do have a PhD in drawing arrows, just not in Figma. This is still a challenge for me. Alright.\u003C/p>\u003Cp>So let's create this and I think what we can do is hijack some of the functionality that already exists in Directus for this sort of thing. Going off label here, off brand. So, we will go into our data model. What I'm gonna do, I'm just gonna create a page variations. Yeah.\u003C/p>\u003Cp>Naming stuff is probably the hardest challenge in development. You know, we could call this personalizations, but that might make somebody mad. Yeah. I'll just do created at, created by. I don't necessarily need this information.\u003C/p>\u003Cp>These are just helpers that you can add. Directus, like, prefix or, like, sets these up for you. So whenever, you save this item or create a new item, it obviously records the user, etcetera. Just some shortcuts. Alright.\u003C/p>\u003Cp>So we have page variations. We've got two different collections here. Great. How are we gonna tie these things together? Now, what I can do and what I'm gonna steal is our translations feature.\u003C/p>\u003Cp>So if we go to the docs actually, let's go to the new docs. These things are in beta as of now. We're gonna look for translations. Great. We have the ability to translate content and manage all those translations for you.\u003C/p>\u003Cp>And if I open this in a new tab, we get, like, this beautiful side by side interface where I could see, okay, here's English versus Spanish or French. And this is like, when we boil it down, like, when I think about it, I'm just now coming to me through my head here, is it basically, translation is a form of personalization anyway. So why not leverage this existing structure? You know, short of, like, the icons and maybe some of the other stuff that is baked in there, I I think it should work. So I'm also going to like, normally, if we were doing translations, you would create, like, a languages collection where you would store all the languages that you wanna translate your content into.\u003C/p>\u003Cp>In this case, what we're gonna do is just use, I'm gonna create a new collection. I'm gonna call it segments. You know, we could manually generate the IDs for these or we could use, like, a generated UUID. You know, if I was trying to scale this out, I would definitely be using, like, generated UUIDs, but let's just call this key we're gonna manually enter a string for this. So whenever we create a new segment, we're gonna manually enter in the key.\u003C/p>\u003Cp>That'll help us keep things a little bit clearer. Updated at updated by just to keep track. And do we want this segment to be active or not? You know, maybe we set that up on a a Boolean toggle. Alright.\u003C/p>\u003Cp>So we got a key for it. Maybe we want, like, a proper name. So we add a name field and drag this down. And what I'm doing here is basically just configuring the actual form that we're gonna use to set this stuff up. Do we have a segment?\u003C/p>\u003Cp>Do we have a category? Yep. There we go. Yeah. Again, naming things is hard.\u003C/p>\u003Cp>Let's add some icons. Just the designer OCD for me kicking in. Doc. Yep. There we go.\u003C/p>\u003Cp>Edit document. And I'm just gonna like actually hide these page variations. So now we have pages, we have segments. Let's create a new segment. Right, I want to speak to developers.\u003C/p>\u003Cp>We could say these are for developers, that's our nice name. Directus is also helpful for content editors, so I'll do content editors. Yeah. I wish I was doing this live so I could say, hey, hyphenate or underscore, but let's just go with a hyphen. Fine.\u003C/p>\u003Cp>Fine. Fine. You know, I could keep adding these as much as I want. Right? So now we've got some segments, we've got some pages.\u003C/p>\u003Cp>We wanna generate these variations. So again, we're gonna reach for the Directus translations interface. And I hope this works out like I think it will or like I hope it will. You can see that we're pre setting the languages collection. Directus is smart enough to know, like, when you're doing translations, you need languages, we can create that for you.\u003C/p>\u003Cp>But what I'm gonna do here, I'm gonna open up advanced field mode instead, and I'm gonna go in and I'm just gonna do all of this mapping myself. So we try to be smart and create junction collections and everything for you, but you can take total control of this as well. So here, this is the collection. So we're gonna call this page variations. I'm just gonna use page and we're gonna call this the segment and the collection that we're targeting is segments.\u003C/p>\u003Cp>So, I'm gonna set these all up to cascade, so if we delete a page variation or deselect one, we delete that content because we're not going to use these variations across different collections or pages. Alright, so the language indicator field. Now normally this is what shows here right up here. Right? Just the indicator of what this is.\u003C/p>\u003Cp>We could use the key. We could use the name. Let's use the name. Language direction. We're gonna ignore that.\u003C/p>\u003Cp>Default language is the primary key. Let's let's say I wanna default to developers. And we're gonna start this split. So we'll start open like this. Great.\u003C/p>\u003Cp>Okay. So now we create this, and we could show related values. You know, you can even preview the translation content. And I already see one thing that's going to bug the crap out of me as I called it translations. I forgot to rename this.\u003C/p>\u003Cp>Alright. So we're gonna call this variations. So it's not translations, it's variations. We're just piggybacking off of functionality. Alright.\u003C/p>\u003Cp>Pages. Cool. This is gonna be the what? Key? No.\u003C/p>\u003Cp>Segment. We're gonna call that segment. That's the segment. The collection is segment. And I gotta set up this nice cascade again.\u003C/p>\u003Cp>Okay. Go through the exact same process all over again. Love shooting myself in the foot Just to make sure we have variations. Is variations spelled correctly? At this point, we don't know.\u003C/p>\u003Cp>Alright. So I probably goofed something up already. Did I? Did I not? Page variations.\u003C/p>\u003Cp>Page variations. Page page is not found. Oh. Yep. Alright.\u003C/p>\u003Cp>So love it when I goof up. Cause more work for myself. We're just gonna start this process all over. Alright. Try it one more time.\u003C/p>\u003Cp>Page, variations, Generate the UID. I'm not even gonna go for that. At this point, we're gonna say translations interface. Great. This is gonna be segments.\u003C/p>\u003Cp>We're going to use the segment name. We're gonna start with the split open view. And let's control this. Right? Page variations.\u003C/p>\u003Cp>This is gonna be page. Segment. Delete. Alright. So definitely don't do what I have done.\u003C/p>\u003Cp>Let's try this. See if that works. Doesn't have field page and doesn't have a relationship. And what in the world field page in collections doesn't have a relationship? Why doesn't it it should have a relationship.\u003C/p>\u003Cp>Pages, segments, translations, start open. Why doesn't this have a relationship? Well, let's just see what happens when we do this in orally. Translations already has a relationship. Alright.\u003C/p>\u003Cp>So we can see here by adding this we get a languages and then we get a pages translations, there's a pages ID, there's an ID. This should work out okay. I don't know what is going on. Maybe I didn't refresh the data model. Key name, segments, page variations.\u003C/p>\u003Cp>Why am I not getting this to work correctly? Let's just take a look at our database and see what's happening. 53Edit. 5 3 2, test, Connect. Alright.\u003C/p>\u003Cp>So now we're behind the scenes into the database trying to figure out what I goofed up to begin with and it looks like there's some relationships here that got left over that didn't get fixed that are causing problems. Alright. So now with that out of the way, let's try this again. Page variations, we're gonna generate an ID for those. I'm just gonna skip all the fanciness.\u003C/p>\u003Cp>And now I'm gonna go in and do translations. We're gonna call this variations. Instead of languages we're going to use segments, we're going to use the name of that. We are then going to set up to use page variations, page segments, segment. Okay.\u003C/p>\u003Cp>Now is that going to work out how we want fingers crossed. Let's log in and see what have we got. Okay. So now I get this side by side interface of content editors and developers. Right?\u003C/p>\u003Cp>But I don't see anything inside the actual form. So inside this junction table is where we're gonna go in and create our content that is actually going to vary. So in this case, it's just basically going to be our headline and our content. And I can quickly duplicate these. So we'll go to page variations.\u003C/p>\u003Cp>I'm gonna say copy headline. We're gonna copy the content. Just make sure you change that copy that we prepend at the end so that you don't, you know, get confused. But with this information set up, now I can get an interface that looks like this where we can say, hey. Great.\u003C/p>\u003Cp>Now we've got developers. We've got content editors. Cool. And I can be able to store different contexts here. Context switching sucks.\u003C/p>\u003Cp>Great. That's what we'll change the default headline to. We're gonna change this for devs, Context switching sucks for content editors. This content is for editors. This content is for devs.\u003C/p>\u003Cp>Alright. So now thinking through our mental model, we've got our default content. And then if we identify a segment, like if the visitor falls into the content editor segment or they fall into the developer segment, we're going to show them different content. Now, what is this actually going to look like? So if I go and like we look at the API now, got my API request, you could see those variations.\u003C/p>\u003Cp>One of the beauty, beautiful things of Directus is the ability to use the rest API in a GraphQL like manner. So I could do something like this where I just say, hey. Give me all of the root level fields and also give me all the fields within our variations. So now I can see here's my variations. The segment here is developers.\u003C/p>\u003Cp>The, segment here is content editors and presumably on the Nuxt side of it. And, you know, I could rig something up via the API to do this swap for me, but depending on how I'm rendering my site, you know, let's say I'm statically generating the site, if I want to still offer this, I've got to pre generate the payload, you know, pre generate our JSON and store that so I can use it when rendering it. So we're gonna try to do this on the client side. Inside our page level data here, right, we're still getting the same result. And if I add the data back, you know, now we should be seeing our variations, but hey, lo and behold we're not because again we need to go back into our access policies and set that up.\u003C/p>\u003Cp>So we're going to allow read access for our page variations and for our segments just so we could see those things. You know, when we're going to production, we'd probably make sure there's some kind of status on these so that, you know we're only showing published pages etcetera, but outside the scope. So now I can see my variations. We're gonna go here and let's just specify our fields, right. It's not content, it is variations of this content.\u003C/p>\u003Cp>And now we could see that. Great. Alright. So now we need to translate this content or personalize it in this case. Right?\u003C/p>\u003Cp>How do we assign a segment? You know, like, writing a rules engine here would be, kinda challenging with the twenty seven minutes we have left, I would say. But, you know, at the core, we we've gotta have some way to assign the segment. What I'm going to do I I think we're just gonna use a cookie, for this. So Nuxt has a use cookie composable.\u003C/p>\u003Cp>Let's just call this the segment key. We're gonna use cookie or you call that segment. Great. And I'm just gonna make this stupid simple. We'll add a div.\u003C/p>\u003Cp>We'll flex these, add some gap, make sure that's in the middle, etcetera. And then we're gonna add some buttons. I've got the Nuxt UI library in here. Segment key equals developers. No.\u003C/p>\u003Cp>We're not gonna do designers, but, we're gonna do content editors. Content editor. And then maybe we just add I am a great. Alright. Maybe we wrap this whole thing in.\u003C/p>\u003Cp>I think it's a u container from the Nuxt UI library. Okay. Yep. So we get a little bit of spacing there, And we can add, just some space between these space y eight. Okay.\u003C/p>\u003Cp>Great. And this is probably all part of the same section. Okay. So I am a developer. I am a content editor.\u003C/p>\u003Cp>I am a developer's. Great. Again, naming is crucial here. But basically, like, what I should be able to see now if I go into, like, my cookie storage, I can see that segment as well. Right?\u003C/p>\u003Cp>So based on this, I'm tagging that person. You know, and we're just explicitly asking. But, you know, we could set up, like, some kind of client side rule engine or, you know, you could potentially do this on the server side as well. Whatever pages you visit, weight those against each other and assign a, you know, if I've got five articles for developers and you look at three of those, then I'm probably safe to say that you're a developer, versus, you know, reading the content editor pages. Alright.\u003C/p>\u003Cp>So how do we actually translate this? Right? We probably want, like, a helper function that takes our page data and then evaluates the segment and then, you know, spits out the proper content. So let's just see how awesome AI has become here and see if this is actually worthwhile or I should just probably write it out. But, this wouldn't be fun if we weren't using something like cursor and, you know, something super opinionated or where everybody has an opinion about AI at this moment.\u003C/p>\u003Cp>Here we go. Write a let's just describe this. Right? Personalize the page content based on the segment key. Where is this gonna come up with?\u003C/p>\u003Cp>Personalize content, page value dot variations. I okay. You know, maybe this would come back. Let's see. Helper function to get the personalized content.\u003C/p>\u003Cp>Page variations return page dot that's not really content. Right? We just wanna return yeah. Okay. So I'm probably not prompting right.\u003C/p>\u003Cp>Let's see what we can do. Take in the page and the segment key and return a merged page with the proper variation based on the segment key. That's better, still not great. Right? You would probably want, like, a personalized function where it, just basically takes in, the page content and the different segments and the current segment and returns that.\u003C/p>\u003Cp>But nevertheless we got our personalized page or we should, right. Great. We can see our variations, we can see the content. If I check-in view dev tools, we go to our slug page. Do we have the personalized page?\u003C/p>\u003Cp>Great. That doesn't really Context switching sucks. No. But that doesn't look right to me. Right?\u003C/p>\u003Cp>So we do personalized page dot headline, personalized page dot content. Context switching sucks. I am currently in the developer side of things, so it should be showing should be toggling this. Right? So there's something wrong here.\u003C/p>\u003Cp>Page dot value dot variation. Yeah. So, basically, what we wanna do is loop through the trying to to do this the AI way and not get frustrated. Loop through the page variations and merge them with the page based on the segment key. Still showing the same thing.\u003C/p>\u003Cp>Personalized page. Yeah. Okay. Yeah. Alright.\u003C/p>\u003Cp>So this is the final page. Let's define that. The final page. Okay. Then we are going to is this getting smarter now?\u003C/p>\u003Cp>For constant of variations, if segment dot key, final page, return final page. What does that do? Why is it why is it still not doing what we want? Page.valuepage.value.variations.developers. Oh, yeah.\u003C/p>\u003Cp>That's why. Basically, we need to use, like, a file. Constant variations equals variations. Selected Constant variation is going to be we basically need to look through this array. Variations dot find.\u003C/p>\u003Cp>We'll look for the variation. And there we go. Okay. Yeah. So we're getting that array.\u003C/p>\u003Cp>That's the problem. So now if I remove this blah blah blah page, Now I have personalized content for folks. Amazing. This is the personalized content engine, website personalization engine. Badabing badaboom.\u003C/p>\u003Cp>This is beautiful. Right? So now I can define all of these variations within our actual CMS. Like here's the segments that we want to create content for. You know, I could potentially set up some type of rules engine for this.\u003C/p>\u003Cp>And then, you know, on our individual pages, then I can go through and define this content. Now is this a real world scenario? Probably not. You would take this and use something like our mini to any builder where you're building these dynamic pages and within each section you have personalized content for that. But at the very least, hey, this is now we've got, a pretty robust I won't say robust.\u003C/p>\u003Cp>This is not robust at all. But it is a a pretty it's a start to a personalization engine basically. That's it. That's all. Now where do we go from here?\u003C/p>\u003Cp>Right? I this would be extracted out into a, like, a helper function, so that, like, each page or each block, we would we would call this function, return the content if there is a variation, like, if we have a segment key. Yeah. And we could even go as far as, like, creating a rule engine for this. So let's just continue to beat on AI, which is, honestly not been great so far for this.\u003C/p>\u003Cp>Let's say extract this out to out to a helper function that is more robust, maybe. I don't know. Let's see. I'm gonna take a sip of coffee while it's trying to choke that down. The auto completions are not great, but here we go.\u003C/p>\u003Cp>Let's see what it's come up with. Personalization. This looks to be a composable. Use personalized page. Here's the page.\u003C/p>\u003Cp>Segment key. Update the page component. Here's how we're gonna use that. Personalize page. Use a page.\u003C/p>\u003Cp>There is a segment key. Okay. AI. I will bite. Alright.\u003C/p>\u003Cp>App utils. Personalization. And I don't know why it's using the is there let's see, like, an actual it's using the composable format for view, but import type maybe rest. Return computed. I mean, to me, this is a I guess this is a this is a composable.\u003C/p>\u003Cp>I'm not sure why it's doing that. So we'll stick that there. This is use personalization. This is the name of it. Use personalization.\u003C/p>\u003Cp>Again, probably not a strong choice for, like a composable here because this is probably something that you might want to extract out and use, like, across different projects. Use personalization. Use personalization. Okay. Cursor.\u003C/p>\u003Cp>Okay. Now that you use this helper, can we have it apply this automatically? Yes. But knowing Nuxt, we should automatically use this. Use personalization.\u003C/p>\u003Cp>There's our page content. There's the segment key. Does this still get us what we want? So far, looks like it does. Right?\u003C/p>\u003Cp>And the nice thing here is because we're using a cookie, and even though, like, this would be server side rendered, I'm still getting the content that we want. Right? Because it's automatically running a this cookie first before we actually display the content. Cool. Now let's do we have time?\u003C/p>\u003Cp>What kind of time do we have here? We got fifteen minutes left. Where do we take this further? Right? Where would we go from here?\u003C/p>\u003Cp>How do we set this up into, like, something like blocks, right, where we want to build a page out of the blocks? So for that, we can use the mini to any builder inside Directus. We've got this mini to any builder. We're gonna call this blocks. We don't have any related items yet.\u003C/p>\u003Cp>So first thing we're gonna do here is create a hero block. Call it block hero because I like to be obtuse. Alright. The hero is gonna have a headline. It's gonna have a description.\u003C/p>\u003Cp>Great. Okay. And then we have a block CTA. Cool. And that is going to have a call out and a button text.\u003C/p>\u003Cp>Great. Okay. Now we're gonna put those together with the many to any builder. These are gonna be our page blocks. We're gonna say block CTA, block hero.\u003C/p>\u003Cp>Great. Basically, this many to any relationship, it it doesn't necessarily exist, inside, like, standard SQL. Right? So Directus is doing a bit of API magic here. Basically, what what's happening, and we'll just hide, like, what we've got already.\u003C/p>\u003Cp>Alright. Hide this field. Don't destroy. Just hide. So we can see what's happening.\u003C/p>\u003Cp>We've created these separate collections, and then there's a junction collection here that is storing the page's ID and then we're storing the ID for the item. And then we also have a string for the collection that we're pulling this from. So it's, you know, basically some API magic to string these things together. But now if I go in and I update the page, we could see we've got a CTA that we'd say, this is you should try Directus. Do it now.\u003C/p>\u003Cp>Great. There's our CTA. There's our hero. Brian is cool. Well, he's not.\u003C/p>\u003Cp>There's our description. Alright. And let's hit save and stay. Let's just look at our page content now that we're getting back. Right.\u003C/p>\u003Cp>We're showing two different blocks here, but again, we need to go in and set our permissions. Directus keeps you secure by default. So we're gonna add just read permissions for all this. Again, for production, we wouldn't do this, but can we get to personalized blocks in twelve minutes or less? I need to get my ass in gear.\u003C/p>\u003Cp>Alright. Alright so we're gonna go back to our slug here. We're gonna change this up where we have our blocks. And actually we could use like a object syntax as well. Blocks, we're going to have here.\u003C/p>\u003Cp>We're going to get the we could get that. And then we're probably also going to have, like, variations within the blocks themselves. Well, this is getting messy. Right? Okay.\u003C/p>\u003Cp>So now let's just go back to the page. Variations blocks. I don't wanna do variations. We wanna do blocks. Alright.\u003C/p>\u003Cp>I'm gonna pull this back in. For our page render, we're just gonna have to pull that out. And now we could see we got, like, some data for each block. Right? Blocks, items, we're gonna wanna grab the item for each block.\u003C/p>\u003Cp>I don't need it. Fields comma. What do we got here? Okay. Well, yes, no, maybe so.\u003C/p>\u003Cp>Something is off. We got one too many. How nested did we go here? This one needs okay. Did we get it right this time?\u003C/p>\u003Cp>We still did not get this right. Direct as requests. AI is totally screwing this up, and I've had too much coffee to fix this properly. Alright. So we got the block.\u003C/p>\u003Cp>We're within the block, we're going to have, the items items, and then we're gonna grab the variations. Where okay. Backup. Backup. Backup.\u003C/p>\u003Cp>Love all the formatting. Items. Oh, yeah. This is not going well for me, is it? Fields.\u003C/p>\u003Cp>We're gonna grab the collection field. Great. Okay. Now, we got item. If I change this to item, we should see the item text.\u003C/p>\u003Cp>Great. And then also we're gonna have variations on that same item. Cool. Alright. But we don't have those yet.\u003C/p>\u003Cp>Alright. So let's go in and add those. So now the same way that I created that translation function on, let's say, the page level, we're gonna go through and we're gonna do this speed run style in less than eight minutes. We're gonna call this variations. Great.\u003C/p>\u003Cp>This is gonna be segments. This is the collection there. And what are we gonna call this? We're gonna call this the segments. Block hero ID, block hero variations.\u003C/p>\u003Cp>Great. Primary key is gonna be the or the primary indicator is gonna be that. We'll set this up. Alright, so now on our block hero I should be able to set up like a variation here except I don't have that content yet. So we'll go to our block CTA.\u003C/p>\u003Cp>Oh, no. Block hero. We're gonna copy that headline and description down into the next collection, right, to for our variations so that we can have that specific content. Block hero variations, duplicate. Great.\u003C/p>\u003Cp>Now, I go in, we've got our block hero I'm gonna stick up at the top. We have a headline for Brian's cool to editors. Brian is cool to developers. Great. Okay.\u003C/p>\u003Cp>And we're gonna create a hero component. View, create a hero component with Tailwind that has two props, headline and description. Let's see what it comes up with. Outsourcing the work here. Six minutes.\u003C/p>\u003Cp>You know, we could declare this one good, but, yeah, let's make it fun. Alright. We got the hero component. Okay. So now within the like this page section, I guess, Let's go and call that hero component with the page headline and description.\u003C/p>\u003Cp>Context switching sucks. Do we have a oh, no. We don't have that at the page level. Right? We're gonna loop through all of our blocks.\u003C/p>\u003Cp>So the, let's do template v four page blocks within the blocks. If the block collection equals the hero to block hero, basically, block underscore hero, that's the name of our collection, we're going to render that out. But now we also need to use the personalized page, page dot blocks, personalized page dot blocks. But low and behold, we're still not changing that content based on that because our personalization is what? At the page level.\u003C/p>\u003Cp>Right? So AI, help us in our moment of need. Five minutes left. Change this to well, accept a what a to accept content and recursively merge if there is a variations key within The array within the objects. There's a variations key.\u003C/p>\u003Cp>Date. And task. Objects. Let's see what this comes with back. What if just curious.\u003C/p>\u003Cp>Alright. Merge variations, current segment, matching variations. Is this to me, like, obviously highlights the dangers of AI? I am on a crunch here. Right?\u003C/p>\u003Cp>I don't have a clue. I don't have enough time to, like, figure out if this is actually going to do what I wanted to do anyway. So, you know, great that you can I I mean, I love the fact that I can just, like, quickly POC this, but, you know, it's like a what are we coming back with here? Personalized page. Let's just see what it comes back with.\u003C/p>\u003Cp>Blocks zero. So, again, like, is yeah. See, that's goofed up. Oh, and I'm not even giving the personalization content, though, am I? Why is that?\u003C/p>\u003Cp>I should be fetching it here. Items, personalizations. We gotta go back into our permissions, block hero variations, access. Save. Are we now at least getting that?\u003C/p>\u003Cp>Yes. Okay. So AI did save the day here, basically. You know, before I shipped any of this, I would need to go through and, like, study this in detail to figure out is this actually doing doing right. It appears to be doing right, but are there gonna be, like, dangerous side effects?\u003C/p>\u003Cp>But here I could see Brian is cool to developers. You know, maybe we go back in and within this, do we have a does don't like him? Alright. No. He's not.\u003C/p>\u003Cp>Neither do content editors. So there is that. You know, the one thing I noticed is we should have, like, a fallback and it should take that fallback into account. So it's not doing, like, a deep merge, but now we could see that content. We could clean this up just to show it out.\u003C/p>\u003Cp>And boom. So now we've got, like, page block level data where I could go through and build a page and have segments and personalize that data, for each individual segment. So I'm I'm gonna call that a win. Right? We got one minute fifty six seconds left.\u003C/p>\u003Cp>Would I have been able to achieve this without cursor? %. Would it have taken me more than an hour? One hundred percent. Is this still cool and fancy?\u003C/p>\u003Cp>Probably not. This is the start to something incredibly cool where, you know, I could go in and on the client side have some type of tracking and some type of rule engine that just, like, consistently con like, creates these different segments or groups of visitor into segments. And if you keep that on the client side, it could be privacy friendly as well. But I hope this was an interesting episode, an exciting episode for you. I've had a good time.\u003C/p>\u003Cp>I'm probably gonna figure out a way for us to leverage this somehow in the future. That's it for this episode of 100 apps, one hundred hours. Thanks for joining me. I do roll like the success message. See you guys next time.\u003C/p>","Welcome back to another episode of 100 apps, one hundred hours. I'm your host, Brian Gillespie, here from Directus. And today should be well, I think it's gonna be an exciting show. My confidence level on this one, just to put that out there, is fairly lower than normal. But today, we are going to be building a website personalization engine. I went through several different titles for this one. I don't know if that's actually gonna stick when this goes on Directus TV or not. But, what are we trying to do? Well, first, let's explain the rules. If you're new to 100 apps, one hundred hours, the rules are simple. Basically, we have sixty minutes to plan and build a clone, an application, a website. So that is plan and build, nothing more, nothing less. And then the only other rule, which is the I call it the anti rule, is use whatever you have at your disposal, which I will probably be leveraging, some AI today because I am wading into uncharted territory for me. So website personalization engine, sixty minutes. Let's do this together. Or it'll, like, publicly laugh at Bryant. Try. Alright. So we're gonna put sixty minutes on the clock, and let's cover this. Alright. So website personalization, you know, originally, this idea was for, like, personalized landing pages. Like, I know a specific company or target account that I want. Let's create a specific landing page for them. But in the light research that I did before this, which is basically, like, ten minutes of Google, basically, when it comes to personalization, it's more of a segment based approach. So that's what we're gonna sketch out here. You know, we wanna bucket visitors into a segment and then display certain content for that spec segment to personalize it. So, let's plan out our kinda data model. And, you know, I could leverage, like, some of our direct to CMS starters here. We're just gonna keep this stupid simple super simple. I tell my kids not to say stupid, so I probably shouldn't say that either. But alright. So as far as our data model, let's flesh out the application. Maybe we do just wanna, like, flesh out what are the actual features that we want out of this specific application and make that less huge. Alright. So we want to have a piece of content. Great. Directus will manage that for us pretty easily. And then we want to show variations of that content based on traits of a visitor or segments segments that a visitor belongs to. Now, something like this or or even when you get into, like, AB testing, you know, it's fairly easy. I won't well, I won't say fairly easy, but it's easy enough to set up, like, an AB testing code. If this, do that. If this if else, do that. But, like, the intersection of, like, handing this off to a content or a marketing team and setting it up in code and, like, managing all of that to work together for front end, back end, and making it cohesive, really difficult task. Right? So this is really all I'm trying to achieve today. We could make this fancier maybe down the the line, but let's let's discuss the data model for this. What are we gonna set up? So I could just call this content. Maybe it's, let's just have a pages collection. You know, this could be like a post or something like that. And then we're going to have what else? We're probably gonna have some segments. Like, here's the different segments that we want. And, you know, if you were doing, like, a proper system, you'd probably want something like events. Not super concerned with that. You know, the segments probably have, like, some rules for each segment or yeah. What are the rules? This sort of thing. I don't even know if we'll get this far. Let's just try to wade through this and and show something on the page. Alright. So let's get into what I've got set up. This is, just a Nuxt application. This is my standard starter for, you know, like, if I was just YOLO ing into a hackathon, this would be my starter. You could see a bunch of commented out code. But basically, I've got a blank Directus instance, if I can actually get logged into this bad boy. Directus is gonna be our CMS. It's gonna handle all of the back end for us. Dear lord. Why can I not get logged into this thing? Boy. Boy, oh, boy. Admin example. Password is the password. It's not even up, but that would be why that would be why I couldn't get into the application. Alright. So I've got Directus set up, in a Docker Compose, just a standard local setup kind of format. And like I said, Directus is gonna be our back end, our CMS. Okay. Great. Blank slate. Nothing fancy here. We do have templates available for stuff like CMS if if that's something that you're into. And then we have a Nuxt website. Alright. So let's start building some functionality. And inside the Nuxt website, we've got, like, an index page. So that's what we're staring at here. Alright, so let's add some pages content. Great. Go in, we'll create a new collection inside pages. For those of you who are new to Directus, what's happening behind the scenes here is Directus is connected to my Postgres database and it is basically gonna mirror whatever changes I make here to that SQL database. And I could connect like a an existing SQL database to this as well. So we have some pages. Great. Got a collection that's gonna show up here. When I hit new, there's nothing shows up. So we need to add some fields to our pages. And again, like, we're modeling our data here. We're generating an API and we're building a form for our content editors all in one place. So page needs a title. Great. A page needs a slug. So we're just gonna create a new field called slug. I'm gonna go into the interface settings and make sure this is URL safe. Do I have my little pointer? Yeah. I do. That's a cool little tool. Occasionally, I get questions on it. It is called mouse pose, Mac only, I think. Alright. So what else are we gonna have on our pages? We got a title. We got a slug. Maybe we'll keep this simple. Right? We've got a headline. That could be some text. Cool. And because I am totally gonna make it easy on myself, we're just gonna call this content. One big rich HTML rich text content block. This probably looks more akin to, like, a blog post than a page. Directus does have that amazing many to any like page builder scenario that's covered in some of our templates but we gotta crawl before we can walk. Alright so we got our pages, lets go ahead and let's just create a page. Right? This is Brian's page. Brian's page. Cool. Hey, yo. Why am I talking to myself? Great. Alright. So we got some content. Now if I want to query that via the API, I can go to the directus URL, go to items, pages, and boom. I could see I have a page here. There's my content. API is ready to go. This is already I've I've got the Nuxt application wired up with, like, a simple Directus SDK client. So we're creating Directus, we're using REST, and then we're providing that to the Nuxt application. So So if I go in and I do something like this, where we're just gonna create a new dynamic route inside Nuxt and do I have, like, a v t s? Yep. Script set up. Really need to work on my snippets to save time here. And my script at the top guy, when I'm working with Nuxt review. Alright. So now we want to fetch some of this page content. Right? Because what happens now, I've got this. If I go to Bryant's page, nothing shows. Yeah. That sucks. We need to resolve that. Alright. So what we're gonna do, we will do const can't type today. You'll notice I am using cursor here. Directus equals use Nuxt app. And, okay, we've got some auto completions that are not necessarily gonna give us what we want. Alright, so we're gonna get the data. Nuxt has the use async data function or the composable that we're going to use. If I can actually type that, great. We're gonna give this a key. So, the key, if we grab the routes, is going to be the slug, route. Params. Slug page. Let's just tag this as a literal. There we go. Thank you. Alright. And then we are going to return directus dot request, we're gonna do read items pages. Pages. K. And we're gonna filter where the slug, that's right, is equal to route dot params dot slug. K. So we have direct us there. We're gonna import read items from our SDK. You know, I could potentially, like, import that here and provide that as well if I wanted to, but we'll just do it this way, keep it nice and easy. And then let's see if we can just actually get the data. It'd been a minute since I messed with this. Is this actually going to work? Are we making a network call? Oh, wait. Use async data. There's that. There's the call that we're returning. Read items pages. Let's check our network requests. Are we seeing any actual network requests? It is server rendering these. So we are not seeing that. Probably want to add some error handling. So we'll just add that. If there's an error dot value, we are going to console dot error, error dot value. Great. Do I have any formatting set on this? Got a little bit of formatting. Alright. Can we see the error? H three error create error. What is going on there? There's an error. Error dot value dot message. What's the format for this? Create error. We're throwing an error. Hey. I'm guessing though, just from past experience, this is because we have not enabled any access to that page's collection. Right? So when I do this, I am still using the session token from Directus. But if I open this up in a new browser, we could see, hey. I don't have any permissions. So I'm just gonna enable permissions for pages. Let's do read permissions, see what we got. Boom. We can actually get some data and wrap this in a nice pre tag to see what we've got. Okay. Cool. Now let's take this data that we have and we'll add an h one. This will be the page dot headline. And then we are going to check and see what all I do have in this application. I don't have, like, a pros component. So we'll just do something like this where we have VHtml content. Now if I refresh if I refresh if I refresh, why is it that actually showing? Where's my content? Page dot headline. How is it that I could see the page content? Oh, I do have it here. Page dot headline. Why isn't that actually showing on the page, though? Interesting question. Interesting. Definitely. Let's just center this up. Maybe let's max auto, max width, four x up. Add some padding. Why isn't my content displaying via page? Do we need to wait on the page? Am I really not smart enough to figure this part of it out? Is there some type of error that we're getting paged out headline? I could clearly see oh, yes. That's why, Brian. You are getting an array here, so we need to transform that. So async data. I think it's here that we can do a transform. Data. Data. Just the first item. There we go. Alright. There we go. Hey, yo. Why am I talking to myself? Make sure that you understand what your data looks like before you start trying to mess around with it. We're gonna make that giant. And if we slap a pros tag onto this, pros l g. I think I've got Telen typography included here so we could see this data. Okay. So we're rendering something out to the page. Great. Now, this is super fancy. Right? Let's just grab something from the Directus blog to use as content. Here's a post that I wrote. I'm just gonna copy this entire thing. We're gonna go in and throw that here. Great. Just to get something on the page. Okay. We'll call this context switching sucks for devs. Cool. Alright. So now we are showing a basic thing on the page. Hallelujah this is amazing this is the coolest thing we've ever built. How are we doing on time? We got about forty five minutes left. All right so we have a piece of content now we want to show variations of that content for an actual visitor. So how are we going to achieve this sort of thing? And, you know, if we boil this down to the basics, we've got a page. We're going to have variations of page content. Yeah. We could call this personalizations or page variations. That's gonna be linked to our segments, and this is not right. I do have a PhD in drawing arrows, just not in Figma. This is still a challenge for me. Alright. So let's create this and I think what we can do is hijack some of the functionality that already exists in Directus for this sort of thing. Going off label here, off brand. So, we will go into our data model. What I'm gonna do, I'm just gonna create a page variations. Yeah. Naming stuff is probably the hardest challenge in development. You know, we could call this personalizations, but that might make somebody mad. Yeah. I'll just do created at, created by. I don't necessarily need this information. These are just helpers that you can add. Directus, like, prefix or, like, sets these up for you. So whenever, you save this item or create a new item, it obviously records the user, etcetera. Just some shortcuts. Alright. So we have page variations. We've got two different collections here. Great. How are we gonna tie these things together? Now, what I can do and what I'm gonna steal is our translations feature. So if we go to the docs actually, let's go to the new docs. These things are in beta as of now. We're gonna look for translations. Great. We have the ability to translate content and manage all those translations for you. And if I open this in a new tab, we get, like, this beautiful side by side interface where I could see, okay, here's English versus Spanish or French. And this is like, when we boil it down, like, when I think about it, I'm just now coming to me through my head here, is it basically, translation is a form of personalization anyway. So why not leverage this existing structure? You know, short of, like, the icons and maybe some of the other stuff that is baked in there, I I think it should work. So I'm also going to like, normally, if we were doing translations, you would create, like, a languages collection where you would store all the languages that you wanna translate your content into. In this case, what we're gonna do is just use, I'm gonna create a new collection. I'm gonna call it segments. You know, we could manually generate the IDs for these or we could use, like, a generated UUID. You know, if I was trying to scale this out, I would definitely be using, like, generated UUIDs, but let's just call this key we're gonna manually enter a string for this. So whenever we create a new segment, we're gonna manually enter in the key. That'll help us keep things a little bit clearer. Updated at updated by just to keep track. And do we want this segment to be active or not? You know, maybe we set that up on a a Boolean toggle. Alright. So we got a key for it. Maybe we want, like, a proper name. So we add a name field and drag this down. And what I'm doing here is basically just configuring the actual form that we're gonna use to set this stuff up. Do we have a segment? Do we have a category? Yep. There we go. Yeah. Again, naming things is hard. Let's add some icons. Just the designer OCD for me kicking in. Doc. Yep. There we go. Edit document. And I'm just gonna like actually hide these page variations. So now we have pages, we have segments. Let's create a new segment. Right, I want to speak to developers. We could say these are for developers, that's our nice name. Directus is also helpful for content editors, so I'll do content editors. Yeah. I wish I was doing this live so I could say, hey, hyphenate or underscore, but let's just go with a hyphen. Fine. Fine. Fine. You know, I could keep adding these as much as I want. Right? So now we've got some segments, we've got some pages. We wanna generate these variations. So again, we're gonna reach for the Directus translations interface. And I hope this works out like I think it will or like I hope it will. You can see that we're pre setting the languages collection. Directus is smart enough to know, like, when you're doing translations, you need languages, we can create that for you. But what I'm gonna do here, I'm gonna open up advanced field mode instead, and I'm gonna go in and I'm just gonna do all of this mapping myself. So we try to be smart and create junction collections and everything for you, but you can take total control of this as well. So here, this is the collection. So we're gonna call this page variations. I'm just gonna use page and we're gonna call this the segment and the collection that we're targeting is segments. So, I'm gonna set these all up to cascade, so if we delete a page variation or deselect one, we delete that content because we're not going to use these variations across different collections or pages. Alright, so the language indicator field. Now normally this is what shows here right up here. Right? Just the indicator of what this is. We could use the key. We could use the name. Let's use the name. Language direction. We're gonna ignore that. Default language is the primary key. Let's let's say I wanna default to developers. And we're gonna start this split. So we'll start open like this. Great. Okay. So now we create this, and we could show related values. You know, you can even preview the translation content. And I already see one thing that's going to bug the crap out of me as I called it translations. I forgot to rename this. Alright. So we're gonna call this variations. So it's not translations, it's variations. We're just piggybacking off of functionality. Alright. Pages. Cool. This is gonna be the what? Key? No. Segment. We're gonna call that segment. That's the segment. The collection is segment. And I gotta set up this nice cascade again. Okay. Go through the exact same process all over again. Love shooting myself in the foot Just to make sure we have variations. Is variations spelled correctly? At this point, we don't know. Alright. So I probably goofed something up already. Did I? Did I not? Page variations. Page variations. Page page is not found. Oh. Yep. Alright. So love it when I goof up. Cause more work for myself. We're just gonna start this process all over. Alright. Try it one more time. Page, variations, Generate the UID. I'm not even gonna go for that. At this point, we're gonna say translations interface. Great. This is gonna be segments. We're going to use the segment name. We're gonna start with the split open view. And let's control this. Right? Page variations. This is gonna be page. Segment. Delete. Alright. So definitely don't do what I have done. Let's try this. See if that works. Doesn't have field page and doesn't have a relationship. And what in the world field page in collections doesn't have a relationship? Why doesn't it it should have a relationship. Pages, segments, translations, start open. Why doesn't this have a relationship? Well, let's just see what happens when we do this in orally. Translations already has a relationship. Alright. So we can see here by adding this we get a languages and then we get a pages translations, there's a pages ID, there's an ID. This should work out okay. I don't know what is going on. Maybe I didn't refresh the data model. Key name, segments, page variations. Why am I not getting this to work correctly? Let's just take a look at our database and see what's happening. 53Edit. 5 3 2, test, Connect. Alright. So now we're behind the scenes into the database trying to figure out what I goofed up to begin with and it looks like there's some relationships here that got left over that didn't get fixed that are causing problems. Alright. So now with that out of the way, let's try this again. Page variations, we're gonna generate an ID for those. I'm just gonna skip all the fanciness. And now I'm gonna go in and do translations. We're gonna call this variations. Instead of languages we're going to use segments, we're going to use the name of that. We are then going to set up to use page variations, page segments, segment. Okay. Now is that going to work out how we want fingers crossed. Let's log in and see what have we got. Okay. So now I get this side by side interface of content editors and developers. Right? But I don't see anything inside the actual form. So inside this junction table is where we're gonna go in and create our content that is actually going to vary. So in this case, it's just basically going to be our headline and our content. And I can quickly duplicate these. So we'll go to page variations. I'm gonna say copy headline. We're gonna copy the content. Just make sure you change that copy that we prepend at the end so that you don't, you know, get confused. But with this information set up, now I can get an interface that looks like this where we can say, hey. Great. Now we've got developers. We've got content editors. Cool. And I can be able to store different contexts here. Context switching sucks. Great. That's what we'll change the default headline to. We're gonna change this for devs, Context switching sucks for content editors. This content is for editors. This content is for devs. Alright. So now thinking through our mental model, we've got our default content. And then if we identify a segment, like if the visitor falls into the content editor segment or they fall into the developer segment, we're going to show them different content. Now, what is this actually going to look like? So if I go and like we look at the API now, got my API request, you could see those variations. One of the beauty, beautiful things of Directus is the ability to use the rest API in a GraphQL like manner. So I could do something like this where I just say, hey. Give me all of the root level fields and also give me all the fields within our variations. So now I can see here's my variations. The segment here is developers. The, segment here is content editors and presumably on the Nuxt side of it. And, you know, I could rig something up via the API to do this swap for me, but depending on how I'm rendering my site, you know, let's say I'm statically generating the site, if I want to still offer this, I've got to pre generate the payload, you know, pre generate our JSON and store that so I can use it when rendering it. So we're gonna try to do this on the client side. Inside our page level data here, right, we're still getting the same result. And if I add the data back, you know, now we should be seeing our variations, but hey, lo and behold we're not because again we need to go back into our access policies and set that up. So we're going to allow read access for our page variations and for our segments just so we could see those things. You know, when we're going to production, we'd probably make sure there's some kind of status on these so that, you know we're only showing published pages etcetera, but outside the scope. So now I can see my variations. We're gonna go here and let's just specify our fields, right. It's not content, it is variations of this content. And now we could see that. Great. Alright. So now we need to translate this content or personalize it in this case. Right? How do we assign a segment? You know, like, writing a rules engine here would be, kinda challenging with the twenty seven minutes we have left, I would say. But, you know, at the core, we we've gotta have some way to assign the segment. What I'm going to do I I think we're just gonna use a cookie, for this. So Nuxt has a use cookie composable. Let's just call this the segment key. We're gonna use cookie or you call that segment. Great. And I'm just gonna make this stupid simple. We'll add a div. We'll flex these, add some gap, make sure that's in the middle, etcetera. And then we're gonna add some buttons. I've got the Nuxt UI library in here. Segment key equals developers. No. We're not gonna do designers, but, we're gonna do content editors. Content editor. And then maybe we just add I am a great. Alright. Maybe we wrap this whole thing in. I think it's a u container from the Nuxt UI library. Okay. Yep. So we get a little bit of spacing there, And we can add, just some space between these space y eight. Okay. Great. And this is probably all part of the same section. Okay. So I am a developer. I am a content editor. I am a developer's. Great. Again, naming is crucial here. But basically, like, what I should be able to see now if I go into, like, my cookie storage, I can see that segment as well. Right? So based on this, I'm tagging that person. You know, and we're just explicitly asking. But, you know, we could set up, like, some kind of client side rule engine or, you know, you could potentially do this on the server side as well. Whatever pages you visit, weight those against each other and assign a, you know, if I've got five articles for developers and you look at three of those, then I'm probably safe to say that you're a developer, versus, you know, reading the content editor pages. Alright. So how do we actually translate this? Right? We probably want, like, a helper function that takes our page data and then evaluates the segment and then, you know, spits out the proper content. So let's just see how awesome AI has become here and see if this is actually worthwhile or I should just probably write it out. But, this wouldn't be fun if we weren't using something like cursor and, you know, something super opinionated or where everybody has an opinion about AI at this moment. Here we go. Write a let's just describe this. Right? Personalize the page content based on the segment key. Where is this gonna come up with? Personalize content, page value dot variations. I okay. You know, maybe this would come back. Let's see. Helper function to get the personalized content. Page variations return page dot that's not really content. Right? We just wanna return yeah. Okay. So I'm probably not prompting right. Let's see what we can do. Take in the page and the segment key and return a merged page with the proper variation based on the segment key. That's better, still not great. Right? You would probably want, like, a personalized function where it, just basically takes in, the page content and the different segments and the current segment and returns that. But nevertheless we got our personalized page or we should, right. Great. We can see our variations, we can see the content. If I check-in view dev tools, we go to our slug page. Do we have the personalized page? Great. That doesn't really Context switching sucks. No. But that doesn't look right to me. Right? So we do personalized page dot headline, personalized page dot content. Context switching sucks. I am currently in the developer side of things, so it should be showing should be toggling this. Right? So there's something wrong here. Page dot value dot variation. Yeah. So, basically, what we wanna do is loop through the trying to to do this the AI way and not get frustrated. Loop through the page variations and merge them with the page based on the segment key. Still showing the same thing. Personalized page. Yeah. Okay. Yeah. Alright. So this is the final page. Let's define that. The final page. Okay. Then we are going to is this getting smarter now? For constant of variations, if segment dot key, final page, return final page. What does that do? Why is it why is it still not doing what we want? Page.valuepage.value.variations.developers. Oh, yeah. That's why. Basically, we need to use, like, a file. Constant variations equals variations. Selected Constant variation is going to be we basically need to look through this array. Variations dot find. We'll look for the variation. And there we go. Okay. Yeah. So we're getting that array. That's the problem. So now if I remove this blah blah blah page, Now I have personalized content for folks. Amazing. This is the personalized content engine, website personalization engine. Badabing badaboom. This is beautiful. Right? So now I can define all of these variations within our actual CMS. Like here's the segments that we want to create content for. You know, I could potentially set up some type of rules engine for this. And then, you know, on our individual pages, then I can go through and define this content. Now is this a real world scenario? Probably not. You would take this and use something like our mini to any builder where you're building these dynamic pages and within each section you have personalized content for that. But at the very least, hey, this is now we've got, a pretty robust I won't say robust. This is not robust at all. But it is a a pretty it's a start to a personalization engine basically. That's it. That's all. Now where do we go from here? Right? I this would be extracted out into a, like, a helper function, so that, like, each page or each block, we would we would call this function, return the content if there is a variation, like, if we have a segment key. Yeah. And we could even go as far as, like, creating a rule engine for this. So let's just continue to beat on AI, which is, honestly not been great so far for this. Let's say extract this out to out to a helper function that is more robust, maybe. I don't know. Let's see. I'm gonna take a sip of coffee while it's trying to choke that down. The auto completions are not great, but here we go. Let's see what it's come up with. Personalization. This looks to be a composable. Use personalized page. Here's the page. Segment key. Update the page component. Here's how we're gonna use that. Personalize page. Use a page. There is a segment key. Okay. AI. I will bite. Alright. App utils. Personalization. And I don't know why it's using the is there let's see, like, an actual it's using the composable format for view, but import type maybe rest. Return computed. I mean, to me, this is a I guess this is a this is a composable. I'm not sure why it's doing that. So we'll stick that there. This is use personalization. This is the name of it. Use personalization. Again, probably not a strong choice for, like a composable here because this is probably something that you might want to extract out and use, like, across different projects. Use personalization. Use personalization. Okay. Cursor. Okay. Now that you use this helper, can we have it apply this automatically? Yes. But knowing Nuxt, we should automatically use this. Use personalization. There's our page content. There's the segment key. Does this still get us what we want? So far, looks like it does. Right? And the nice thing here is because we're using a cookie, and even though, like, this would be server side rendered, I'm still getting the content that we want. Right? Because it's automatically running a this cookie first before we actually display the content. Cool. Now let's do we have time? What kind of time do we have here? We got fifteen minutes left. Where do we take this further? Right? Where would we go from here? How do we set this up into, like, something like blocks, right, where we want to build a page out of the blocks? So for that, we can use the mini to any builder inside Directus. We've got this mini to any builder. We're gonna call this blocks. We don't have any related items yet. So first thing we're gonna do here is create a hero block. Call it block hero because I like to be obtuse. Alright. The hero is gonna have a headline. It's gonna have a description. Great. Okay. And then we have a block CTA. Cool. And that is going to have a call out and a button text. Great. Okay. Now we're gonna put those together with the many to any builder. These are gonna be our page blocks. We're gonna say block CTA, block hero. Great. Basically, this many to any relationship, it it doesn't necessarily exist, inside, like, standard SQL. Right? So Directus is doing a bit of API magic here. Basically, what what's happening, and we'll just hide, like, what we've got already. Alright. Hide this field. Don't destroy. Just hide. So we can see what's happening. We've created these separate collections, and then there's a junction collection here that is storing the page's ID and then we're storing the ID for the item. And then we also have a string for the collection that we're pulling this from. So it's, you know, basically some API magic to string these things together. But now if I go in and I update the page, we could see we've got a CTA that we'd say, this is you should try Directus. Do it now. Great. There's our CTA. There's our hero. Brian is cool. Well, he's not. There's our description. Alright. And let's hit save and stay. Let's just look at our page content now that we're getting back. Right. We're showing two different blocks here, but again, we need to go in and set our permissions. Directus keeps you secure by default. So we're gonna add just read permissions for all this. Again, for production, we wouldn't do this, but can we get to personalized blocks in twelve minutes or less? I need to get my ass in gear. Alright. Alright so we're gonna go back to our slug here. We're gonna change this up where we have our blocks. And actually we could use like a object syntax as well. Blocks, we're going to have here. We're going to get the we could get that. And then we're probably also going to have, like, variations within the blocks themselves. Well, this is getting messy. Right? Okay. So now let's just go back to the page. Variations blocks. I don't wanna do variations. We wanna do blocks. Alright. I'm gonna pull this back in. For our page render, we're just gonna have to pull that out. And now we could see we got, like, some data for each block. Right? Blocks, items, we're gonna wanna grab the item for each block. I don't need it. Fields comma. What do we got here? Okay. Well, yes, no, maybe so. Something is off. We got one too many. How nested did we go here? This one needs okay. Did we get it right this time? We still did not get this right. Direct as requests. AI is totally screwing this up, and I've had too much coffee to fix this properly. Alright. So we got the block. We're within the block, we're going to have, the items items, and then we're gonna grab the variations. Where okay. Backup. Backup. Backup. Love all the formatting. Items. Oh, yeah. This is not going well for me, is it? Fields. We're gonna grab the collection field. Great. Okay. Now, we got item. If I change this to item, we should see the item text. Great. And then also we're gonna have variations on that same item. Cool. Alright. But we don't have those yet. Alright. So let's go in and add those. So now the same way that I created that translation function on, let's say, the page level, we're gonna go through and we're gonna do this speed run style in less than eight minutes. We're gonna call this variations. Great. This is gonna be segments. This is the collection there. And what are we gonna call this? We're gonna call this the segments. Block hero ID, block hero variations. Great. Primary key is gonna be the or the primary indicator is gonna be that. We'll set this up. Alright, so now on our block hero I should be able to set up like a variation here except I don't have that content yet. So we'll go to our block CTA. Oh, no. Block hero. We're gonna copy that headline and description down into the next collection, right, to for our variations so that we can have that specific content. Block hero variations, duplicate. Great. Now, I go in, we've got our block hero I'm gonna stick up at the top. We have a headline for Brian's cool to editors. Brian is cool to developers. Great. Okay. And we're gonna create a hero component. View, create a hero component with Tailwind that has two props, headline and description. Let's see what it comes up with. Outsourcing the work here. Six minutes. You know, we could declare this one good, but, yeah, let's make it fun. Alright. We got the hero component. Okay. So now within the like this page section, I guess, Let's go and call that hero component with the page headline and description. Context switching sucks. Do we have a oh, no. We don't have that at the page level. Right? We're gonna loop through all of our blocks. So the, let's do template v four page blocks within the blocks. If the block collection equals the hero to block hero, basically, block underscore hero, that's the name of our collection, we're going to render that out. But now we also need to use the personalized page, page dot blocks, personalized page dot blocks. But low and behold, we're still not changing that content based on that because our personalization is what? At the page level. Right? So AI, help us in our moment of need. Five minutes left. Change this to well, accept a what a to accept content and recursively merge if there is a variations key within The array within the objects. There's a variations key. Date. And task. Objects. Let's see what this comes with back. What if just curious. Alright. Merge variations, current segment, matching variations. Is this to me, like, obviously highlights the dangers of AI? I am on a crunch here. Right? I don't have a clue. I don't have enough time to, like, figure out if this is actually going to do what I wanted to do anyway. So, you know, great that you can I I mean, I love the fact that I can just, like, quickly POC this, but, you know, it's like a what are we coming back with here? Personalized page. Let's just see what it comes back with. Blocks zero. So, again, like, is yeah. See, that's goofed up. Oh, and I'm not even giving the personalization content, though, am I? Why is that? I should be fetching it here. Items, personalizations. We gotta go back into our permissions, block hero variations, access. Save. Are we now at least getting that? Yes. Okay. So AI did save the day here, basically. You know, before I shipped any of this, I would need to go through and, like, study this in detail to figure out is this actually doing doing right. It appears to be doing right, but are there gonna be, like, dangerous side effects? But here I could see Brian is cool to developers. You know, maybe we go back in and within this, do we have a does don't like him? Alright. No. He's not. Neither do content editors. So there is that. You know, the one thing I noticed is we should have, like, a fallback and it should take that fallback into account. So it's not doing, like, a deep merge, but now we could see that content. We could clean this up just to show it out. And boom. So now we've got, like, page block level data where I could go through and build a page and have segments and personalize that data, for each individual segment. So I'm I'm gonna call that a win. Right? We got one minute fifty six seconds left. Would I have been able to achieve this without cursor? %. Would it have taken me more than an hour? One hundred percent. Is this still cool and fancy? Probably not. This is the start to something incredibly cool where, you know, I could go in and on the client side have some type of tracking and some type of rule engine that just, like, consistently con like, creates these different segments or groups of visitor into segments. And if you keep that on the client side, it could be privacy friendly as well. But I hope this was an interesting episode, an exciting episode for you. I've had a good time. I'm probably gonna figure out a way for us to leverage this somehow in the future. That's it for this episode of 100 apps, one hundred hours. Thanks for joining me. I do roll like the success message. See you guys next time.","published",[135],{"people_id":136},{"id":137,"first_name":138,"last_name":139,"avatar":140,"bio":141,"links":142},"791e1503-1d88-463d-9347-0b9192933576","Bryant","Gillespie","9013afc8-e8d7-4182-9b18-44db08117bb9","Developer Advocate at Directus",[143,146],{"url":144,"service":145},"https://directus.io/team/bryant-gillespie","website",{"service":147,"url":148},"github","https://github.com/bryantgillespie",[],{"id":151,"number":152,"year":153,"episodes":154,"show":164},"d6b229fe-38fc-495b-ba0c-c574ebfea38f",3,"2025",[155,156,157,158,159,160,122,161,162,163],"ec88bef1-fffd-43eb-9d93-3123dc381b97","ab550907-1a5a-4fc0-8208-764465a1864f","97806b9d-063a-447f-9dca-617217ae5879","2a920d49-91e0-4237-bbd7-12e1181ad02a","fee34ecb-a4b6-4218-ac25-d15052c7604d","1ac7647d-0f09-4698-8ca0-e4448a4fe0e9","09a64df3-79d6-4ede-a394-30a2499c5fee","bfb4b33c-1494-4111-8f33-d59bfde9df65","36839053-13e7-4eb6-8e73-efb73bf61fb1",{"title":165,"tile":166},"100 Apps In 100 Hours","fb0f9d45-be21-4634-94d4-2ef1cc5146f2",{"title":8,"meta_description":8},{"id":169,"slug":170,"season":171,"vimeo_id":172,"description":173,"tile":174,"length":175,"resources":8,"people":8,"episode_number":176,"published":177,"title":178,"video_transcript_html":179,"video_transcript_text":180,"content":8,"seo":181,"status":133,"episode_people":182,"recommendations":186},"2c5b1e3e-c12f-4dc5-aef8-52082d41c192","new-years-resolution-bingo-generator","9095d401-66bc-416a-997b-f038365ce5ed","1163992739","Bryant is joined by Marc and Alvaro with the goal of building a goal bingo app in just 10 minutes using the MCP.","1e2aef3d-30ec-4672-be1e-f439efe7e045",18,1,"2026-03-02","New Years Resolution Bingo Generator","\u003Cp>Speaker 0: Alright, viewers. Welcome to, yet another episode of 100 app, 100 I don't know. No. No. No.\u003C/p>\u003Cp>One app in ten minutes. Right? We are doing the remix version today where we have ten minutes to build and plan plan and build an amazing app clone, crazy suggestion, and I have no idea what we're gonna do. So the rules. Right?\u003C/p>\u003Cp>Ten minutes to plan and build. No more, no less. How we're gonna do that? We are going to use some, amazing tools that we have built into Directus. And then, rule number two, the anti rule.\u003C/p>\u003Cp>Use whatever you've got at your disposal. Today, I've got two awesome dudes at my disposal, mister Alvaro and Mark from our team here at Directus. No strangers to the VUE community. Welcome to the show, gents.\u003C/p>\u003Cp>Speaker 1: Thanks for having us, Bryant.\u003C/p>\u003Cp>Speaker 2: Thank you very much for the nice intro. Happy, to be here.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. No. I'm super excited. Have you guys given any thought to what we're what we're gonna build?\u003C/p>\u003Cp>Speaker 2: I think Mark has some idea though.\u003C/p>\u003Cp>Speaker 1: Yeah. So yesterday, we talked a little bit. I talked with Ava what we could build. And, I don't know if if I showed it to you, Brian, but on my website, I have a, instead of new year for solutions, I have new year's bingo cards. So you have five by five grid of stuff I want to do in the year.\u003C/p>\u003Cp>And if I get at least one in a row, so diagonal or horizontal or vertical, I already have bingo and it's a success. So I don't have to do all of them. And if you go to mark.dev/bingo\u003C/p>\u003Cp>Speaker 0: Okay. Let's check it out, guys.\u003C/p>\u003Cp>Speaker 1: You can it's still since it's just well, now February, not a lot has happened there. But\u003C/p>\u003Cp>Speaker 2: But it's a it's a really nice way to actually do some of the New Year's resolution. I always get the press at the end of the year like I have done, like, a quarter of them.\u003C/p>\u003Cp>Speaker 0: Yeah. I love it. Alright. So hey. This is neat, man.\u003C/p>\u003Cp>I I miss Yep.\u003C/p>\u003Cp>Speaker 1: And each of them can be either, like, you did it or you didn't do it or it can be progressive. Like, read six books and you are, like, one books, two books, three books in. And I think I also have, like, subtasks if we can make that work. Like, if one one, let's say, one bingo item has a few sub items as well. Like, don't have an example now, but that would also\u003C/p>\u003Cp>Speaker 0: be cool. Gotcha. Okay. New Year's resolution. Bingo card generator.\u003C/p>\u003Cp>Alright. That's what we're doing. This is gonna be amazing. This should be fun. What color are you guys feeling?\u003C/p>\u003Cp>Purple, pink?\u003C/p>\u003Cp>Speaker 2: I go I go purple. Blue. Oh, purple.\u003C/p>\u003Cp>Speaker 0: Purple. There we go.\u003C/p>\u003Cp>Speaker 1: Direct is purple. Nice.\u003C/p>\u003Cp>Speaker 0: Direct is purple. Alright, guys. Alright. So I'm sure you've seen the show. We're gonna start the clock.\u003C/p>\u003Cp>We got ten minutes to plan and build this thing. Let's do it. Alright. So, the first thing I usually do here is cover requirements. Right?\u003C/p>\u003Cp>So what are the requirements we need out of this? Right? We need to generate bingo cards. Like, what do you what were you calling those?\u003C/p>\u003Cp>Speaker 1: Like, items probably or\u003C/p>\u003Cp>Speaker 0: Okay.\u003C/p>\u003Cp>Speaker 1: Goals. Yeah.\u003C/p>\u003Cp>Speaker 2: Items. Yeah. Like a grid of of items.\u003C/p>\u003Cp>Speaker 1: Mhmm. Yeah.\u003C/p>\u003Cp>Speaker 0: Alright. So we got some goals. Those are what kinda fields are you tracking on those? Just the name of the goal?\u003C/p>\u003Cp>Speaker 1: Yeah. A name description and then the status.\u003C/p>\u003Cp>Speaker 0: Status of the goal. Progress. Progress. Is it are you status and progress interchangeable?\u003C/p>\u003Cp>Speaker 1: Yeah. I guess if you like the if the progress is under percent, the status\u003C/p>\u003Cp>Speaker 0: Okay. Yeah. Yeah. Yeah. Got it.\u003C/p>\u003Cp>Okay. And then we've got we've got goals. You've got what? Items underneath the goals? What do you do?\u003C/p>\u003Cp>We want, like, subtasks, like, if it's\u003C/p>\u003Cp>Speaker 1: You you can have subtasks. Let's see if there's one that has subtasks. I don't remember.\u003C/p>\u003Cp>Speaker 0: Tasks. It's called test. Alright. So the tasks would\u003C/p>\u003Cp>Speaker 1: play into into progress as well. I guess.\u003C/p>\u003Cp>Speaker 0: Into goal. And then the task completed increases progress. Cool. Alright. And task needs what?\u003C/p>\u003Cp>Name? Description? No. Just name? Date date probably.\u003C/p>\u003Cp>Speaker 1: Maybe the, the item can have a a completed ad. Yeah. They've completed as well for the task for the, item on top. Yeah.\u003C/p>\u003Cp>Speaker 0: Alright. And then we we wanna try to get a front end set up for this as well. Yeah. Alright. And we need a front end to display the bingo cards.\u003C/p>\u003Cp>Alright. This could be a stretch in seven minutes now. Let's see how we do. Alright. So what are we using today?\u003C/p>\u003Cp>Right? We've got a blank directus project. We've got Claude Code over here. Let's dive into it. Alright?\u003C/p>\u003Cp>I'm going to I'm not sure what you guys have been coding with. I've been using Super Whisper. I dig it. Hi. How are you doing?\u003C/p>\u003Cp>Alright, guys. We are building a New Year's resolution bingo card generator. I'm gonna copy and paste the data model that we want. You have access to a direct assistance. I want you to create our schema for that.\u003C/p>\u003Cp>We're also going to be building a front end to display the bingo cards. Let me know what questions you have. Let's create a plan. Alright. So this is crunching the transcript for that right now.\u003C/p>\u003Cp>Cool. There we go. I'll just, copy and paste this. Hopefully, we'll get some something good out of it. And we're gonna ask Claude Code to plan.\u003C/p>\u003Cp>Alright. So now we've got the schema. So we've got the direct us MCP connected to this thing. And I I think you guys have had a chance to try this out already. Right?\u003C/p>\u003Cp>Speaker 1: Yeah. I think Avro has. I haven't.\u003C/p>\u003Cp>Speaker 2: Yeah. Play with it in the morning. It's gonna create the collections, the schemas for you.\u003C/p>\u003Cp>Speaker 0: Yeah. Alright. So it's got a fresh direction. No custom collections. Alright.\u003C/p>\u003Cp>And I can zoom in just a little bit more so we could see this. What is the plan? And this is probably one of my favorite parts about this thing where it will prompt you for questions. Direct us flow, that's what we wanna do there. Vanilla JS.\u003C/p>\u003Cp>Yeah. That's what we'll do. What do you guys think? Five by five grid? Four by four?\u003C/p>\u003Cp>Speaker 1: We we can do also four by four so we don't have to come up with 25 things.\u003C/p>\u003Cp>Speaker 0: Amazing. Right? We got five minutes left.\u003C/p>\u003Cp>Speaker 2: You you can say to the MCP, hey, cloud, get, your twenty twenty six, bingo.\u003C/p>\u003Cp>Speaker 1: Oh, that was cool.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah. Alright. Public read, that's fine. Anyone can view those.\u003C/p>\u003Cp>Cool. Alright. And now, hopefully, this thing should have a plan.\u003C/p>\u003Cp>Speaker 2: I wonder which resolutions Cloud Code could have.\u003C/p>\u003Cp>Speaker 0: I don't know. Let's see. We'll we'll spin that up in an in a\u003C/p>\u003Cp>Speaker 1: new find out.\u003C/p>\u003Cp>Speaker 0: Alright. Cool. Right? Here's the direct to schema. There's our it's gonna create a flow.\u003C/p>\u003Cp>It's gonna create the front end. Sounds good. Let's let's roll with it. Right? I don't know what we're actually doing other than just talking this through here, but, I'm curious to see just how this thing works.\u003C/p>\u003Cp>I've you know, of course, like, spent a ton of time testing and building the MCP, but I've not spent a ton of time using it with, the latest Opus four five model. Alright. So it is checked the existing schema. Now we are it should start implementing. Yes.\u003C/p>\u003Cp>Please just start jamming on here. And if I refresh, now we should see some collections start to come in to the direct instance. We should see some collections. Start to come into the direct assistance. There we go.\u003C/p>\u003Cp>Okay. Alright. Oh, nice. I was just worried that I did something wrong. So we got our goals.\u003C/p>\u003Cp>We got our tasks. Amazing. Right? Now I could go in. We could potentially create some new ones if we need.\u003C/p>\u003Cp>One of the things that I like about this is it, like it seems like the anthropic models do a better job of, like, actually putting together a cohesive form than than, like, the OpenAI wants. So it's going through creating relations and fields. Alright, guys. So in this other one, create, some New Year's resolutions for yourself, Claude. Alright.\u003C/p>\u003Cp>You guys have any more guidance for this thing?\u003C/p>\u003Cp>Speaker 1: They should follow this the smart principle, probably.\u003C/p>\u003Cp>Speaker 0: Follow the smart principle. What's the smart principle?\u003C/p>\u003Cp>Speaker 1: Now you got me. So it's like measurable, achievable.\u003C/p>\u003Cp>Speaker 0: I know what you're talking about now. Yeah. The smart goals. Yeah. Yeah.\u003C/p>\u003Cp>And include the add them to the goals and tasks inside.\u003C/p>\u003Cp>Speaker 1: For the for me, the most important one is always measurable. You have to be able to measure what you do. If not, you lose yourself.\u003C/p>\u003Cp>Speaker 2: You lose yourself. That's so funny. It's okay.\u003C/p>\u003Cp>Speaker 0: That is very poetic. I love it, man. Alright. So it looks like okay. Yeah.\u003C/p>\u003Cp>I was just making sure we've got the relationship created correctly there. Alright. It is going to so we got two claws going. We got two minutes here. Let's see.\u003C/p>\u003Cp>I can see their goals and tasks.\u003C/p>\u003Cp>Speaker 1: Alright.\u003C/p>\u003Cp>Speaker 2: This is the next development, man. Right? Resting to\u003C/p>\u003Cp>Speaker 0: the next development. Yeah. This thing is going to yeah. I need to enter YOLO mode so we can actually, have this thing not stop to do these calls. But, behind the scenes, right, it is building this progress calculator flow.\u003C/p>\u003Cp>And and flows are\u003C/p>\u003Cp>Speaker 2: Yeah.\u003C/p>\u003Cp>Speaker 0: A a nice piece of functionality. It can be a little time consuming to set up, like, complex flows via the UI. So having direct us put these together, is, yeah, definitely time saving. Right? That's probably, like, five, ten steps there.\u003C/p>\u003Cp>Yes. Create those items. Alright. Let's see what we've got. Are we gonna get to the front end for this thing?\u003C/p>\u003Cp>I don't know if we are, man. I should've had Bryant. Should've had, Claude do that first. It's connecting the operations. Claude,\u003C/p>\u003Cp>Speaker 1: you need\u003C/p>\u003Cp>Speaker 0: to go faster, my friend. Alright. So what are the what are the goals that Claude set for itself? This should be interesting.\u003C/p>\u003Cp>Speaker 2: Put that description, Steven.\u003C/p>\u003Cp>Speaker 0: I'll reduce average response latency by 20%, Achieve 95% task completion rate without clarification. What an interesting goal. Here's the the individual tasks. And, I'll\u003C/p>\u003Cp>Speaker 2: And that was the end there. The front end.\u003C/p>\u003Cp>Speaker 0: Now it's doing it. No. Let me open this test project up. Is it going to have enough time? Yes.\u003C/p>\u003Cp>Proceed. New Year's resolution. Bingo. Oh, no. We ran out of time.\u003C/p>\u003Cp>So close. MCP connection should have access. No need to set up. I think, you know, this was so close, guys. I'm just going to it's against the rules, but you know what?\u003C/p>\u003Cp>We can make up our own rules here. I am just going to give access here to see and see if this will actually finish. Of course. There it is, man. The API permissions got us.\u003C/p>\u003Cp>We could see the bingo card here. There's the individual tasks. Ten minutes, full working back end with permissions, so close to a working front end. It did\u003C/p>\u003Cp>Speaker 1: pretty cool.\u003C/p>\u003Cp>Speaker 0: This is this is very cool. Right?\u003C/p>\u003Cp>Speaker 2: Even even with the subtask because that that wasn't an extra thing. Like, now it's the only iteration. Like, put the progress in the front end and\u003C/p>\u003Cp>Speaker 0: Yeah. I'm very curious to see. Right? It's already got, it looks like it maybe did it miss some of the flows? Right?\u003C/p>\u003Cp>So the thing to take away here is obviously, like, you could build incredibly quickly with Directus and MCP, and this is not loading, probably because of my computer. Just hates running all these Docker containers locally. What is going on?\u003C/p>\u003Cp>Speaker 2: How many do you have?\u003C/p>\u003Cp>Speaker 0: There's probably, like, five or 10 running at the moment, like, different instances. And I'm sure if I, like, killed the camera, it would probably stop doing this. I don't I don't know what's going on here. Local host, 8055. I at least wanna end this episode on a high note and show something.\u003C/p>\u003Cp>Come on.\u003C/p>\u003Cp>Speaker 1: Are you you're, are you running open claw? That's\u003C/p>\u003Cp>Speaker 0: I'm not yet. No. No. Not yet. I did test that thing out.\u003C/p>\u003Cp>It's, it's definitely an interesting one. I'm not sure that it is, it's not all there yet. Alright. It it's my testing was, like, most things you're gonna have to oh, great. Now I'm locked out of the actual instance as well.\u003C/p>\u003Cp>Hey. What is going on? Local host. 8055. Yeah.\u003C/p>\u003Cp>My open claw testing was basically, is it you're just gonna have to invest a lot of time into it to get something amazing out of it.\u003C/p>\u003Cp>Speaker 2: You you saw it. Right? Yeah.\u003C/p>\u003Cp>Speaker 0: Yeah. Alright. So we can see the flows. Did they yeah. It actually connected the flow.\u003C/p>\u003Cp>So I'm just curious. Right. Just wanting to see. Right? Build a mastering five new programming frameworks.\u003C/p>\u003Cp>Let's say we completed this right now. Does this flow actually work? And So it it it\u003C/p>\u003Cp>Speaker 2: could increase the progress of the task of the goal.\u003C/p>\u003Cp>Speaker 0: It should. And, of course, doing a hard refresh here is not not a great idea. Alright. Well, gents, you know, I'm not sure whether to put a, like, a thumbs up stamp on this one. Thumbs up stamp.\u003C/p>\u003Cp>We can just do I think. Yeah. This was, I think we got most of the functionality\u003C/p>\u003Cp>Speaker 2: there. Good at dive.\u003C/p>\u003Cp>Speaker 0: We just didn't get, the front end all the way there.\u003C/p>\u003Cp>Speaker 1: Oh, Brian, you are lagging quite.\u003C/p>\u003Cp>Speaker 0: Of course, it did.\u003C/p>\u003Cp>Speaker 1: Is it done? I think you you get a a thumbs up, Brian, because it we got a working thing at the end, and you had the the grid showing everything with the progress. So I think you get a thumbs up.\u003C/p>\u003Cp>Speaker 0: Yeah. Alright, guys. My computer is struggling, so we are going to sign off for this episode. Mark Alvaro, I've heard a little rumor that there might be a podcast coming up, so I'm super excited for that. Thanks for joining me for this episode of one app in ten minutes.\u003C/p>","Alright, viewers. Welcome to, yet another episode of 100 app, 100 I don't know. No. No. No. One app in ten minutes. Right? We are doing the remix version today where we have ten minutes to build and plan plan and build an amazing app clone, crazy suggestion, and I have no idea what we're gonna do. So the rules. Right? Ten minutes to plan and build. No more, no less. How we're gonna do that? We are going to use some, amazing tools that we have built into Directus. And then, rule number two, the anti rule. Use whatever you've got at your disposal. Today, I've got two awesome dudes at my disposal, mister Alvaro and Mark from our team here at Directus. No strangers to the VUE community. Welcome to the show, gents. Thanks for having us, Bryant. Thank you very much for the nice intro. Happy, to be here. Yeah. Yeah. No. I'm super excited. Have you guys given any thought to what we're what we're gonna build? I think Mark has some idea though. Yeah. So yesterday, we talked a little bit. I talked with Ava what we could build. And, I don't know if if I showed it to you, Brian, but on my website, I have a, instead of new year for solutions, I have new year's bingo cards. So you have five by five grid of stuff I want to do in the year. And if I get at least one in a row, so diagonal or horizontal or vertical, I already have bingo and it's a success. So I don't have to do all of them. And if you go to mark.dev/bingo Okay. Let's check it out, guys. You can it's still since it's just well, now February, not a lot has happened there. But But it's a it's a really nice way to actually do some of the New Year's resolution. I always get the press at the end of the year like I have done, like, a quarter of them. Yeah. I love it. Alright. So hey. This is neat, man. I I miss Yep. And each of them can be either, like, you did it or you didn't do it or it can be progressive. Like, read six books and you are, like, one books, two books, three books in. And I think I also have, like, subtasks if we can make that work. Like, if one one, let's say, one bingo item has a few sub items as well. Like, don't have an example now, but that would also be cool. Gotcha. Okay. New Year's resolution. Bingo card generator. Alright. That's what we're doing. This is gonna be amazing. This should be fun. What color are you guys feeling? Purple, pink? I go I go purple. Blue. Oh, purple. Purple. There we go. Direct is purple. Nice. Direct is purple. Alright, guys. Alright. So I'm sure you've seen the show. We're gonna start the clock. We got ten minutes to plan and build this thing. Let's do it. Alright. So, the first thing I usually do here is cover requirements. Right? So what are the requirements we need out of this? Right? We need to generate bingo cards. Like, what do you what were you calling those? Like, items probably or Okay. Goals. Yeah. Items. Yeah. Like a grid of of items. Mhmm. Yeah. Alright. So we got some goals. Those are what kinda fields are you tracking on those? Just the name of the goal? Yeah. A name description and then the status. Status of the goal. Progress. Progress. Is it are you status and progress interchangeable? Yeah. I guess if you like the if the progress is under percent, the status Okay. Yeah. Yeah. Yeah. Got it. Okay. And then we've got we've got goals. You've got what? Items underneath the goals? What do you do? We want, like, subtasks, like, if it's You you can have subtasks. Let's see if there's one that has subtasks. I don't remember. Tasks. It's called test. Alright. So the tasks would play into into progress as well. I guess. Into goal. And then the task completed increases progress. Cool. Alright. And task needs what? Name? Description? No. Just name? Date date probably. Maybe the, the item can have a a completed ad. Yeah. They've completed as well for the task for the, item on top. Yeah. Alright. And then we we wanna try to get a front end set up for this as well. Yeah. Alright. And we need a front end to display the bingo cards. Alright. This could be a stretch in seven minutes now. Let's see how we do. Alright. So what are we using today? Right? We've got a blank directus project. We've got Claude Code over here. Let's dive into it. Alright? I'm going to I'm not sure what you guys have been coding with. I've been using Super Whisper. I dig it. Hi. How are you doing? Alright, guys. We are building a New Year's resolution bingo card generator. I'm gonna copy and paste the data model that we want. You have access to a direct assistance. I want you to create our schema for that. We're also going to be building a front end to display the bingo cards. Let me know what questions you have. Let's create a plan. Alright. So this is crunching the transcript for that right now. Cool. There we go. I'll just, copy and paste this. Hopefully, we'll get some something good out of it. And we're gonna ask Claude Code to plan. Alright. So now we've got the schema. So we've got the direct us MCP connected to this thing. And I I think you guys have had a chance to try this out already. Right? Yeah. I think Avro has. I haven't. Yeah. Play with it in the morning. It's gonna create the collections, the schemas for you. Yeah. Alright. So it's got a fresh direction. No custom collections. Alright. And I can zoom in just a little bit more so we could see this. What is the plan? And this is probably one of my favorite parts about this thing where it will prompt you for questions. Direct us flow, that's what we wanna do there. Vanilla JS. Yeah. That's what we'll do. What do you guys think? Five by five grid? Four by four? We we can do also four by four so we don't have to come up with 25 things. Amazing. Right? We got five minutes left. You you can say to the MCP, hey, cloud, get, your twenty twenty six, bingo. Oh, that was cool. Yeah. Yeah. Alright. Public read, that's fine. Anyone can view those. Cool. Alright. And now, hopefully, this thing should have a plan. I wonder which resolutions Cloud Code could have. I don't know. Let's see. We'll we'll spin that up in an in a new find out. Alright. Cool. Right? Here's the direct to schema. There's our it's gonna create a flow. It's gonna create the front end. Sounds good. Let's let's roll with it. Right? I don't know what we're actually doing other than just talking this through here, but, I'm curious to see just how this thing works. I've you know, of course, like, spent a ton of time testing and building the MCP, but I've not spent a ton of time using it with, the latest Opus four five model. Alright. So it is checked the existing schema. Now we are it should start implementing. Yes. Please just start jamming on here. And if I refresh, now we should see some collections start to come in to the direct instance. We should see some collections. Start to come into the direct assistance. There we go. Okay. Alright. Oh, nice. I was just worried that I did something wrong. So we got our goals. We got our tasks. Amazing. Right? Now I could go in. We could potentially create some new ones if we need. One of the things that I like about this is it, like it seems like the anthropic models do a better job of, like, actually putting together a cohesive form than than, like, the OpenAI wants. So it's going through creating relations and fields. Alright, guys. So in this other one, create, some New Year's resolutions for yourself, Claude. Alright. You guys have any more guidance for this thing? They should follow this the smart principle, probably. Follow the smart principle. What's the smart principle? Now you got me. So it's like measurable, achievable. I know what you're talking about now. Yeah. The smart goals. Yeah. Yeah. And include the add them to the goals and tasks inside. For the for me, the most important one is always measurable. You have to be able to measure what you do. If not, you lose yourself. You lose yourself. That's so funny. It's okay. That is very poetic. I love it, man. Alright. So it looks like okay. Yeah. I was just making sure we've got the relationship created correctly there. Alright. It is going to so we got two claws going. We got two minutes here. Let's see. I can see their goals and tasks. Alright. This is the next development, man. Right? Resting to the next development. Yeah. This thing is going to yeah. I need to enter YOLO mode so we can actually, have this thing not stop to do these calls. But, behind the scenes, right, it is building this progress calculator flow. And and flows are Yeah. A a nice piece of functionality. It can be a little time consuming to set up, like, complex flows via the UI. So having direct us put these together, is, yeah, definitely time saving. Right? That's probably, like, five, ten steps there. Yes. Create those items. Alright. Let's see what we've got. Are we gonna get to the front end for this thing? I don't know if we are, man. I should've had Bryant. Should've had, Claude do that first. It's connecting the operations. Claude, you need to go faster, my friend. Alright. So what are the what are the goals that Claude set for itself? This should be interesting. Put that description, Steven. I'll reduce average response latency by 20%, Achieve 95% task completion rate without clarification. What an interesting goal. Here's the the individual tasks. And, I'll And that was the end there. The front end. Now it's doing it. No. Let me open this test project up. Is it going to have enough time? Yes. Proceed. New Year's resolution. Bingo. Oh, no. We ran out of time. So close. MCP connection should have access. No need to set up. I think, you know, this was so close, guys. I'm just going to it's against the rules, but you know what? We can make up our own rules here. I am just going to give access here to see and see if this will actually finish. Of course. There it is, man. The API permissions got us. We could see the bingo card here. There's the individual tasks. Ten minutes, full working back end with permissions, so close to a working front end. It did pretty cool. This is this is very cool. Right? Even even with the subtask because that that wasn't an extra thing. Like, now it's the only iteration. Like, put the progress in the front end and Yeah. I'm very curious to see. Right? It's already got, it looks like it maybe did it miss some of the flows? Right? So the thing to take away here is obviously, like, you could build incredibly quickly with Directus and MCP, and this is not loading, probably because of my computer. Just hates running all these Docker containers locally. What is going on? How many do you have? There's probably, like, five or 10 running at the moment, like, different instances. And I'm sure if I, like, killed the camera, it would probably stop doing this. I don't I don't know what's going on here. Local host, 8055. I at least wanna end this episode on a high note and show something. Come on. Are you you're, are you running open claw? That's I'm not yet. No. No. Not yet. I did test that thing out. It's, it's definitely an interesting one. I'm not sure that it is, it's not all there yet. Alright. It it's my testing was, like, most things you're gonna have to oh, great. Now I'm locked out of the actual instance as well. Hey. What is going on? Local host. 8055. Yeah. My open claw testing was basically, is it you're just gonna have to invest a lot of time into it to get something amazing out of it. You you saw it. Right? Yeah. Yeah. Alright. So we can see the flows. Did they yeah. It actually connected the flow. So I'm just curious. Right. Just wanting to see. Right? Build a mastering five new programming frameworks. Let's say we completed this right now. Does this flow actually work? And So it it it could increase the progress of the task of the goal. It should. And, of course, doing a hard refresh here is not not a great idea. Alright. Well, gents, you know, I'm not sure whether to put a, like, a thumbs up stamp on this one. Thumbs up stamp. We can just do I think. Yeah. This was, I think we got most of the functionality there. Good at dive. We just didn't get, the front end all the way there. Oh, Brian, you are lagging quite. Of course, it did. Is it done? I think you you get a a thumbs up, Brian, because it we got a working thing at the end, and you had the the grid showing everything with the progress. So I think you get a thumbs up. Yeah. Alright, guys. My computer is struggling, so we are going to sign off for this episode. Mark Alvaro, I've heard a little rumor that there might be a podcast coming up, so I'm super excited for that. Thanks for joining me for this episode of one app in ten minutes.","0878a4f4-5836-4a78-b897-a8b4789c9e7c",[183,184,185],"851de303-b434-4092-8b15-fb6720b1e4c2","010213cf-c50a-4f60-b264-afb8fcf3e4b3","487b9dd3-5c6e-4894-bf34-a2bb1f6d2b64",[],{"reps":188},[189,245],{"name":190,"sdr":8,"link":191,"countries":192,"states":194},"John Daniels","https://meet.directus.io/meetings/john2144/john-contact-form-meeting",[193],"United States",[195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244],"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":246,"link":247,"countries":248},"Michelle Riber","https://meetings.hubspot.com/mriber",[249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,226,437,438],"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",1773850448110]