Index.razor
@page "/"
@using BlazorWallAreaCalculator.Data
@inject IProjectService ProjectService
@inject IRoomService RoomService
@inject IWallService WallService
@inject IDeductionService DeductionService
@inject SfDialogService DialogService
<PageTitle>Wall Area Calculator</PageTitle>
<div class="control_wrapper">
<SfDropDownList TItem="Project"
TValue="string"
DataSource="@projects"
Placeholder="Select a Project"
PopupHeight="200px"
PopupWidth="250px">
<DropDownListFieldSettings Text="ProjectName" Value="ProjectID"></DropDownListFieldSettings>
<DropDownListEvents TItem="Project" TValue="string" ValueChange="@OnChangeProject"></DropDownListEvents>
</SfDropDownList>
</div>
<hr />
<h6><b>Rooms</b></h6>
<SfGrid @ref="RoomGrid"
DataSource="@rooms"
AllowSorting="true"
AllowResizing="true"
Height="70"
Toolbar="@RoomToolbaritems">
<GridColumns>
<GridColumn Field="@nameof(Room.RoomName)"
HeaderText="Room Name"
TextAlign="@TextAlign.Left"
Width="50">
</GridColumn>
</GridColumns>
<GridEvents OnToolbarClick="RoomToolbarClickHandler" TValue="Room" RowSelected="RoomRowSelectHandler"></GridEvents>
</SfGrid>
<hr />
<h6><b>Walls</b></h6>
<SfGrid @ref="WallGrid"
DataSource="@walls"
AllowSorting="true"
AllowResizing="true"
Height="150"
Toolbar="@WallToolbaritems">
<GridColumns>
<GridColumn Field="@nameof(Wall.WallName)"
HeaderText="Wall Name"
TextAlign="@TextAlign.Left"
Width="50">
</GridColumn>
<GridColumn Field="@nameof(Wall.WallSqM)"
HeaderText="Area"
Format="n3"
TextAlign="@TextAlign.Right"
Width="50">
</GridColumn>
</GridColumns>
<GridAggregates>
<GridAggregate>
<GridAggregateColumns>
<GridAggregateColumn Field=@nameof(Wall.WallSqM) Type="AggregateType.Sum" Format="n3">
<FooterTemplate>
@{
var aggregate = (context as AggregateTemplateContext);
<div>
<p>Total Net Area (SqM): @aggregate.Sum</p>
</div>
}
</FooterTemplate>
</GridAggregateColumn>
</GridAggregateColumns>
</GridAggregate>
</GridAggregates>
<GridEvents OnToolbarClick="WallToolbarClickHandler" TValue="Wall" RowSelected="WallRowSelectHandler"></GridEvents>
</SfGrid>
<hr />
<h6><b>Deductions</b></h6>
<SfGrid ID="DeductionGrid"
DataSource="@deductions"
AllowSorting="true"
AllowResizing="true"
Height="125"
Toolbar="@DeductionToolbaritems">
<GridColumns>
<GridColumn Field="@nameof(Deduction.DeductionName)"
HeaderText="Description"
TextAlign="@TextAlign.Left"
Width="50">
</GridColumn>
<GridColumn Field="@nameof(Deduction.SqM)"
HeaderText="Area SqM"
TextAlign="@TextAlign.Right"
Format="n3"
Width="20">
<Template>
@{
var value = (context as Deduction);
decDeductionWidth = Convert.ToDecimal(value.DeductionWidth);
decDeductionHeight = Convert.ToDecimal(value.DeductionHeight);
decimal SqM = decimal.Round(((decDeductionWidth * decDeductionHeight) / 1000000), 3, MidpointRounding.AwayFromZero);
string SqMString = SqM.ToString("F3");
<div>@SqMString</div>
}
</Template>
</GridColumn>
</GridColumns>
<GridEvents OnToolbarClick="DeductionToolbarClickHandler"
TValue="Deduction" RowSelected="DeductionRowSelectHandler">
</GridEvents>
</SfGrid>
<SfDialog @ref="DialogRoom" IsModal="true" Width="420px" ShowCloseIcon="false" Visible="false" AllowDragging="true">
<DialogTemplates>
<Header> @dialogTitle</Header>
<Content>
<EditForm Model="@roomAddEdit" OnValidSubmit="@RoomSave">
<div>
<SfTextBox Enabled="true" Placeholder="Room Name"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="roomAddEdit.RoomName">
</SfTextBox>
</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="@CancelRoom">Cancel</button>
</div>
</div>
</EditForm>
</Content>
</DialogTemplates>
</SfDialog>
<SfDialog @ref="DialogWall" IsModal="true" Width="420px" ShowCloseIcon="false" Visible="false" AllowDragging="true">
<DialogTemplates>
<Header> @dialogTitle</Header>
<Content>
<EditForm Model="@wallAddEdit" OnValidSubmit="@WallSave">
<div>
<SfTextBox Enabled="true" Placeholder="Wall Name"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="wallAddEdit.WallName">
</SfTextBox>
<div>
<br />
</div>
<hr />
@if (IsAdd)
{
<SfDropDownList TItem="WallType"
TValue="string"
FloatLabelType="@FloatLabelType.Always"
DataSource="@LocalData"
Placeholder="Select a Wall Type"
PopupHeight="200px"
PopupWidth="250px">
<DropDownListFieldSettings Text="WallTypeName" Value="WallTypeID"></DropDownListFieldSettings>
<DropDownListEvents TItem="WallType" TValue="string" ValueChange="@OnChangeWallType"></DropDownListEvents>
</SfDropDownList>
}
else
{
<SfTextBox Enabled="false" Placeholder="Wall Type"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="wallAddEdit.WallTypeName">
</SfTextBox>
}
@switch (wallAddEdit.WallTypeName)
{
case "Simple":
<div>
<br />
<img src=".\\media\\simple.jpg" />
<br />
</div>
<div class="grid-container">
<div class="grid-child left-column">
<SfNumericTextBox Enabled="true"
Placeholder="Wall Length (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="wallAddEdit.WallLengthMax"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
</div>
<div class="grid-child right-column">
<SfNumericTextBox Enabled="true"
Placeholder="Wall Height (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="wallAddEdit.WallHeightMax"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
</div>
</div>
break;
case "Complex - Left":
<div>
<br />
<img src=".\\media\\complex-left.jpg" />
<br />
</div>
<div class="grid-container">
<div class="grid-child left-column">
<SfNumericTextBox Enabled="true"
Placeholder="Max Wall Length (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
Width="50"
@bind-Value="wallAddEdit.WallLengthMax"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
<SfNumericTextBox Enabled="true"
Placeholder="Min Wall Length (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
Width="50"
@bind-Value="wallAddEdit.WallLengthMin"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
</div>
<div class="grid-child right-column">
<SfNumericTextBox Enabled="true"
Placeholder="Max Wall Height (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
Width="50"
@bind-Value="wallAddEdit.WallHeightMax"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
<SfNumericTextBox Enabled="true"
Placeholder="Min Wall Height (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
Width="50"
@bind-Value="wallAddEdit.WallHeightMin"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
</div>
</div>
break;
case "Complex - Right":
<div>
<br />
<img src=".\\media\\complex-right.jpg" />
<br />
</div>
<div class="grid-container">
<div class="grid-child left-column">
<SfNumericTextBox Enabled="true"
Placeholder="Max Wall Length (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
Width="50"
@bind-Value="wallAddEdit.WallLengthMax"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
<SfNumericTextBox Enabled="true"
Placeholder="Min Wall Length (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
Width="50"
@bind-Value="wallAddEdit.WallLengthMin"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
</div>
<div class="grid-child right-column">
<SfNumericTextBox Enabled="true"
Placeholder="Max Wall Height (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
Width="50"
@bind-Value="wallAddEdit.WallHeightMax"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
<SfNumericTextBox Enabled="true"
Placeholder="Min Wall Height (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
Width="50"
@bind-Value="wallAddEdit.WallHeightMin"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
</div>
</div>
break;
}
</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="@CancelWall">Cancel</button>
</div>
</div>
</EditForm>
</Content>
</DialogTemplates>
</SfDialog>
<SfDialog @ref="DialogDeduction" IsModal="true" Width="420px" ShowCloseIcon="false" Visible="false" AllowDragging="true">
<DialogTemplates>
<Header> @dialogTitle</Header>
<Content>
<EditForm Model="@deductionAddEdit" OnValidSubmit="@DeductionSave">
<div>
<SfTextBox Enabled="true" Placeholder="Description"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="deductionAddEdit.DeductionName">
</SfTextBox>
<div class="grid-container">
<div class="grid-child left-column">
<SfNumericTextBox Enabled="true"
Placeholder="Width (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="deductionAddEdit.DeductionWidth"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
</div>
<div class="grid-child right-column">
<SfNumericTextBox Enabled="true"
Placeholder="Height (mm)"
Format="n0"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="deductionAddEdit.DeductionHeight"
ShowSpinButton=false
CssClass="e-style">
</SfNumericTextBox>
</div>
</div>
</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="@CancelDeduction">Cancel</button>
</div>
</div>
</EditForm>
</Content>
</DialogTemplates>
</SfDialog>
<style>
.control_wrapper {
width: 250px;
}
.e-numeric.e-style .e-control.e-numerictextbox {
text-align: right;
padding: 0px 5px 0px 0px;
}
.grid-container {
display: grid;
max-width: 400px; /* 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: 100px; /* size of the gap between columns */
}
</style>
@code {
public IEnumerable<Project>? projects;
public IEnumerable<Room>? rooms;
public IEnumerable<Wall>? walls;
public IEnumerable<Deduction>? deductions;
[Parameter]
public int SelectedProjectID { get; set; } = 0;
public int SelectedRoomID { get; set; } = 0;
public int SelectedWallID { get; set; } = 0;
public string SelectedWallName { get; set; } = string.Empty;
public decimal SelectedWallSqM { get; set; } = decimal.Zero;
public int SelectedDeductionID { get; set; } = 0;
public string SelectedDeductionName { get; set; } = string.Empty;
public int SelectedDeductionWidth { get; set; } = 0;
public int SelectedDeductionHeight { get; set; } = 0;
SfGrid<Room>? RoomGrid;
SfGrid<Wall>? WallGrid;
SfDialog? DialogRoom;
Room roomAddEdit = new Room();
SfDialog? DialogWall;
Wall wallAddEdit = new Wall();
SfDialog? DialogDeduction;
Deduction deductionAddEdit = new Deduction();
public string SelectedWallTypeName { get; set; } = string.Empty;
public bool IsAdd { get; set; }
public string dialogTitle = "";
public string SelectedRoomName { get; set; } = string.Empty;
private List<ItemModel> RoomToolbaritems = new List<ItemModel>();
private List<ItemModel> WallToolbaritems = new List<ItemModel>();
private List<ItemModel> DeductionToolbaritems = new List<ItemModel>();
public int SelectedWallLengthMax { get; set; } = 0;
public int SelectedWallLengthMin { get; set; } = 0;
public int SelectedWallHeightMax { get; set; } = 0;
public int SelectedWallHeightMin { get; set; } = 0;
public decimal decWallSqM = decimal.Zero;
public decimal decWallLengthMax = decimal.Zero;
public decimal decWallLengthMin = decimal.Zero;
public decimal decWallHeightMax = decimal.Zero;
public decimal decWallHeightMin = decimal.Zero;
public decimal decDeductionWidth = decimal.Zero;
public decimal decDeductionHeight = decimal.Zero;
protected override async Task OnInitializedAsync()
{
//Populate the list of projects objects from the Project table.
projects = await ProjectService.ProjectReadAll();
RoomToolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new room", PrefixIcon = "e-add" });
RoomToolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected room", PrefixIcon = "e-edit" });
RoomToolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected room", PrefixIcon = "e-delete" });
WallToolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new wall", PrefixIcon = "e-add" });
WallToolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected wall", PrefixIcon = "e-edit" });
WallToolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected wall", PrefixIcon = "e-delete" });
DeductionToolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a deduction", PrefixIcon = "e-add" });
DeductionToolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected deduction", PrefixIcon = "e-edit" });
DeductionToolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected deduction", PrefixIcon = "e-delete" });
}
public class WallType
{
public int WallTypeID { get; set; } = 0;
public string WallTypeName { get; set; } = string.Empty;
}
List<WallType> LocalData = new List<WallType>
{
new WallType() {WallTypeID =1, WallTypeName= "Simple" },
new WallType() {WallTypeID =2, WallTypeName= "Complex - Left" },
new WallType() {WallTypeID =3, WallTypeName= "Complex - Right" },
};
#region Rooms
public async Task RoomToolbarClickHandler(ClickEventArgs args)
{
if (args.Item.Text == "Add")
{
//Code for adding goes here
//Check that a Project has been selected:
if (SelectedProjectID == 0)
{
await DialogService.AlertAsync("Please select a project.", "No Project Selected");
return;
}
dialogTitle = "Add a Room";
roomAddEdit = new();
await DialogRoom.ShowAsync(false);
}
if (args.Item.Text == "Edit")
{
//Code for editing
dialogTitle = "Edit a Room";
//Check that a Room has been selected
if (SelectedRoomID == 0)
{
await DialogService.AlertAsync("Please select a room.", "No Room Selected");
return;
}
else
{
//populate roomAddEdit (temporary data set used for the editing process)
roomAddEdit = new();
roomAddEdit.RoomID = SelectedRoomID;
roomAddEdit.RoomName = SelectedRoomName;
await DialogRoom.ShowAsync(false);
}
}
if (args.Item.Text == "Delete")
{
//Code for deleting
if (SelectedRoomID == 0)
{
await DialogService.AlertAsync("Please select a room.", "No Room Selected");
return;
}
else
{
bool isConfirm = await DialogService.ConfirmAsync(
"Are you sure you want to delete " + SelectedRoomName + "?",
"Delete " + SelectedRoomName);
if (isConfirm == true)
{
await RoomService.RoomDelete(SelectedRoomID);
//Refresh datagrid
rooms = await RoomService.RoomsReadByProject(SelectedProjectID);
walls = Enumerable.Empty<Wall>();
deductions = Enumerable.Empty<Deduction>();
SelectedRoomID = 0;
StateHasChanged();
}
}
}
}
public async Task RoomRowSelectHandler(RowSelectEventArgs<Room> args)
{
//{args.Data} returns the current selected records.
SelectedRoomID = args.Data.RoomID;
SelectedRoomName = args.Data.RoomName;
walls = await WallService.WallsReadByRoom(SelectedRoomID);
deductions = Enumerable.Empty<Deduction>();
}
public async Task OnChangeProject(ChangeEventArgs<string, Project> args)
{
SelectedProjectID = args.ItemData.ProjectID;
rooms = await RoomService.RoomsReadByProject(SelectedProjectID);
SelectedRoomID = 0;
walls = Enumerable.Empty<Wall>();
deductions = Enumerable.Empty<Deduction>();
}
protected async Task RoomSave()
{
if (roomAddEdit.RoomID == 0)
{
// Insert if RoomID is zero.
//Check for duplicates
int duplicates = await RoomService.CountRoomsByNameAndProject(roomAddEdit.RoomName, SelectedProjectID);
if (duplicates == 0)
{
roomAddEdit.ProjectID = SelectedProjectID;
try
{
await RoomService.RoomCreate(roomAddEdit);
//Refresh datagrid
rooms = await RoomService.RoomsReadByProject(SelectedProjectID);
walls = Enumerable.Empty<Wall>();
deductions = Enumerable.Empty<Deduction>();
SelectedRoomID = 0;
StateHasChanged();
await DialogRoom.HideAsync();
}
catch
{
// Display warning message
await DialogService.AlertAsync("An unexpected error has occured.", "Unknown Error");
return;
}
}
else
{
//Room already exists - warn the user
await DialogService.AlertAsync("This Room already exists; it cannot be added again.", "Warning!");
return;
}
}
else
{
// Record is being edited
// Check for duplicates
int duplicates = await RoomService.CountRoomsByNameAndProjectAndId(roomAddEdit.RoomName, roomAddEdit.RoomID, SelectedProjectID);
if (duplicates == 0)
{
try
{
await RoomService.RoomUpdate(roomAddEdit);
//Refresh datagrid
rooms = await RoomService.RoomsReadByProject(SelectedProjectID);
StateHasChanged();
await DialogRoom.HideAsync();
}
catch
{
// Display warning message
await DialogService.AlertAsync("An unexpected error has occured.", "Unknown Error");
return;
}
}
else
{
//Room already exists - warn the user
await DialogService.AlertAsync("This room already exists", "Room already Exists");
return;
}
}
}
void CancelRoom()
{
DialogRoom.HideAsync();
}
#endregion
#region Walls
public void OnChangeWallType(ChangeEventArgs<string, WallType> args)
{
wallAddEdit.WallTypeID = args.ItemData.WallTypeID;
wallAddEdit.WallTypeName = args.ItemData.WallTypeName;
}
public async Task WallToolbarClickHandler(ClickEventArgs args)
{
//Check that a room has been selected
if (SelectedRoomID == 0)
{
await DialogService.AlertAsync("Please select a room.", "No Room Selected");
return;
}
if (args.Item.Text == "Add")
{
//Code for adding goes here
IsAdd = true;
SelectedWallID = 0;
SelectedWallTypeName = string.Empty;
dialogTitle = "Add a Wall";
wallAddEdit = new();
await DialogWall.ShowAsync(false);
}
if (args.Item.Text == "Edit")
{
//Code for editing
IsAdd = false;
dialogTitle = "Edit a Wall";
//Check that a wall has been selected
if (SelectedWallID == 0)
{
await DialogService.AlertAsync("Please select a wall.", "No Wall Selected");
return;
}
else
{
//populate wallAddEdit (temporary data set used for the editing process)
wallAddEdit.WallID = SelectedWallID;
wallAddEdit.RoomID = SelectedRoomID;
wallAddEdit.WallName = SelectedWallName;
wallAddEdit.WallSqM = SelectedWallSqM;
wallAddEdit.WallTypeName = SelectedWallTypeName; //Omitted for Dynamic Dialog video
wallAddEdit.WallLengthMax = SelectedWallLengthMax;
wallAddEdit.WallLengthMin = SelectedWallLengthMin;
wallAddEdit.WallHeightMax = SelectedWallHeightMax;
wallAddEdit.WallHeightMin = SelectedWallHeightMin;
await DialogWall.ShowAsync(false);
StateHasChanged();
}
}
if (args.Item.Text == "Delete")
{
//Code for deleting
if (SelectedWallID == 0)
{
await DialogService.AlertAsync("Please select a wall.", "No Wall Selected");
return;
}
else
{
bool isConfirm = await DialogService.ConfirmAsync(
"Are you sure you want to delete " + SelectedWallName + "?",
"Delete " + SelectedWallName);
if (isConfirm == true)
{
await WallService.WallDelete(SelectedWallID);
//Refresh datagrid
walls = await WallService.WallsReadByRoom(SelectedRoomID);
deductions = Enumerable.Empty<Deduction>();
StateHasChanged();
SelectedWallID = 0;
}
}
}
}
public async Task WallRowSelectHandler(RowSelectEventArgs<Wall> args) //Note <Wall>
{
//{args.Data} returns the current selected records.
SelectedWallID = args.Data.WallID;
SelectedWallName = args.Data.WallName;
SelectedWallTypeName = args.Data.WallTypeName; //Omitted for Dynamic Dialog video
SelectedWallLengthMax = args.Data.WallLengthMax;
SelectedWallLengthMin = args.Data.WallLengthMin;
SelectedWallHeightMax = args.Data.WallHeightMax;
SelectedWallHeightMin = args.Data.WallHeightMin;
deductions = await DeductionService.DeductionsReadByWall(SelectedWallID);
StateHasChanged();
}
protected async Task WallSave()
{
//Run CalculateArea
await CalculateArea(wallAddEdit, 0);
if (wallAddEdit.WallID == 0) //It's an insert
{
//Check for duplicates
int duplicates = await WallService.CountWallsByNameAndRoom(wallAddEdit.WallName, SelectedRoomID);
if (duplicates == 0)
{
wallAddEdit.RoomID = SelectedRoomID;
try
{
await WallService.WallCreate(wallAddEdit);
//Refresh datagrid
walls = await WallService.WallsReadByRoom(SelectedRoomID);
deductions = Enumerable.Empty<Deduction>();
StateHasChanged();
await DialogWall.HideAsync();
}
catch
{
// Display warning message
await DialogService.AlertAsync("An unexpected error has occured.", "Unknown Error");
return;
}
}
else
{
//Room already exists - warn the user
await DialogService.AlertAsync("This wall already exists for this room; it cannot be added again.", "Warning!");
return;
}
}
else //It's an edit
{
//It's an edit
// Check for duplicates
int duplicates = await WallService.CountWallsByNameAndRoomAndId(wallAddEdit.WallName, wallAddEdit.WallID, SelectedRoomID);
if (duplicates == 0)
{
try
{
//Upate Wall
await WallService.WallUpdate(wallAddEdit);
//Refresh datagrid
walls = await WallService.WallsReadByRoom(SelectedRoomID);
StateHasChanged();
await DialogWall.HideAsync();
}
catch
{
// Display warning message
await DialogService.AlertAsync("An unexpected error has occured.", "Unknown Error");
return;
}
}
else
{
//Wall already exists - warn the user
await DialogService.AlertAsync("This wall already exists for this room", "Wall Already Exists");
return;
}
}
wallAddEdit = new();
}
void CancelWall()
{
DialogWall.HideAsync();
}
#endregion
#region Deduction
public async Task DeductionToolbarClickHandler(ClickEventArgs args)
{
//Check that a Wall has been selected
if (args.Item.Text == "Add")
{
if (SelectedWallID == 0)
{
await DialogService.AlertAsync("Please select a wall.", "No Wall Selected");
return;
}
else
{
//Code for adding goes here
dialogTitle = "Add a Deduction";
deductionAddEdit = new();
deductionAddEdit.WallID = SelectedWallID;
await DialogDeduction.ShowAsync(false);
}
}
if (args.Item.Text == "Edit")
{
//Code for editing
dialogTitle = "Edit a Deduction";
//Check that a wall has been selected
if (SelectedDeductionID == 0)
{
await DialogService.AlertAsync("Please select a deduction.", "No Deduction Selected");
return;
}
else
{
//populate deductionAddEdit (temporary data set used for the editing process)
deductionAddEdit = new();
deductionAddEdit.DeductionID = SelectedDeductionID;
deductionAddEdit.WallID = SelectedWallID;
deductionAddEdit.DeductionName = SelectedDeductionName;
deductionAddEdit.DeductionWidth = SelectedDeductionWidth;
deductionAddEdit.DeductionHeight = SelectedDeductionHeight;
await DialogDeduction.ShowAsync(false);
StateHasChanged();
}
}
if (args.Item.Text == "Delete")
{
//Code for deleting
if (SelectedDeductionID == 0)
{
await DialogService.AlertAsync("Please select a deduction.", "No Deduction Selected");
return;
}
else
{
bool isConfirm = await DialogService.ConfirmAsync(
"Are you sure you want to delete " + SelectedDeductionName + "?",
"Delete " + SelectedDeductionName);
if (isConfirm == true)
{
await DeductionService.DeductionDelete(SelectedDeductionID);
//Always do area calculation
await CalculateArea(wallAddEdit, SelectedWallID);
//Refresh datagrid
deductions = await DeductionService.DeductionsReadByWall(SelectedWallID);
walls = await WallService.WallsReadByRoom(SelectedRoomID);
StateHasChanged();
SelectedWallID = 0;
}
}
}
}
public void DeductionRowSelectHandler(RowSelectEventArgs<Deduction> args)
{
//{args.Data} returns the current selected record.
SelectedDeductionID = args.Data.DeductionID;
SelectedDeductionName = args.Data.DeductionName;
SelectedDeductionWidth = args.Data.DeductionWidth;
SelectedDeductionHeight = args.Data.DeductionHeight;
StateHasChanged();
}
protected async Task DeductionSave()
{
if (deductionAddEdit.DeductionID == 0) //It's an insert
{
await DeductionService.DeductionCreate(deductionAddEdit);
}
else
{
await DeductionService.DeductionUpdate(deductionAddEdit);
}
//Always do area calculation
await CalculateArea(wallAddEdit, SelectedWallID);
deductions = await DeductionService.DeductionsReadByWall(SelectedWallID);
walls = await WallService.WallsReadByRoom(SelectedRoomID);
await DialogDeduction.HideAsync();
StateHasChanged();
}
void CancelDeduction()
{
DialogDeduction.HideAsync();
}
#endregion
#region Area Calculation
public async Task CalculateArea(Wall wallAddEdit, int SelectedWallID)
{
//If coming from WallSave, wallAddEdit will be populated.
//If coming from Deductions wallAddEdit will be empty (but SelectedWallID will be non-zero),
//so we need to populate wallAddEdit using SelectedWallID
if (SelectedWallID != 0)
{
wallAddEdit = await WallService.WallReadOne(SelectedWallID);
}
switch (wallAddEdit.WallTypeName)
{
case "Simple":
decWallLengthMax = Convert.ToDecimal(wallAddEdit.WallLengthMax);
decWallHeightMax = Convert.ToDecimal(wallAddEdit.WallHeightMax);
decimal area = decimal.Round((decWallLengthMax * decWallHeightMax) / 1000000, 3, MidpointRounding.AwayFromZero);
wallAddEdit.WallSqM = area;
break;
case "Complex - Left":
case "Complex - Right":
decWallLengthMax = Convert.ToDecimal(wallAddEdit.WallLengthMax);
decWallHeightMax = Convert.ToDecimal(wallAddEdit.WallHeightMax);
decWallLengthMin = Convert.ToDecimal(wallAddEdit.WallLengthMin);
decWallHeightMin = Convert.ToDecimal(wallAddEdit.WallHeightMin);
decimal area1 = (decWallHeightMax * decWallLengthMin);
decimal area2 = ((decWallLengthMax - decWallLengthMin) * decWallHeightMin);
decimal area3 = decimal.Round((((decWallLengthMax - decWallLengthMin) * (decWallHeightMax - decWallHeightMin)) / 2), 3, MidpointRounding.AwayFromZero);
wallAddEdit.WallSqM = decimal.Round(((area1 + area2 + area3) / 1000000), 3, MidpointRounding.AwayFromZero);
break;
}
//Calculate Deductions
//Always set Deductions total to 0
decimal wallDeductionTotal = decimal.Zero;
// If a new wall is being inserted wallAddEdit.WallID will be 0 and we can skip deductions
if (wallAddEdit.WallID != 0)
{
//Get deductions for this Wall
deductions = await DeductionService.DeductionsReadByWall(wallAddEdit.WallID);
foreach (var deductionItem in deductions)
{
decDeductionWidth = Convert.ToDecimal(deductionItem.DeductionWidth);
decDeductionHeight = Convert.ToDecimal(deductionItem.DeductionHeight);
decimal areaDeduction = decimal.Round((decDeductionWidth * decDeductionHeight), 3, MidpointRounding.AwayFromZero) / 1000000;
wallDeductionTotal = wallDeductionTotal + areaDeduction;
}
}
wallAddEdit.WallSqM = wallAddEdit.WallSqM - wallDeductionTotal;
if (SelectedWallID != 0) //SelectedWall is set to 0 for WallSave
{
// Otherwise, update wall record with revised calculation
await WallService.WallUpdateArea(wallAddEdit.WallID, wallAddEdit.WallSqM);
}
}
#endregion
}
DeductionsService.cs
using Dapper;
using System.Data.SQLite;
using System.Data;
namespace BlazorWallAreaCalculator.Data
{
public class DeductionService : IDeductionService
{
// Database connection
private readonly IConfiguration _configuration;
public DeductionService(IConfiguration configuration)
{
_configuration = configuration;
}
public string connectionId = "Default";
public string sqlCommand = "";
private Deduction? deduction;
// DeductionCreate
public async Task<bool> DeductionCreate(Deduction deduction)
{
var parameters = new DynamicParameters();
parameters.Add("@WallID", deduction.WallID, DbType.Int32);
parameters.Add("@DeductionName", deduction.DeductionName, DbType.String);
parameters.Add("@DeductionWidth", deduction.DeductionWidth, DbType.Int32);
parameters.Add("@DeductionHeight", deduction.DeductionHeight, DbType.Int32);
sqlCommand = "Insert into Deduction (WallID, DeductionName, DeductionWidth, DeductionHeight) ";
sqlCommand += "values(@WallID, @DeductionName, @DeductionWidth, @DeductionHeight )";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
await conn.ExecuteAsync(sqlCommand, parameters);
}
return true;
}
// DeductionRead
public async Task<IEnumerable<Deduction>> DeductionReadAll()
{
IEnumerable<Deduction> deductions;
sqlCommand = "Select * from Deduction ORDER BY DeductionName";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
deductions = await conn.QueryAsync<Deduction>(sqlCommand);
}
return deductions;
}
public async Task<IEnumerable<Deduction>> DeductionsReadByWall(int WallID)
{
IEnumerable<Deduction> deductions;
var parameters = new DynamicParameters();
parameters.Add("WallID", WallID, DbType.Int32);
sqlCommand = "Select * from Deduction ";
sqlCommand += "WHERE WallID = @WallID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
deductions = await conn.QueryAsync<Deduction>(sqlCommand, parameters);
}
return deductions;
}
public async Task<int> CountDeductionsByName(string DeductionName)
{
var parameters = new DynamicParameters();
parameters.Add("@DeductionName", DeductionName, DbType.String);
sqlCommand = "Select Count(*) from Deduction ";
sqlCommand += "where Upper(DeductionName) = Upper(@DeductionName)";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
var countDeduction = await conn.QueryFirstOrDefaultAsync<int>(sqlCommand, parameters);
return countDeduction;
}
}
public async Task<int> CountDeductionsByNameAndId(string DeductionName, int DeductionID)
{
var parameters = new DynamicParameters();
parameters.Add("@DeductionName", DeductionName, DbType.String);
parameters.Add("@DeductionID", DeductionID, DbType.Int32);
sqlCommand = "Select Count(*) from Deduction ";
sqlCommand += "where Upper(DeductionName) = Upper(@DeductionName) ";
sqlCommand += "and DeductionID <> @DeductionID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
var countDeduction = await conn.QueryFirstOrDefaultAsync<int>(sqlCommand, parameters);
return countDeduction;
}
}
//DeductionUpdate
public async Task<bool> DeductionUpdate(Deduction deduction)
{
var parameters = new DynamicParameters();
parameters.Add("DeductionID", deduction.DeductionID, DbType.Int32);
parameters.Add("DeductionName", deduction.DeductionName, DbType.String);
parameters.Add("@DeductionWidth", deduction.DeductionWidth, DbType.Int32);
parameters.Add("@DeductionHeight", deduction.DeductionHeight, DbType.Int32);
sqlCommand = "Update Deduction ";
sqlCommand += "SET DeductionName = @DeductionName, ";
sqlCommand += "DeductionWidth = @DeductionWidth, ";
sqlCommand += "DeductionHeight = @DeductionHeight ";
sqlCommand += "WHERE DeductionID = @DeductionID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
await conn.ExecuteAsync(sqlCommand, parameters);
}
return true;
}
//DeductionDelete
public async Task<bool> DeductionDelete(Int32 DeductionID)
{
var parameters = new DynamicParameters();
parameters.Add("@DeductionID", DeductionID, DbType.Int32);
//PRAGMA is specific to SQLite and this command is required for DELETE CASCADE to work
sqlCommand = "PRAGMA foreign_keys = ON;";
sqlCommand += "Delete from Deduction ";
sqlCommand += "WHERE DeductionID = @DeductionID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
await conn.ExecuteAsync(sqlCommand, parameters);
}
return true;
}
}
}
IDeductionService.cs
namespace BlazorWallAreaCalculator.Data
{
public interface IDeductionService
{
Task<bool> DeductionCreate(Deduction deduction);
Task<IEnumerable<Deduction>> DeductionReadAll();
Task<IEnumerable<Deduction>> DeductionsReadByWall(int WallID);
Task<int> CountDeductionsByName(string DeductionName);
Task<int> CountDeductionsByNameAndId(string DeductionName, int DeductionID);
Task<bool> DeductionUpdate(Deduction deduction);
Task<bool> DeductionDelete(Int32 DeductionID);
}
}
WallService.cs
using Dapper;
using System.Data.SQLite;
using System.Data;
namespace BlazorWallAreaCalculator.Data
{
public class WallService : IWallService
{
// Database connection
private readonly IConfiguration _configuration;
public WallService(IConfiguration configuration)
{
_configuration = configuration;
}
public string connectionId = "Default";
public string sqlCommand = "";
private Wall? wall;
// WallCreate
public async Task<bool> WallCreate(Wall wall)
{
var parameters = new DynamicParameters();
parameters.Add("@RoomID", wall.RoomID, DbType.Int32);
parameters.Add("@WallName", wall.WallName, DbType.String);
parameters.Add("@WallTypeID", wall.WallTypeID, DbType.Int32);
parameters.Add("@WallTypeName", wall.WallTypeName, DbType.String);
parameters.Add("@WallLengthMax", wall.WallLengthMax, DbType.Int32);
parameters.Add("@WallLengthMin", wall.WallLengthMin, DbType.Int32);
parameters.Add("@WallHeightMax", wall.WallHeightMax, DbType.Int32);
parameters.Add("@WallHeightMin", wall.WallHeightMin, DbType.Int32);
parameters.Add("@WallSqM", wall.WallSqM, DbType.Decimal);
sqlCommand = "Insert into Wall (RoomID, WallName, WallTypeID, WallTypeName, " +
"WallLengthMax, WallLengthMin, WallHeightMax, WallHeightMin, WallSqM) ";
sqlCommand += "values(@RoomID, @WallName, @WallTypeID, @WallTypeName, " +
"@WallLengthMax, @WallLengthMin, @WallHeightMax, @WallHeightMin, @WallSqM) ";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
await conn.ExecuteAsync(sqlCommand, parameters);
}
return true;
}
// WallRead
public async Task<IEnumerable<Wall>> WallsReadByRoom(int RoomID)
{
IEnumerable<Wall> walls;
var parameters = new DynamicParameters();
parameters.Add("RoomID", RoomID, DbType.Int32);
sqlCommand = "Select * from Wall ";
sqlCommand += "WHERE RoomID = @RoomID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
walls = await conn.QueryAsync<Wall>(sqlCommand, parameters);
}
return walls;
}
#region CountWalls
public async Task<int> CountWallsByNameAndRoom(string WallName, int RoomID)
{
var parameters = new DynamicParameters();
parameters.Add("@WallName", WallName, DbType.String);
parameters.Add("@RoomID", RoomID, DbType.Int32);
sqlCommand = "Select Count(*) from Wall ";
sqlCommand += "where Upper(WallName) = Upper(@WallName) ";
sqlCommand += "and RoomID = @RoomID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
var countWall = await conn.QueryFirstOrDefaultAsync<int>(sqlCommand, parameters);
return countWall;
}
}
public async Task<int> CountWallsByNameAndRoomAndId(string WallName, int WallID, int RoomID)
{
var parameters = new DynamicParameters();
parameters.Add("@WallName", WallName, DbType.String);
parameters.Add("@WallID", WallID, DbType.Int32);
parameters.Add("@RoomID", RoomID, DbType.Int32);
sqlCommand = "Select Count(*) from Wall ";
sqlCommand += "where Upper(WallName) = Upper(@WallName) ";
sqlCommand += "and RoomID = @RoomID ";
sqlCommand += "and WallID <> @WallID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
var countWall = await conn.QueryFirstOrDefaultAsync<int>(sqlCommand, parameters);
return countWall;
}
}
#endregion
//WallUpdate
public async Task<bool> WallUpdate(Wall wall)
{
var parameters = new DynamicParameters();
parameters.Add("@WallID", wall.WallID, DbType.Int32);
parameters.Add("@WallName", wall.WallName, DbType.String);
parameters.Add("@WallTypeID", wall.WallTypeID, DbType.Int32);
parameters.Add("@WallTypeName", wall.WallTypeName, DbType.String);
parameters.Add("@WallLengthMax", wall.WallLengthMax, DbType.Int32);
parameters.Add("@WallLengthMin", wall.WallLengthMin, DbType.Int32);
parameters.Add("@WallHeightMax", wall.WallHeightMax, DbType.Int32);
parameters.Add("@WallHeightMin", wall.WallHeightMin, DbType.Int32);
parameters.Add("@WallSqM", wall.WallSqM, DbType.Decimal);
sqlCommand = "Update Wall ";
sqlCommand += "SET WallName = @WallName, " +
"WallTypeID = @WallTypeID, " +
"WallTypeName = @WallTypeName, " +
"WallLengthMax = @WallLengthMax, " +
"WallLengthMin = @WallLengthMin, " +
"WallHeightMax = @WallHeightMax, " +
"WallHeightMin = @WallHeightMin, " +
"WallSqM = @WallSqM ";
sqlCommand += "WHERE WallID = @WallID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
await conn.ExecuteAsync(sqlCommand, parameters);
}
return true;
}
//WallDelete
public async Task<bool> WallDelete(Int32 WallID)
{
var parameters = new DynamicParameters();
parameters.Add("@WallID", WallID, DbType.Int32);
//PRAGMA is specific to SQLite and this command is required for DELETE CASCADE to work
sqlCommand = "PRAGMA foreign_keys = ON;";
sqlCommand += "Delete from Wall ";
sqlCommand += "WHERE WallID = @WallID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
await conn.ExecuteAsync(sqlCommand, parameters);
}
return true;
}
//WallUpdateArea
public async Task<bool> WallUpdateArea(int WallID, decimal WallSqM)
{
var parameters = new DynamicParameters();
parameters.Add("@WallID", WallID, DbType.Int32);
parameters.Add("@WallSqM", WallSqM, DbType.Decimal);
sqlCommand = "Update Wall ";
sqlCommand += "SET WallSqM = @WallSqM ";
sqlCommand += "WHERE WallID = @WallID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
await conn.ExecuteAsync(sqlCommand, parameters);
}
return true;
}
public async Task<Wall> WallReadOne(Int32 @WallID)
{
var parameters = new DynamicParameters();
parameters.Add("@WallID", WallID, DbType.Int32);
sqlCommand = "Select * from Wall ";
sqlCommand += "WHERE WallID = @WallID";
using IDbConnection conn = new SQLiteConnection(_configuration.GetConnectionString(connectionId));
{
wall = await conn.QueryFirstOrDefaultAsync<Wall>(sqlCommand, parameters);
}
return wall;
}
}
}
IWallService.cs
namespace BlazorWallAreaCalculator.Data
{
public interface IWallService
{
Task<bool> WallCreate(Wall wall);
Task<IEnumerable<Wall>> WallsReadByRoom(int RoomID);
Task<Wall> WallReadOne(Int32 @WallID);
Task<int> CountWallsByNameAndRoom(string WallName, int RoomID);
Task<int> CountWallsByNameAndRoomAndId(string WallName, int WallID, int RoomID);
Task<bool> WallUpdate(Wall wall);
Task<bool> WallUpdateArea(int WallID, decimal WallSqM);
Task<bool> WallDelete(Int32 WallID);
}
}