00:09
is our status, difficulty, and access level, but we want this to be where we can click on one of those values and change it from a list of our tags in a dropdown.
00:18
So, we're going to create this as a reusable component because we also want that to be for access level and status on our lessons and modules here as well.
00:27
So, we're going to call that component our tag selector. So, let's jump back into our application here and jump back into the text editor now and start by adding that component in.
00:35
So, we'll scroll up to our components, right click, new file, and call this tag selector.vue.
00:40
We'll want a script setup line TS, and this is going to have props, so we'll define props
00:47
with a type of model value for the view model, and that's going to be a number, which will hold our access level ID, status ID, and difficulty ID.
00:56
Then we want to allow options that'll either be a status DTO array or a difficulty DTO
01:03
array or an access level DTO array. Then we'll have a patch optional prop.
01:10
This will be an object with a path of type string and a key of type string. The path is going to be the endpoint that we'll end up hitting whenever we switch out
01:18
one of these tags, and the key is the model property that we'll be updating with that patch request. Because we have a model value that we're going to want to be able to update, we'll have an
01:28
emit that will define our emits here, and that'll be update model value. We're going to want a computed property that holds the selected tag, so we'll add a computed
01:37
here from view that reaches for our props options, and then we'll want to find the item where the item ID equals the props model value.
01:47
Then we're going to want to have our internal value that we'll work with inside of this component, so this will as well be computed, but we'll want a separate getter that reaches for our
01:56
props model value, and we're going to end up passing this internal value into a shod cn component.
02:02
So we do want to switch this to string here so that it's compatible with that, but then whenever we go ahead and set this value, we can parse it back to a number so that it works
02:11
more seamlessly with our outside components. So our editors are going to be an async function that takes in that new value, and at the end
02:18
of the day, it's going to emit the updated model value, and we can go ahead and parse that back to an int so that we're emitting back up that number.
02:26
Prior to doing that, though, we want to check our patch options to see whether or not we can send a patch request along with this change.
02:33
So we'll add in a const can patch that is set to whether or not we have a props patch path and a props patch e.
02:43
If those are both set, then we can go ahead and patch this change up to our database,
02:48
and we can do that using the router from Inertia Vue 3, so we can send the patch request up
02:54
with that to the props patch path with an object where the key of that object is set
02:59
to our props patch e and then setting the value to that parse int value. In addition to that, we also want to preserve the scroll position, so we'll add in a preserve
03:09
scroll option and set that to true. I don't think the router imported on me, so I'm going to hit command dot and make sure that import is added.
03:17
OK, now, rather than parsing an int in two separate places, why don't we move that out to a variable here so we could do just const int value and set that equal to parse int
03:27
and switch this to int value and this one down here to int value there as well. For our actual template, so let's add the template in here.
03:35
We're going to make use of a drop down menu, so we'll have our drop down menu trigger,
03:41
which is the button that you click to open it up as a div with a class relative with a span class opacity 25.
03:49
Set this to absolute width full h full left zero top zero and set it to be rounded.
03:56
We can use the style to set the background color to the selected tag elements color property that's selected not select. There we go.
04:05
And we don't need anything inside of that span, so we can self close it there.
04:08
And next to it, we'll have a div class text extra small flex items center gap to PX 2.5
04:16
PY1 negative margin Y1 rounded full. And then we want to make sure that this is on top. So we'll set this to relative Z10 there inside of the div.
04:24
We'll have another span class with three H3 rounded full. This is going to be a little color orb so we can set the style of this to have a background
04:34
color of our selected items color.
04:38
So close that and we'll have one more span class opacity 75 that can display the selected items name.
04:46
Otherwise, we can say no match found. Our tags are all required, so we should never see no match found, but we'll put it there just in case.
04:54
That then takes us into our drop down menu contents. We'll drop down menu content just below our trigger.
05:02
And inside of here, we're going to want to make use of a drop down menu radio group. And this is the exact same component that we used up in our organization select.
05:09
It's going to allow our users to dynamically change what the selected item is, and we can match that with a view model to get that change.
05:16
So on this radio group, we want to add that view model in and set that to our internal value. Inside of the radio group, we'll have an individual radio item.
05:25
So drop down menu radio item is the full tag name for that. And for this, we want to loop over our items. So we'll do V4 option in the options that we pass in.
05:35
We'll have our key as the option ID and then set the value to option ID. And then it requires a string. So we'll add to string for that.
05:45
Inside of the drop down menu item, we'll add in one more span with the class width 4, h4 rounded full. Set this to absolute left 2.
05:55
And then we'll take a pixel away from the left by doing negative ML pix. And then set the style to our background color for the options color, and then we can self-close that span.
06:05
Next to it, we just want to add in the options name, and that is our tag selector. So when we click a drop down menu item, that's going to change our internal value.
06:14
When our internal value changes, that's going to check and see whether or not we passed information into this component, allowing a patch request to go out.
06:22
If a patch request can go out, we'll send that patch request and then propagate the model change upward, which in turn will update our getter value there as well.
06:32
And it will also update our selected item or display in our drop down menu trigger. To make use of this component, let's now jump back into our courses show page and underneath
06:42
our little header block. Let's add in a UL class grid gap 3, margin bottom 6, px 4.
06:49
We'll add an LI with a class flex items center gap 3. Then we'll add a div class width 24 that says status.
06:59
This is the little label portion that we saw to the left of our tag selector whenever we looked at it out on production. For the actual tag selector component, we'll add that in just below it, finding it the
07:09
view model for our internal course and setting the value to the status ID off of that. Then for the options, we're going to want to reach from our organization to the statuses
07:19
that we have in there. And I'm going to go ahead and break this down on separate lines because that then takes us into our patch options.
07:26
This is going to be an object and it's going to have a path and a key. The path is going to be the same for all of our tag select usages here.
07:34
So up in our template, we can go ahead and just add a variable in for that. So we can create a new const called path and set that to, and I'm going to use computed
07:41
from view return slash courses slash, and we can reach through our prompts course and grab the ID.
07:48
We'll create an endpoint called slash tags specifically for this change for the key. That's then going to be the object key from our course that we want to update with this
07:58
patch. So that's going to be the status ID for this one. We can go ahead and give this a copy and paste it in two more times because we'll have one
08:06
also for our difficulty as well as the access level. So for our difficulty, we just want to switch the IDs to difficulty ID, the option to difficulties,
08:16
and then once more difficulty ID there and do the same thing here for our access level
08:22
ID, access levels, and then one more access level ID. Let's go ahead and jump into our terminal now so that we can create a new action.
08:32
We'll put this inside of our courses folder and call it update course tag and clear that out. Jump back into our text editor.
08:39
Now, before we jump into that action, let's jump into our course validator and let's create
08:43
an export const course patch tag validator that this is the vine with metadata organization
08:52
metadata compile and we'll pass in a vine object to here. We're going to have our status ID of type vine number.
09:00
We'll add in our exists and use the exists in organization helper that we created to check and make sure that this ID exists in the statuses table.
09:09
We also want this rule to be optional, but we want it to be required if the other two are missing. So if our difficulty and access level IDs are missing, we want the status ID to then
09:19
be required. So we can use the required if missing rule to verify that if the difficulty ID and access
09:27
level ID are missing, then it will set our status ID to be required. Before we save and format this, I'm going to give that line there a copy and I'm going
09:36
to paste it in two times because we also need that for our difficulty ID and our access level ID.
09:43
For the difficulty ID, we want that to read from our difficulties table for the exists in organization check. And then for our access levels, that'll be from the access underscore levels table.
09:53
And for the required if missing for the difficulty ID, we want to switch that with the status ID. And for the access levels, we want to switch the access level ID with status ID there.
10:03
So despite all three of these properties being optional, if all of them are missing, thanks to how we have this required if missing set up, we will get a required validation error
10:13
because they're all set to be required if the others are missing for them. So we should now be able to give this a save to format, which should take care of our red squigglies.
10:21
And we can now jump into our update course tag action. This will take in our organization of type organization.
10:29
The courses ID as a type of number and then the data which we can infer from the type of our course patch tag validator, extract those out of our handle method.
10:39
And then let's go ahead and grab our course. So awaits organization related courses query where ID burst or fail.
10:48
Then thanks to the way that we have that validation, we can simply await course, emerge that data in and save it as though we're making a typical request.
10:57
And then we can go ahead and return that course back just in case it's needed. Takes us now to our course controller. And while we're right here, I see this to do because we wanted to redirect to the courses
11:07
show page after we create a new course. So we can go ahead and take care of that while we're here before we forget.
11:12
So we'll do to route courses show and pass the ID as that course ID there. Okay. That should be all set.
11:21
We can now scroll down to underneath our update and add in an async method called tag.
11:25
This would be params request response and organization from the HTTP context.
11:32
And again, first things first, let's validate our data using request validate using and
11:36
pass it our course patch tag validator with the organization metadata providing that the organization's ID.
11:44
Then we'll await update course tag, all the handle method and pass in the ID as params ID, the organization, and then that validated data.
11:53
We can then go ahead and return response and redirect the user right on back where they came from.
11:59
Last thing we need to jump into our web routes and add in our patch request. So router patch there for courses, ID tags.
12:09
This will use our courses controller and it's tag method is courses tags, and that should do it. So let's jump into our browser. Okay.
12:18
So I can see the label here, but I don't see the actual tag components. So let's go ahead and jump back into our text editor, go into the courses, show a page and whoops.
12:27
I called this tag select, but we called the component tag select door. So let's tack an O R onto the end of each of those. My apologies about that. Give that a save.
12:37
And now we should be able to jump back into it. Yep. There it is. Cool. So if we select one of these tags, we should get our dropdown just as we do here.
12:44
And if we click on any one of these other tags, so if I click on planned here and we do get a 404, but I can see immediately that should be courses, not course.
12:53
So let's scroll back up to our scripts and edit S onto that path, jump back into our browser and let's go back up and just refresh. Okay.
13:03
So now if we try to switch our status from not started to planned, there we go. Now it went off. We can go ahead and refresh just to make sure everything worked.
13:10
And sure enough, it did do the same thing here for our difficulty and access. Awesome. We can verify that that's persisting into our edit flag now. And sure enough, it is.
13:19
If we switch our edit updates that persists onto these tags here as well. So the entire flow there seems to be working a okay now.