This commit is contained in:
Đăng Nguyễn 2025-09-27 14:47:42 +07:00
parent 2bbcc19076
commit 2640fb92c1
27 changed files with 437 additions and 388 deletions

View File

@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Components.Web;
namespace RobotApp.Client;
public class ClientRenderMode
{
public static InteractiveWebAssemblyRenderMode InteractiveWebAssemblyNoPrerender { get; } = new(prerender: false);
}

View File

@ -1,20 +0,0 @@
@inherits LayoutComponentBase
@using MudBlazor
<div class="app-shell">
<AuthorizeView>
<Authorized>
<NavMenu />
</Authorized>
</AuthorizeView>
<main class="page">
@Body
</main>
</div>
<MudPopoverProvider />
<MudThemeProvider IsDarkMode />
<MudDialogProvider MaxWidth="MaxWidth.ExtraLarge" />
<MudSnackbarProvider />

View File

@ -1,17 +0,0 @@

.app-shell {
display: flex;
min-height: 100vh;
min-width: 100vw;
width: 100vw;
height: 100vh;
overflow: hidden;
flex-direction: row;
}
.page {
flex: 1 1 auto;
min-width: 0;
display: flex;
overflow: hidden;
}

View File

@ -1,78 +0,0 @@
@using Microsoft.AspNetCore.Components.Routing
<script>
function toggleSidebar() {
let sidebar = document.querySelector(".sidebar");
sidebar.classList.toggle("collapsed");
}
</script>
<div class="sidebar collapsed">
<div class="flex-grow-1 d-flex flex-column">
<div class="title">
<img src="images/logoLight.svg" alt="PhenikaaX" style="height: 35px;" onclick="toggleSidebar()" />
<button class="btn button" onclick="toggleSidebar()">
<i class="mdi mdi-menu" style="color: white; font-size: 35px"></i>
</button>
</div>
<hr />
@foreach (var nav in Navs)
{
<div class="nav-item px-3">
<NavLink class="nav-link" href="@nav.Path" Match="@nav.Match">
<div class="d-flex align-items-center">
<div class="nav-icon">
<span class="mdi @nav.Icon mdi-36px" aria-hidden="true"></span>
</div>
<span class="nav-label">@nav.Label</span>
</div>
</NavLink>
</div>
}
</div>
<div class="user">
<div>
<span class="mdi mdi-account mdi-36px text-white "></span>
</div>
<AuthorizeView>
<Authorized>
<div class="nav-label">
<MudText Class="text-white name" Typo="Typo.subtitle1">@context.User.Identity?.Name</MudText>
</div>
</Authorized>
</AuthorizeView>
<MudSpacer />
<form action="Account/Logout" method="post">
<AntiforgeryToken />
<input type="hidden" name="ReturnUrl" value="" />
<button class="btn button">
<i class="mdi mdi-logout" style="color: white; font-size: 35px"></i>
</button>
@* <MudIconButton Class="text-white" ButtonType="@ButtonType.Submit" Icon="@Icons.Material.Filled.Logout" /> *@
</form>
</div>
</div>
@code {
public class NavModel
{
public string Icon { get; set; } = "";
public string Path { get; set; } = "";
public string Label { get; set; } = "";
public NavLinkMatch Match { get; set; }
}
public NavModel[] Navs = [
new(){Icon = "mdi-view-dashboard", Path="/", Label = "Dashboard", Match = NavLinkMatch.All},
new(){Icon = "mdi-map-legend", Path="/Maps-manager", Label = "Mapping", Match = NavLinkMatch.All},
];
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}

View File

@ -0,0 +1,85 @@
@inherits LayoutComponentBase
<script>
function toggleSidebar() {
let sidebar = document.querySelector(".sidebar");
sidebar.classList.toggle("collapsed");
}
</script>
<div class="app-shell">
<div class="sidebar collapsed">
<div class="flex-grow-1 d-flex flex-column">
<div class="title">
<img src="images/logoLight.svg" alt="PhenikaaX" style="height: 35px;" onclick="toggleSidebar()" />
<button class="btn button" onclick="toggleSidebar()">
<i class="mdi mdi-menu" style="color: white; font-size: 35px"></i>
</button>
</div>
<hr />
@foreach (var nav in Navs)
{
<div class="nav-item px-3">
<NavLink class="nav-link" href="@nav.Path" Match="@nav.Match">
<div class="d-flex align-items-center">
<div class="nav-icon">
<span class="mdi @nav.Icon mdi-36px" aria-hidden="true"></span>
</div>
<span class="nav-label">@nav.Label</span>
</div>
</NavLink>
</div>
}
</div>
<div class="user">
<div>
<span class="mdi mdi-account mdi-36px text-white "></span>
</div>
<AuthorizeView>
<Authorized>
<div class="nav-label">
<MudText Class="text-white" Typo="Typo.subtitle1">@context.User.Identity?.Name</MudText>
</div>
</Authorized>
</AuthorizeView>
<MudSpacer />
<form action="Account/Logout" method="post">
<AntiforgeryToken />
<input type="hidden" name="ReturnUrl" value="" />
<button class="btn button">
<i class="mdi mdi-logout" style="color: white; font-size: 35px"></i>
</button>
@* <MudIconButton Class="text-white" ButtonType="@ButtonType.Submit" Icon="@Icons.Material.Filled.Logout" /> *@
</form>
</div>
</div>
<main class="page">
@Body
</main>
</div>
@code{
public class NavModel
{
public string Icon { get; set; } = "";
public string Path { get; set; } = "";
public string Label { get; set; } = "";
public NavLinkMatch Match { get; set; }
}
public NavModel[] Navs = [
new(){Icon = "mdi-view-dashboard", Path="/", Label = "Dashboard", Match = NavLinkMatch.All},
new(){Icon = "mdi-map-legend", Path="/maps-manager", Label = "Mapping", Match = NavLinkMatch.All},
];
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}

View File

@ -1,3 +1,21 @@
.app-shell {
display: flex;
min-height: 100vh;
min-width: 100vw;
width: 100vw;
height: 100vh;
overflow: hidden;
flex-direction: row;
}
.page {
flex: 1 1 auto;
min-width: 0;
display: flex;
overflow: hidden;
}
.sidebar { .sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);

View File

@ -0,0 +1,12 @@
@inherits LayoutComponentBase
@layout RobotApp.Client.MainLayout
<MudThemeProvider @rendermode="InteractiveWebAssemblyNoPrerender" IsDarkMode/>
<MudPopoverProvider @rendermode="InteractiveWebAssemblyNoPrerender" />
<MudDialogProvider @rendermode="InteractiveWebAssemblyNoPrerender" MaxWidth="MaxWidth.ExtraLarge"
CloseButton="false"
BackdropClick="false"
Position="DialogPosition.Center" />
<MudSnackbarProvider @rendermode="InteractiveWebAssemblyNoPrerender" />
@Body

View File

@ -1,13 +0,0 @@
@page "/auth"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>Auth</PageTitle>
<h1>You are authenticated</h1>
<AuthorizeView>
Hello @context.User.Identity?.Name!
</AuthorizeView>

View File

@ -0,0 +1,40 @@
@inject HttpClient Http
<div class="d-flex flex-column h-100 w-100">
<span Class="title">@MapName</span>
<div class="d-flex flex-grow-1 w-100 flex-column">
<div class="map-item">
<img src="@imageSrc" alt="map image" onerror="this.onerror=null; this.src='/images/Image-not-found.png';" />
</div>
<MudButton StartIcon="@Icons.Material.Filled.FileDownload" Class="m-2" Size="Size.Small" Variant="Variant.Filled" Color="Color.Primary" Disabled="Disable">
DOWNLOAD
</MudButton>
</div>
</div>
@code {
private bool Disable = true;
private string imageSrc = "/images/Image-not-found.png";
private string MapName = "Map preview";
private Guid MapId = Guid.Empty;
public void SetMapPreview(MapDto? map)
{
if (map is null)
{
Disable = true;
MapName = "Map preview";
imageSrc = "/images/Image-not-found.png";
MapId = Guid.Empty;
}
else
{
imageSrc = $"api/Images/map/{map.Id}?t={DateTime.Now}";
MapName = map.Name;
MapId = map.Id;
Disable = false;
}
StateHasChanged();
}
}

View File

@ -0,0 +1,42 @@
.map-item {
display: flex;
justify-content: center;
width: 100%;
height: fit-content;
}
.map-item img {
justify-content: center;
width: 95%;
height: 400px;
object-fit: contain;
border-radius: 10px;
image-rendering: pixelated;
}
.title {
display: flex;
height: 77px;
width: 100%;
justify-content: center;
align-content: center;
/*border-bottom: 0.5px solid gray;*/
font-size: 30px;
flex-wrap: wrap;
padding-bottom: .5rem;
}
.map-update-item {
display: flex;
justify-content: center;
width: 100%;
height: fit-content;
}
.map-update-item img {
width: 100%;
height: 400px;
object-fit: contain;
border-radius: 10px;
image-rendering: pixelated;
}

View File

@ -0,0 +1,165 @@
@inject HttpClient Http
<style>
.selected {
background-color: #3399ff !important;
}
.selected > td {
color: white !important;
}
.selected > td .mud-input {
color: white !important;
}
</style>
<div class="d-flex flex-row w-100 h-100 overflow-hidden">
<MudTable @ref="Table" Items="@MapsShow" T="MapDto" Dense Hover ReadOnly FixedHeader RowClass="cursor-pointer" Striped Elevation="10"
ServerData="ReloadData" Loading=@IsLoading RowClassFunc="@SelectedRowClassFunc" OnRowClick="RowClickEvent" Height="90vh" HorizontalScrollbar>
<ToolBarContent>
<MudText Typo="Typo.h6">Maps</MudText>
</ToolBarContent>
<HeaderContent>
<MudTh>Nr</MudTh>
<MudTh>Name</MudTh>
<MudTh>Width (m)</MudTh>
<MudTh>Height (m)</MudTh>
<MudTh>Resolution (m/px)</MudTh>
<MudTh>OriginX</MudTh>
<MudTh>OriginY</MudTh>
<MudTh></MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Nr">
@(Table?.CurrentPage * Table?.RowsPerPage + MapsShow.IndexOf(context) + 1)
</MudTd>
<MudTd DataLabel="Name">
@context.Name
</MudTd>
<MudTd DataLabel="Width">
@context.Width
</MudTd>
<MudTd DataLabel="Height">
@context.Height
</MudTd>
<MudTd DataLabel="Resolution">
@context.Resolution
</MudTd>
<MudTd DataLabel="OriginX">
@context.OriginX
</MudTd>
<MudTd DataLabel="OriginY">
@context.OriginY
</MudTd>
<MudTd>
<MudMenuItem Icon="@Icons.Material.Filled.Edit" IconColor="Color.Info">Active</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error">Delete</MudMenuItem>
</MudTd>
</RowTemplate>
<PagerContent>
<div class="d-flex w-100 flex-row-reverse">
<MudTablePager Style="width: 100%;" PageSizeOptions="new[] { 25, 100, 200 }" />
</div>
</PagerContent>
</MudTable>
</div>
@code {
private string txtSearch = "";
private bool IsLoading = false;
private List<MapDto> Maps = [];
private List<MapDto> MapsShow = [];
private int selectedRowNumber = -1;
private MapDto MapSelected = new();
private MudTable<MapDto>? Table;
private MapPreview? MapPreviewRef;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (!firstRender) return;
// await LoadMaps();
}
private async Task LoadMaps()
{
try
{
IsLoading = true;
Maps.Clear();
StateHasChanged();
var maps = await Http.GetFromJsonAsync<IEnumerable<MapDto>>($"api/MapsManager?txtSearch={txtSearch}");
Maps.AddRange(maps ?? []);
Table?.ReloadServerData();
IsLoading = false;
StateHasChanged();
}
catch
{
return;
}
}
private void TextSearchChanged(string text)
{
txtSearch = text;
Table?.ReloadServerData();
}
private bool FilterFunc(MapDto map)
{
if (string.IsNullOrWhiteSpace(txtSearch))
return true;
if (map.Name is not null && map.Name.Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
if ($"{map.Name}".Contains(txtSearch))
return true;
return false;
}
private Task<TableData<MapDto>> ReloadData(TableState state, CancellationToken _)
{
MapsShow.Clear();
var tasks = new List<MapDto>();
Maps.ForEach(map =>
{
if (FilterFunc(map)) tasks.Add(map);
});
MapsShow = tasks.Skip(state.Page * state.PageSize).Take(state.PageSize).ToList();
return Task.FromResult(new TableData<MapDto>() { TotalItems = tasks.Count, Items = MapsShow });
}
private void RowClickEvent(TableRowClickEventArgs<MapDto> tableRowClickEventArgs) { }
private string SelectedRowClassFunc(MapDto element, int rowNumber)
{
if (selectedRowNumber == rowNumber && Table?.SelectedItem != null && !Table.SelectedItem.Equals(element))
{
return string.Empty;
}
else if (selectedRowNumber == rowNumber && Table?.SelectedItem != null && Table.SelectedItem.Equals(element))
{
return "selected";
}
else if (Table?.SelectedItem != null && Table.SelectedItem.Equals(element))
{
selectedRowNumber = rowNumber;
MapSelected = element;
// MapPreviewRef?.SetMapPreview(MapSelected);
return "selected";
}
else
{
return string.Empty;
}
}
}

View File

@ -0,0 +1,5 @@
.map-preview {
width: 20%;
height: 100%;
border-left: 1px solid silver;
}

View File

@ -1,18 +0,0 @@
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}

View File

@ -1,181 +1,20 @@
@page "/maps-manager" @page "/maps-manager"
@using MudBlazor
@using RobotApp.Common.Shares.Dtos @rendermode InteractiveWebAssemblyNoPrerender
@attribute [Authorize] @attribute [Authorize]
@inject HttpClient Http
<PageTitle>Map Manager</PageTitle> <PageTitle>Map Manager</PageTitle>
<style> <div class="d-flex w-100 h-100 p-2 overflow-hidden flex-row">
.selected { <div style="height: 100%; width: 40%">
background-color: #3399ff !important; <RobotApp.Client.Pages.Components.Mapping.MapTable />
}
.selected > td {
color: white !important;
}
.selected > td .mud-input {
color: white !important;
}
</style>
<div class="d-flex flex-row w-100 h-100 p-2 overflow-hidden">
<div class="d-flex h-100 flex-column flex-grow-1 pe-2">
<div class="d-flex justify-content-between align-items-center mb-2">
<MudTextField Value="txtSearch" T="string" Label="Search" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Search"
AdornmentColor="Color.Secondary" ValueChanged="TextSearchChanged" Style="max-width: 550px" />
<MudButton Class="ms-3" StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled" Color="Color.Primary" Size="Size.Large">
NEW
</MudButton>
</div>
<div class="d-flex" style="height: 92%">
<MudTable Class="w-100" @ref="Table" Items="@MapsShow" T="MapDto" Dense=true Hover=true ReadOnly=true FixedHeader=true RowClass="cursor-pointer" Striped="true"
ServerData="ReloadData" Loading=@IsLoading Outlined="true" RowClassFunc="@SelectedRowClassFunc" OnRowClick="RowClickEvent" Height="95%">
<HeaderContent>
<MudTh>Nr</MudTh>
<MudTh>Name</MudTh>
<MudTh>Width (m)</MudTh>
<MudTh>Height (m)</MudTh>
<MudTh>Resolution (m/px)</MudTh>
<MudTh>OriginX</MudTh>
<MudTh>OriginY</MudTh>
<MudTh></MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Nr">
@(Table?.CurrentPage * Table?.RowsPerPage + MapsShow.IndexOf(context) + 1)
</MudTd>
<MudTd DataLabel="Name">
@context.Name
</MudTd>
<MudTd DataLabel="Width">
@context.Width
</MudTd>
<MudTd DataLabel="Height">
@context.Height
</MudTd>
<MudTd DataLabel="Resolution">
@context.Resolution
</MudTd>
<MudTd DataLabel="OriginX">
@context.OriginX
</MudTd>
<MudTd DataLabel="OriginY">
@context.OriginY
</MudTd>
<MudTd>
<div class="d-flex flex-row-reverse">
<MudMenu Icon="@Icons.Material.Filled.MoreVert" AnchorOrigin="Origin.BottomCenter">
<MudMenuItem Icon="@Icons.Material.Filled.Edit" IconColor="Color.Info">Edit</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error">Delete</MudMenuItem>
</MudMenu>
</div>
</MudTd>
</RowTemplate>
<PagerContent>
<div class="d-flex w-100 flex-row-reverse">
<MudTablePager Style="width: 100%;" PageSizeOptions="new[] {25 , 100, 200}" />
</div>
</PagerContent>
</MudTable>
</div>
</div> </div>
<div class="map-preview"> <div class="flex-grow-1 h-100" style="width: 60%">
<RobotApp.Client.Pages.Components.Mapping.MapView />
</div> </div>
</div> </div>
@code { @code {
private string txtSearch = "";
private bool IsLoading = false;
private List<MapDto> Maps = [];
private List<MapDto> MapsShow = []; private List<MapDto> MapsShow = [];
private int selectedRowNumber = -1;
private MapDto MapSelected = new();
private MudTable<MapDto>? Table; protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (!firstRender) return;
await LoadMaps();
}
private async Task LoadMaps()
{
try
{
IsLoading = true;
Maps.Clear();
StateHasChanged();
var maps = await Http.GetFromJsonAsync<IEnumerable<MapDto>>($"api/MapsManager?txtSearch={txtSearch}");
Maps.AddRange(maps ?? []);
Table?.ReloadServerData();
IsLoading = false;
StateHasChanged();
}
catch
{
return;
}
}
private void TextSearchChanged(string text)
{
txtSearch = text;
Table?.ReloadServerData();
}
private bool FilterFunc(MapDto map)
{
if (string.IsNullOrWhiteSpace(txtSearch))
return true;
if (map.Name is not null && map.Name.Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
return true;
if ($"{map.Name}".Contains(txtSearch))
return true;
return false;
}
private Task<TableData<MapDto>> ReloadData(TableState state, CancellationToken _)
{
MapsShow.Clear();
var tasks = new List<MapDto>();
Maps.ForEach(map =>
{
if (FilterFunc(map)) tasks.Add(map);
});
MapsShow = tasks.Skip(state.Page * state.PageSize).Take(state.PageSize).ToList();
return Task.FromResult(new TableData<MapDto>() { TotalItems = tasks.Count, Items = MapsShow });
}
private void RowClickEvent(TableRowClickEventArgs<MapDto> tableRowClickEventArgs) { }
private string SelectedRowClassFunc(MapDto element, int rowNumber)
{
if (selectedRowNumber == rowNumber && Table?.SelectedItem != null && !Table.SelectedItem.Equals(element))
{
return string.Empty;
}
else if (selectedRowNumber == rowNumber && Table?.SelectedItem != null && Table.SelectedItem.Equals(element))
{
return "selected";
}
else if (Table?.SelectedItem != null && Table.SelectedItem.Equals(element))
{
selectedRowNumber = rowNumber;
MapSelected = element;
// NavigationMapPreviewRef.SetMapPreview(MapSelected);
return "selected";
}
else
{
return string.Empty;
}
}
} }

View File

@ -1,63 +0,0 @@
@page "/weather"
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th aria-label="Temperature in Celsius">Temp. (C)</th>
<th aria-label="Temperature in Farenheit">Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate a loading indicator
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}

View File

@ -0,0 +1,16 @@
@layout RobotApp.Client.Pages.AssemblyLayout
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using static RobotApp.Client.ClientRenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using RobotApp.Client
@using Microsoft.AspNetCore.Authorization
@using MudBlazor
@using RobotApp.Common.Shares
@using RobotApp.Common.Shares.Dtos

View File

@ -1,11 +1,20 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MudBlazor.Services; using MudBlazor.Services;
using System.Globalization;
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
var builder = WebAssemblyHostBuilder.CreateDefault(args); var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddAuthorizationCore(); builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState(); builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthenticationStateDeserialization(); builder.Services.AddAuthenticationStateDeserialization();
builder.Services.AddMudServices();
builder.Services.AddScoped(_ => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddMudServices(config =>
{
config.SnackbarConfiguration.VisibleStateDuration = 2000;
config.SnackbarConfiguration.HideTransitionDuration = 500;
config.SnackbarConfiguration.ShowTransitionDuration = 500;
});
await builder.Build().RunAsync(); await builder.Build().RunAsync();

View File

@ -18,4 +18,9 @@
<ProjectReference Include="..\RobotApp.Common.Shares\RobotApp.Common.Shares.csproj" /> <ProjectReference Include="..\RobotApp.Common.Shares\RobotApp.Common.Shares.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Models\" />
<Folder Include="wwwroot\js\" />
</ItemGroup>
</Project> </Project>

View File

@ -5,6 +5,7 @@
@using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode @using static Microsoft.AspNetCore.Components.Web.RenderMode
@using static RobotApp.Client.ClientRenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop @using Microsoft.JSInterop
@using RobotApp.Client @using RobotApp.Client

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18 # Visual Studio Version 17
VisualStudioVersion = 18.0.11018.127 d18.0 VisualStudioVersion = 17.14.36511.14 d17.14
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RobotApp", "RobotApp\RobotApp.csproj", "{BF0BB137-2EF9-4E1B-944E-9BF41C5284F7}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RobotApp", "RobotApp\RobotApp.csproj", "{BF0BB137-2EF9-4E1B-944E-9BF41C5284F7}"
EndProject EndProject

View File

@ -4,23 +4,23 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RobotApp</title>
<base href="/" /> <base href="/" />
<link rel="stylesheet" href="@Assets["lib/bootstrap/css/bootstrap.min.css"]" /> <link rel="stylesheet" href="@Assets["lib/bootstrap/css/bootstrap.min.css"]" />
<link rel="stylesheet" href="@Assets["lib/mdi/font/css/materialdesignicons.min.css"]" /> <link rel="stylesheet" href="@Assets["lib/mdi/font/css/materialdesignicons.min.css"]" />
<link rel="stylesheet" href="@Assets["app.css"]" /> <link rel="stylesheet" href="@Assets["app.css"]" />
<link rel="stylesheet" href="@Assets["RobotApp.styles.css"]" /> <link rel="stylesheet" href="@Assets["RobotApp.styles.css"]" />
<link rel="stylesheet" href="@Assets["_content/MudBlazor/MudBlazor.min.css"]" /> <link rel="stylesheet" href="@Assets["_content/MudBlazor/MudBlazor.min.css"]" />
<link rel="icon" type="image/png" href="favicon.png" /> <link rel="icon" type="image/svg+xml" href="favicon.svg" />
<ImportMap /> <ImportMap />
<ImportMap @rendermode="InteractiveServer" /> <HeadOutlet />
<HeadOutlet @rendermode="InteractiveServer" />
</head> </head>
<body> <body>
<CascadingAuthenticationState> <CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly"> <Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData"> <Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(RobotApp.Client.Layout.MainLayout)"> <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(RobotApp.Client.MainLayout)">
<NotAuthorized> <NotAuthorized>
<RedirectToLogin /> <RedirectToLogin />
</NotAuthorized> </NotAuthorized>
@ -28,7 +28,7 @@
</Found> </Found>
<NotFound> <NotFound>
<PageTitle>Not found</PageTitle> <PageTitle>Not found</PageTitle>
<LayoutView Layout="typeof(RobotApp.Client.Layout.MainLayout)"> <LayoutView Layout="typeof(RobotApp.Client.MainLayout)">
<p>Không tìm thấy trang.</p> <p>Không tìm thấy trang.</p>
</LayoutView> </LayoutView>
</NotFound> </NotFound>
@ -39,7 +39,7 @@
<script src="_framework/blazor.web.js"></script> <script src="_framework/blazor.web.js"></script>
<script src="@Assets["lib/bootstrap/js/bootstrap.bundle.min.js"]"></script> <script src="@Assets["lib/bootstrap/js/bootstrap.bundle.min.js"]"></script>
<script src="@Assets["lib/bootstrap/js/bootstrap.min.js"]"></script> <script src="@Assets["lib/bootstrap/js/bootstrap.min.js"]"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script> <script src="@Assets["_content/MudBlazor/MudBlazor.min.js"]"></script>
</body> </body>
</html> </html>

View File

@ -1,4 +1,7 @@
@page "/" @page "/"
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@rendermode InteractiveServer
@attribute [Authorize] @attribute [Authorize]
<h1>Welcome to RobotApp!</h1> <h1>Welcome to RobotApp!</h1>

View File

@ -0,0 +1,12 @@
@inherits LayoutComponentBase
@layout RobotApp.Client.MainLayout
<MudThemeProvider @rendermode="InteractiveServer" IsDarkMode/>
<MudPopoverProvider @rendermode="InteractiveServer" />
<MudDialogProvider @rendermode="InteractiveServer" MaxWidth="MaxWidth.ExtraLarge"
CloseButton="false"
BackdropClick="false"
Position="DialogPosition.Center" />
<MudSnackbarProvider @rendermode="InteractiveServer" />
@Body

View File

@ -6,7 +6,6 @@ using RobotApp.Components;
using RobotApp.Components.Account; using RobotApp.Components.Account;
using RobotApp.Data; using RobotApp.Data;
using RobotApp.Services.Robot; using RobotApp.Services.Robot;
using RobotApp.Client;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);

View File

@ -16,7 +16,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\RobotApp.Client\RobotApp.Client.csproj" /> <ProjectReference Include="..\RobotApp.Client\RobotApp.Client.csproj" />
<ProjectReference Include="..\RobotApp.Common.Shares\RobotApp.Common.Shares.csproj" />
<ProjectReference Include="..\RobotApp.VDA5050\RobotApp.VDA5050.csproj" /> <ProjectReference Include="..\RobotApp.VDA5050\RobotApp.VDA5050.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.9" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.9" /> <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.9" />

Binary file not shown.