NG Grids Update – Servoy Tech Webinar #55
NG Grids Update – Servoy Tech Webinar #55
Okay, this is the number 55, if you can believe that. This is following the other episodes that were regarding the 2019 O3, which is the Q1 release of Servoy. And this particular one we’re going to talk about the latest release of the NG Grids web package. So we have moved to quarterly builds, and we are also now trying to time our extensions. This is an extension, it’s not part of the core platform, but we’re trying to also time our extensions around the same quarterly release cycle so that all the updates are kind of coming in chunks where it’s easier to process them. So today is about NG Grids, which goes to the 2.0 release as of this morning. It was in a release candidate for a while, but today we announced the stable build and I think we did a webinar episode number 40 was the last time we talked about this. So it’s been quite a while if you’ve seen the prior webinar, there’s some new stuff and if this is brand new to you there’s a lot to digest. So let’s get going. So we’re going to start off always with demos. I have sort of three examples. There are two different kinds of components, something we call a data grid, which we’ll get into and something we call a power grid. And we’re first going to look at an example that just shows grouping plus some filtering, which is new and a bit about how to persist grid state. So we’ll look at some editing scenarios because this is also a powerful component for editing and then we’ll look at the power grid, which is a read only grid, which is meant more for analysis. So we have some different features for that grid. So let’s get right into the demos. We’ll start off here with the first example. We are looking at a list of orders from the trusty example database. And right away you might notice that we have some filtering up up at the top here for each column. This is new since the last major release of this component. And I will demonstrate what this could look like. We could filter say on customer. So you can see that we get Tom’s but also Domio and bottom all having that string fragment. We could modify that a bit because it is by default contains we could do starts with. And you can see that it’s narrow down. I’ll go back to contains because we get a bit more to work with them. And I could combine multiple filters so I could reduce things down. You can imagine that we might want to filter by date, which will look a bit different. So we can come in here and. Well, I want to do a before. So we can do less than. And we’ll do. Say everything in 1996. That is also containing TOM. And you know, we could do something here as well. Maybe we just do Spain. So you get the idea. These are all. values that you enter. I understand that probably the next logical thing would be instead of typing in. String fragment for a customer, we might want to have a list of discrete values. And you can multi select that list so we could go through and pick off customers and. And there we’re getting sort of an in filter. That is something which we’re looking into for the next quarter. probability about, you might find this. Well,00008 again. We would like to perform this COL for 2019.06. Release to have discreet multicultural values in the filter. That’s filtering I also want to point out that that this is a server side. So for those of you new to Server node, the data binding is really. can do a lot of things in the browser. Still, when it’s talking about data, that’s data bound. So this is filtering on the back end. So you’re guaranteed that even if there’s quite a large number of records in the database that the filter is valid for all the records. Let’s go into some examples about grouping. I will clear out these filters here for that. And I just want to show some of the grouping. This has been in the component kind of from the beginning. But for those of you who haven’t seen it, we’ll go through it. We can, by default, this grid wasn’t grouped. It’s quite easy to group. Let’s do maybe country first. We’ll do country and then employee. So we want to look at how our sales reps are doing per country. And you can see the breakdown there. If I wanted to set up the grouping to be a certain way by design. So initially, I came into this form and it was ungrouped. But suppose that I wanted to have country and employee as my default groups. What I could do is come into the developer and go to this form. And I’ll show you some properties of the component. If we were to come into, say, country and set the row group index, negative one is the default. And that means it’s not grouped. I’m going to set it to zero, meaning it’s the top level group. And then employee will set to one. So it would be the next one in the list. So right there, we are changing the default grouping. And I will relaunch this, although it probably broadcast in there. I think I’ve done something. So employee row group index was 0, 1. It should be. I know what’s happening. I have part of my demo is to persist the grid state. And it’s overriding the grid state. What I can do is come back to the browser and clear that. Actually, I can do that with this button, I think. Let me there so that it reverse it. You’ll see what happened when I get to the next demo because it was using a stored version of that grid and remembering it and reapplying it over top of it. So back on track now, you can see that the default that I set up for the grouping by setting country and then employee. So it’s not like it’s totally up to your user to figure out the grouping if you want the design a grid to be a certain way. And you can even lock it out that way so that they can’t change it. Then you can do that. And this is one approach. Another thing I’d like to point out about the filtering is that you don’t have to have these filters above here if you don’t want them. You could have it be that they have to pop it up to do the filtering. So let’s take a look at how the filtering was done. So one of the fields we filtered on was customer. And you can see over here, filter type is set to text. So the default would be that it’s set to none. And that means that the filter box won’t appear. And this column won’t be filterable. However, there’s a property for the entire grid, which I’ve added here under grid options. And you can just click here and add it and then type it in. And that’s floating filter is set to true. So that’s the sort of on top of the column. That’s the floating filter is showing by default. What I would like to do is clear that property and save my grid, say my form. And now you can see that that filter is not available. However, if I were to come into, say, what do we filter on before date? You can see that there’s a filter button here in the little toolbar. So it’s still an option. However, I turned it off for customer. So you can see that the little filter button does not appear here. So you as a developer have some control over which columns are filterable. And if you want to have that sort of inline filter floating at the top or not with a simple property, you can configure on the whole grid. OK, let’s jump into how we might persist the grid state. So the default here now out of the design mode is that we’re grouping by country and employee. But let’s say the user does some changes to the setup of the grid. And what you would like is for their user experience to be continuous so that the next time they come back into this application that they’re getting the grid for this particular form, let’s say, in the way that they last left it. So I’m going to drill in, say, from country to employee and then maybe we’ll group one more on, let’s do city kind of in between. So we go country city then employee. So we’ve changed it that way. And then perhaps we want to also hide those columns. So we’ll hide employee and country and cities already hidden. That makes a bit more sense now because you see that anyway in the grouping here. So now I need sort of a hook to persist that. There are many ways to do that. I just set it up so that it’s on form hide. So if I came into this form and came back, it would have persisted it. And now what I would like to do is close this and relaunch it out of the IDE. And I should get that. Let’s see. Country employee. I had it with country city employee. Well, let’s go through it explicitly, programmatically. I’ll do this again with, I’ll just stick city in between. And we’ll come over here and turn off employee and country. That is the way I had it. I have some code here underneath that is saying to save the grid state, which I thought I was doing on hide. And then on load, I was restoring it. Maybe I didn’t save something over here. I’m quite sure. Now it hurts to do all the way back. I think I undid something. Yeah, yeah. OK. Let’s just add city to the group and try this again. So I will programmatically call this through the command console, which is a nice trick when you’re debugging something. So forms.orders.savegridstate. And what that does is it calls a method on the grid to get the current column state. And then I actually send it to local storage. So I’ll show you what that looks like. Now you could save this in the database as well. But I have this key value, my grid state. And yeah, you can see the row groups are country city employee. So the next time it comes in, it should read that out when it first shows. So let’s close this and relaunch it. And hopefully it works this time. There we go. OK. So that was me logging in a second time as a user and finding that it is in the way that I left it with city being part of the group. So in this case, I want to show you that, sorry, in this case, I want to show you that in the script editor for this form, in the on show event, I’m calling this method load grid state. And here I’m using my web storage plugin to read that back out of the browser local storage. And then I’m calling this method restore column state on the grid component itself. So there’s two methods you can call there’s when I’m saving it, which is here. I’m calling get column state and I’m calling restore column state. And then finally, if I want to get rid of this and go back to the way it was initially by design, I have a revert button here. I click it. That’s calling this method to revert column state, which is down here and there’s restore column state. And there’s one API call. So there’s three methods there for you to manage that. It’s up to you how you want to store it. You basically get the serialized information about the grid. In my case, I put it in the browser, which means if I were to log in from a different device, I wouldn’t get what I had done. So if you wanted that, then you could save it in a database, for example. OK, another example that I want to point out here is let me clear these groups and show you the raw setup. You may notice that there’s these dollar signs here. And I’ve made a rule that those are going to show up whenever the order total is greater than $1,000. So you can see any of these that go above $1,000. We get the little dollar sign. We also get a bold font going across there. I just want to point out some of the little tricks you can do. In this case, you can mix any markup into the grid. So that’s how I’m getting that little font icon in there. Let’s take a look at how that was done. If we go to the design of the grid, you’ll notice that there’s a column here, the first column status. And the data provider is a calculation display order status. And it is just injecting a little bit of markup with the rule if the order total goes over $1,000. And so you can render markup if you want inside the grid. There’s just one thing you have to remember to do. And you’ll notice that there’s a property or show as on that particular column. The default is text, meaning you just show the raw value that’s coming from the data binding. However, if it’s HTML, you want to let it know to render that as such. And then there’s a third option to do sanitized HTML so that it will actually strip out unwanted stuff. Probably if you had something in there that you wanted to use, you need to do HTML and not sanitized HTML so that the browser is still trusting it. All right. Oh, finally, how did we get this bold here? This is one other example. This is on the grid as a whole, a property called row style class data provider right here. And you can see that I’ve said it to be a calculation. If I jump into that calculation, it’s, again, evaluating if the order total is greater than 1,000, and it returns a class name, otherwise it’s returning empty string or null. If I jump into the style sheet for this particular solution, which is this less file I have here, you can see that I have a style selector called large order, and I just make the font weight bold. So there you can see that that’s what’s causing these to become bold. So again, you can dynamically style cells, and we’ll have an example of that in a minute, cells and rows based on the data. You can also inject markup, and that markup can also be based on the data. So we get people asking to do kind of tricks in their grid, and there are definitely plenty of hooks to do that. Okay, let’s jump into the second example, which is an editable grid. So for this example, I have a grid here, which is showing the order lines for a particular order, and we’ll just look at a bit about editing. This particular order, let’s say that I want to, I don’t know, change the product. So there you can see I’ve connected to a value list. I can change the product there. I, this is just reading, read only from the product. So this is not editable, but I could also change the quantity, and you can see that as I change it, the value should change as well. I don’t know why they didn’t, the calculated value should change. Um, we’ll look into that in a second. What I want to do is show you how to set up the grid to be editable. That’s more important. So if we come to the order detail form, you can see I have the design of the grid here. And you’ll notice that this grid, the found set is using a relation, order to order details. So even though the header is the order aligns here, the whole grid is based on that relation. That’s what loads those records. So it’s still data bound. And I put the data providers on there and here’s the foreign key to the product, but you’ll notice that I also placed a value list and that the edit type is type ahead. So you have to change this edit type property. If you want the column to be editable, you’ll notice that for quantity available, which is just a look up off the product, the edit type is none, because that would be read only. And then for quantity and price, those are both made editable, but the sub total, which is a calculated value, is not editable. So pretty easy to make things editable. You’ll also notice that I disabled, or I can disable grouping. So you might not want someone to be able to group this. You could just enable, disable row group and they won’t get any of the group ability on these columns. Do that here and I’ll save that. If I come in here, now you’ll see that I don’t get the option to group by, and I can’t drag these down here to group by those columns. So you may want some grids that we’re grouping is really disabled and you have control over that. One of the other things that you’ll wanna do when you’re doing an editable grid is to handle some of the data changes in code. So you’ll see that there’s an event on this grid called on column data change. And I’m just gonna set a rule here that if I change the product, that I wanna relook up the unit price from that product. So, and you can see that what gets passed in is the record index and then the column index that was changed. And I use the grid API to get the column and get the data provider. And if it was product ID, then I wanna go and reset the unit price. So if I select a different product, it doesn’t make sense that the unit price stayed the same. So, we would look it up and let’s test that out. So, coming in here, I’m gonna change from the crab meat to chai. Try another one. I was working moments ago. Make sure this is getting called. It is getting called. I have done, I know what it is. I have a bug in my code here. It worked because I used the first record before. This should be get record, get record, the concept index. I just assumed it was, or the selected record, but it’s passing in the index. So there you get to see live debugging. Now it should be working. Let’s try this again. Oh, I wanna turn this off now. Thank you all for patiently following as I debug my example. All right. No. That worked. That looked it up. Must be something on some bug I have in my code. Anyway, you get the idea because it passes in the, the found set index and the column index, you know exactly which cell and which record were edited. It seems that something is only doing working on the first record in my code, I think. So that’s how you can handle data changes and I attach them rule to look up the, to look up the price. I want to point out that the, I should pick something which was, I think out of stock. Let me, let me just relaunch here to get it in a state where it was. Now we have, let’s see if we can get one where there’s something out of stock. No. If I pick something out of stock, there we go. That’s right. Yeah. I just wanted to refresh. Okay. Thank you. There we go. So you’ll notice that when the quantity is not available, this one becomes highlighted in red. And although I could change say the quantity of this, out’s updating. That’s good. If I double click on this, it doesn’t, it’s not editable. So how do you, how do you say whether or not a field is editable? And that can be done through what’s called an is editable data provider. And I’ll also talk about how I got the style to switch on the cell itself. So let’s take a look back at this grid at the quantity. And you’ll notice that that although the edit type is text field, I have something else down here called is editable data provider. And I have a calculation called can change quantity. And basically it’s looking at the product quantity available field. And if that’s greater than zero, it’s true or one. Less than is false. Basically, if you just return a non zero value out of your calculation, then the field will be marked as editable. So it does that on a record by record basis, which is why you saw that the, the first line was not editable, but subsequent lines were editable. There’s also a cell style class. And we’ll, I guess, since we’re here, we’ll look at the calculation first. And I’ll show where it binds on the form. And this is basically saying if the product is unavailable, we’ll use the style class quantity locked. Otherwise, we’ll use quantity OK. And if we look back now on the form, over here, you can see that there is a, a style class data provider for that particular column. And it’s calling that calculation. And finally, if we go to the, the CSS for that or the less file for that, you can see that I have two selectors here, quantity locked and quantity OK. And that’s where the color and the bold comes from. Otherwise, it just inherits whatever would have been there instead. And that’s how we got the, the red to show up there. So I hope that shows you some of the tricks you can do for editable. You can do handle data change and everything is data bound. And, and so you should see, for example, as you edit calculations are, are updating here, etc. I have one more example, which is the, the power grid. And this is a different component, although they’re similar, it’s, it’s a bit different. So what I want to do is show you, these are, this is sort of like a sales analysis. And I want to look at say product sales by customer for the year or, by, let’s do employee and country. And these are grouped in this case. The one thing you’ll notice that’s different is the, it’ll show the number of, of records in the, in the group. And it’ll also, should make these bigger. It’ll also show the total sales and the total units sold. And this is, this is right now in pivot mode. So it’s doing it by, by year as well. So we can kind of do a cross tab or a pivot to really drill down into what we, what we want to see. We can take it off of pivot mode and then we can just, you know, run it by grouping and see all the, the fields, which doesn’t make as much sense in this case unless we turn a lot of those off because they’re really meant to be groupable. So if we go back to pivot mode, you can see that, that it’s pretty easy to change what you want to look at. I’ll leave year as the, the vertical or the column, uh, value. And if we wanted instead look at, you know, by customer and by, you know, what’s good is products and categories. So we can do category. And product is probably better example. So now you can see it, you know, year over year by, by category by product. Uh, this is just like the, the grid itself is different. It’s read only. It takes a, a data set. Uh, so it’s not really data bound to, uh, a back end data source, but it takes a data set, which is, you know, uh, in cash, effectively. And, uh, it analyzes it within the component. Um, uh, so it’s a little bit different. It’s somewhat disconnected from the back end. Um, it’s also read only for that reason. So you wouldn’t, you wouldn’t, uh, edit these. Uh, however, uh, many of the other events apply. So for example, if you wanted to, um, construct a pivot view that you want to give to your customers, but don’t want them to edit it, that’s possible. You could also, uh, allow them to, to arrange the pivot or the, the grouping anywhere they like and then persist it and, and so that every time they come back, uh, it looks the same or they could even save different versions, so they could do some analysis and then, and then have different, you know, slices of, of sales here. And, uh, they could come back and, and review those by saving them as different, sort of grid configurations. It’s quite easy to do. Um, if you, again, if you, sort of similar to, uh, and I’ll show this form, it’s called, uh, power grid, if you wanted to have something preconfigured, uh, again, you can just go to, um, first of all, you can determine whether or not it shows up in pivot mode with this. Um, you can also, for example, the year was, uh, one of the pivot columns, uh, and you can see that I set it to pivot index zero instead of negative one, uh, and then also I set enable pivot to true, which means I can drag it in as, as the column pivot. Um, whereas the other ones I couldn’t, it didn’t make much sense to look at the other things. It is a column pivot, uh, but if I wanted to preconfigure some stuff, I could say, uh, say we want to do a category and, proto, that’s how I had it set up category and product zero and one. You know, that means that by default is going to show up grouped like that or pivoted like that. Uh, it’s up to you if you want to sort of predefined how you want it to look and then you can also, uh, disable, uh, people from changing it if that’s what you want it. Uh, so that’s the power grid. Um, it lets look at the code behind that because I think that’s important. Uh, because in this case, it’s not bound to the, the same data sources to form. Uh, it, it just takes a, uh, effectively a cached data set from a query in this case. And you can see that, uh, using server-wise query builder, I did a select on order details, which gets me kind of the low level data. And here’s all the things I might want to aggregate by in my joins. And then I just added those. And what’s important is that these, the names of your, uh, fields in your data set match the data provider property. So if we look at the. And you want of these, they have this property data provider, that has to be the same. If those are the same, then the value that you’ve loaded will show up in the grid. And, uh, all we do here is we run the query and we just call this, uh, render data and pass in the data set. So if you, if you have other things going on, you want to change what’s in actually in the data, you can do that as well dynamically and just keep pumping data in, uh, without necessarily blowing away all your grouping settings or your pivot settings. So pretty easy to do this, but this is, they just want to get across the point. This is a very different sort of setup than when we’re doing the data bound to the backend stuff. Um, one last thing I want to show is something that I’ve done, uh, some configurations that I’ve done globally on the grids. And this is new in this release. Um, so if I look at the on solution open, this is an event that fires when, when I first open the application, uh, I’ve set some default, uh, options. So, um, uh, on the, um, data grid, I want to make, I like that nice comfy row height of, uh, of 40 pixels. I come back here. Uh, you can see that this is quite some, uh, nice spacing. Uh, and it’s the same here. Uh, that’s actually being set as a global config option, uh, through that plugin. Uh, so that means all my grids, even if I had 500 of them, I can set things and it’s not just row height. There’s lots of properties you can set. You can change the icons. You can change, uh, really, you know, fine tune or fine tweak the, uh, the grids on a global setting. What’s more, you can override them on an individual level. So let’s say, you know, for, uh, you know, line item, uh, view like this, I want my columns to be a bit, uh, a bit closer together. I could come into that form, uh, which was order detail. And I could, um, also add the config option here of row height, um, and the value I’m going to set for that is going to be say, uh, 25 instead of the 40 that I had set. So now I’ll relaunch the client. And you can see on the first grid, I still have that comfy 40 pixel row height, but on the second grid, you can see it’s a bit tighter. It overrides, uh, the default. Uh, so, um, you have some control to set global settings, but then override it on an individual level. The values that you can set, um, not to get too much under the hood, but, um, the underlying library for this is the AG grid component, which is a, um, uh, really a third party, uh, um, library. And there are, um, if you go to their grid properties documentation, uh, here’s many of these properties that you can set. Now some of them don’t really make sense in the context of Servoy, because we handle a lot of things for you, but, um, you can imagine, uh, just changing like sort icons and things like that, um, uh, reconfiguring the tool panel that can all be set, uh, through there. So we have our own documentation, but we also have links to this documentation because at some point you just have to go right to that source and look it up. Um, okay, that concludes all of the examples. Um, I’m wondering if maybe we have some questions, which we can have, maybe those can come in and I’ll just take us through a quick overview of what we saw. Yeah, there’s a couple of questions coming in, uh, Sean. Okay. The question and others that to have any burning questions, then feel free to, uh, to post them. And now is the time to do so. Any kind of question is fine. We won’t laugh at you, I promise. Although, depending on how silly your questions we may laugh at you. One asks on Servoy 8.2.3 when we use the AG grids, our application required internet access to show the component on runtime is this still the case in the latest version. Uh, that’s a good question. I remember seeing this case. Uh, I’m not sure if this is solved in the latest version or not. I’ll check with, uh, with R&D. This, uh, the latest release was built this morning and there is, uh, I should go to the project page, um, just to show you. So if you go to, um, actually jump to the proper repository. So if you go to the, um, the project page, you’ll get some documentation and you’ll get some release notes. So, um, this release was built this morning. Um, and I don’t see something about the internet connection. Uh, but I am aware of that case. So I’m not sure if it made it in this release. Uh, if not, it will be in the 20 1906 release. Um, the Q2 release. Okay. And a second question, I think you covered part of it, but maybe not all of it. On editable grids, do you have to call a save function or does this, does this happen automatically? How can we handle reverting any changes made? Oh, okay. Yeah, that’s a good question. Um, so you, you don’t have to explicitly call a save function. Um, nor does it necessarily automatically save. That’s more of how you generally program anything data bound in Servoy. So, uh, again, you are at the end of the day editing these record objects, which are in a found set, which is Servoy’s, uh, sort of world of data binding. Uh, and there are two modes that you can operate in when you’re editing data. There’s, uh, auto save on and auto save off. So if you have set auto save to off, uh, this is like a global setting, then nothing that you would do in the grid would incidentally save something. However, if auto save were on, then yes, as you make changes in the grid, those get pushed back up to the server and then ultimately pushed to the database. There is a webinar we did quite some time ago, all about this whole topic of working with data and editing and saving. Uh, so if you go to the tech webinar homepage where we have all the recordings posted, I think the name of what was called saving data, uh, and are working with data and that is all covered, I think, in detail in that webinar. Okay. So the expected behavior is it’s the same as any other components or where, right? Auto save and found sets and transactions and rollbacks. Yeah. It all works the same. Excellent. That’s very cool. All right. That’s it for the questions for today. I guess, uh, this is extremely clear or this is way above the heads of many people or people are sleeping. I’m not sure the quite a few people in the, in the session today and there seem to be awfully quiet compared to other days. I guess everybody is installing, uh, 2019, 03 because it is a very cool release, I think. Yeah, maybe they’re busy, uh, trying it out. Um, yeah. And if you, if you want to get this, uh, it’s available through the web package manager. Uh, so I didn’t demonstrate how to do that, but generally any component you can go through the IDE, launch the web package manager and, and add the component to your project. And I think in 2019, O, O three now, when you create a new project, uh, the NG grids, since it’s a common library is automatically installed in your project. So one less step you have to take. Um, I think we can, we can kind of just cruise through the overview. I think we covered a lot. There’s the data grid, which we showed, which has the, um, the editability, the data binding to the back end. So all the normal stuff you get with serve, boy is available. We have the power grid, which was the third example I showed that is sort of working with a cash data set. So really good for analytic stuff, uh, pivot mode. Um, and you can really pump a lot of records into that. It is cash. So it’s not like it’s going to the database fluidly like that, like the data grid, but we see, uh, uh, performance pretty good up to a hundred thousand records, depending on, you know, some other variables. Um, the third thing that, that I, or the fourth thing that I showed was the NG service. That is new in this release. And that was, uh, a plug in, which allows you to programmatically set your global configs, uh, for things like icons, toolbars, little level configuration. Uh, and you can override that on the component level if you want to. So that’s a nice, uh, nice control there. Um, yeah, I think, uh, I think you get the idea here, some of the editor types that that we were able to show. Um, finally, uh, I want to just give you guys one slide about Servoy world. Again, it’s in November, the early bird special is still running. It’s, well, the end of this month, uh, after which the price goes up a bit. So if you are planning to come, make your arrangements now, uh, before the price goes up, also there’s some limited availability on, uh, the conference hotel at, uh, at the cheaper price. So you want to make those accommodations as soon as you’re sure that you’re going. Is that the Mofen pick? We did it there. Y’all was it 2007? Yeah. I think that was one of the earlier conferences. Yeah. Yeah. Yeah. And that’s a really nice place right on the water. Very nice. Direct access to, uh, to downtown. If you want to, I would, I’d like to emphasize on the fact that we are expecting all of the regular attendees to also be at the hackathon, where you will be, uh, coding, uh, in a competition to build an application independent of your level of server. You don’t have to be in a top level expert because a lot of it will be very functional, getting things out and done. We are expecting you all to, uh, to join, and we charge a very small fee for it. And all of those proceeds go to a good cost that we will select it together. So, uh, make sure to, uh, to sign up for that. It’s very cool to do a hackathon. It’s a great learning experience. Um, and it also gives us insight on how you guys make use of the tools. Our engineers will be walking around their life and you’ll be able to show them what you like and what you don’t like about the, uh, the IDs. It’s also for us and for Sean as, uh, as, as, product owner, a great inputs to get to, uh, life inside in, in how you guys use the tool. So I’m, yeah, we’re, we’re also taking suggestions for the hackathon, uh, both what is built and also, uh, if you have a charity that you want to, uh, lobby for the proceeds to go for, uh, give us a post on the forum or a direct email. If you have feedback on that, I’ll put up some useful links here as well as, uh, as we take it home. Um, uh, the forum is where you can give us, uh, all of your feedback and also ideas about the hackathon, ideas for more webinars, questions, comments abuse. I think that’s it. Excellent. Well, we run a little bit late, but we covered a few more things and we all look forward to seeing you again. Do we have an upcoming topic? Sean or is that still new works? Our upcoming topic is still in the works to be determined. We have, we have quite a number of, um, uh, extensions that have been developed in the past a few weeks and months. Uh, but we’ve been so busy with releasing, uh, uh, some of the core stuff that we haven’t given those much attention, but they are out there. Uh, we have customers using them and probably ever want you to know about them. So probably one of those or a couple of those extensions we’d like to share with you guys in a webinar. Excellent. I look forward to seeing you all again in two weeks. We will post this soon. Um, first place to go as forum and YouTube where they usually show before on the website and thanks again, Sean for all the work and the great presentation. Thanks.