A few more wrinkles - code

SQL to create ControlTable

USE [PurchaseOrders]
GO
DROP TABLE IF EXISTS [dbo].[ControlTable]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ControlTable](
	[ControlTableID] [int] IDENTITY(1,1) NOT NULL,
	[SmtpHost] [nvarchar](50) NULL,
	[SmtpPort] [int] NULL,
	[SmtpUserFriendlyName] [nvarchar](50) NULL,
	[SmtpUserEmailAddress] [nvarchar](256) NULL,
	[SmtpPass] [nvarchar](50) NULL,
	[SENDGRID_APIKEY] [nvarchar](256) NULL,
	[SenderEmail] [nvarchar](256) NULL,
	[SenderEmailFriendlyName] [nvarchar](50) NULL,
	[DefaultTaxID] [int] NULL,
	[EmailSendMethod] [nvarchar](10) NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[ControlTable] ON 
GO
INSERT [dbo].[ControlTable] ([ControlTableID], [SmtpHost], [SmtpPort], [SmtpUserFriendlyName], [SmtpUserEmailAddress], [SmtpPass], [SENDGRID_APIKEY], [SenderEmail], [SenderEmailFriendlyName], [DefaultTaxID], [EmailSendMethod]) VALUES (1, N'smtp.host.com', 587, N'John Smith', N'johnsmith@hostname.com', N'johnsmithpassword', N'Sendgrid.api.key', N'johnsmith@hostname.com', N'John Smith', 1, N'SendGrid')
GO
SET IDENTITY_INSERT [dbo].[ControlTable] OFF
GO

SQL stored procedures for ControlTable

--------------  Stored Proc for SELECT (one)
DROP PROCEDURE IF EXISTS [dbo].[spControlTable_GetOne]
GO

CREATE PROCEDURE spControlTable_GetOne
-- Needs one parameter for primary key
AS 
BEGIN
-- SQL Select for one table row
SELECT top(1) ControlTableID, SmtpHost, SmtpPort, SmtpUserFriendlyName, SmtpUserEmailAddress, SmtpPass, SENDGRID_APIKEY, SenderEmail, SenderEmailFriendlyName, DefaultTaxID, EmailSendMethod FROM ControlTable ORDER BY ControlTableID DESC
END

GO

--------------  Stored Proc for UPDATE
DROP PROCEDURE IF EXISTS [dbo].[spControlTable_Update]
GO
CREATE PROCEDURE spControlTable_Update
-- Parameters for Update stored procedure.
@ControlTableID int,
@SmtpHost nvarchar(50),
@SmtpPort int,
@SmtpUserFriendlyName nvarchar(50),
@SmtpUserEmailAddress nvarchar(50),
@SmtpPass nvarchar(50),
@SENDGRID_APIKEY nvarchar(255),
@SenderEmail nvarchar(50),
@SenderEmailFriendlyName nvarchar(50),
@DefaultTaxID int,
@EmailSendMethod nvarchar(10)
AS
BEGIN
-- SQL for Update stored procedure
UPDATE ControlTable SET SmtpHost = @SmtpHost, SmtpPort = @SmtpPort, SmtpUserFriendlyName = @SmtpUserFriendlyName, SmtpUserEmailAddress = @SmtpUserEmailAddress, SmtpPass = @SmtpPass, SENDGRID_APIKEY = @SENDGRID_APIKEY, SenderEmail = @SenderEmail, SenderEmailFriendlyName = @SenderEmailFriendlyName, DefaultTaxID = @DefaultTaxID, EmailSendMethod = @EmailSendMethod WHERE ControlTableID = @ControlTableID
END

GO

Model: ControlTable.cs

using System;
using System.ComponentModel.DataAnnotations;
// This is the model for one row in the database table. You may need to make some adjustments.
namespace BlazorPurchaseOrders.Data
{
    public class ControlTable
    {
	[Required]
public int ControlTableID { get; set; }
[StringLength(50)]
public string SmtpHost { get; set; }
public int SmtpPort { get; set; }
[StringLength(50, ErrorMessage = "'Email' has a maximum length of 50 characters.")]
public string SmtpUserEmailAddress { get; set; }
[StringLength(50, ErrorMessage = "'Friendly Name' has a maximum length of 50 characters.")]
public string SmtpUserFriendlyName { get; set; }
[StringLength(50, ErrorMessage = "'Smtp Password' has a maximum length of 50 characters.")]
public string SmtpPass { get; set; }
[StringLength(255)]
public string SENDGRID_APIKEY { get; set; }
[StringLength(50, ErrorMessage = "'Email' has a maximum length of 50 characters.")]
public string SenderEmail { get; set; }
[StringLength(50, ErrorMessage = "'Friendly Name' has a maximum length of 50 characters.")]
public string SenderEmailFriendlyName { get; set; }
public int DefaultTaxID { get; set; }
[StringLength(10)]
public string EmailSendMethod { get; set; }

    }
}

Service: ControlTableService.cs

using Dapper;
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;

namespace BlazorPurchaseOrders.Data
{
    public class ControlTableService : IControlTableService
    {
        // Database connection
        private readonly SqlConnectionConfiguration _configuration;
        public ControlTableService(SqlConnectionConfiguration configuration)
        {
            _configuration = configuration;
        }

        // Get the top(1) record from controltable (SQL Select)
        public async Task<ControlTable> ControlTable_GetOne()
        {
            ControlTable controltable = new ();
              
            using (var conn = new SqlConnection(_configuration.Value))
            {
                controltable = await conn.QueryFirstOrDefaultAsync<ControlTable>("spControlTable_GetOne", commandType: CommandType.StoredProcedure);
            }
            return controltable;
        }
        // Update one ControlTable row based on its ControlTableID (SQL Update)
        public async Task<bool> ControlTableUpdate(ControlTable controltable)
        {
            using (var conn = new SqlConnection(_configuration.Value))
            {
                var parameters = new DynamicParameters();
                parameters.Add("ControlTableID", controltable.ControlTableID, DbType.Int32);

                parameters.Add("SmtpHost", controltable.SmtpHost, DbType.String);
                parameters.Add("SmtpPort", controltable.SmtpPort, DbType.Int32);
                parameters.Add("SmtpUserFriendlyName", controltable.SmtpUserFriendlyName, DbType.String);
                parameters.Add("SmtpUserEmailAddress", controltable.SmtpUserEmailAddress, DbType.String);
                parameters.Add("SmtpPass", controltable.SmtpPass, DbType.String);
                parameters.Add("SENDGRID_APIKEY", controltable.SENDGRID_APIKEY, DbType.String);
                parameters.Add("SenderEmail", controltable.SenderEmail, DbType.String);
                parameters.Add("SenderEmailFriendlyName", controltable.SenderEmailFriendlyName, DbType.String);
                parameters.Add("DefaultTaxID", controltable.DefaultTaxID, DbType.Int32);
                parameters.Add("EmailSendMethod", controltable.EmailSendMethod, DbType.String);

                await conn.ExecuteAsync("spControlTable_Update", parameters, commandType: CommandType.StoredProcedure);
            }
            return true;
        }
    }
}

Interface: IControlTableService.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorPurchaseOrders.Data
{
    // Each item below provides an interface to a method in ControlTableServices.cs
    public interface IControlTableService
    {
        Task<ControlTable> ControlTable_GetOne();
        Task<bool> ControlTableUpdate(ControlTable controltable);
    }
}

ControlTablePage.razor

@page "/controltable"
@using BlazorPurchaseOrders.Data
@inject IControlTableService ControlTableService
@inject ITaxService TaxService
@inject NavigationManager NavigationManager

<h3>Control Table</h3>
<hr />

<EditForm Model="@controltable" OnValidSubmit="@controltableSave">
    <DataAnnotationsValidator />

    <h5>SMTP Email Settings</h5>

    <div class=flex-container>

        <SfTextBox Enabled="true" Placeholder="SMTP Host (e.g. smtp.live.com)"
                   FloatLabelType="@FloatLabelType.Always"
                   @bind-Value="controltable.SmtpHost"></SfTextBox>


        <SfNumericTextBox Enabled="true" Placeholder="SMTP Port No. (e.g. 587)" Width="50"
                          Format="N0"
                          ShowSpinButton="false"
                          FloatLabelType="@FloatLabelType.Always"
                          @bind-Value="controltable.SmtpPort"></SfNumericTextBox>
    </div>

    <div class=flex-container>
        <SfTextBox Enabled="true" Placeholder="SMTP Email - Friendly Name (e.g. John Smith)"
                   FloatLabelType="@FloatLabelType.Always"
                   @bind-Value="controltable.SmtpUserFriendlyName"></SfTextBox>
        <ValidationMessage For="@(() => controltable.SmtpUserFriendlyName)" />



        <SfTextBox Enabled="true" Placeholder="SMTP Email Address (e.g. johnsmith@outlook.com)"
                   FloatLabelType="@FloatLabelType.Always"
                   @bind-Value="controltable.SmtpUserEmailAddress"></SfTextBox>
        <ValidationMessage For="@(() => controltable.SmtpUserEmailAddress)" />


        <SfTextBox Enabled="true" Placeholder="SMTP Email Password"
                   Type="InputType.Password"
                   FloatLabelType="@FloatLabelType.Always"
                   @bind-Value="controltable.SmtpPass"></SfTextBox>
        <ValidationMessage For="@(() => controltable.SmtpPass)" />
    </div>
    <br />

    <h5>SendGrid Email Settings</h5>
    <div class=flex-container>
        <SfTextBox Enabled="true" Placeholder="SendGrid API Key"
                   Type="InputType.Password"
                   FloatLabelType="@FloatLabelType.Always"
                   @bind-Value="controltable.SENDGRID_APIKEY"></SfTextBox>
        <ValidationMessage For="@(() => controltable.SENDGRID_APIKEY)" />
    </div>
    <div class=flex-container>

        <SfTextBox Enabled="true" Placeholder="SendGrid Email - Friendly Name"
                   FloatLabelType="@FloatLabelType.Always"
                   @bind-Value="controltable.SenderEmailFriendlyName"></SfTextBox>
        <ValidationMessage For="@(() => controltable.SenderEmailFriendlyName)" />

        <SfTextBox Enabled="true" Placeholder="SendGrid Email Address"
                   FloatLabelType="@FloatLabelType.Always"
                   @bind-Value="controltable.SenderEmail"></SfTextBox>
        <ValidationMessage For="@(() => controltable.SenderEmail)" />
    </div>
    <br />

    <h5>Select Email Method</h5>
    <div class=flex-container>
        <SfDropDownList TValue="string" TItem="EmailMethod" Placeholder="Select an Email Method" DataSource="@LocalData" @bind-Value="controltable.EmailSendMethod">
            <DropDownListFieldSettings Value="EmailItem" Text="EmailItem"></DropDownListFieldSettings>
        </SfDropDownList>
    </div>
    <br />

    <h5>Default Tax Rate</h5>
    <div class=flex-container>
        <SfDropDownList DataSource="@taxrate"
                        TItem="Tax"
                        TValue="int"
                        Text="TaxID"
                        @bind-Value="controltable.DefaultTaxID"
                        FloatLabelType="@FloatLabelType.Auto"
                        Placeholder="Select a Default Tax Rate"
                        Enabled="true">
            <DropDownListFieldSettings Text="TaxDescription" Value="TaxID"></DropDownListFieldSettings>
        </SfDropDownList>
    </div>
    <br />

    <div class=flex-box>
        <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>
</EditForm>

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


<style>
    .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; /* The gap betwen columns */
    }

    .flex-box {
        display: flex;
        justify-content: flex-end;
        margin: 10px;
        column-gap:10px;
    }

</style>

@code {

    ControlTable controltable = new();
    IEnumerable<Tax> taxrate;

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

    public class EmailMethod
    {
        public string EmailItem { get; set; }
    }

    List<EmailMethod> LocalData = new List<EmailMethod> {
    new EmailMethod() { EmailItem= "SMTP" },
    new EmailMethod() { EmailItem= "SendGrid" },
    };

    protected override async Task OnInitializedAsync()
    {
        //Get the only record - should be ID=1
        controltable = await ControlTableService.ControlTable_GetOne();
        taxrate = await TaxService.TaxList();
    }

    protected async Task controltableSave()
    {
        bool Success = await ControlTableService.ControlTableUpdate(controltable);

        if (Success == false)
        {
            WarningHeaderMessage = "Warning!";
            WarningContentMessage = "Control record was not saved.";
            Warning.OpenDialog();
            return;
        }
        else
        {
            NavigationManager.NavigateTo("/");
        }
    }

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

}
<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>

        <AuthorizeView Roles="Admin, Manager">
            <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>
        </AuthorizeView>

        <AuthorizeView Roles="Admin">
            <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="controltable">
                    <span class="oi oi-list-rich" aria-hidden="true"></span> Control Table
                </NavLink>
            </li>
        </AuthorizeView>

    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

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

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

PurchaseOrderPage.razor

@page "/purchaseorder/{POHeaderGuid:guid}"
@using BlazorPurchaseOrders.Data
@using System.Data

@inject NavigationManager NavigationManager
@inject ISupplierService SupplierService
@inject IPOHeaderService POHeaderService
@inject IPOLineService POLineService
@inject IProductService ProductService
@inject ITaxService TaxService
@inject IPurchaseOrderService PurchaseOrderService
@inject IControlTableService ControlTableService

@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;

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

    ControlTable controltable = new();                                          //NEW
    private int defaultTaxID { get; set; } = 0;                                 //NEW
    private decimal defaultTaxRate { get; set; } = 0;                           //NEW

    [Parameter]
    public Guid POHeaderGuid { 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();

        controltable = await ControlTableService.ControlTable_GetOne();                         //NEW
        defaultTaxID = controltable.DefaultTaxID;                                               //NEW
        defaultTaxRate = (decimal) tax.FirstOrDefault(x => x.TaxID == defaultTaxID).TaxRate;    //NEW

        if (POHeaderGuid == Guid.Empty)
        {
            pagetitle = "Add an Order";

            //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;

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

        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 = defaultTaxID;                                //NEW
                addeditOrderLine.POLineTaxRate = defaultTaxRate;                            //NEW
                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")
        {
            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)
        {
            //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.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 a new purchase order
            {
                DataTable orderLinesDataTable = new DataTable();

                orderLinesDataTable.Columns.Add("POLineHeaderID", typeof(Int32));
                orderLinesDataTable.Columns.Add("POLineProductID", typeof(Int32));
                orderLinesDataTable.Columns.Add("POLineProductDescription", typeof(string));
                orderLinesDataTable.Columns.Add("POLineProductQuantity", typeof(decimal));
                orderLinesDataTable.Columns.Add("POLineProductUnitPrice", typeof(decimal));
                orderLinesDataTable.Columns.Add("POLineTaxRate", typeof(decimal));
                orderLinesDataTable.Columns.Add("POLineTaxID", typeof(decimal));

                foreach (var individualPOLine in orderLines)
                {
                    DataRow newRow = orderLinesDataTable.NewRow();

                    // Set values in the columns:
                    //newRow["POLineHeaderID"] = HeaderID;
                    newRow["POLineProductID"] = individualPOLine.POLineProductID;
                    newRow["POLineProductDescription"] = individualPOLine.POLineProductDescription;
                    newRow["POLineProductQuantity"] = individualPOLine.POLineProductQuantity;
                    newRow["POLineProductUnitPrice"] = individualPOLine.POLineProductUnitPrice;
                    newRow["POLineTaxRate"] = individualPOLine.POLineTaxRate;
                    newRow["POLineTaxID"] = individualPOLine.POLineTaxID;

                    // Add the row to the rows collection.
                    orderLinesDataTable.Rows.Add(newRow);
                }

                bool Success = await PurchaseOrderService.POInsert(
                    orderaddedit.POHeaderOrderDate,
                    orderaddedit.POHeaderSupplierID,
                    orderaddedit.POHeaderSupplierAddress1,
                    orderaddedit.POHeaderSupplierAddress2,
                    orderaddedit.POHeaderSupplierAddress3,
                    orderaddedit.POHeaderSupplierPostCode,
                    orderaddedit.POHeaderSupplierEmail,
                    orderaddedit.POHeaderRequestedBy,
                    orderLinesDataTable);

                if (Success == false)
                {
                    WarningHeaderMessage = "Warning!";
                    WarningContentMessage = "Order was not saved.";
                    Warning.OpenDialog();
                    return;
                }

                orderLinesDataTable.Clear();

                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("/");
            }
        }
    }
}

EmailSenderMailKit.cs

using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using MimeKit.Text;

namespace BlazorPurchaseOrders.Data
{
    public interface IEmailService
    {
        bool Send(string SmtpHost,
            int SmtpPort,
            string SmtpUserFriendlyName,
            string SmtpUserEmailAddress,
            string SmtpPass,
            string to,
            string subject,
            string body);
    }

    public class EmailService : IEmailService
    {
        public bool Send(string SmtpHost, 
            int SmtpPort, 
            string SmtpUserFriendlyName, 
            string SmtpUserEmailAddress,
            string SmtpPass,
            string to, 
            string subject, 
            string body)
        {

            // create message
            var email = new MimeMessage();
            email.From.Add(new MailboxAddress(SmtpUserFriendlyName, SmtpUserEmailAddress));
            email.To.Add(new MailboxAddress(to, to));
            email.Subject = subject;
            email.Body = new TextPart(TextFormat.Html) { Text = body };

            try
            {
                // send email
                using var smtp = new SmtpClient();
                smtp.Connect(SmtpHost, SmtpPort, SecureSocketOptions.StartTls);
                smtp.Authenticate(SmtpUserEmailAddress, SmtpPass);
                smtp.Send(email);
                smtp.Disconnect(true);

                return true;
            }
            catch
            {
                return false;
            }

        }
    }
}

EmailSenderSendGrid.cs

using System.Threading.Tasks;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace BlazorPurchaseOrders.Data
{
    public interface IEmailSender
    {
        Task<bool> SendEmail(string apiKey,
            string FromFriendlyName,
            string FromEmailAddress,
            string toEmail,
            string subject,
            string body);
    }

    public class EmailSender : IEmailSender
    {
        public async Task<bool> SendEmail(string apiKey, 
            string FromFriendlyName,
            string FromEmailAddress,
            string toEmail, 
            string subject, 
            string body)
        {
            var client = new SendGridClient(apiKey);
            var from = new EmailAddress(FromEmailAddress, FromFriendlyName);            
            var to = new EmailAddress(toEmail, toEmail);
            var plainTextContent = body;
            var htmlContent = body;
            var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
            var response = await client.SendEmailAsync(msg).ConfigureAwait(false);
            return response.IsSuccessStatusCode;           
        }
    }
}

Index.razor

@page "/"
@using BlazorPurchaseOrders.Data
@using Microsoft.Extensions.Configuration

@inject IPOHeaderService POHeaderService
@inject NavigationManager NavigationManager
@inject IJSRuntime IJS
@inject IEmailService EmailService
@inject IEmailSender EmailSender
@inject IConfiguration _config
@inject IControlTableService ControlTableService

@attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]

@inject AuthenticationStateProvider AuthenticationStateProvider

<head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
</head>

<div class="col-sm-12">
    <AuthorizeView>
        <NotAuthorized>
            <h5>Please Log in</h5>
            <h6>or</h6>
            <h5>Register to use Blazor Purchase Orders</h5>
        </NotAuthorized>
        <Authorized>
            <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" />

        </Authorized>
    </AuthorizeView>
</div>

<div class="col-lg-12 control-section toast-default-section">
    <SfToast @ref="ToastObj" Timeout="4000">
        <ToastPosition X="Right" Y="Top"></ToastPosition>
    </SfToast>
</div>

<style>
    @@font-face {
        font-family: 'Toast_icons';
        src: url(data:application/x-font-ttf;charset=utf-8;base64,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) format('truetype');
        font-weight: normal;
        font-style: normal;
    }

    #toast_types button {
        margin: 5px;
        min-width: 160px;
        max-width: 160px;
    }

    .toast-icons {
        font-family: 'Toast_icons' !important;
        speak: none;
        font-size: 55px;
        font-style: normal;
        font-weight: normal;
        font-variant: normal;
        text-transform: none;
        line-height: 1;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    #toast_type .e-toast-icon.e-icons {
        height: auto;
        font-size: 30px;
    }

    .toast-icons.e-success::before {
        content: "\e701";
    }

    .toast-icons.e-error::before {
        content: "\e700";
    }

    .toast-icons.e-info::before {
        content: "\e704";
    }

    .toast-icons.e-warning::before {
        content: "\e703";
    }

    #toast_types {
        text-align: center;
    }
</style>

@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;
    private Guid selectedPOHeaderGuid { get; set; }
    private string selectedPOHeaderSupplierEmail { get; set; } = "";
    private int selectedPOHeaderOrderNumber { get; set; } = 0;

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

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

    private string emailbody = "";

    [CascadingParameter]
    private Task<AuthenticationState> authenticationStateTask { get; set; }

    SfToast ToastObj;

    string ServerLocation = "";

    ControlTable controltable = new();                                              //NEW
    private string SmtpHost { get; set; } = "";                                      //NEW
    private int SmtpPort { get; set; } = 0;                                         //NEW
    private string SmtpUserFriendlyName { get; set; } = "";                         //NEW
    private string SmtpUserEmailAddress { get; set; } = "";                         //NEW
    private string SmtpPass { get; set; } = "";                                     //NEW
    private string SENDGRID_APIKEY { get; set; } = "";                              //NEW
    private string SenderEmail { get; set; } = "";                                  //NEW
    private string SenderEmailFriendlyName { get; set; } = "";                      //NEW
    private string EmailSendMethod { get; set; } = "";                              //NEW

    private List<ToastModel> Toast = new List<ToastModel>
{
    new ToastModel{ Title = "Warning!", Content="There was a problem with your network connection.", CssClass="e-toast-warning", Icon="e-warning toast-icons" },
    new ToastModel{ Title = "Success!", Content="Your message has been sent successfully.", CssClass="e-toast-success", Icon="e-success toast-icons" },
    new ToastModel{ Title = "Error!", Content="A problem has been occurred while submitting your email", CssClass="e-toast-danger", Icon="e-error toast-icons" },
    new ToastModel{ Title = "Information!", Content="Please read the comments carefully.", CssClass="e-toast-info", Icon="e-info toast-icons" }
    };

    protected override async Task OnInitializedAsync()
    {
        //Populate the list of orders objects from the Purchase Order Header table.
        await GetOrderList();

        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" });
        Toolbaritems.Add(new ItemModel() { Text = "Preview", TooltipText = "Preview selected order", PrefixIcon = "e-print" });
        Toolbaritems.Add(new ItemModel() { Text = "Email", TooltipText = "Email selected order", PrefixIcon = "bi-envelope" });

        ServerLocation = _config.GetValue<string>("ApplicationSettings:PreviewOrderLinkRoute");

        controltable = await ControlTableService.ControlTable_GetOne();                 //NEW
        SmtpHost = controltable.SmtpHost;                                               //NEW
        SmtpPort = controltable.SmtpPort;                                               //NEW
        SmtpUserFriendlyName = controltable.SmtpUserFriendlyName;                       //NEW
        SmtpUserEmailAddress = controltable.SmtpUserEmailAddress;                       //NEW
        SmtpPass = controltable.SmtpPass;                                               //NEW
        SENDGRID_APIKEY = controltable.SENDGRID_APIKEY;                                 //NEW
        SenderEmail = controltable.SenderEmail;                                         //NEW
        SenderEmailFriendlyName = controltable.SenderEmailFriendlyName;                 //NEW
        EmailSendMethod = controltable.EmailSendMethod;                                 //NEW
    }


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

        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/{selectedPOHeaderGuid}");
            }
        }

        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();
            }
        }

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

            //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
            {
                await IJS.InvokeAsync<object>("open", new object[] { "/previeworder/" + selectedPOHeaderGuid + "", "_blank" });
            }
        }

        if (args.Item.Text == "Email")
        {
            //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();
                return;
            }

            if(selectedPOHeaderSupplierEmail==null || selectedPOHeaderSupplierEmail == "")
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Supplier Email Address is missing.";
                Warning.OpenDialog();
                return;
            }

            //Create body of email using html
            emailbody = "<p>Dear Supplier</p>";
            emailbody = emailbody + "<p>Please click one of the following links (or copy and paste the link into your browser) to view our ";
            emailbody = emailbody + "order number " + selectedPOHeaderOrderNumber + " </p>";

            //without the anchor tag
            emailbody = emailbody + "<p>" + ServerLocation + "/previeworder/"
                + selectedPOHeaderGuid + "</p>";

            //with the anchor tag
            emailbody = emailbody + "<p><a href= "" + ServerLocation + "/previeworder/"
                + selectedPOHeaderGuid + "">" + ServerLocation + "/previeworder/"
                + selectedPOHeaderGuid + "<a></p>";


            emailbody = emailbody + "<p>Kind regards</p>";
            emailbody = emailbody + "<p>BlazorCode</p>";

            bool emailSent = false;

            switch (EmailSendMethod)
            {
                case "SMTP":
                    emailSent = EmailService.Send(SmtpHost,
                        SmtpPort,
                        SmtpUserFriendlyName,
                        SmtpUserEmailAddress,
                        SmtpPass,
                        selectedPOHeaderSupplierEmail,
                        "Purchase Order",
                        emailbody);
                    break;

                case "SendGrid":
                    emailSent = await EmailSender.SendEmail(SENDGRID_APIKEY,
                        SenderEmailFriendlyName,
                        SenderEmail,
                        selectedPOHeaderSupplierEmail,
                        "Purchase Order",
                        emailbody);
                    break;

                default:
                    WarningHeaderMessage = "Warning!";
                    WarningContentMessage = "No Email Method has been setup in Control Table.";
                    Warning.OpenDialog();
                    break;
            }

            if (emailSent)
            {
                await this.ToastObj.Show(Toast[1]);
            }
            else
            {
                await this.ToastObj.Show(Toast[2]);
            }
        }
    }

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

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

    protected async Task GetOrderList()
    {
        var user = (await authenticationStateTask).User;


        if (user.IsInRole("Admin") || user.IsInRole("Manager"))
        {
            poheader = await POHeaderService.POHeaderList(null);   //leave user name blank
        }
        else
        {
            poheader = await POHeaderService.POHeaderList(user.Identity.Name); //pass user name
        }

    }
}