My boss gave me the idea for this project. Recently we switched to a different ticketing software at my work. This new software, RepairShopr, has a pretty extensive API. This let me create this site. The idea behind the project was to create a website that would be displayed on a TV in our service department and it would contain information about what repairs we have coming up. Recently I’ve been getting really into learning C# and figured this was a great chance to test my skills. I decided to use Blazor because I really like how it works and I’m not the biggest fan of Javascript.
Anyways, this post is not going to be a full tutorial since this was a larger project but I wanted to document my thought process behind some of the decisions I made during the build process.
The site can be found here. But you won’t be able to log in. So I will post screenshots of it below. If you want to checkout the full code you can head over to the Github repo.
- At first I had the idea of using SignalR to handle the page update process where the Blazor components would connect to a hub which would make the API calls for the tickets. This didn’t work out. Since the ticket software we are using doesn’t have a webhook (as far as I could tell) I have to manually call the API every time I want to update the tickets. This put a lot of stress on the SignalR connection and used up lots of resources when it was published. So instead I realized I didn’t need SignalR and just made the API calls from the Blazor server.
/// <summary> /// Returns the list of tickets from the RS API /// </summary> /// <returns></returns> public List<RepairTicket> GetTicketList() { List<RepairTicket> fullList = new List<RepairTicket>(); string ticket_url = _urlHeader + _urlPrefix + _urlRoute; try { using (WebClient webClient = new WebClient()) { webClient.Headers.Add("Content-Type", "application/json; charset=utf-8"); //; charset=utf-8 webClient.Headers.Add("Authorization", _apiKey); JObject job; _pageNum = 1; int pages; do { webClient.BaseAddress = ticket_url + _pageNum++; var json = webClient.DownloadString(""); job = JObject.Parse(json); pages = (int)job.SelectToken("meta").SelectToken("total_pages"); fullList.AddRange(ParseJson(job)); } while (_pageNum <= pages); return fullList; } } catch (WebException ex) { throw; } }
This is one of my API connection methods. This gets the list of tickets as json, the parses the data into RepairTicket objects
- Something I had trouble with at first was figuring out how to share data between Blazor components. The solution I found was to pass the values needed into them at creation. The image below shows an example of this.
<TopControls _apiConn="@_apiConnG" _context="@context" _userContext="@httpContextAccessor" _userGuid="@_userGuid" Greensboro="@_greensboro" Winston="@_winston" _refreshRate="@REFRESH_RATE" /> <div id="grid"> <div class="row"> @*<ContactList />*@ </div> <div class="row task-list-card-row"> <MainInfoComponent _apiConn="@_apiConnG" _userContext="@httpContextAccessor" _refreshRate="@REFRESH_RATE" /> </div> <div class="row task-list-card-row"> <TicketListComponent _apiConn="@_apiConnG" _userContext="@httpContextAccessor" _refreshRate="@REFRESH_RATE" /> </div> <div class="row task-list-card-row"> <div class="col-md-6"> @*<PieChart />*@ </div> </div> </div>
- To keep the tickets up to date, I have to repeatedly call the api and re-show the data. This is one spot where I’m sure there’s a better way to keep the page updated without hammering the API server but I couldn’t figure out anything else.
- Since I havn’t really done much website development before, figuring out how to create a dropdown menu and let users interact with it (especially the draggable dropdown menu) was a bit of a challenge. This is what I came up with.
<div class="dropdown-menu btn btn-light" ondragover="event.preventDefault();" data-toggle="dropdown" aria-labelledby="dLabel"> @foreach (var item in itemList) { if (item != null) { <span class="dropdown-item" draggable="true" style="list-style-type:none; height:30px" @key="item.number" tabindex="1" @ondrop="@(()=> Drop(item))" @ondrag="@(()=> StartDrag(item))"> <span>@item.status</span> </span> } else { <li>Oh snap, something went wrong :(</li> } } </div>
- Something I realized early on was that it’s annoying to have to re-enter all of your customizations every time you visit the page. To fix this, and to add some security, I added a log-in feature and a database. Now when you register, you add your api key; and when you change a setting it updates your users setting table row.
The login screen.
- RepairShopr (the new ticketing software we’re using) is great, and it’s API is also great. It allowed me to get not only the tickets, but basically any information I want. Upon registering and logging in for the first time a UserSetting model is created and an API call is made to retrieve the logo for the organizations logo. When you log in next time, you will see your companies logo above the RepairShopr logo.
This page shows the logos and buttons to take you to the location of the shop you’re working at. This will determine what tickets are displayed on the page
- The main ticket page has two different layouts: Normal and Status. The normal layout displays the tickets sorted purely by the time they were created (or when the device was checked in for repair). This page is nice if you want to get an overall feel for where things are, and what devices have been in house for too long and need attention.
Normal ticket layout.
- The status page is likely where most technicans will be. This page sorts the tickets by date of arrival but also groups them by status. This is nice for staying on top of the most critical repairs. You can reorder and exclude the statuses to your liking as well. It also shows any new messages from customers in the table at the top.
Status layout.
That’s pretty much it for now. There’s a lot more I could go over about this project but it’s late and I’m tired so I’ll leave it at that for now. If you are interested in checking out the full project take a look at the repo.
Thanks!
-K