Smoothing out a few wrinkles - Code

These are the files that have been amended during this process.  Not all changes were documented in the YouTube video.  I also took the opportunity, to 

  • Deal with the numeric textbox on the products page.
  • Tidy up the menu by removing 'Counter' and renaming 'Home' to Purchase Orders' and editing the menu title to add spaces between words.
  • Delete redundant files, viz.
    • Counter.razor
    • SurveyPrompt.razor
    • FetchData.razor

There were no changes to any SQL.

C#

Index.razor

@page "/"
@using BlazorPurchaseOrders.Data

@inject IPOHeaderService POHeaderService
@inject NavigationManager NavigationManager

<div class="col-sm-12">
    <h3>Purchase Orders</h3>
    <br />
    <SfGrid DataSource="@poheader"
            Toolbar="Toolbaritems">
        <GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="POHeader"></GridEvents>
        <GridColumns>
            <GridColumn Field="@nameof(POHeader.POHeaderOrderNumber)"
                        HeaderText="No"
                        TextAlign="@TextAlign.Left"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POHeader.POHeaderOrderDate)"
                        HeaderText="Date"
                        Format="d"
                        Type="ColumnType.Date"
                        TextAlign="@TextAlign.Center"
                        Width="15">
            </GridColumn>
            <GridColumn Field="@nameof(POHeader.SupplierName)"
                        HeaderText="Supplier"
                        TextAlign="@TextAlign.Left"
                        Width="40">
            </GridColumn>
            <GridColumn Field="@nameof(POHeader.TotalOrderValue)"
                        HeaderText="Value"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="20">
            </GridColumn>
            <GridColumn Field="@nameof(POHeader.POHeaderRequestedBy)"
                        HeaderText="Requested by"
                        TextAlign="@TextAlign.Left"
                        Width="40">
            </GridColumn>
        </GridColumns>
    </SfGrid>

    <WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />

    <ConfirmPage @ref="ConfirmOrderDelete" ConfirmHeaderMessage="@ConfirmHeaderMessage" ConfirmContentMessage="@ConfirmContentMessage" ConfirmationChanged="ConfirmOrderArchive" />
</div>

@code {

    // Create an empty list, named poheader, of empty POHeader objects.
    IEnumerable<POHeader> poheader;
    POHeader orderHeader = new POHeader();

    private List<ItemModel> Toolbaritems = new List<ItemModel>();

    int POHeaderID = 0;

    private int selectedPOHeaderID { get; set; } = 0;

    WarningPage Warning;
    string WarningHeaderMessage = "";
    string WarningContentMessage = "";

    ConfirmPage ConfirmOrderDelete;
    string ConfirmHeaderMessage = "";
    string ConfirmContentMessage = "";
    public bool ConfirmationChanged { get; set; } = false;

    protected override async Task OnInitializedAsync()
    {
        //Populate the list of countries objects from the Countries table.
        poheader = await POHeaderService.POHeaderList();

        Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new order", PrefixIcon = "e-add" });
        Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected order", PrefixIcon = "e-edit" });
        Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected order", PrefixIcon = "e-delete" });
    }


    public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
    {
        if (args.Item.Text == "Add")
        {
            //Code for adding goes here
            POHeaderID = 0;
            NavigationManager.NavigateTo($"/purchaseorder/{POHeaderID}");

        }

        if (args.Item.Text == "Edit")
        {

            //Code for editing - Check that an Order has been selected from the grid
            if (selectedPOHeaderID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                NavigationManager.NavigateTo($"/purchaseorder/{selectedPOHeaderID}");
            }

        }

        if (args.Item.Text == "Delete")
        {
            //Code for deleting
            if (selectedPOHeaderID == 0)    //Check that an order has been selected
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //Populate orderHeader using selectedPOHeaderID
                orderHeader = await POHeaderService.POHeader_GetOne(selectedPOHeaderID);

                ConfirmHeaderMessage = "Confirm Deletion";
                ConfirmContentMessage = "Please confirm that this order should be deleted.";
                ConfirmOrderDelete.OpenDialog();
            }
        }
    }

    public void RowSelectHandler(RowSelectEventArgs<POHeader> args)
    {
        //{args.Data} returns the current selected records.
        selectedPOHeaderID = args.Data.POHeaderID;
    }

    protected async Task ConfirmOrderArchive(bool archiveConfirmed)
    {
        if (archiveConfirmed)
        {
            orderHeader.POHeaderIsArchived = true;
            bool Success = await POHeaderService.POHeaderUpdate(orderHeader);
            poheader = await POHeaderService.POHeaderList();
            StateHasChanged();
        }
    }
}

ProductPage.razor

@page "/product"
@using BlazorPurchaseOrders.Data
@inject IProductService ProductService
@inject ISupplierService SupplierService


<h3>Products</h3>
<br />

<SfGrid DataSource="@product"
        Toolbar="@Toolbaritems"
        AllowResizing="true">
    <GridColumns>
        <GridColumn Field="@nameof(Product.ProductCode)"
                    HeaderText="Code"
                    TextAlign="TextAlign.Left"
                    Width="40">
        </GridColumn>
        <GridColumn Field="@nameof(Product.ProductDescription)"
                    HeaderText="Description"
                    TextAlign="TextAlign.Left"
                    Width="60">
        </GridColumn>
        <GridColumn Field="@nameof(Product.ProductUnitPrice)"
                    HeaderText="Unit Price"
                    TextAlign="TextAlign.Right"
                    Format="C2"
                    Width="40">
        </GridColumn>
        <GridColumn Field="@nameof(Product.SupplierName)"
                    HeaderText="Supplier"
                    TextAlign="TextAlign.Left"
                    Width="60">
        </GridColumn>
    </GridColumns>
    <GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="Product"></GridEvents>
</SfGrid>

<SfDialog @ref="DialogAddEditProduct" IsModal="true" Width="500px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> @HeaderText </Header>
    </DialogTemplates>
    <EditForm Model="@addeditProduct" OnValidSubmit="@ProductSave">
        <DataAnnotationsValidator />
        <div>
            <SfTextBox Enabled="true" Placeholder="Product Code"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditProduct.ProductCode"></SfTextBox>
            <ValidationMessage For="@(() => addeditProduct.ProductCode)" />

            <SfTextBox Enabled="true" Placeholder="Description"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditProduct.ProductDescription"></SfTextBox>
            <ValidationMessage For="@(() => addeditProduct.ProductDescription)" />

            <SfNumericTextBox Enabled="true" Placeholder="Unit Price"
                              FloatLabelType="@FloatLabelType.Auto"
                              ShowSpinButton="false"
                              Format="c2"
                              Decimals="2"
                              ValidateDecimalOnType="true"
                              @bind-Value="addeditProduct.ProductUnitPrice"></SfNumericTextBox>

            <SfDropDownList DataSource="@supplier"
                            TItem="Supplier"
                            TValue="int"
                            Text="SupplierID"
                            @bind-Value="addeditProduct.ProductSupplierID"
                            FloatLabelType="@FloatLabelType.Auto"
                            Placeholder="Select a Supplier"
                            Enabled="true">
                <DropDownListFieldSettings Text="SupplierName" Value="SupplierID"></DropDownListFieldSettings>
            </SfDropDownList>
        </div>
        <br /><br />
        <div class="e-footer-content">
            <div class="button-container">
                <button type="submit" class="e-btn e-normal e-primary">Save</button>
                <button type="button" class="e-btn e-normal" @onclick="@CloseDialog">Cancel</button>
            </div>
        </div>
    </EditForm>
</SfDialog>



<SfDialog @ref="DialogDeleteProduct" IsModal="true" Width="500px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> Confirm Delete </Header>
        <Content>
            <SfTextBox Enabled="false" Placeholder="Product Code"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditProduct.ProductCode"></SfTextBox>
            <SfTextBox Enabled="false" Placeholder="Description"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditProduct.ProductDescription"></SfTextBox>
            <SfNumericTextBox Enabled="false" Placeholder="Unit Price"
                              FloatLabelType="@FloatLabelType.Auto"
                              @bind-Value="addeditProduct.ProductUnitPrice"></SfNumericTextBox>
            <SfDropDownList DataSource="@supplier"
                            TItem="Supplier"
                            TValue="int"
                            Text="SupplierID"
                            @bind-Value="addeditProduct.ProductSupplierID"
                            FloatLabelType="@FloatLabelType.Auto"
                            Placeholder="Select a Supplier"
                            Enabled="false">
                <DropDownListFieldSettings Text="SupplierName" Value="SupplierID"></DropDownListFieldSettings>
            </SfDropDownList>
            <br />
            <br />
            <span class="text-danger">Please confirm that you want to delete this record</span>
        </Content>
    </DialogTemplates>
    <DialogButtons>
        <DialogButton Content="Delete" IsPrimary="true" OnClick="@ConfirmDeleteYes" />
        <DialogButton Content="Cancel" IsPrimary="false" OnClick="@ConfirmDeleteNo" />
    </DialogButtons>
</SfDialog>

<WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />

<style>
    .e-control-wrapper.ToUpperCase .e-textbox {
        text-transform: uppercase;
    }
</style>

@code {

    IEnumerable<Product> product;
    IEnumerable<Supplier> supplier;
    private List<ItemModel> Toolbaritems = new List<ItemModel>();

    SfDialog DialogAddEditProduct;
    Product addeditProduct = new Product();
    string HeaderText = "";

    WarningPage Warning;
    string WarningHeaderMessage = "";
    string WarningContentMessage = "";

    public int SelectedProductId { get; set; } = 0;

    SfDialog DialogDeleteProduct;


    protected override async Task OnInitializedAsync()
    {
        //Populate the list of Product objects from the Product table.
        product = await ProductService.ProductList();
        supplier = await SupplierService.SupplierList();

        Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new Product", PrefixIcon = "e-add" });
        Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected Product", PrefixIcon = "e-edit" });
        Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected Product", PrefixIcon = "e-delete" });
    }

    public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
    {
        if (args.Item.Text == "Add")
        {
            //Code for adding goes here
            addeditProduct = new Product();             // Ensures a blank form when adding
            HeaderText = "Add Product";
            await this.DialogAddEditProduct.Show();
        }
        if (args.Item.Text == "Edit")
        {
            //Code for editing goes here
            //Check that a Product Rate has been selected
            if (SelectedProductId == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Product from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //populate addeditProduct (temporary data set used for the editing process)
                HeaderText = "Edit Product";
                addeditProduct = await ProductService.Product_GetOne(SelectedProductId);
                await this.DialogAddEditProduct.Show();
            }

        }
        if (args.Item.Text == "Delete")
        {
            //code for deleting goes here
            if (SelectedProductId == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Product from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //populate addeditProduct (temporary data set used for the editing process)
                HeaderText = "Delete Product";
                addeditProduct = await ProductService.Product_GetOne(SelectedProductId);
                await this.DialogDeleteProduct.Show();
            }
        }
    }

    protected async Task ProductSave()
    {
        if (addeditProduct.ProductID == 0)
        {
            int Success = await ProductService.ProductInsert(
                addeditProduct.ProductCode,
                addeditProduct.ProductDescription,
                addeditProduct.ProductUnitPrice,
                addeditProduct.ProductSupplierID
                );
            if (Success != 0)
            {
                //Product Rate already exists
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "This Product Description already exists; it cannot be added again.";
                Warning.OpenDialog();
                // Data is left in the dialog so the user can see the problem.
            }
            else
            {
                // Clears the dialog and is ready for another entry
                // User must specifically close or cancel the dialog
                addeditProduct = new Product();
            }
        }
        else
        {
            // Item is being edited
            int Success = await ProductService.ProductUpdate(
                SelectedProductId,
                addeditProduct.ProductCode,
                addeditProduct.ProductDescription,
                addeditProduct.ProductUnitPrice,
                addeditProduct.ProductSupplierID,
                addeditProduct.ProductIsArchived);
            if (Success != 0)
            {
                //Product Rate already exists
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "This Product already exists; it cannot be added again.";
                Warning.OpenDialog();
            }
            else
            {
                await this.DialogAddEditProduct.Hide();
                this.StateHasChanged();
                addeditProduct = new Product();
                SelectedProductId = 0;
            }
        }

        //Always refresh datagrid
        product = await ProductService.ProductList();
        StateHasChanged();
    }

    private async Task CloseDialog()
    {
        await this.DialogAddEditProduct.Hide();
    }

    public void RowSelectHandler(RowSelectEventArgs<Product> args)
    {
        //{args.Data} returns the current selected records.
        SelectedProductId = args.Data.ProductID;
    }

    public async void ConfirmDeleteNo()
    {
        await DialogDeleteProduct.Hide();
        SelectedProductId = 0;
    }

    public async void ConfirmDeleteYes()
    {
        int Success = await ProductService.ProductUpdate(
            SelectedProductId,
            addeditProduct.ProductCode,
            addeditProduct.ProductDescription,
            addeditProduct.ProductUnitPrice,
            addeditProduct.ProductSupplierID,
            addeditProduct.ProductIsArchived = true);
        if (Success != 0)
        {
            //Product Rate already exists - THis should never happen when marking a record 'IsArchived'.
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Unknown error has occurred - the record has not been deleted!";
            Warning.OpenDialog();
        }
        else
        {
            await this.DialogDeleteProduct.Hide();
            product = await ProductService.ProductList();
            this.StateHasChanged();
            addeditProduct = new Product();
            SelectedProductId = 0;
        }
    }
}

PurchaseOrderPage.razor

@page "/purchaseorder/{POHeaderID:int}"
@using BlazorPurchaseOrders.Data

@inject NavigationManager NavigationManager
@inject ISupplierService SupplierService
@inject IPOHeaderService POHeaderService
@inject IPOLineService POLineService
@inject IProductService ProductService
@inject ITaxService TaxService


@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider
@using System
@using System.Collections.Generic


<h3>@pagetitle</h3>

<EditForm Model="@orderaddedit" OnValidSubmit="@OrderSave">
    <DataAnnotationsValidator />
    <div class="grid-container">
        <div class="grid-child left-column">
            <SfDropDownList DataSource="@supplier"
                            TItem="Supplier"
                            TValue="int"
                            Text="SupplierID"
                            @bind-Value="orderaddedit.POHeaderSupplierID"
                            FloatLabelType="@FloatLabelType.Auto"
                            Placeholder="Select a Supplier"
                            Enabled="@supplierEnabled">
                <DropDownListFieldSettings Text="SupplierName" Value="SupplierID"></DropDownListFieldSettings>
                <DropDownListEvents TItem="Supplier" TValue="int" ValueChange="OnChangeSupplier"></DropDownListEvents>
            </SfDropDownList>

            <SfTextBox Enabled="true" Placeholder="Address"
                       FloatLabelType="@FloatLabelType.Always"
                       @bind-Value="orderaddedit.POHeaderSupplierAddress1"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress1)" />

            <SfTextBox Enabled="true" Placeholder=""
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="orderaddedit.POHeaderSupplierAddress2"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress2)" />

            <SfTextBox Enabled="true" Placeholder=""
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="orderaddedit.POHeaderSupplierAddress3"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress3)" />

            <SfTextBox Enabled="true" Placeholder="Post Code"
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="orderaddedit.POHeaderSupplierPostCode"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierPostCode)" />

            <SfTextBox Enabled="true" Placeholder="Email"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="orderaddedit.POHeaderSupplierEmail"></SfTextBox>
            <ValidationMessage For="@(() => orderaddedit.POHeaderSupplierEmail)" />
        </div>
        <div class="grid-child right-column">
            <SfNumericTextBox Enabled="false" Placeholder="Order No"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              @bind-Value="orderaddedit.POHeaderOrderNumber"></SfNumericTextBox>

            <SfDatePicker TValue="DateTime"
                          Placeholder='Order Date'
                          FloatLabelType="@FloatLabelType.Auto"
                          @bind-Value="orderaddedit.POHeaderOrderDate"></SfDatePicker>

            <SfTextBox Enabled="false" Placeholder="Requested by"
                       FloatLabelType="@FloatLabelType.Always"
                       @bind-Value="orderaddedit.POHeaderRequestedBy"></SfTextBox>
        </div>
    </div>
    <br />
    <SfGrid @ref="OrderLinesGrid"
            DataSource="@orderLines"
            Toolbar="@Toolbaritems"
            AllowResizing="true">
        <GridColumns>
            <GridColumn Field="@nameof(POLine.POLineProductCode)"
                        HeaderText="Product"
                        TextAlign="@TextAlign.Left"
                        Width="20">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineProductDescription)"
                        HeaderText="Description"
                        TextAlign="@TextAlign.Left"
                        Width="30">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineProductQuantity)"
                        HeaderText="Quantity"
                        TextAlign="@TextAlign.Right"
                        Format="n0"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineProductUnitPrice)"
                        HeaderText="Unit Price"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineNetPrice)"
                        HeaderText="Net Price"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineTaxRate)"
                        HeaderText="Tax Rate"
                        TextAlign="@TextAlign.Right"
                        Format="p2"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineTaxAmount)"
                        HeaderText="Tax"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="10">
            </GridColumn>
            <GridColumn Field="@nameof(POLine.POLineGrossPrice)"
                        HeaderText="Total"
                        TextAlign="@TextAlign.Right"
                        Format="C2"
                        Width="10">
            </GridColumn>
        </GridColumns>

        <GridAggregates>
            <GridAggregate>
                <GridAggregateColumns>
                    <GridAggregateColumn Field=@nameof(POLine.POLineNetPrice) Type="AggregateType.Sum" Format="C2">
                        <FooterTemplate Context="NetContext">
                            @{
                                var aggregate = NetContext as AggregateTemplateContext;
                                <div>
                                    <p>@aggregate.Sum</p>
                                </div>
                            }
                        </FooterTemplate>
                    </GridAggregateColumn>

                    <GridAggregateColumn Field=@nameof(POLine.POLineTaxAmount) Type="AggregateType.Sum" Format="C2">
                        <FooterTemplate Context="TaxContext">
                            @{
                                var aggregate = TaxContext as AggregateTemplateContext;
                                <div>
                                    <p>@aggregate.Sum</p>
                                </div>
                            }
                        </FooterTemplate>
                    </GridAggregateColumn>

                    <GridAggregateColumn Field=@nameof(POLine.POLineGrossPrice) Type="AggregateType.Sum" Format="C2">
                        <FooterTemplate Context="GrossContext">
                            @{
                                var aggregate = GrossContext as AggregateTemplateContext;
                                <div>
                                    <p>@aggregate.Sum</p>
                                </div>
                            }
                        </FooterTemplate>
                    </GridAggregateColumn>
                </GridAggregateColumns>
            </GridAggregate>

        </GridAggregates>
        <GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="POLine"></GridEvents>
    </SfGrid>
    <br />
        <div class="e-footer-content" style="text-align: right; width: 100%;">        
            <div class="button-container">
                <button type="submit" class="e-btn e-normal e-primary">Save</button>
                <button type="button" class="e-btn e-normal" @onclick="@Cancel">Cancel</button>
            </div>
        </div>
</EditForm>

<SfDialog @ref="DialogAddEditOrderLine" IsModal="true" Width="600px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> @dialogHeaderText </Header>
    </DialogTemplates>
    <EditForm Model="@addeditOrderLine" OnValidSubmit="@OrderLineSave">
        <DataAnnotationsValidator />
        <div class=flex-container>
            <SfDropDownList DataSource="@product"
                            TItem="Product"
                            TValue="int"
                            Text="ProductID"
                            @bind-Value="addeditOrderLine.POLineProductID"
                            FloatLabelType="@FloatLabelType.Always"
                            Placeholder="Select a Product"
                            Enabled="true">
                <DropDownListTemplates TItem="Product">
                    <HeaderTemplate>
                        <span class='head'>
                            <span class='productcode'>Code</span>
                            <span class='description'>Description</span>
                        </span>
                    </HeaderTemplate>
                    <ItemTemplate Context="contextName">
                        <span class='item'>
                            <span class='productcode'>@((contextName as Product).ProductCode)</span>
                            <span class='description'>@((contextName as Product).ProductDescription)</span>
                        </span>
                    </ItemTemplate>
                </DropDownListTemplates>

                <DropDownListFieldSettings Text="ProductCode" Value="ProductID"></DropDownListFieldSettings>
                <DropDownListEvents TItem="Product" TValue="int" OnValueSelect="OnChangeProduct"></DropDownListEvents>
            </SfDropDownList>
        </div>
        <div class=flex-container>
            <SfTextBox Enabled="true" Placeholder="Product Description"
                       FloatLabelType="@FloatLabelType.Always"
                       @bind-Value="addeditOrderLine.POLineProductDescription"></SfTextBox>
        </div>
        <div class=flex-container>
            <ValidationMessage For="@(() => addeditOrderLine.POLineProductDescription)" />
        </div><div class=flex-container>
            <SfNumericTextBox Enabled="true" Placeholder="Quantity"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="n0"
                              Decimals="0"
                              ValidateDecimalOnType="true"
                              CssClass="e-style"
                              @bind-Value="addeditOrderLine.POLineProductQuantity"
                              @onfocusout='@POLineCalc'>
            </SfNumericTextBox>

            <SfNumericTextBox Enabled="true" Placeholder="Unit Price"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="c2"
                              Decimals="2"
                              ValidateDecimalOnType="true"
                              CssClass="e-style"
                              @bind-Value="addeditOrderLine.POLineProductUnitPrice"
                              @onfocusout='@POLineCalc'>
            </SfNumericTextBox>

            <SfNumericTextBox Enabled="false" Placeholder="Net Price"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="c2"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineNetPrice">
            </SfNumericTextBox>
        </div>
        <div class=flex-container>
            <SfDropDownList DataSource="@tax"
                            TItem="Tax"
                            TValue="int"
                            Text="TaxID"
                            @bind-Value="addeditOrderLine.POLineTaxID"
                            FloatLabelType="@FloatLabelType.Always"
                            Placeholder="Tax Rate"
                            Enabled="true">
                <DropDownListFieldSettings Text="TaxDescription" Value="TaxID"></DropDownListFieldSettings>
                <DropDownListEvents TItem="Tax" TValue="int" OnValueSelect="OnChangeTax"></DropDownListEvents>
            </SfDropDownList>

            <SfNumericTextBox Enabled="false" Placeholder="Tax Rate %"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="p2"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineTaxRate">
            </SfNumericTextBox>

            <SfNumericTextBox Enabled="false" Placeholder="Tax Amount"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="c2"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineTaxAmount">
            </SfNumericTextBox>
        </div>
        <div class=flex-container>
            <SfNumericTextBox Enabled="false" Placeholder="Total Price"
                              FloatLabelType="@FloatLabelType.Always"
                              ShowSpinButton="false"
                              Format="c2"
                              EnableRtl="true"
                              @bind-Value="addeditOrderLine.POLineGrossPrice">
            </SfNumericTextBox>
        </div>

        <br />
        <div class="e-footer-content">
            <div class="button-container">
                <button type="submit" class="e-btn e-normal e-primary">Save</button>
                <button type="button" class="e-btn e-normal" @onclick="@CloseDialog">Cancel</button>
            </div>
        </div>
    </EditForm>
</SfDialog>

<ConfirmPage @ref="ConfirmDeletion" ConfirmHeaderMessage="@ConfirmHeaderMessage" ConfirmContentMessage="@ConfirmContentMessage" ConfirmationChanged="ConfirmDelete" />

<WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />

<ConfirmPage @ref="ConfirmSaveOrder" ConfirmHeaderMessage="@ConfirmHeaderMessage" ConfirmContentMessage="@ConfirmContentMessage" ConfirmationChanged="OrderSaveProcess" />

<style>
    .grid-container {
        display: grid;
        max-width: 900px; /* Maximum width of the whole container - in this case both columns */
        grid-template-columns: 1fr 1fr; /* Relative width of each column (1fr 1fr is equivalent to, say, 33fr 33fr */
        grid-gap: 75px; /* size of the gap between columns */
    }

    .flex-container {
        display: flex;
        flex-direction: row; /* Causes tab to move along row and then onto following row */
        justify-content: space-evenly; /* Equal space left and right margin and between elements */
        margin: 10px; /* This appears to be vertical margin between rows */
        column-gap: 10px; /* Tgap betwen columns */
    }

    .e-numeric.e-style .e-control.e-numerictextbox {
        text-align: right;
        padding: 0px 5px 0px 0px;
    }

    .head, .item {
        display: table;
        width: 100%;
        margin: auto;
        text-align: left;
    }

    .head {
        height: 30px;
        font-size: 15px;
        font-weight: 600;
    }

    .productcode {
        display: table-cell;
        vertical-align: middle;
        text-align: left;
        width: 25%;
    }

    .description {
        display: table-cell;
        vertical-align: middle;
        text-align: left;
        width: 75%;
    }

    .head .productcode {
        text-indent: 17px;
    }

    .head .description {
        text-indent: 14px;
    }
</style>


@code {
    POHeader orderaddedit = new POHeader();
    IEnumerable<Supplier> supplier;
    IEnumerable<Product> product;
    IEnumerable<Tax> tax;
    IEnumerable<POLine> orderLinesByPOHeader;

    string pagetitle = "";
    string dialogHeaderText = "";

    private string UserName;

    SfGrid<POLine> OrderLinesGrid;
    public List<POLine> orderLines = new List<POLine>();
    private List<ItemModel> Toolbaritems = new List<ItemModel>();

    private List<int> OrderLinesToBeDeleted = new List<int>();

    SfDialog DialogAddEditOrderLine;

    public POLine addeditOrderLine = new POLine();

    WarningPage Warning;
    string WarningHeaderMessage = "";
    string WarningContentMessage = "";

    ConfirmPage ConfirmDeletion;
    ConfirmPage ConfirmSaveOrder;
    string ConfirmHeaderMessage = "";
    string ConfirmContentMessage = "";
    public bool ConfirmationChanged { get; set; } = false;

    public bool supplierEnabled { get; set; } = true;

    private int tmpPOLineID { get; set; } = 0;
    private int selectedPOLineID { get; set; } = 0;

    [Parameter]
    public int POHeaderID { get; set; }

    //Executes on page open, sets headings and gets data in the case of edit
    protected override async Task OnInitializedAsync()
    {
        supplier = await SupplierService.SupplierList();
        orderaddedit.POHeaderOrderDate = DateTime.Now;
        tax = await TaxService.TaxList();

        if (POHeaderID == 0)
        {
            pagetitle = "Add an Order";
        }
        else
        {
            pagetitle = "Edit an Order";
            orderaddedit = await POHeaderService.POHeader_GetOne(POHeaderID);
            orderLinesByPOHeader = await POLineService.POLine_GetByPOHeader(POHeaderID);
            orderLines = orderLinesByPOHeader.ToList(); //Convert from IEnumable to List
            supplierEnabled = false;
        }

        //Get user if logged in and populate the 'Requested by' column
        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity.IsAuthenticated)
        {
            UserName = user.Identity.Name;
        }
        else
        {
            UserName = "The user is NOT authenticated.";
        }

        orderaddedit.POHeaderRequestedBy = UserName;

        Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new order line", PrefixIcon = "e-add" });
        Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected order line", PrefixIcon = "e-edit" });
        Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected order line", PrefixIcon = "e-delete" });

    }

    private void OnChangeSupplier(Syncfusion.Blazor.DropDowns.ChangeEventArgs<int, Supplier> args)
    {
        this.orderaddedit.POHeaderSupplierAddress1 = args.ItemData.SupplierAddress1;
        this.orderaddedit.POHeaderSupplierAddress2 = args.ItemData.SupplierAddress2;
        this.orderaddedit.POHeaderSupplierAddress3 = args.ItemData.SupplierAddress3;
        this.orderaddedit.POHeaderSupplierPostCode = args.ItemData.SupplierPostCode;
        this.orderaddedit.POHeaderSupplierEmail = args.ItemData.SupplierEmail;

    }

    // Executes OnValidSubmit of EditForm above
    protected async Task OrderSave()
    {
        if (orderaddedit.POHeaderSupplierID == 0)
        {
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Please Select a Supplier before saving the order.";
            Warning.OpenDialog();
            return;
        }

        if (orderLines.Count == 0)
        {
            ConfirmHeaderMessage = "Confirm Save!";
            ConfirmContentMessage = "There are no order lines. Please confirm order should be saved.";
            ConfirmSaveOrder.OpenDialog();
        }
        else
        {
            await OrderSaveProcess(true);
        }

    }

    //Executes if user clicks the Cancel button.
    void Cancel()
    {
        NavigationManager.NavigateTo("/");
    }

    public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
    {

        //Refresh product to select only those products for this supplier (and products with null suppliers)
        product = await ProductService.ProductListBySupplier(orderaddedit.POHeaderSupplierID);

        if (args.Item.Text == "Add")
        {
            //Code for adding goes here
            dialogHeaderText = "Add an Order Line";
            //Check that a supplier has been selected from the drop-down list
            if (orderaddedit.POHeaderSupplierID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please Select a Supplier before adding order lines.";
                Warning.OpenDialog();
            }
            else
            {
                addeditOrderLine = new POLine();          // Ensures a blank form when adding
                addeditOrderLine.POLineNetPrice = 0;
                addeditOrderLine.POLineTaxID = 0;
                addeditOrderLine.POLineProductID = 0;
                await this.DialogAddEditOrderLine.Show();
            }

        }

        if (args.Item.Text == "Edit")
        {
            dialogHeaderText = "Edit an Order Line";
            //Check that an order line has been selected
            if (selectedPOLineID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order Line from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //populate addeditOrderLine (temporary data set used for the editing process)
                addeditOrderLine = orderLines.Where(x => x.POLineID == selectedPOLineID).FirstOrDefault();
                StateHasChanged();
                await this.DialogAddEditOrderLine.Show();
            }
        }

        if (args.Item.Text == "Delete")
        {
            //Code for adding goes here
            if (selectedPOLineID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order Line from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                ConfirmHeaderMessage = "Confirm Deletion";
                ConfirmContentMessage = "Please confirm that this order line should be deleted.";
                ConfirmDeletion.OpenDialog();
            }
        }
    }

    private async Task OrderLineSave()
    {
        if (addeditOrderLine.POLineID == 0)
        {
            //Code to save new order line goes here
            //Check that a product has been selected from the drop-down list
            if (addeditOrderLine.POLineProductCode == null || addeditOrderLine.POLineProductCode == "")
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Product.";
                Warning.OpenDialog();
            }
            //And check that a tax rate has been selected from the drop-down list
            else if (addeditOrderLine.POLineTaxID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Tax Rate.";
                Warning.OpenDialog();
            }
            else
            {
                POLineCalc();
                tmpPOLineID = tmpPOLineID - 1;        //used to provide a temporary ID for POLineID

                orderLines.Add(new POLine
                {
                    POLineID = tmpPOLineID,
                    POLineHeaderID = 0,
                    POLineProductID = addeditOrderLine.POLineProductID,
                    POLineProductCode = addeditOrderLine.POLineProductCode,
                    POLineProductDescription = addeditOrderLine.POLineProductDescription,
                    POLineProductQuantity = addeditOrderLine.POLineProductQuantity,
                    POLineProductUnitPrice = addeditOrderLine.POLineProductUnitPrice,
                    POLineNetPrice = addeditOrderLine.POLineNetPrice,
                    POLineTaxRate = addeditOrderLine.POLineTaxRate,
                    POLineTaxAmount = addeditOrderLine.POLineTaxAmount,
                    POLineGrossPrice = addeditOrderLine.POLineGrossPrice,
                    POLineTaxID = addeditOrderLine.POLineTaxID
                });


                OrderLinesGrid.Refresh();
                StateHasChanged();                  //<-----  THIS IS ABSOLUTELY ESSENTIAL

                //addeditOrderLine = new POLine();  //<-----  THIS gives errors (nulls)

                addeditOrderLine.POLineProductID = 0;
                addeditOrderLine.POLineProductCode = "";
                addeditOrderLine.POLineProductDescription = "";
                addeditOrderLine.POLineProductQuantity = 0;
                addeditOrderLine.POLineProductUnitPrice = 0;
                addeditOrderLine.POLineNetPrice = 0;
                //addeditOrderLine.POLineTaxID = 0;                 //Leave - highly likely to be same as previous record
                //addeditOrderLine.POLineTaxRate = 0;               //Leave - highly likely to be same as previous record
                addeditOrderLine.POLineTaxAmount = 0;
                addeditOrderLine.POLineGrossPrice = 0;

                //We now have order lines, so prevent user from changing the supplier
                supplierEnabled = false;
            }
        }

        else
        //An order line is being edited
        //Check that a product has been selected from the drop-down list
        if (addeditOrderLine.POLineProductCode == null || addeditOrderLine.POLineProductCode == "")
        {
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Please select a Product.";
            Warning.OpenDialog();
        }
        //And check that a tax rate has been selected from the drop-down list
        else if (addeditOrderLine.POLineTaxID == 0)
        {
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Please select a Tax Rate.";
            Warning.OpenDialog();
        }
        else
        {
            OrderLinesGrid.Refresh();
            StateHasChanged();                  //<-----  THIS IS ABSOLUTELY ESSENTIAL
            await CloseDialog();                //No need to keep dialog open
        }
    }


    private async Task CloseDialog()
    {
        await this.DialogAddEditOrderLine.Hide();
    }

    private void OnChangeProduct(Syncfusion.Blazor.DropDowns.SelectEventArgs<Product> args)
    {
        this.addeditOrderLine.POLineProductCode = args.ItemData.ProductCode;
        this.addeditOrderLine.POLineProductDescription = args.ItemData.ProductDescription;
        this.addeditOrderLine.POLineProductUnitPrice = args.ItemData.ProductUnitPrice;
        POLineCalc();
    }

    private void OnChangeTax(Syncfusion.Blazor.DropDowns.SelectEventArgs<Tax> args)
    {
        // int testTaxId = args.ItemData.TaxID;
        this.addeditOrderLine.POLineTaxRate = args.ItemData.TaxRate;
        POLineCalc();
    }

    private void POLineCalc()
    {
        addeditOrderLine.POLineNetPrice = addeditOrderLine.POLineProductUnitPrice * addeditOrderLine.POLineProductQuantity;
        addeditOrderLine.POLineTaxAmount = addeditOrderLine.POLineNetPrice.Value * addeditOrderLine.POLineTaxRate;
        addeditOrderLine.POLineGrossPrice = addeditOrderLine.POLineNetPrice.Value * (1 + addeditOrderLine.POLineTaxRate);
    }

    public void RowSelectHandler(RowSelectEventArgs<POLine> args)
    {
        //{args.Data} returns the current selected records.
        selectedPOLineID = args.Data.POLineID;
    }

    private void OrderLineDelete()
    {
        if (selectedPOLineID > 0)
        {
            //Order line has already been saved to the database, and was present at start of this order edit
            //Add to list of orders to be deleted when order is saved
            OrderLinesToBeDeleted.Add(selectedPOLineID);
        }

        //And then delete from orderLines

        var itemToRemove = orderLines.Single(x => x.POLineID == selectedPOLineID);
        orderLines.Remove(itemToRemove);
        OrderLinesGrid.Refresh();
    }

    protected void ConfirmDelete(bool deleteConfirmed)
    {
        if (deleteConfirmed)
        {
            OrderLineDelete();
            StateHasChanged();
            selectedPOLineID = 0;
        }
    }

    protected async Task OrderSaveProcess(bool saveConfirmed)
    {
        if (saveConfirmed)
        {
            if (POHeaderID == 0)
            {
                //Save the record - 1st - the POHeader

                int HeaderID = await POHeaderService.POHeaderInsert(
                               orderaddedit.POHeaderOrderDate,
                               orderaddedit.POHeaderSupplierID,
                               orderaddedit.POHeaderSupplierAddress1,
                               orderaddedit.POHeaderSupplierAddress2,
                               orderaddedit.POHeaderSupplierAddress3,
                               orderaddedit.POHeaderSupplierPostCode,
                               orderaddedit.POHeaderSupplierEmail,
                               orderaddedit.POHeaderRequestedBy
                               );

                //2nd - the POLines
                foreach (var individualPOLine in orderLines)
                {
                    individualPOLine.POLineHeaderID = HeaderID;
                    bool Success = await POLineService.POLineInsert(individualPOLine);
                }

                NavigationManager.NavigateTo("/");
            }
            else
            {
                //Order is being edited
                //POHeader
                bool Success = await POHeaderService.POHeaderUpdate(orderaddedit);

                //POLines
                foreach (var individualPOLine in orderLines)
                {
                    //If POLineHeaderID is positive it means it has been edited during the edit of this order
                    if (individualPOLine.POLineID > 0)
                    {
                        Success = await POLineService.POLineUpdate(individualPOLine);
                    }
                    else
                    //If POLineID is negative it means it has been added during the edit of this order
                    {
                        individualPOLine.POLineHeaderID = POHeaderID;
                        Success = await POLineService.POLineInsert(individualPOLine);
                    }
                }

                foreach (var individualPOLine in OrderLinesToBeDeleted)
                {
                    Success = await POLineService.POLineDeleteOne(individualPOLine);
                }
                //Clear the list of POLines to be deleted
                OrderLinesToBeDeleted.Clear();

                NavigationManager.NavigateTo("/");
            }
        }
    }
}

SupplierPage.razor

@page "/supplier"
@using BlazorPurchaseOrders.Data
@inject ISupplierService SupplierService
@using Syncfusion.Blazor.Navigations

<h3>Suppliers</h3>
<br />

<SfGrid DataSource="@supplier"
        Toolbar="@Toolbaritems"
        AllowResizing="true">
    <GridColumns>
        <GridColumn Field="@nameof(Supplier.SupplierName)"
                    HeaderText="Name"
                    TextAlign="TextAlign.Left"
                    Width="40">
        </GridColumn>
        <GridColumn Field="@nameof(Supplier.CombinedAddress)"
                    HeaderText="Address"
                    TextAlign="TextAlign.Left"
                    Width="60">
        </GridColumn>
        <GridColumn Field="@nameof(Supplier.SupplierEmail)"
                    HeaderText="Email"
                    TextAlign="TextAlign.Left"
                    Width="40">
        </GridColumn>
    </GridColumns>
    <GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="Supplier"></GridEvents>
</SfGrid>

<SfDialog @ref="DialogAddEditSupplier" IsModal="true" Width="500px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> @HeaderText </Header>
    </DialogTemplates>
    <EditForm Model="@addeditSupplier" OnValidSubmit="@SupplierSave">
        <DataAnnotationsValidator />
        <div>
            <SfTextBox Enabled="true" Placeholder="Supplier Name"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditSupplier.SupplierName"></SfTextBox>
            <ValidationMessage For="@(() => addeditSupplier.SupplierName)" />
            <SfTextBox Enabled="true" Placeholder="Address"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditSupplier.SupplierAddress1"></SfTextBox>
            <ValidationMessage For="@(() => addeditSupplier.SupplierAddress1)" />
            <SfTextBox Enabled="true" Placeholder=""
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="addeditSupplier.SupplierAddress2"></SfTextBox>
            <ValidationMessage For="@(() => addeditSupplier.SupplierAddress2)" />
            <SfTextBox Enabled="true" Placeholder=""
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="addeditSupplier.SupplierAddress3"></SfTextBox>
            <ValidationMessage For="@(() => addeditSupplier.SupplierAddress3)" />
            <SfTextBox Enabled="true" Placeholder="Post Code"
                       FloatLabelType="@FloatLabelType.Auto"
                       CssClass="ToUpperCase"
                       @bind-Value="addeditSupplier.SupplierPostCode"></SfTextBox>
            <ValidationMessage For="@(() => addeditSupplier.SupplierPostCode)" />
            <SfTextBox Enabled="true" Placeholder="Email"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditSupplier.SupplierEmail"></SfTextBox>
            <ValidationMessage For="@(() => addeditSupplier.SupplierEmail)" />
        </div>
        <br /><br />
        <div class="e-footer-content">
            <div class="button-container">
                <button type="submit" class="e-btn e-normal e-primary">Save</button>
                <button type="button" class="e-btn e-normal" @onclick="@CloseDialog">Cancel</button>
            </div>
        </div>
    </EditForm>
</SfDialog>

<SfDialog @ref="DialogDeleteSupplier" IsModal="true" Width="500px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> Confirm Delete </Header>
        <Content>
            <SfTextBox Enabled="false" Placeholder="Supplier Name"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditSupplier.SupplierName"></SfTextBox>
            <SfTextBox Enabled="false" Placeholder="Address"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditSupplier.SupplierAddress1"></SfTextBox>
            <SfTextBox Enabled="false" Placeholder="Address"
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="addeditSupplier.SupplierAddress2"></SfTextBox>
            <SfTextBox Enabled="false" Placeholder="Address"
                       FloatLabelType="@FloatLabelType.Never"
                       @bind-Value="addeditSupplier.SupplierAddress3"></SfTextBox>
            <SfTextBox Enabled="false" Placeholder="Post Code"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditSupplier.SupplierAddress3"></SfTextBox>
            <SfTextBox Enabled="false" Placeholder="Email"
                       FloatLabelType="@FloatLabelType.Auto"
                       @bind-Value="addeditSupplier.SupplierEmail"></SfTextBox>
            <br />
            <br />
            <span class="text-danger">Please confirm that you want to delete this record</span>
        </Content>
    </DialogTemplates>
    <DialogButtons>
        <DialogButton Content="Delete" IsPrimary="true" OnClick="@ConfirmDeleteYes" />
        <DialogButton Content="Cancel" IsPrimary="false" OnClick="@ConfirmDeleteNo" />
    </DialogButtons>
</SfDialog>

<WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />

<style>
    .e-control-wrapper.ToUpperCase .e-textbox {
        text-transform: uppercase;
    }
</style>

@code {

    IEnumerable<Supplier> supplier;
    private List<ItemModel> Toolbaritems = new List<ItemModel>();

    SfDialog DialogAddEditSupplier;
    Supplier addeditSupplier = new Supplier();
    string HeaderText = "";

    WarningPage Warning;
    string WarningHeaderMessage = "";
    string WarningContentMessage = "";

    public int SelectedSupplierId { get; set; } = 0;

    SfDialog DialogDeleteSupplier;


    protected override async Task OnInitializedAsync()
    {
        //Populate the list of Supplier objects from the Supplier table.
        supplier = await SupplierService.SupplierList();

        Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new Supplier", PrefixIcon = "e-add" });
        Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected Supplier", PrefixIcon = "e-edit" });
        Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected Supplier", PrefixIcon = "e-delete" });
    }

    public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
    {
        if (args.Item.Text == "Add")
        {
            //Code for adding goes here
            addeditSupplier = new Supplier();             // Ensures a blank form when adding
            HeaderText = "Add Supplier";
            await this.DialogAddEditSupplier.Show();
        }
        if (args.Item.Text == "Edit")
        {
            //Code for editing goes here
            //Check that a Supplier Rate has been selected
            if (SelectedSupplierId == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Supplier from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //populate addeditSupplier (temporary data set used for the editing process)
                HeaderText = "Edit Supplier";
                addeditSupplier = await SupplierService.Supplier_GetOne(SelectedSupplierId);
                await this.DialogAddEditSupplier.Show();
            }

        }
        if (args.Item.Text == "Delete")
        {
            //code for deleting goes here
            if (SelectedSupplierId == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Supplier from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //populate addeditSupplier (temporary data set used for the editing process)
                HeaderText = "Delete Supplier";
                addeditSupplier = await SupplierService.Supplier_GetOne(SelectedSupplierId);
                await this.DialogDeleteSupplier.Show();
            }
        }
    }

    protected async Task SupplierSave()
    {
        if (addeditSupplier.SupplierID == 0)
        {
            int Success = await SupplierService.SupplierInsert(addeditSupplier.SupplierName,
                addeditSupplier.SupplierAddress1,
                addeditSupplier.SupplierAddress2,
                addeditSupplier.SupplierAddress3,
                addeditSupplier.SupplierPostCode,
                addeditSupplier.SupplierEmail
                );
            if (Success != 0)
            {
                //Supplier Rate already exists
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "This Supplier Description already exists; it cannot be added again.";
                Warning.OpenDialog();
                // Data is left in the dialog so the user can see the problem.
            }
            else
            {
                // Clears the dialog and is ready for another entry
                // User must specifically close or cancel the dialog
                addeditSupplier = new Supplier();
            }
        }
        else
        {
            // Item is being edited
            int Success = await SupplierService.SupplierUpdate(
                SelectedSupplierId,
                addeditSupplier.SupplierName,
                addeditSupplier.SupplierAddress1,
                addeditSupplier.SupplierAddress2,
                addeditSupplier.SupplierAddress3,
                addeditSupplier.SupplierPostCode,
                addeditSupplier.SupplierEmail,
                addeditSupplier.SupplierIsArchived);
            if (Success != 0)
            {
                //Supplier Rate already exists
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "This Supplier already exists; it cannot be added again.";
                Warning.OpenDialog();
            }
            else
            {
                await this.DialogAddEditSupplier.Hide();
                this.StateHasChanged();
                addeditSupplier = new Supplier();
                SelectedSupplierId = 0;
            }
        }

        //Always refresh datagrid
        supplier = await SupplierService.SupplierList();
        StateHasChanged();
    }

    private async Task CloseDialog()
    {
        await this.DialogAddEditSupplier.Hide();
    }

    public void RowSelectHandler(RowSelectEventArgs<Supplier> args)
    {
        //{args.Data} returns the current selected records.
        SelectedSupplierId = args.Data.SupplierID;
    }

    public async void ConfirmDeleteNo()
    {
        await DialogDeleteSupplier.Hide();
        SelectedSupplierId = 0;
    }

    public async void ConfirmDeleteYes()
    {
        int Success = await SupplierService.SupplierUpdate(

            SelectedSupplierId,
                addeditSupplier.SupplierName,
                addeditSupplier.SupplierAddress1,
                addeditSupplier.SupplierAddress2,
                addeditSupplier.SupplierAddress3,
                addeditSupplier.SupplierPostCode,
                addeditSupplier.SupplierEmail,
                addeditSupplier.SupplierIsArchived = true);
        if (Success != 0)
        {
            //Supplier Rate already exists - THis should never happen when marking a record 'IsArchived'.
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Unknown error has occurred - the record has not been deleted!";
            Warning.OpenDialog();
        }
        else
        {
            await this.DialogDeleteSupplier.Hide();
            supplier = await SupplierService.SupplierList();
            this.StateHasChanged();
            addeditSupplier = new Supplier();
            SelectedSupplierId = 0;
        }
    }
}

TaxPage.razor

@page "/tax"
@using BlazorPurchaseOrders.Data
@inject ITaxService TaxService
@using Syncfusion.Blazor.Navigations

<h3>Tax Rates</h3>
<br />

<SfGrid DataSource="@tax"
        Toolbar="@Toolbaritems">
    <GridColumns>
        <GridColumn Field="@nameof(Tax.TaxDescription)"
                    HeaderText="Description"
                    TextAlign="TextAlign.Left"
                    Width="60">
        </GridColumn>
        <GridColumn Field="@nameof(Tax.TaxRate)"
                    HeaderText="Rate %"
                    TextAlign="TextAlign.Right"
                    Format="p2"
                    Width="40">
        </GridColumn>
    </GridColumns>
    <GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="Tax"></GridEvents>
</SfGrid>

<SfDialog @ref="DialogAddEditTax" IsModal="true" Width="500px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> @HeaderText </Header>
    </DialogTemplates>
    <EditForm Model="@addeditTax" OnValidSubmit="@TaxSave">
        <DataAnnotationsValidator />
        <div>
            <SfTextBox Enabled="true" Placeholder="Description"
                       FloatLabelType="@FloatLabelType.Always"
                       @bind-Value="addeditTax.TaxDescription"></SfTextBox>
            <ValidationMessage For="@(() => addeditTax.TaxDescription)" />
            <SfNumericTextBox Enabled="true" Placeholder="Tax Rate" Width="50"
                              Format="p2"
                              FloatLabelType="@FloatLabelType.Always"
                              @bind-Value="addeditTax.TaxRate"></SfNumericTextBox>

            <ValidationMessage For="@(() => addeditTax.TaxRate)" />
        </div>
        <br /><br />
        <div class="e-footer-content">
            <div class="button-container">
                <button type="submit" class="e-btn e-normal e-primary">Save</button>
                <button type="button" class="e-btn e-normal" @onclick="@CloseDialog">Cancel</button>
            </div>
        </div>
    </EditForm>
</SfDialog>

<SfDialog @ref="DialogDeleteTax" IsModal="true" Width="500px" ShowCloseIcon="true" Visible="false">
    <DialogTemplates>
        <Header> Confirm Delete </Header>
        <Content>
            <SfTextBox Enabled="false" Placeholder="Description"
                       FloatLabelType="@FloatLabelType.Always"
                       @bind-Value="addeditTax.TaxDescription"></SfTextBox>
            <SfNumericTextBox Enabled="false" Placeholder="Tax Rate" Width="50"
                              Format="p2"
                              FloatLabelType="@FloatLabelType.Always"
                              @bind-Value="addeditTax.TaxRate"></SfNumericTextBox>
            <br />
            <br />
            <span class="text-danger">Please confirm that you want to delete this record</span>
        </Content>
    </DialogTemplates>
    <DialogButtons>
        <DialogButton Content="Delete" IsPrimary="true" OnClick="@ConfirmDeleteYes" />
        <DialogButton Content="Cancel" IsPrimary="false" OnClick="@ConfirmDeleteNo" />
    </DialogButtons>
</SfDialog>

<WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />

@code {

    IEnumerable<Tax> tax;
    private List<ItemModel> Toolbaritems = new List<ItemModel>();

    SfDialog DialogAddEditTax;
    Tax addeditTax = new Tax();
    string HeaderText = "";

    WarningPage Warning;
    string WarningHeaderMessage = "";
    string WarningContentMessage = "";

    public int SelectedTaxId { get; set; } = 0;

    SfDialog DialogDeleteTax;


    protected override async Task OnInitializedAsync()
    {
        //Populate the list of Tax objects from the Tax table.
        tax = await TaxService.TaxList();

        Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new Tax Rate", PrefixIcon = "e-add" });
        Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected Tax Rate", PrefixIcon = "e-edit" });
        Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected Tax Rate", PrefixIcon = "e-delete" });
    }

    public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
    {
        if (args.Item.Text == "Add")
        {
            //Code for adding goes here
            addeditTax = new Tax();             // Ensures a blank form when adding
            HeaderText = "Add Tax Rate";
            await this.DialogAddEditTax.Show();
        }
        if (args.Item.Text == "Edit")
        {
            //Code for editing goes here
            //Check that a Tax Rate has been selected
            if (SelectedTaxId == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Tax Rate from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //populate addeditTax (temporary data set used for the editing process)
                HeaderText = "Edit Tax Rate";
                addeditTax = await TaxService.Tax_GetOne(SelectedTaxId);
                await this.DialogAddEditTax.Show();
            }

        }
        if (args.Item.Text == "Delete")
        {
            //code for deleting goes here
            if (SelectedTaxId == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select a Tax Rate from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //populate addeditTax (temporary data set used for the editing process)
                HeaderText = "Delete Tax Rate";
                addeditTax = await TaxService.Tax_GetOne(SelectedTaxId);
                await this.DialogDeleteTax.Show();
            }
        }
    }

    protected async Task TaxSave()
    {
        if (addeditTax.TaxID == 0)
        {
            int Success = await TaxService.TaxInsert(addeditTax.TaxDescription, addeditTax.TaxRate);
            if (Success != 0)
            {
                //Tax Rate already exists
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "This Tax Description already exists; it cannot be added again.";
                Warning.OpenDialog();
                // Data is left in the dialog so the user can see the problem.
            }
            else
            {
                // Clears the dialog and is ready for another entry
                // User must specifically close or cancel the dialog
                addeditTax = new Tax();
            }
        }
        else
        {
            // Item is being edited
            int Success = await TaxService.TaxUpdate(addeditTax.TaxDescription, addeditTax.TaxRate, SelectedTaxId, addeditTax.TaxIsArchived);
            if (Success != 0)
            {
                //Tax Rate already exists
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "This Tax Description already exists; it cannot be added again.";
                Warning.OpenDialog();
            }
            else
            {
                await this.DialogAddEditTax.Hide();
                this.StateHasChanged();
                addeditTax = new Tax();
                SelectedTaxId = 0;
            }
        }

        //Always refresh datagrid
        tax = await TaxService.TaxList();
        StateHasChanged();
    }

    private async Task CloseDialog()
    {
        await this.DialogAddEditTax.Hide();
    }

    public void RowSelectHandler(RowSelectEventArgs<Tax> args)
    {
        //{args.Data} returns the current selected records.
        SelectedTaxId = args.Data.TaxID;
    }

    public async void ConfirmDeleteNo()
    {
        await DialogDeleteTax.Hide();
        SelectedTaxId = 0;
    }

    public async void ConfirmDeleteYes()
    {
        int Success = await TaxService.TaxUpdate(addeditTax.TaxDescription, addeditTax.TaxRate, SelectedTaxId, addeditTax.TaxIsArchived = true);
        if (Success != 0)
        {
            //Tax Rate already exists - THis should never happen when marking a record 'IsArchived'.
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Unknown error has occurred - the record has not been deleted!";
            Warning.OpenDialog();
        }
        else
        {
            await this.DialogDeleteTax.Hide();
            tax = await TaxService.TaxList();
            this.StateHasChanged();
            addeditTax = new Tax();
            SelectedTaxId = 0;
        }
    }
}
<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">Blazor Purchase Orders</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Purchase Orders
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="tax">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Tax Rates
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="supplier">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Suppliers
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="product">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Products
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}