left-icon

Blazor Succinctly®
by Michael Washington

Previous
Chapter

of
A
A
A

CHAPTER 9

Help Desk Ticket Administration


In this chapter, we will detail the steps to create screens that allow administrators to manage help desk tickets.

In the ticket administration page that we will construct, help desk tickets can be updated and deleted, but ticket details can only be added.

Create the ticket administration page

 New item

Figure 61: New item

We will now create a page that will allow all users to see their tickets, and for administrators to see all tickets.

  1. Add the ticket administration page to the project and name it Tickets.razor.
  2. Use the following code for the Razor control:

Code Listing 37: Tickets.razor

@page "/tickets"

@using Microsoft.AspNetCore.Identity

@using Microsoft.EntityFrameworkCore

@using SyncfusionHelpDesk.Data;

@using SyncfusionHelpDesk.Models

@using System.Security.Claims

@inject AuthenticationStateProvider AuthenticationStateProvider

@inject RoleManager<IdentityRole> RoleManager

@inject UserManager<ApplicationUser> UserManager

@inject IDbContextFactory<SyncfusionHelpDeskContext> DbFactory

@inject SyncfusionHelpDeskService SyncfusionHelpDeskService

@code {

#nullable disable

    ClaimsPrincipal CurrentUser = new ClaimsPrincipal();

    private string PageTitle = "Administration";

    private string ADMINISTRATION_ROLE = "Administrators";

    private bool isAdmin = false;

    public IQueryable<HelpDeskTicket> colHelpDeskTickets { get; set; }

    private HelpDeskTicket SelectedTicket = new HelpDeskTicket();

    public bool EditDialogVisibility { get; set; } = false;

    public bool DeleteRecordConfirmVisibility { get; set; } = false;

}

Add link in NavMenu.razor

Here’s how to add a link to the ticket control in the navigation menu:

  1. Open the NavMenu.razor control in the Shared folder and remove the code for the links to the counter, auth, and weather pages from Code Listing 38.

Code Listing 38: Remove NavMenu Code

    <div class="nav-item px-3">

        <NavLink class="nav-link" href="counter">

            <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter

        </NavLink>

    </div>

    <div class="nav-item px-3">

        <NavLink class="nav-link" href="weather">

            <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather

        </NavLink>

    </div>

    <div class="nav-item px-3">

        <NavLink class="nav-link" href="auth">

            <span class="bi bi-lock-nav-menu" aria-hidden="true"></span> Auth Required

        </NavLink>

    </div>

  1. Add the code from Code Listing 39 under the <Authorized> tag.

Code Listing 39: Add NavMenu Code

    <div class="nav-item px-3">

        <NavLink class="nav-link" href="tickets">

            <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Tickets

        </NavLink>

    </div>

This will display a link to the Tickets.razor page, but this link will only be displayed for users who are logged in.

 Tickets link

Figure 62: Tickets link

When we run the application and log in, we will see the link to the Tickets page. When we click on the link, we are brought to the Tickets page, which is currently blank.

Using the Syncfusion DataGrid

The Syncfusion DataGrid, programmatically called SfGrid, is a control that allows you to display tabular data. We will use it to display the help desk tickets. This control will allow us to page, sort, and trigger record editing.

It has a DataSource property that we will bind to a collection of the help desk tickets. It also has properties that allow for paging and sorting.

The control also allows us to define the columns, which will contain the tabular data, as well as Edit and Delete buttons. To use the Syncfusion DataGrid:

  1. Add the following markup to the Tickets.razor file:

Code Listing 40: DataGrid

<style>

    .custom-dialog .e-dialog {

        max-height: 90vh !important; /* Customize the max height */

    }

</style>

<AuthorizeView>

    <Authorized>

        <h1>@PageTitle</h1>

        <div>

            <div id="target" style="height: 500px;">

                <SfGrid ID="Grid"

                        DataSource="@colHelpDeskTickets"

                        AllowPaging="true"

                        AllowSorting="true"

                        AllowResizing="true"

                        AllowReordering="true">

                    <GridPageSettings PageSize="5"></GridPageSettings>

                    <GridEvents CommandClicked="OnCommandClicked"

                                TValue="HelpDeskTicket">

                    </GridEvents>

                    <GridColumns>

                        <GridColumn HeaderText="" TextAlign="TextAlign.Left" Width="150">

                            <GridCommandColumns>

                                <GridCommandColumn Type=CommandButtonType.Edit

                                               ButtonOption="@(new CommandButtonOptions() {

                                               Content = "Edit",

                                               CssClass = "e-primary" })">

                                </GridCommandColumn>

                                @if (isAdmin)

                                {

                                    <GridCommandColumn Type=CommandButtonType.Delete

                                                ButtonOption="@(new CommandButtonOptions() {

                                                Content = "Delete",

                                                CssClass = "e-danger"})">

                                    </GridCommandColumn>

                                }

                            </GridCommandColumns>

                        </GridColumn>

                        <GridColumn IsPrimaryKey="true" Field=@nameof(HelpDeskTicket.Id)

                          HeaderText="ID #" TextAlign="@TextAlign.Left" Width="70">

                        </GridColumn>

                        <GridColumn Field=@nameof(HelpDeskTicket.TicketStatus)

                          HeaderText="Status" TextAlign="@TextAlign.Left" Width="80">

                        </GridColumn>

                        <GridColumn Field=@nameof(HelpDeskTicket.TicketDate)

                          HeaderText="Date" TextAlign="@TextAlign.Left"

                                    Format="d" Width="80">

                        </GridColumn>

                        <GridColumn Field=@nameof(HelpDeskTicket.TicketDescription)

                           HeaderText="Description" TextAlign="@TextAlign.Left" Width="150">

                        </GridColumn>

                        <GridColumn Field=@nameof(HelpDeskTicket.TicketRequesterEmail)

                            HeaderText="Requester" TextAlign="@TextAlign.Left" Width="150">

                        </GridColumn>

                    </GridColumns>

                </SfGrid>

            </div>

        </div>   

    </Authorized>

    <NotAuthorized>

        <p>You are not authorized to view this page.</p>

    </NotAuthorized>

</AuthorizeView>

  1. Add the following to the @code section:

Code Listing 41: DataGrid code

    protected override async Task OnInitializedAsync()

    {

        // Get the current user

        var authState = await AuthenticationStateProvider

        .GetAuthenticationStateAsync();

        CurrentUser = authState.User;

        // Check if the current user is an Administrator

        if (CurrentUser.Identity?.IsAuthenticated == true)

        {

            var CurrentApplicationUser =

            await UserManager

            .FindByNameAsync(CurrentUser.Identity.Name);

            // See if the current user is an Administrator

            isAdmin =

            await UserManager.IsInRoleAsync(

                CurrentApplicationUser, ADMINISTRATION_ROLE);

        }

        // GetHelpDeskTickets returns IQueryable that the

        // SfGrid will use to only pull records for the

        // page that is currently selected

        colHelpDeskTickets = SyncfusionHelpDeskService

        .GetHelpDeskTickets(

            DbFactory, isAdmin, CurrentUser.Identity.Name);

        // Set PageTitle

        PageTitle = isAdmin

        ? "Administration"

        : "My Help Desk Tickets";

    }

    public async void OnCommandClicked(

        CommandClickEventArgs<HelpDeskTicket> args)

    {

        // Code to be added later

    }

The OnInitializedAsync method is the first method to run when the page loads. This method populates the colHelpDeskTickets collection, which is bound to the DataSource property of the DataGrid.

DataGrid control

Figure 63: DataGrid control

Run the application, log in as an administrator, and navigate to the Tickets page. We will see the help desk ticket records displayed in the DataGrid.

Sorting

Figure 64: Sorting

Sorting is performed by clicking on the column headers.

Paging

Figure 65: Paging

The grid also enables paging.

Deleting a record

We will implement the functionality to delete a help desk ticket record. To enable this, the DataGrid allows us to create custom command buttons by using the CommandClicked property that we currently have wired up to the OnCommandClicked method.

We will use the OnCommandClicked method to display a pop-up window using the Syncfusion Dialog control, which will require the user to confirm that they want to delete the record.

Syncfusion Dialog control

The Dialog control is used to display information and accept user input. It can be displayed as a modal control that requires the user to interact with it before continuing to use any other part of the application. Here are the steps to implement it:

  1. Enter the code from Code Listing 42 inside the AuthorizeView control tag.

Code Listing 42: Confirm delete dialog

<SfDialog Target="#target"

            Width="100px"

            Height="120px"

            IsModal="true"

            ShowCloseIcon="false"

            @bind-Visible="DeleteRecordConfirmVisibility">

    <DialogTemplates>

        <Header> DELETE RECORD ? </Header>

        <Content>

            <div class="button-container">

                <SfButton CssClass="e-primary"

                OnClick="ConfirmDeleteYes">Yes</SfButton>

                <SfButton OnClick="ConfirmDeleteNo">No</SfButton>

            </div>

        </Content>

    </DialogTemplates>

</SfDialog>

  1. Change the OnCommandClicked method to the following to respond to the Delete button being selected for a help desk record. This method will open the confirmation dialog.

Code Listing 43: Open confirmation dialog

public async void OnCommandClicked(

     CommandClickEventArgs<HelpDeskTicket> args)

 {

     // Get the selected Help Desk Ticket

     SelectedTicket =

         await SyncfusionHelpDeskService.GetHelpDeskTicketAsync(

             DbFactory, args.RowData.TicketGuid);

     if (args.CommandColumn.ButtonOption.Content == "Delete")

     {

         // Open Delete confirmation dialog

         this.DeleteRecordConfirmVisibility = true;

         StateHasChanged();

     }

 }

Note: StateHasChanged, which is used in Code Listing 43, notifies a component (in this case, the Tickets page) that its state has changed and causes that component to re-render. This is usually only required when that state was changed by a JavaScript interop call. The Syncfusion Dialog control uses JavaScript interop in the underlying code to open the dialog, so StateHasChanged is required in this case.

  1. Add the method from Code Listing 44, which will just close the dialog if the user selects No.

Code Listing 44: Close dialog

public void ConfirmDeleteNo()

 {

     // Open the dialog

     // to give the user a chance

     // to confirm they want to delete the record

     this.DeleteRecordConfirmVisibility = false;

 }

Using @ref to capture references to components

Adding a @ref attribute to a component allows you to programmatically access and manipulate a control or component. To implement it:

  1. Add the @ref attribute to a component, then define a field with the same type as the component.
  2. Add the property from Code Listing 45 to the SfGrid control.

Code Listing 45: DataGrid @ref

@ref="gridObj"

  1. Now, add the corresponding field to the @code section from Code Listing 46.

Code Listing 46: DataGrid property

SfGrid<HelpDeskTicket> gridObj;

  1. Add the method from Code Listing 47 to delete the record when the user selects Yes in the dialog. This method will refresh the DataGrid by calling gridObj.Refresh(), which uses the gridObj object defined with the @ref attribute.

Code Listing 47: Delete record

public async void ConfirmDeleteYes()

 {

     // The user selected Yes to delete the

     // selected Help Desk Ticket

     // Delete the record

     var result =

         await SyncfusionHelpDeskService.DeleteHelpDeskTicketsAsync(

             DbFactory, SelectedTicket);

     // Close the dialog

     this.DeleteRecordConfirmVisibility = false;

     // Refresh the SfGrid

     // so the deleted record will not show

     await gridObj.Refresh();

 }

When we run the application, we can click the Delete button to open the dialog.

 A screenshot of a computer

Description automatically generated

Figure 66: Delete confirmation

Selecting No will just close the dialog. Selecting Yes will delete the selected record and refresh the DataGrid.

Edit ticket control

Edit ticket control

Figure 67: Edit ticket control

We will construct an EditTicket control that will be placed inside a dialog on the Tickets page, which will be displayed when you edit a help desk ticket. We do this to allow this control to be reused in the EmailTicketEdit.razor page covered in the following chapter. To construct the EditTicket control:

  1. In the Pages folder, create a new control called EditTicket.razor using the code from Code Listing 48.

Code Listing 48: EditTicket.razor

@using System.Security.Claims;

@using Microsoft.AspNetCore.Identity

@using Microsoft.EntityFrameworkCore

@using SyncfusionHelpDesk.Data

@using SyncfusionHelpDesk.Models

@inject RoleManager<IdentityRole> RoleManager

@inject UserManager<ApplicationUser> UserManager

@inject AuthenticationStateProvider AuthenticationStateProvider

@inject IDbContextFactory<SyncfusionHelpDeskContext> DbFactory

@inject SyncfusionHelpDeskService SyncfusionHelpDeskService

  1. Next, add the code from Code Listing 49 for the markup of the control.

Code Listing 49: EditTicket markup

<!-- This style will show -->

<!-- the entire content of TicketDescription -->

<!-- on hover over the field -->

<style>

    .e-grid .e-rowcell {

        white-space: nowrap;

        overflow: hidden;

        text-overflow: ellipsis;

    }

        .e-grid .e-rowcell:hover {

            overflow: visible;

        }

</style>

<div>

    <SfDropDownList Enabled="isAdmin" TItem="HelpDeskStatus" TValue="string"

                    PopupHeight="230px"

                    FloatLabelType="@FloatLabelType.Always"

                    Placeholder="Ticket Status"

                    @bind-Value="@SelectedTicket.TicketStatus"

                    DataSource="@HelpDeskStatusData.Statuses">

        <DropDownListEvents TItem="HelpDeskStatus" TValue="string" />

        <DropDownListFieldSettings Text="Text" Value="ID" />

    </SfDropDownList>

</div>

<div>

    <SfDatePicker ID="TicketDate" Enabled="isAdmin"

                  Placeholder="Ticket Date"

                  FloatLabelType="@FloatLabelType.Always"

                  @bind-Value="@SelectedTicket.TicketDate"

                  Max="DateTime.Now"

                  ShowClearButton="false">

    </SfDatePicker>

</div>

<div>

    <SfTextBox Enabled="isAdmin" Placeholder="Ticket Description"

               FloatLabelType="@FloatLabelType.Always"

               @bind-Value="@SelectedTicket.TicketDescription">

    </SfTextBox>

</div>

<div>

    <SfTextBox Enabled="isAdmin" Placeholder="Requester Email"

               FloatLabelType="@FloatLabelType.Always"

               @bind-Value="@SelectedTicket.TicketRequesterEmail">

    </SfTextBox>

</div>

<br />

<SfGrid @ref="GridRef" DataSource="@SelectedTicket.HelpDeskTicketDetails">

    <GridColumns>

        <GridColumn Field="TicketDetailDate"

                    HeaderText="Date"

                    Format="MM/dd/yyyy"

                    TextAlign="TextAlign.Left"

                    Width="150">

        </GridColumn>

        <GridColumn Field="TicketDescription"

                    HeaderText="Description"

                    TextAlign="TextAlign.Left"

                    Width="300">

        </GridColumn>

    </GridColumns>

</SfGrid>

<br />

<SfTextBox Placeholder="New Help Desk Ticket Detail"

           @bind-Value="@NewHelpDeskTicketDetail">

</SfTextBox>

<br />

<br />

<SfButton CssClass="e-small e-success"

          @onclick="AddHelpDeskTicketDetail">

    Add Detail

</SfButton>

<br />

<br />

  1. Add the following code, which implements the code for the control:

Code Listing 50: EditTicket code

@code {

#nullable disable

    [Parameter]

    public HelpDeskTicket SelectedTicket { get; set; }

    private SfGrid<HelpDeskTicketDetail> GridRef;

    private string ADMINISTRATION_ROLE = "Administrators";

    private bool isAdmin = false;

    private string NewHelpDeskTicketDetail = "";

    protected override async Task OnInitializedAsync()

    {

        var authState =

        await AuthenticationStateProvider

        .GetAuthenticationStateAsync();

        var user = authState.User;

        if (user.Identity?.IsAuthenticated == true)

        {

            var CurrentUser =

            await UserManager.FindByNameAsync(user.Identity.Name);

            // If they are an Administrator

            // Enable editing

            isAdmin =

            await UserManager

            .IsInRoleAsync(CurrentUser, ADMINISTRATION_ROLE);

        }

    }

    private void AddHelpDeskTicketDetail()

    {

        // Create New HelpDeskTicketDetails record

        HelpDeskTicketDetail NewHelpDeskTicketDetails =

            new HelpDeskTicketDetail();

        NewHelpDeskTicketDetails.HelpDeskTicketId =

            SelectedTicket.Id;

        NewHelpDeskTicketDetails.TicketDetailDate =

            DateTime.Now;

        NewHelpDeskTicketDetails.TicketDescription =

            NewHelpDeskTicketDetail;

        // Add to collection

        SelectedTicket.HelpDeskTicketDetails

            .Add(NewHelpDeskTicketDetails);

        // Clear the Text Box

        NewHelpDeskTicketDetail = "";

        // Update the Grid

        GridRef.Refresh();

    }

}

Notice that this exposes a SelectedTicket parameter (of type HelpDeskTickets) that will accept a reference of a help desk ticket record.

  1. In the Tickets.razor control, add the markup from Code Listing 51 to display the EditTicket.razor control in a new Dialog control.

Code Listing 51: EditTicket Dialog

<SfDialog Target="#target"

            CssClass="custom-dialog"

            Width="500px"

            IsModal="true"

            ShowCloseIcon="true"

            @bind-Visible="EditDialogVisibility">

    <DialogTemplates>

        <Header> EDIT TICKET # @SelectedTicket.Id </Header>

        <Content>

            <EditTicket SelectedTicket="@SelectedTicket" />

        </Content>

        <FooterTemplate>

            <div class="button-container">

                <SfButton CssClass="e-primary"

                OnClick="SaveTicket">Save</SfButton>

            </div>

        </FooterTemplate>

    </DialogTemplates>

</SfDialog>

Note that this instantiates the EditTicket control (the EditTicket.razor code page) and passes a reference to the currently selected help desk ticket record (@SelectedTicket) through the SelectedTicket property.

  1. Add the following to the @code section to implement the functionality for the Save button in the dialog:

Code Listing 52: SaveTicket method

    public async Task SaveTicket()

    {

        // Update the selected Help Desk Ticket

        var result =

            await SyncfusionHelpDeskService.UpdateTicketAsync(

                DbFactory, SelectedTicket);

        // Close the Edit dialog

        this.EditDialogVisibility = false;

        // Refresh the SfGrid

        // so the changes to the selected

        // Help Desk Ticket are reflected

        await gridObj.Refresh();

    }

  1. Add the following code to the OnCommandClicked method under the existing SelectedTicket line, to open the dialog when the Edit button is clicked on a row in the DataGrid.

Code Listing 53: Open dialog

        if (args.CommandColumn.ButtonOption.Content == "Edit")

        {

            // Open the Edit dialog

            this.EditDialogVisibility = true;

            StateHasChanged();

        }

 Edit ticket dialog

Figure 68: Edit ticket dialog

When we run the application, we can click the Edit button next to a record to open it in the dialog. The help desk ticket values at the top of the form can be edited and saved by clicking the Save button at the bottom of the dialog.

On the bottom half of the dialog, help desk ticket detail records can be added by entering text in the text box and clicking the Add Detail button.

 Edit ticket

Figure 69: Edit ticket

The help desk detail record will be added, but it will not be saved until the Save button is clicked.

Scroll To Top
Disclaimer

DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.