This commit is contained in:
nutchayut
2024-05-31 00:38:31 +07:00
commit 88ddddd7c2
234 changed files with 37557 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
@using System.Collections.Generic
@using Microsoft.AspNetCore.Authentication
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Mvc.Localization
@model UserModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<h1><i class="fas fa-user"></i> @ViewData["Title"]</h1>
<div class="row">
<div class="col-md-8">
<section>
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Username" class="col-md-2 control-label">@Localizer["Username"]</label>
<div class="col-md-10">
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Password" class="col-md-2 control-label">@Localizer["Password"]</label>
<div class="col-md-10">
<input asp-for="Password" type="password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-primary">@Localizer["Login"]</button>
</div>
</div>
</form>
</section>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

View File

@@ -0,0 +1,264 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model ChargePointViewModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
@if (Model != null)
{
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<h4>@Localizer["EditChargePoint"]</h4>
<br />
<div class="container">
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="ChargePointId">@Localizer["ChargePointIdLabel"]</label>
</div>
</div>
@if (Model.CurrentId == "@")
{
<div class="col-sm-6">
<div class="form-group">
<input type="text" class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 100)" data-val-length-max="100" data-val-required="@Localizer["RequiredField"]" id="ChargePointId" maxlength="100" name="ChargePointId" placeholder="@Localizer["ChargePointIdPlaceholder"]" value="@Model.ChargePointId">
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="ChargePointId" data-valmsg-replace="true"></span>
</div>
}
else
{
<div class="col-sm-6">
<div class="form-group">
<input type="text" readonly class="form-control" id="ChargePointId" name="ChargePointId" placeholder="@Localizer["ChargePointIdPlaceholder"]" value="@Model.ChargePointId">
</div>
</div>
}
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="Name">@Localizer["NameLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 100)" data-val-length-max="100" data-val-required="@Localizer["RequiredField"]" id="Name" maxlength="100" name="Name" placeholder="@Localizer["NamePlaceholder"]" type="text" value="@Model.Name" />
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="Name" data-valmsg-replace="true"></span>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="Comment">@Localizer["CommentLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 200)" data-val-length-max="200" id="Comment" maxlength="200" name="Comment" placeholder="@Localizer["CommentPlaceholder"]" type="text" value="@Model.Comment" />
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="Comment" data-valmsg-replace="true"></span>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="Username">@Localizer["UsernameLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 50)" data-val-length-max="50" id="Username" maxlength="50" name="Username" placeholder="@Localizer["UsernamePlaceholder"]" type="text" value="@Model.Username" />
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="Username" data-valmsg-replace="true"></span>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="Password">@Localizer["PasswordLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 50)" data-val-length-max="50" id="Password" maxlength="50" name="Password" placeholder="@Localizer["PasswordPlaceholder"]" type="text" value="@Model.Password" />
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="Password" data-valmsg-replace="true"></span>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="ClientCertThumb">@Localizer["ClientCertThumbLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 100)" data-val-length-max="100" id="ClientCertThumb" maxlength="100" name="ClientCertThumb" placeholder="@Localizer["ClientCertThumbPlaceholder"]" type="text" value="@Model.ClientCertThumb" />
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="ClientCertThumb" data-valmsg-replace="true"></span>
</div>
</div>
<div class="row">
<div class="col-sm-12 text-danger">
@ViewBag.ErrorMsg
</div>
</div>
<div class="row">
<div class="col-sm-1">
&nbsp;
</div>
</div>
<div class="row">
<div class="col-sm-2">
<button type="submit" class="btn btn-primary">@Localizer[(Model.CurrentId == "@") ? "SaveNew" : "Save"].Value</button>
</div>
</div>
@if (!string.IsNullOrWhiteSpace(Model.ChargePointId))
{
<div class="row">
<div class="col-sm-1">
&nbsp;
</div>
</div>
<div class="row">
<div class="col-sm-1">
&nbsp;
</div>
</div>
<div class="row">
<div class="col-sm-2">
<button type="button" class="btn btn-info" id="btnReset" title="@Localizer["TitleReset"]" onclick="ResetChargepoint()"><i class="fas fa-redo"></i> @Localizer["TitleReset"]</button>
</div>
<div class="col-sm-2">
<button type="button" class="btn btn-info" id="btnUnlock" title="@Localizer["TitleUnlockConnector"]" onclick="UnlockConnector()"><i class="fas fa-lock-open"></i> @Localizer["TitleUnlockConnector"]</button>
</div>
</div>
}
</div>
}
@section scripts {
@if (!string.IsNullOrWhiteSpace(Model.ChargePointId))
{
<script>
function ResetChargepoint() {
var dialog = new BootstrapDialog({
title: '@Localizer["TitleReset"]',
message: '@string.Format(Localizer["DialogReset"].Value, Model.Name)',
spinicon: 'fa fa-spinner fa-fw',
buttons: [{
id: 'btnDialogReset',
label: '@Localizer["TitleReset"]',
icon: 'fas fa-redo',
autospin: true,
action: function (dialogRef) {
dialogRef.enableButtons(false);
dialogRef.setClosable(false);
dialogRef.getModalBody().html('@Localizer["ExecuteReset"]');
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status == 200) {
dialogRef.getModalBody().html(xmlhttp.responseText);
}
else {
dialogRef.getModalBody().html('@Localizer["ErrorReset"]');
}
dialogRef.setClosable(true);
dialogRef.enableButtons(true);
var $resetButton = dialog.getButton('btnDialogReset');
$resetButton.hide();
var $cancelButton = dialog.getButton('btnDialogCancel');
$cancelButton.text('@Localizer["Close"]');
}
};
xmlhttp.open("GET", "@Html.Raw(Url.Content("~/API/Reset/" + Uri.EscapeDataString(Model.ChargePointId)))", true);
xmlhttp.send();
}
}, {
id: 'btnDialogCancel',
label: '@Localizer["Cancel"]',
action: function (dialogRef) {
dialogRef.close();
}
}]
});
dialog.open();
}
function UnlockConnector() {
var dialog = new BootstrapDialog({
title: '@Localizer["TitleUnlockConnector"]',
message: '@string.Format(Localizer["DialogUnlockConnector"].Value, Model.Name)',
spinicon: 'fa fa-spinner fa-fw',
buttons: [{
id: 'btnUnlock',
label: '@Localizer["TitleUnlockConnector"]',
icon: 'fas fa-lock-open',
autospin: true,
action: function (dialogRef) {
dialogRef.enableButtons(false);
dialogRef.setClosable(false);
dialogRef.getModalBody().html('@Localizer["ExecuteUnlock"]');
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status == 200) {
dialogRef.getModalBody().html(xmlhttp.responseText);
}
else {
dialogRef.getModalBody().html('@Localizer["ErrorUnlock"]');
}
dialogRef.setClosable(true);
dialogRef.enableButtons(true);
var $resetButton = dialog.getButton('btnUnlock');
$resetButton.hide();
var $cancelButton = dialog.getButton('btnDialogCancel');
$cancelButton.text('@Localizer["Close"]');
}
};
xmlhttp.open("GET", "@Html.Raw(Url.Content("~/API/UnlockConnector/" + Uri.EscapeDataString(Model.ChargePointId)))", true);
xmlhttp.send();
}
}, {
id: 'btnDialogCancel',
label: '@Localizer["Cancel"]',
action: function (dialogRef) {
dialogRef.close();
}
}]
});
dialog.open();
}
</script>
}
}
}

View File

@@ -0,0 +1,37 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model ChargePointViewModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
@if (Model != null)
{
<table id="dtChargeTags" class="table table-striped table-bordered table-sm table-hover" cellspacing="0" width="100%">
<thead>
<tr>
<th class="th-sm">@Localizer["ChargePointId"]</th>
<th class="th-sm">@Localizer["Name"]</th>
<th class="th-sm">@Localizer["Comment"]</th>
</tr>
</thead>
<tbody>
@if (Model.ChargePoints != null)
{
foreach (ChargePoint cp in Model.ChargePoints)
{
<tr class="table-row" data-href='@Url.Action("ChargePoint", Constants.HomeController, new { id = cp.ChargePointId })'>
<td>@cp.ChargePointId</td>
<td>@cp.Name</td>
<td>@cp.Comment</td>
</tr>
}
}
</tbody>
</table>
<br />
<a class="btn btn-secondary" href="~/Home/ChargePoint/@@">@Localizer["AddNew"]</a>
}

View File

@@ -0,0 +1,129 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model ChargeTagViewModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
@if (Model != null)
{
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<h4>@Localizer["EditChargeTag"]</h4>
<br />
<div class="container">
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="TagId">@Localizer["ChargeTagIdLabel"]</label>
</div>
</div>
@if (Model.CurrentTagId == "@")
{
<div class="col-sm-6">
<div class="form-group">
<input type="text" class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 50)" data-val-length-max="50" data-val-required="@Localizer["RequiredField"]" id="TagId" maxlength="50" name="TagId" placeholder="@Localizer["ChargeTagIdPlaceholder"]" value="@Model.TagId">
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="TagId" data-valmsg-replace="true"></span>
</div>
}
else
{
<div class="col-sm-6">
<div class="form-group">
<input type="text" readonly class="form-control" id="TagId" name="TagId" placeholder="@Localizer["ChargeTagIdPlaceholder"]" value="@Model.TagId">
</div>
</div>
}
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="TagName">@Localizer["ChargeTagNameLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 200)" data-val-length-max="200" data-val-required="@Localizer["RequiredField"]" id="TagName" maxlength="200" name="TagName" placeholder="@Localizer["ChargeTagNamePlaceholder"]" type="text" value="@Model.TagName" />
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="TagName" data-valmsg-replace="true"></span>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="TagName">@Localizer["GroupNameLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 50)" data-val-length-max="50" id="ParentTagId" maxlength="50" name="ParentTagId" placeholder="@Localizer["GroupNamePlaceholder"]" type="text" value="@Model.ParentTagId" />
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="ParentTagId" data-valmsg-replace="true"></span>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="expiryDatetimepicker">@Localizer["ChargeTagExpirationLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<div class="input-group date" id="expiryDatetimepicker" style="max-width: 180px">
<input type="text" class="form-control" id="ExpiryDate" name="ExpiryDate" value="@Model.ExpiryDate?.ToString(ViewBag.DatePattern)">
<span class="input-group-append"><i class="input-group-text fa fa-calendar" style="padding-top: 10px;"></i></span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<label class="form-check-label inline-label" for="Blocked">@Localizer["ChargeTagBlockedLabel"]</label>
</div>
<div class="col-sm-6">
<div class="form-check">
@Html.CheckBoxFor(m => m.Blocked, new { @class = "form-check-input checkbox-lg" })
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 text-danger">
@ViewBag.ErrorMsg
</div>
</div>
<div class="row">
<div class="col-sm-1">
&nbsp;
</div>
</div>
<div class="row">
<div class="col-sm-2">
<button type="submit" class="btn btn-primary">@Localizer[(Model.CurrentTagId == "@") ? "SaveNew" : "Save"].Value</button>
</div>
</div>
</div>
}
@section scripts {
<script>
$(function () {
$('#expiryDatetimepicker').datepicker({
weekStart: 1,
todayBtn: true,
language: "@ViewBag.Language",
todayHighlight: true
});
});
</script>
}
}

View File

@@ -0,0 +1,41 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model ChargeTagViewModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
@if (Model != null)
{
<table id="dtChargeTags" class="table table-striped table-bordered table-sm table-hover" cellspacing="0" width="100%">
<thead>
<tr>
<th class="th-sm">@Localizer["TagId"]</th>
<th class="th-sm">@Localizer["TagName"]</th>
<th class="th-sm">@Localizer["GroupName"]</th>
<th class="th-sm">@Localizer["ExpiryDate"]</th>
<th class="th-sm">@Localizer["Blocked"]</th>
</tr>
</thead>
<tbody>
@if (Model.ChargeTags != null)
{
foreach (ChargeTag tag in Model.ChargeTags)
{
<tr class="table-row" data-href='@Url.Action("ChargeTag", Constants.HomeController, new { id = tag.TagId })'>
<td>@tag.TagId</td>
<td>@tag.TagName</td>
<td>@tag.ParentTagId</td>
<td>@((tag.ExpiryDate != null) ? tag.ExpiryDate.Value.ToString(ViewBag.DatePattern) : "-")</td>
<td>@((tag.Blocked == true) ? "1" : "0")</td>
</tr>
}
}
</tbody>
</table>
<br />
<a class="btn btn-secondary" href="~/Home/ChargeTag/@@">@Localizer["AddNew"]</a>
}

View File

@@ -0,0 +1,123 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model ConnectorStatusViewModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
@if (Model != null)
{
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<h4>@Localizer["EditConnector"]</h4>
<br />
<div class="container">
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="ChargePointIdRO">@Localizer["ChargePointIdLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input type="text" readonly class="form-control" id="ChargePointIdRO" name="ChargePointIdRO" value="@Model.ChargePointId">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="ConnectorIdRO">@Localizer["ConnectorIdLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input type="text" readonly class="form-control" id="ConnectorIdRO" name="ConnectorIdRO" value="@Model.ConnectorId">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="ConnectorName">@Localizer["ConnectorNameLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input class="form-control" data-val="true" data-val-length="@string.Format(Localizer["FieldMaxLength"].Value, 200)" data-val-length-max="200" id="ConnectorName" maxlength="200" name="ConnectorName" placeholder="@Localizer["ConnectorNamePlaceholder"]" type="text" value="@Model.ConnectorName" />
</div>
</div>
<div class="col-sm-2">
<span class="field-validation-valid text-danger" data-valmsg-for="ConnectorName" data-valmsg-replace="true"></span>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="LastStatusRO">@Localizer["LastStatusLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input type="text" readonly class="form-control" id="LastStatusRO" name="LastStatusRO" value="@Model.LastStatus">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="LastStatusTimeRO">@Localizer["LastStatusTimeLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input type="text" readonly class="form-control" id="LastStatusTimeRO" name="LastStatusTimeRO" value="@((Model.LastStatusTime.HasValue) ? string.Format("{0:G}", Model.LastStatusTime.Value.ToLocalTime()) : "-")">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="LastMeterRO">@Localizer["LastMeterLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input type="text" readonly class="form-control" id="LastMeterRO" name="LastMeterRO" value="@((Model.LastMeter.HasValue) ? string.Format("{0:0.0## kWh}", Model.LastMeter.Value) : "-" )">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-2 align-self-center">
<div class="form-group">
<label class="inline-label" for="LastMeterTimeRO">@Localizer["LastMeterTimeLabel"]</label>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<input type="text" readonly class="form-control" id="LastMeterTimeRO" name="LastMeterTimeRO" value="@((Model.LastMeterTime.HasValue) ? string.Format("{0:G}", Model.LastMeterTime.Value.ToLocalTime()) : "-")">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 text-danger">
@ViewBag.ErrorMsg
</div>
</div>
<div class="row">
<div class="col-sm-1">
&nbsp;
</div>
</div>
<div class="row">
<div class="col-sm-2">
<button type="submit" class="btn btn-primary">@Localizer["Save"]</button>
</div>
</div>
</div>
}
}

View File

@@ -0,0 +1,42 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model ConnectorStatusViewModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
@if (Model != null)
{
<table id="dtChargeTags" class="table table-striped table-bordered table-sm table-hover" cellspacing="0" width="100%">
<thead>
<tr>
<th class="th-sm">@Localizer["ChargePointId"]</th>
<th class="th-sm">@Localizer["ConnectorId"]</th>
<th class="th-sm">@Localizer["ConnectorName"]</th>
<th class="th-sm">@Localizer["LastStatus"]</th>
<th class="th-sm">@Localizer["LastStatusTime"]</th>
<th class="th-sm">@Localizer["LastMeter"]</th>
<th class="th-sm">@Localizer["LastMeterTime"]</th>
</tr>
</thead>
<tbody>
@if (Model.ConnectorStatuses != null)
{
foreach (ConnectorStatus cs in Model.ConnectorStatuses)
{
<tr class="table-row" data-href='@Url.Action("Connector", Constants.HomeController, new { id = cs.ChargePointId, connectorId = cs.ConnectorId.ToString() })'>
<td>@cs.ChargePointId</td>
<td>@cs.ConnectorId</td>
<td>@cs.ConnectorName</td>
<td>@((!string.IsNullOrEmpty(cs.LastStatus)) ? cs.LastStatus : "-")</td>
<td>@((cs.LastStatusTime.HasValue) ? string.Format("{0:G}", cs.LastStatusTime.Value.ToLocalTime()) : "-")</td>
<td>@((cs.LastMeter.HasValue) ? string.Format("{0:0.0##}", cs.LastMeter.Value) : "-" )</td>
<td>@((cs.LastMeterTime.HasValue) ? string.Format("{0:G}", cs.LastMeterTime.Value.ToLocalTime()): "-")</td>
</tr>
}
}
</tbody>
</table>
}

View File

@@ -0,0 +1,91 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model OverviewViewModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
@if (Model != null)
{
<div class="tilegrid">
@foreach (ChargePointsOverviewViewModel cpvm in Model.ChargePoints)
{
string chargePointName = string.IsNullOrWhiteSpace(cpvm.Name) ? $"{cpvm.ChargePointId}:{cpvm.ConnectorId}" : cpvm.Name;
string lastCharge = (cpvm.MeterStart >= 0 && cpvm.MeterStop != null) ? string.Format(Localizer["ChargekWh"].Value, (cpvm.MeterStop - cpvm.MeterStart)) : null;
string chargeTime = null;
if (cpvm.StartTime != null && cpvm.StopTime == null)
{
TimeSpan timeSpan = DateTime.UtcNow.Subtract(cpvm.StartTime.Value);
chargeTime = string.Format(Localizer["ChargeTime"].Value, (timeSpan.Days*24 + timeSpan.Hours), timeSpan.Minutes);
}
string cpIcon = "fa-plug";
string cpColor = "successColor";
string cpTitle = Localizer["Available"].Value;
switch (cpvm.ConnectorStatus)
{
case ConnectorStatusEnum.Occupied:
cpIcon = "fa-bolt"; //"fa-car";
cpColor = "errorColor";
cpTitle = Localizer["Charging"].Value;
break;
case ConnectorStatusEnum.Faulted:
cpIcon = "fa-times-circle";
cpColor = "unavailableColor";
cpTitle = Localizer["Faulted"].Value;
break;
case ConnectorStatusEnum.Unavailable:
cpIcon = "fa-ban";
cpColor = "unavailableColor";
cpTitle = Localizer["Unavailable"].Value;
break;
}
<div class="card border-secondary" style="max-width: 18rem;">
<a href="~/Home/Transactions/@Uri.EscapeDataString(cpvm.ChargePointId)/@cpvm.ConnectorId" class="text-decoration-none">
<div class="card-header @cpColor">
<i class="fas @cpIcon fa-2x"></i> @chargePointName
</div>
<div class="card-body text-secondary">
<h5 class="card-title">@cpTitle</h5>
@if (!string.IsNullOrEmpty(chargeTime))
{
<p class="card-text">@chargeTime</p>
}
else if (!string.IsNullOrEmpty(lastCharge))
{
<p class="card-text">@lastCharge</p>
}
else
{
<p class="card-text">&nbsp;</p>
}
</div>
@if (Model.ServerConnection)
{
<div class="card-footer text-muted d-flex justify-content-between">
<div>@cpvm.CurrentChargeData</div>
@if (cpvm.Online)
{
<div><i class="fas fa-link" title="@Localizer["ChargePointOnline"]"></i></div>
}
else
{
<div><i class="fas fa-unlink" title="@Localizer["ChargePointOffline"]"></i></div>
}
</div>
}
</a>
</div>
}
</div>
@if (!string.IsNullOrEmpty(ViewBag.ErrorMsg))
{
<br/>
<div class="alert alert-danger" role="alert">
@ViewBag.ErrorMsg
</div>
}
}

View File

@@ -0,0 +1,169 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model TransactionListViewModel
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
@{
string timespan = (Model.Timespan == 2) ? "?t=2" : ((Model.Timespan == 3) ? "?t=3" : string.Empty);
List<ConnectorStatusViewModel> connectorStatusViewModels = new List<ConnectorStatusViewModel>();
// Copy CP-Names in dictionary for name resolution and
Dictionary<string, string> chargePointNames = new Dictionary<string, string>();
if (Model.ChargePoints != null)
{
foreach (ChargePoint cp in Model.ChargePoints)
{
chargePointNames.Add(cp.ChargePointId, cp.Name);
}
}
// Count connectors for every charge point (=> naming scheme)
Dictionary<string, int> dictConnectorCount = new Dictionary<string, int>();
string currentConnectorName = string.Empty;
foreach (ConnectorStatus cs in Model.ConnectorStatuses)
{
if (dictConnectorCount.ContainsKey(cs.ChargePointId))
{
// > 1 connector
dictConnectorCount[cs.ChargePointId] = dictConnectorCount[cs.ChargePointId] + 1;
}
else
{
// first connector
dictConnectorCount.Add(cs.ChargePointId, 1);
}
ConnectorStatusViewModel csvm = new ConnectorStatusViewModel();
csvm.ChargePointId = cs.ChargePointId;
csvm.ConnectorId = cs.ConnectorId;
string connectorName = cs.ConnectorName;
if (string.IsNullOrEmpty(connectorName))
{
// Default: use charge point name
chargePointNames.TryGetValue(cs.ChargePointId, out connectorName);
if (string.IsNullOrEmpty(connectorName))
{
// Fallback: use charge point ID
connectorName = cs.ChargePointId;
}
connectorName = $"{connectorName}:{cs.ConnectorId}";
}
csvm.ConnectorName = connectorName;
connectorStatusViewModels.Add(csvm);
if (cs.ChargePointId == Model.CurrentChargePointId && cs.ConnectorId == Model.CurrentConnectorId)
{
currentConnectorName = connectorName;
}
}
}
<div class="container fill">
<div class="row">
<div class="col-md-auto align-self-center">
@Localizer["ChargePointLabel"]
</div>
<div class="col-md-auto align-self-center">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="ddbChargePoint" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@currentConnectorName
</button>
<div class="dropdown-menu" aria-labelledby="ddbChargePoint">
@foreach (ConnectorStatusViewModel csvm in connectorStatusViewModels)
{
<a class="dropdown-item" href="~/Home/Transactions/@Uri.EscapeDataString(csvm.ChargePointId)/@csvm.ConnectorId@timespan">@csvm.ConnectorName</a>
}
</div>
</div>
</div>
<div class="col-1">
</div>
<div class="col-md-auto align-self-center">
@Localizer["TimeSpanLabel"]
</div>
<div class="col-md-auto align-self-center">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="ddbTimespan" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@if (Model.Timespan == 2)
{
@Localizer["TimeSpan90"];
}
else if (Model.Timespan == 3)
{
@Localizer["TimeSpan365"];
}
else
{
@Localizer["TimeSpan30"];
}
</button>
<div class="dropdown-menu" aria-labelledby="ddbTimespan">
<a class="dropdown-item" href="~/Home/Transactions/@Uri.EscapeDataString(Model.CurrentChargePointId)/@Model.CurrentConnectorId">@Localizer["TimeSpan30"]</a>
<a class="dropdown-item" href="~/Home/Transactions/@Uri.EscapeDataString(Model.CurrentChargePointId)/@Model.CurrentConnectorId?t=2">@Localizer["TimeSpan90"]</a>
<a class="dropdown-item" href="~/Home/Transactions/@Uri.EscapeDataString(Model.CurrentChargePointId)/@Model.CurrentConnectorId?t=3">@Localizer["TimeSpan365"]</a>
</div>
</div>
</div>
<div class="col-3">
</div>
<div class="col-md-auto align-self-center">
<a href="~/Home/Export/@Uri.EscapeDataString(Model.CurrentChargePointId)/@Model.CurrentConnectorId@timespan" data-toggle="tooltip" data-placement="top" title="@Localizer["DownloadCsv"]">
<i class="fas fa-file-csv fa-2x"></i>
</a>
</div>
</div>
</div>
<br />
@if (Model != null)
{
<table id="dtTransactions" class="table table-striped table-bordered table-sm" cellspacing="0" width="100%">
<thead>
<tr>
<th class="th-sm">@Localizer["Connector"]</th>
<th class="th-sm">@Localizer["StartTime"]</th>
<th class="th-sm">@Localizer["StartTag"]</th>
<th class="th-sm">@Localizer["StartMeter"]</th>
<th class="th-sm">@Localizer["StopTime"]</th>
<th class="th-sm">@Localizer["StopTag"]</th>
<th class="th-sm">@Localizer["StopMeter"]</th>
<th class="th-sm">@Localizer["ChargeSum"]</th>
</tr>
</thead>
<tbody>
@if (Model.Transactions != null)
{
foreach (Transaction t in Model.Transactions)
{
string startTag = t.StartTagId;
string stopTag = t.StopTagId;
if (!string.IsNullOrEmpty(t.StartTagId) && Model.ChargeTags != null && Model.ChargeTags.ContainsKey(t.StartTagId))
{
startTag = Model.ChargeTags[t.StartTagId]?.TagName;
}
if (!string.IsNullOrEmpty(t.StopTagId) && Model.ChargeTags != null && Model.ChargeTags.ContainsKey(t.StopTagId))
{
stopTag = Model.ChargeTags[t.StopTagId]?.TagName;
}
<tr>
<td>@currentConnectorName</td>
<td>@string.Format("{0} {1}", t.StartTime.ToLocalTime().ToShortDateString(), t.StartTime.ToLocalTime().ToShortTimeString())</td>
<td>@startTag</td>
<td>@string.Format("{0:0.0##}", t.MeterStart)</td>
<td>@((t.StopTime != null) ? string.Format("{0} {1}", t.StopTime.Value.ToLocalTime().ToShortDateString(), t.StopTime.Value.ToLocalTime().ToShortTimeString()) : string.Empty)</td>
<td>@stopTag</td>
<td>@((t.MeterStop != null) ? string.Format("{0:0.0##}", t.MeterStop) : string.Empty)</td>
<td>@((t.MeterStop != null) ? string.Format("{0:0.0##}", (t.MeterStop - t.MeterStart)) : string.Empty)</td>
</tr>
}
}
</tbody>
</table>
}

View File

@@ -0,0 +1,34 @@
@using Microsoft.AspNetCore.Mvc.Localization
@model List<ChargePointsOverviewViewModel>
@inject IViewLocalizer Localizer
@{
ViewData["Title"] = @Localizer["Title"];
}
<br />
<h2 class="text-danger">@Localizer["InfoError"]</h2>
@if (!string.IsNullOrEmpty((string)TempData["ErrMessage"]))
{
<br />
<br />
<p>
<strong>@Localizer["TitleDetail"]</strong>
</p>
<p>
@((string)TempData["ErrMessage"])
</p>
}
else if (!string.IsNullOrEmpty((string)TempData["ErrMsgKey"]))
{
<br />
<br />
<p>
<strong>@Localizer["TitleDetail"]</strong>
</p>
<p>
@Localizer[(string)TempData["ErrMsgKey"]]
</p>
}

View File

@@ -0,0 +1,88 @@
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="google" content="notranslate" />
<title>OCPP.Core - @ViewData["Title"]</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css">
<link rel="stylesheet" href="~/lib/bootstrap-dialog/bootstrap-dialog.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" asp-area="" asp-controller="@Constants.HomeController" asp-action="Index"><i class="fas fa-charging-station"></i> OCPP.Core</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
@if (this.User != null && this.User.Identity != null && this.User.Identity.IsAuthenticated)
{
<li class="nav-item active">
<a class="nav-link" href="~/">@Localizer["Overview"] <span class="sr-only">(current)</span></a>
</li>
@if (this.User.IsInRole(Constants.AdminRoleName))
{
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Localizer["Administration"]
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="~/Home/ChargeTag">@Localizer["ChargeTags"]</a>
<a class="dropdown-item" href="~/Home/ChargePoint">@Localizer["ChargePoints"]</a>
<a class="dropdown-item" href="~/Home/Connector">@Localizer["Connectors"]</a>
</div>
</li>
}
}
</ul>
@{
if (this.User != null && this.User.Identity != null && this.User.Identity.IsAuthenticated)
{
<ul class="navbar-nav ml-auto nav-flex-icons">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="navbarDropdownMenuLink-333" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
@this.User.Identity.Name <i class="fas fa-user"></i>
</a>
<div class="dropdown-menu dropdown-menu-right dropdown-default"
aria-labelledby="navbarDropdownMenuLink-333">
<a class="dropdown-item" href="~/Account/Logout">@Localizer["Logout"]</a>
</div>
</li>
</ul>
}
}
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
OCPP.Core Management @string.Format("V{0}", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(3))
</div>
</footer>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.de.min.js"></script>
<script src="~/lib/bootstrap-dialog/bootstrap-dialog.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>

View File

@@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

View File

@@ -0,0 +1,5 @@
@using OCPP.Core.Database
@using OCPP.Core.Management
@using OCPP.Core.Management.Models
@using System.Collections.Generic
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}