Today, we learn a new acronym "JFI" = Just Fiverr It. Join Bryant as he tries to build a clone of the popular online marketplace for freelancers in just 60 minutes.
Speaker 0: Hi, welcome back to another episode of 100 Apps 100 Hours. I'm your host, Brian Gillespie. And in this series, we will be rebuilding or building some of your suggested ideas, some of your favorite apps in 1 hour or less. There's only 2 rules. Number 1, we have 60 minutes to plan and build the application.
No more, no less. And the second rule is use whatever you have at your disposal. Kind of the anti rule, if you will. So today we are going to be working on a freelancer marketplace. Like some of these ideas we run with on the show, this one sounds kind of vague and generic, but if you take a look at, sites like Upwork or Fiverr is one of the ones that I have kind of just imagined for this.
Just got a listing of freelancers, a listing of talent that we can work from. On Upwork, you can actually post a job, with an estimated budget and have people bid on that. But I think the more interesting one for me, personally, is having a list of offers on this freelancer marketplace like web scraping via Excel and VBA And having a profile page here that we could purchase this offer and move forward with that as well. So, I think this is kind of what I'm envisioning for this. Let's start the clock and see how far we get with this.
Alright. So 60 minutes on the clock, we are going to start building this thing, but before we build, we need to plan. So planning wise, I always like to try to discuss or just think out loud what type of functionality I'd like to see from an application. In this case, we want to have a, we need a user profile or a freelancer profile, And we've got a offer, freelancer offer, something like that. We want to be able to have an offer page that customers can buy, and I think that's probably going to get us most of the way there.
That's really what I want. You know, we can set up this offer and then we need to have a form for them to submit with the details that we need to complete the offer and then complete payment. So how can we get that done? We'll see. Alright.
So as far as the data model for this application, right, What do we want that to actually look like? So I'm imagining we've got a a user that logs in. That's gonna be actually using our Directus users collection because we are using Directus behind the scenes. Directus gives us users, it gives us authentication, it gives us permissions, and then we have, you know, this could be the customer as well, but then we have a freelancer profile or, you know, we could just call it a seller profile or something like that. Inside that seller profile, we're gonna have some basic information like a name, location, description, etcetera.
And then attached to that seller profile, we're gonna have the user would be attached to that and then attached to that seller profile, we're probably gonna have some offers. So the offer is gonna be something like the name of the offer, the description. There's probably gonna be a slug on all these things. And then we've got a couple of different offer versions, you know, versions of this offer or packages, something like that. Packages, and those are gonna have a price, deliverables, etcetera.
So that's the way that I think about this. On the flip side of that, we probably also got, an order or orders. Right? So I ordered this offer, there's a relationship there and a relationship there. And now we have, like, this giant rectangular flow thing going on.
At least that's how I'm picturing it in my head. So with that in mind, let's get to work on actually building this thing out. The orders are going to have, let's see, they're going to have an offer ID. Offer ID, seller profile, and probably like some form information, form data. I don't know what that's going to be exactly yet, but we'll flesh it out.
Right? Okay. So let's dive into actually build this application. Alright, I'm going to pull this up side by side. On the setup side, I've just got a Nuxt application we'll use for the front end here and then I've got a blank Directus instance that we're going to start building from.
So, like I said, we get users out of the box for this. Maybe we go in and we create a new role for a user. They don't have app access but we'll give them access to some collections once we create them. But let's dive in and create our first collection. Let's just call it, our seller profiles, or it could just be profiles.
You know, it could be, like, we have a buyer profile as well. You know what? Don't second guess it. Let's just roll with it. So we've got a date created, when this was updated, who updated it, the status of this, is this a published profile or not.
That's great. We'll dive in, we'll add a name for the profile, we'll add a location. I I think this is honestly just like a, maybe it's like a city and a region and a country just so we could, I mean, we could potentially filter on the country if we wanted to. That gives us enough information. All this stuff is being delivered remotely.
And then we have a description. So description may be HTML content. We want it to be rich, we want people to be able to stylize a description, maybe include some images. So we're going to use our WYSIWYG editor in Directus. Alright, so that's our seller profile.
It's great, I can go in and set up my seller profile. This is Bry Ross. I am in Bluefield, West Virginia in the United States, just the US. I paint beautiful directus themes for you. Great.
We may even have a image for that. Right? We've got to have an avatar for our seller profile. We probably have that on the user level as well, but you know, maybe we want to keep that user data private from the general public. So we can attach that to our seller profile and then attach that to our individual user.
Great. Alright. So let's just search for Brad Ross, see what comes up. That's a great snap. That's what we'll use.
Away we go. All right, so we've got our seller profile, let's start connecting that, right? That's going to be connected to a user, so we can build in a many to one relationship to that. And honestly, we probably will want to add that relationship to the user as well. Let's go for a one to many setup.
This is going to be a user. Got the Directus users as the related collection, and the seller profile is going to be the foreign key. We'll show a link to the item. And we're going to make sure this value is unique. So inside Directus, we don't have a one to one relationship, you have to model that using many to one and one to many relationships.
But you can achieve the same effect by saying, hey, this value has to be unique. So you can see that we are going to create a new profile. Great. Let's see. We've got our user.
If I go to our system collections, we go to direct us users, I should now also have a seller profile that we can potentially link to. All right. So I've got that relationship set up. Let's go in and create a new orders table, and I'm just gonna leave that blank for now. I'm just gonna get that off of my mind.
And then we're gonna do offers. Alright. So within our offers we're gonna have the same kind of thing. Who was this created? Is this offer published or not?
And potentially within a profile maybe there's some offers that we want to sort by. So we can add all of these system fields. You can also just go back and delete those later if you don't need those things. So we've got a name for this offer. We've got, we're probably going to need a slug for this offer.
And this is an extension that I've installed called the WP WP interface or WP slug extension. It just allows you to add a nice looking slug and you can auto generate this on Create. It doesn't work via the API, but for any heavy lifting inside the admin here, inside the Data Studio, works great. Alright, so again we've got like a rich description for this specific offer that we want to have and then you know, we probably have some packages, right? So what are the different levels of this specific offer?
You can see here we've got like basic, standard, premium, etcetera. Let's go in and work on that. Right? To me, that would be a one to many relationship because one offer could have many packages, a package is only going to have one offer. So these are going to be constructed on an individual level, on an individual offer or product.
I'm not sure what Fiverr calls these behind the scenes, but this is what we're going to roll with. We will model this out, we'll get the one to many relationship, we're going to call these our packages or it could be versions. Again, we're not going to get carried away with it. What if we call the Related Collection Offer Packages? So you can see I don't have this collection yet.
And basically what Directus, the APIs are doing behind the scenes here is just mirroring all these changes to your SQL database. So that's really nice. We've got the offer is going to be our foreign key And I'm going to dig into the advanced field creation mode here. So if I go to my relationship, you can see that Directus is going to create these tables and this field for me, which is really nice, right? I don't have to have that table existing first.
Directus is just going to create it for me. It makes things simple, especially when I'm just prototyping here. So now I've got packages, and if I back up, we can see that we have offer packages. Those are probably going to be a hidden collection. I don't really need to show those.
And then we can actually work on the details of the package as well. Right? So we've got a title for the package or name. Package name. Sometimes when I'm working with deeply nested relational data, it helps to prefix like the name or title with the actual name of the collection, just because if you're if you're working with that data in a nested structure, it could be hard to know what the name pertains to, if you don't have some other type of identifier within that.
So we have a package name, we've got a, like, a delivery window or something, delivery time. I see that's like 3 day, 5 day. We've got revisions. Are we really concerned about that? No.
We've got like a short description. And in this case, it probably looks like a text description. We'll call it, you know, I could potentially call it short description or let's just call it description. Great. And then we have the deliverables.
Right? So this could just be a list of things. The Repeater interface inside Directus is kind of nice for that, so that's what we'll use. I can just store this data as JSON information. I just want to store a list of deliverables.
Deliverables. And then we have the title, the item. That's fine. That's what we'll call it. We'll do full width.
We'll just make this a simple string, keep it easy. We'll make it an input. And I could do like an icon, but these are just checkboxes here on this site. Great. Alright, so we've got our offer packages, that's linked to an offer.
Okay. And now on our order level, we need to go in and flesh this out further. Alright, so we've got orders, we've probably got a timestamp for the order. We'll just call it that. Or we could do created at or I think the standard format we use is date created.
Maybe we use time stamp. It's fine. I can always rename those. And then when I go into the advanced field creation mode, I can set this up so that when an item is created inside this orders table, it will automatically save the current date and time. Okay, great.
So the timestamp is saved. I can also go in and prevent this from being edited if I need to. That way inside the UI nobody can adjust the timestamp of this order. Alright. So a couple of things.
The order is going to have a user or customer, you know, let's just call it the direct us users. Great. Alright. And then we have what? We have an offer for this and then we have the package.
Right? So we're going to have both of those. We have an offer, that's going to be offers as the related collection. We'll save that many to one relationship and then we'll have a package, offer package, just so we make sure we get the correct package from that offer as well. Offer packages.
Cool. And then, you know, as far as, like, a form after that, you know, maybe we've just got, like, some JSON data that we render. I'm imagining that on the backside of Fiverr, it's like a dynamic form. I could build my own form. So in this case, maybe we just, form data.
Oh, you we just we just create, like, a JSON field that we could automatically populate data to. Hit save. We'll add a template for it. Okay. So that's what our orders look like.
I would probably need a an amount for the order. Let's just use a, decimal. Rate. I could go in and flip this around. Maybe we've got 2 decimal places.
This could be carried out to, I don't know, 10 places. And I could even go in and get fancy and add, like, a dollar sign or something within the interface. Pretty pretty simple. Okay. So offer user amount, form data, that's great.
What else was the other thing that I wanted to do? On the packages, we forgot to set a price. Alright. So we need a price for this package. Let's use a decimal.
You could store this as an integer as well. That's what Stripe does, keeps things simple. But, I like decimals displaying nicely inside the UI. Alright. So we got our seller profile.
Let's create an offer. Right? Why is our offer blank? Oh, some kind of extra table. Okay.
Alright. So we've got offers. Right? Great. We're gonna I will create a Directus theme for you.
We'll go ahead and publish this. One thing that I can already tell that we're forgetting is like a gallery for this specific offer. So let me fire up chatgpt. Let's get a nice formatted description here. Create a rich text description for a Fiverr like gig called I will create a Directus theme for you.
Alright. So while that's working, let's set up our different packages, right? This is the basic package, the delivery time is 3 days. We'll create a simple theme. We've got some deliverables.
This is, one theme, 2 revisions. Great. Alright. The price for that is 199. And then maybe we add another one.
Right? So I can also go in and edit this raw value if I wanted to. We'll just go in and add another package. This is the premium package. This takes 5 days.
We'll create the most amazing theme you have ever seen. One theme. Six revisions. That's a lot of revisions. Let's just dial it back a little bit.
Way more than what I would actually go to. Here's our description for this and then, you know, hand holding and support. Or let's call it theme installation as well. Cool. Alright.
We'll copy this description. Paste that in there. Save. Now we've got our offer. I need to add some images to this as well, right?
So for our offer, maybe we want to show an image gallery. Maybe we have one, there's a featured image that's going to be like a card for this. We'll save that. And then we could potentially also have multiple images. And we'll call this the gallery, image gallery.
Let's just call it gallery. That's great. And what in this case, what it does is it creates a junction collection, ties that back to our Directus files collection, which is basically your file manager inside Directus. Alright. We've got to have a sort field for those so we can control which order those display.
And away we go. Right? So now we have a featured image and a gallery. We can upload a feature image. Let's just make this Bry Ross.
And for our gallery, let's see if we have, like, some screenshots. Yeah. We got some past screenshots here of a few different themes or just one theme, actually. And now we've got an offer. We don't have any orders.
We've got a seller profile. Cool. Cool. We've got the offer. We don't have that linked to our seller profile yet, right?
So let's do that as well. This is going to be a mini to 1. This is going to be the seller profile. We'll pick that seller profiles collection. And I'm going to open up the advanced field mode here.
We're going to add, just this corresponding field in the table Seller Profiles. So we're going to add the reverse relationship there just so we can see that as well. So now if I go into my seller profile for Bry Ross, I should be able to see my offer, but I haven't linked that specific one yet. So we'll go here and pick Bry Ross. There we go, he's got an offer on the marketplace.
Now if I go back to Bry Ross we can see that actual offer, Vice versa, if I am on the offers I can get to the seller profile. Alright, so that is a lot of data modeling. We've eaten up about 20 minutes of time just fleshing this out. Let's actually start building something. Alright.
So we'll just start inside the Nuxt application. I'm going to pull this up. Actually it might be handy just to have this kind of sitting here as well. So on the index page, right, we want to get a list of our offers. We can do that.
I've got a Directus Nuxt plugin here that has some credentials already set up. We've got real time, let's say we wanna view a list of offers. I can do this, I'm gonna access that Directus plug in. So we'll just do Directus equals use Nuxt App. So I'm going to call the Directus client here and then we're going to use the Nuxt Constant Data.
They have a Use Async Data composable that makes this process easy. So we'll just do offers slash index, give this a key, and then we are going to add a function just to call this data. So we'll do directus dot request, we're going to do read items and we want to pick up the offers collection. We'll add some filters for this. Maybe we want to do the filter equals status.
We want that to be equal to published, so we only want to get the published offers. And what else are we going to add to this? We need a little bit of help there. Am I missing What am I missing? Got too many.
Not enough return. Directus offers. Got one more, and we have one more. Use async data, offers index, data, and the mesonoma syntax here. Request.
Okay. Stuff that in there. There we go. Alright. Got that fixed.
Now we are actually reading this data. Let's just remove this. We're going to wrap this in a pre tag just to make sure we're actually getting some kind of data from this application. Alright. So what I don't see is any actual data here.
And if I take a look, we can see if we're getting any API calls. No calls are going out. Number 1, I have to import that read items method from the SDK. Now we can get something going here. On the server side, no, not seeing it.
One of the potential reasons there is we are not logged in, right? So we created this data model, but Directus gives us a couple different roles for our users or our access control out of the box. By default, the public role is what users have access to when they are not authenticated. So anybody can access this level of information from the API, which is always set to 0 out of the box. So maybe we want to show our offers, when I'm getting ready to move into production I would strictly scope this down.
Maybe they don't need actual order information, but as far as the publicly displayed information, we're going to show that. And now I can actually see that. Let's just sort this out here. Okay. So we've got like a login and register buttons.
We don't really need those. Here is our offers. Right? I could drill into the individual packages here, but just to display a list of cards, I don't really need any of that. So we'll just do a quick h two, we'll do some offers, set up a grid.
How many do they show here across graphic design? Maybe I have 4 up Grid calls, 4. Great. And then we've got, let's just add a card component. The offer for I don't really like any of this.
Let's destructure this. This is gonna be our offers. Offer and offers. Key. Great.
Now within our card, we'll just flesh this out a little bit. We'll wrap that in a Nuxt link. So we just want to do 2 offers. I'll have to create a route for this in just a moment. Offer dot ID, or, you know, it could potentially be the slug on the offer if we want.
Let's roll with ID just for now. Make things a little bit easier. And just close this tag. Great. Okay.
And then within the NUCs link we have, what, we've got an offer name. So this could be an h three. That's gonna be an offer dot name. Jeez. I can't actually type.
Then I have a p for the description. Maybe we wanna strip some HTML. Do I have inside my boiler plate here, I should have just a function to strip HTML tags out. So I'll do that. We'll strip HTML.
Great. And, let's add an image for this as well. Right? So we'll do image. I'm pretty sure I've got this set up with Nuxt Image as well, which is just a replacement for the image tag.
Allows you to take advantage of, like Sharp library to optimize images. So we got offer dot featured image is the wording we're gonna use here, the offer dot name. Let's see what we've got. Right? Okay.
So we've got a card, we've got grid calls for, left off an s. That's good. Let's make the font bold for this. Text a little larger, 2 x l. This is way too long as well, so let's truncate the string after we strip all the HTML.
Maybe we limit to like a 150 characters. Much better. Add just a bit of padding. And I think the Nuxt UI library has this U container component we can use to just add that padding to it. Alright.
So we got an offer, great. Let's go in, we'll click on that offer, the offer is not found. We need to set up a quick route for this offer, we'll do that. First I'm going to create just a folder for this, Nuxt does file based routing, which is really nice. And here we'll just do the ID dot view and I can copy a lot of this to carry over.
Alright, so instead of Read Items, I'm just going to Read Item. Now if I was using the slug, I could change my filter to be the slug here. We're going to end up using the route, we need the route params. So we'll just use route. And here, as far as the request, we're gonna change this up just a little bit.
This will be the offer maybe change this to show the offer ID. Still talking pop up? Yes. Still talking. Okay.
Offer dot ID. I will, baby. It's the joys of working from home office. Right? Okay.
So we'll log in. We've got the readitem. Readitem accepts 2 arguments, or actually 3 arguments. We've got the collection, we've got the item ID, and then we have a query that we can use to either return certain fields or not. But in this case, all I'm looking to do here is just grab this ID.
Cannot access offer before initialization. We don't need any of that. We're not even going to need to wrap this. Right? Offer dot image, This is gonna be the offer dot title.
Do we actually get the offer here? No. We're missing something. Gonna read the author, read item, cannot access offer before initialization. Oh, the the route dot params dot ID.
Can't use the offer that you don't have access to yet. Okay. So there we go. We are working with an offer, author, offer. I keep getting those mixed up.
I'm just gonna go back to this one. I'm gonna drag it over here. I just arced myself, didn't I? As far as the web scraping profile. Okay.
I've got this on a separate screen that you can't see at the moment. I'm just going to do, like, some quick formatting for this. U container, grid calls. Why does this look this way? Probably because we have it in a grid.
Set this up. We've got the offer title. Oh, should be the offer name above. That'll actually be an h one tag in this case. Not that you're concerned about semantics on this particular episode.
Alright. So we've got an image, we've got the description, and in this case, what we're going to do, instead of truncating that, we want to show the whole thing. So we'll do vhmml equals offer dot description and because I've got tailwind installed, I'm just going to use the pros class. We'll use, like, a large text for this. And boom, there we go, we got that.
Now, on the right side of our page, we've got, like, the offer details, right. But what I don't have in this case, if we just log this data out again, Alright, if I take a look at it, I don't have my package information, I don't have the seller profile that I want. So how can we actually fetch that information? Directus makes this really easy. We'll just use a fields parameter in our query.
You don't have to, you know, this is available through the regular API, syntaxes. It's pretty much the same across the board here. You don't have to be using the SDK to access this information. So, I don't recommend doing this in production, but because we're just bumping through here, we will use an asterisk to get all the root level fields on this. What else do we want?
We want the packages. We'll get all the fields on those packages. And then for the gallery, let's get the next level fields for the gallery. And now we can see what we're working with here. So we've got a gallery of images.
Cool. We've got access to that information. Okay. So I'll just leave this up. That's cool.
We want to maybe wrap this in another div. We're gonna maybe flex this container. This will be I mean, it it could even be a grid at this stage of the game. Let's make sure we are we're only targeting on, like, a small or a larger screen size. We'll have, like, a I don't know.
Like a 3 column grid. This will be call span 2 columns. See what that looks like. Do I have this running now? Okay.
Great. We'll just hide this pre offer. And then on the next one, this will be class call span 1. And here's where we're gonna put our packages. Alright.
So packages are gonna go over there. Obviously, we're gonna add some nice gap for these just to clean this up a little bit. And, again, this is this is fairly rough. Right? If I want to just move this in here, you could see what do we have as far as our packages?
Just gonna throw that down there. Alright. So as far as our packages, we've got these shown in a couple tabs. Let's look at ui.nuxt. Do we have tabs here?
Yeah, there's like a tabs component. Okay. Tabs equals items. Alright. So let's start flushing this out.
We got tabs. We got items equals, let's just write like an inline function. This is another case of I do as I say, not as I do. But let's actually map these packages out. What have we got here?
Right? Offer dot packages. We're gonna map these to the actual items. What what shows up in the tab here? The content is x.
Does this have, like, a oh, it's got, like, a slot that you can use. I to me, this feels, like, really kinda heavy handed, but, yeah. Whatever. Let's roll with it. Packages dot mat.
Actually, let's not. Let's just like flesh these out real quick. Divv4. We use, like, a selected package. Selected package.
That'll be what's offer dot packages offer.value.packages. And we'll do the first one. Alright. So that's gonna be our selected package dot ID. Let's call it selected package ID.
Good enough. Alright. And then we're just gonna loop through these packages, offer dot packages, offer in offer dot packages. The key here would be key equals offer or, actually, no. We want package Package dot ID.
Okay. How are we doing on time? We'll check this. We have got 20 minutes left. Package dot ID.
Alright. And then within the package, we are going to have the package. Name, package dot name. Let's just see what this gives us. Then we have a list of deliverables.
V 4 package alright. Deliverable item in package dot deliverables. Key equals we'll just call that the item, and we'll do item dot item. K. Item dotitem.
K. What else do we have? We have a short description for that. That'd be a p tag. Package dot description.
Alright. And, oh, transform failed with, like, 5 things. We've got just too much going on there. There's our packages. Those are not showing.
Offer in packages. Package in offer dot packages. Key equals package dot ID. Offer package, oh, package underscore name, package description. Oh, we can't use package.
That doesn't make a ton of sense, but, it does make sense. Pkg offer dot packages. Should learn that a long time ago. Item in pkg. Cannot read properties of dot packages, null.
Offer dot value. Okay. Alright. So there's our different packages. We probably got a price for that as well.
Let's take a look. Cool. I was in a div.flex p dollar sign pkg.price199. Okay. Package dot.
It's actually package name. Okay. How are we doing on time? Probably not good. And then we'll just justify between.
Okay. And what else? So we got our packages. Let's wrap this. Actually, we wanna do this.
We'll do wrap it in a template tag. V if, we get selected packages, selected package ID, offer dot value, unref, offer packages, 0 dot idvif, what, selected package ID equals package.id. I can't read no packages. So this doesn't exist yet. Select the package ID.
Offer dot packages. What are we gonna do with that? Let's just leave that. Null for now. That'll get us back to rendering, but we don't really render anything.
And we do if offer what's that gonna do for us? Can't read, undefined, offer dot packages. Oh, did I even have that correct to begin with? Unref offer dot packages dot zero dot ID dotvalue.packages.id. Okay.
Anyway Alright. And then what we're gonna do, we'll just add a list of buttons here. Div. Flex those. Add some gap to alright.
So we'll do buttonv4, package and offer dot packages. And package dot package name. Alright. So we got those 2. And on the click of the button, that click, we'll do selected package ID equals pkg.id.
No. Why is it doing that? Let's see. Expanding this when I don't want it to. Thank you, GitHub Copilot.
Alright. So there we go. We can switch between these two packages. And then for each package, maybe we just have a buy button for that as well. Let's see what this looks like inside, Fiverr.
They kinda send us out where you can order the quantity, you can change, you can add on, things like that. Not super concerned with that. I'm actually going to just totally cheat here. We'll open up my old faithful Stripe account, log in, Go into test mode for this. Just create a new product.
We'll call this the theming package, theming offer, direct to themes, buy Bry. Great. We've got more pricing options. This is gonna be a one off. We've got flat rate, customer chooses price, price by package, or bundle of packages.
Let's do a flat rate. Hit next. What did we charge for this? 199 for the one package. Do set.
Add another price. This will be the 299 for the premium package. Great. Billing period. That's a flat rate.
Great. Alright. So we got 2 potential options there. We'll add this product. Of course, I gotta log in.
Alright. We're cooking. We're cooking. We got 15 minutes left. And now direct this theme is by Bry.
You know, normally, like, taking the time to go through this, I would certainly go in and then, just basically, like, have this created through a Stripe checkout. So we got a payment link that we'll create for this. Great. We'll create this link, copy this, and if we go into our direct assistance where are you, mister directus instance? Admin@example.com.
Password. Okay. Alright. So for each one of these packages, offered packages, let's have a buy link. That'll just be a string.
We'll serve that on the front end inside a button component. That looks good. Alright. Offer packages. So let's set this up.
Right? This is our offer. Where's our packages? Here's the 199 package. There's the buy link for that.
Lost my Stripe payment link. We'll create a let's create another payment link real quick. Where are you? Directus themes by Bri. We need a payment link for this one.
Create the link. Great. Copy that. Again, this is the quick and dirty way of doing this, but sometimes you got to. Alright.
This will be 299 for this specific package. Save. Save. Save. Save.
If we look local host, now we should have access to that data. So we can see those prices there. And then inside each one of these packages, after our deliverables, we'll just add a button. We'll add a 2. This is gonna be, package pkg.buyurl.
Is that what I set that up as? By link. By underscore link. By pkgpackage_name. And by premium, this should redirect us.
Right? Redirect us to direct us. Yeah. Nothing fancy here. Alright.
So if I hit now, I hit buy premium. We've got what? 13 minutes left? Buy premium. I go in, I check out, that's great, we're gonna have to fill out this information.
But one thing I wanna do here afterwards, right, is after they pay for this, we need to send them to a form. Now if I'm using the Stripe API endpoint, I can set up a referral URL after I check out to redirect. Not going to do that here because I don't really have time to set up all those specific endpoints. Again, we're gonna go quick and dirty mode. We'll go to webhooks.
Let's set up a webhook for this. Of course, this is gonna be in my local environment as well. Okay. I gonna cut this close. Right?
We'll go into flows. Let's create a new flow. We'll call it catch stripe. Webhook. Catch stripe will trigger on a webhook.
Incoming webhook, it will be a post request. What are we going to do when we catch that? Are are am I I I'm I'm getting way ahead of myself. Like, we could catch this and do an email or something like that, but let's just say we yeah. It let's let's do something more interesting.
And, switching gears here. Let's just assume gonna make a bunch of assumptions here. I'm just gonna go ahead and check out in this process. If we go back to Stripe where are you, Stripe? I think within the actual payment links, can we do this by, let's say for the premium link, after payment we are going to just redirect.
We're gonna go to http local host 3,000/submit/offer/submit. Offers slash submit. Alright. So we're just gonna create a new page. This will be submit dot view.
We'll take this information. Submit dot view. And what are we gonna do with this? Right? We're gonna make sure we still let's let's get our offer just to show that up at the top.
And then we're gonna build a form to actually submit some data or actually create an order. Again, normally all this would be via webhook and either flows or like a specific endpoint, but this is what we'll do just to throw something out with our remaining 10 minutes. Right? So we've got our freelancer profile. We've got an offer page that customers can buy.
So we check the box on that functionality. Technically, I guess this is a win. Is it, a smooth win? I don't know. And then we just want to, like, submit an order form.
Alright. So how do we do that? We will do create item. We're gonna need read item. We still wanna pick up that offer.
And let's make sure this submit isn't gonna actually work on our front end. Got too many links going on. Local host 3,000 slash offers slash submit. Cannot read properties. Offer oh, I don't have the route params.
So let's drop that. Let's move offers underscore ID. We'll make that a folder. This is gonna be dropped into that folder. So it's submit.
And this is gonna be index. Alright. So that adjusts things a little bit. And now offers route params.id. Oh, becomes let's go back to the index page.
We go here now. Actually, that's gonna be broken. And it no. That won't be broken. Shouldn't be broken.
There we go. Alright. So now I've got that, and then we'll just have a submit page. There we go. We've got that.
Let's add a h two tag. Thanks for your purchase of this package. Great. And then we've got, like, a form. Alright.
So u form, and what are we gonna do with this form? We are, what, we're going to submit this, we're going to need a function here, async function. Submit order. K. We are going to wrap that in a try catch.
We'll do constant order equals await, direct us create offers, create orders, create item orders. We have the package, selected package dot ID dot value. Let's not worry about the package at this moment. Then we've got form data, which is just gonna be a JSON object. What else do we have inside this Directus instance that keeps magically getting logged out?
7 minutes, offers, orders. Alright. So we had the user. We had the amount. That's gonna be the selected package.
This is probably actually backwards. Right? Maybe we should have submitted this form first and then redirected to checkout after they submitted this form. But, hey. Nobody's perfect.
Alright. So form data. What do we want inside the form data? We don't really have to worry about that at this moment. Then we'll just add one more.
There's our submit order function. Will this create an order? We can console. Log the order after we create it. But let's just build a quick form that will actually submit this.
Alright. So we got you form group. What do we need to actually build a theme for someone. So we need the website URL. We'll have a input for that.
V model equals form dot website URL. Actually, that'll be form under underscore data. I actually need a form here. Form, let's make this reactive. And, honestly, here, this will be form.
Alright. So form dot website URL. Great. Make that required. And I think that actually goes here on the form group.
Again, just rolling off of the Nuxt setup. Label, what else do we want? Form. I got like 35 things going wrong here. Theme, colors.
Maybe we add, like, some kind of extra helper text within this. What is the color palette for this theme. Okay. Let's see what this actually looks like. Okay.
So we got like a we've got an actual form rolling here. Add some spacing. Add a submit button, new button. Submit form. Okay.
Also, let's make sure that you have to be logged in for this. So we'll do constant user equals use state user v if user u form v else. Oh, no. I don't need this. Div v else.
Please log in to submit the form. We'll do u button. Ah, boy. Actually, it would be just 2/login. No.
It's auth/login. Log in. Alright. Are we actually logged in? I guess I am logged in because I'm sharing a session token here.
But if I were to like, go here and look at this, right, it's not gonna show me that because I'm not logged in. And then I could log in, and then I could go back and potentially see that offer or submit. Right. So now that I'm logged in, I can see this information. But we are running dangerously low on time.
Let's make this a text area. And let's just run through this. Right? We have purchased something via the offer page. We're now submitting this data.
What a mess this one's turned into. But we'll do the directis. Io, violet, white, shades of pink, slight gray. Cool. We hit submit form.
What happens what happens if I hit submit form? Nothing happens, because I didn't tie that to any actual button. At submit equals what submit order. Submit order. Submit order.
I should be seeing a fetch request. At button oh, it's a click handler. At submit would have been on the form itself. We'll save. Website URL, httpdirectus.io.
Okay. Do we wanna track, like, a success state? Success equals ref false. I love cutting this down to the wire. Right?
Console log order, Success value equals true. And then if the and no success. Maybe we want to show a success message. The if success. We do yes.
Okay. What are we looking at here? Why is it not logged in? V else if v else if. V else.
So I fix it. Okay. HTTPS, 1 minute 25 seconds. What happens? Nothing is happening.
What did I do? What happened to my click handler? Disappeared. Submit order. I got the wrong I put it in the wrong spot.
Okay. Let's try it again. Refresh. Httpdirectus.io. Where is my success with 48 seconds left to spare.
Has this actually gone through? Has it submitted an order? Can we actually look at it? Do we have an order inside the system? We do.
We don't have the offer and the package in there. So the offer equals the value dot author dot ID. If we go back and we check that just one more time, Submit another order to this. Will that now have the there's the offer. Again, like, we could have this all set up and routed correctly with enough time, but what a wild ride that was to try and get this information actually into the system.
So wow. Where would we take this from here? Right? I would go through and set up this logic to be very robust. Again, as far as the offers here, I would probably I don't know that I would create products inside the Stripe account for each one of these offers.
I would just set up, a checkout to Stripe that would take the order value or the order amount from a cart, throw that to the Stripe checkout, they would check out. Stripe sends a webhook. We also redirect them back to a page where they can fill out the necessary information, on a page like this to give us the order information that we need, to actually complete that order. So we can see that information here. But that's where I would go from here.
Obviously, I would do a little a few things different. If we had to add more than an hour, probably 2 hours here, we could have had a really nice application. But bare bones, this is pretty good. That's it for this episode of 100 apps, 100 hours. I'll catch you on the next one.
Thanks for following along.