Converted most of the application over to AntBlazor 🙃
This commit is contained in:
parent
73a9468c37
commit
99a638b64d
33 changed files with 1060 additions and 1176 deletions
|
@ -1,6 +1,23 @@
|
|||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
|
||||
<AntContainer />
|
||||
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
|
||||
<NotAuthorized>
|
||||
@if (context.User.Identity.IsAuthenticated == false)
|
||||
{
|
||||
<RedirectToLogin />
|
||||
}
|
||||
else
|
||||
{
|
||||
<audio autoplay>
|
||||
<source src="~/static/access-denied.mp3" type="audio/mp3" />
|
||||
</audio>
|
||||
}
|
||||
</NotAuthorized>
|
||||
</AuthorizeRouteView>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
|
|
|
@ -1,48 +1,70 @@
|
|||
@using LANCommander.Data.Models
|
||||
@using LANCommander.Extensions
|
||||
@inject IDialogService DialogService
|
||||
@using LANCommander.Models;
|
||||
@using System.IO.Compression;
|
||||
@inject ModalService ModalService
|
||||
|
||||
<MudTable Items="@OrderedActions" Elevation="0" Dense="true">
|
||||
<HeaderContent>
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh>Path</MudTh>
|
||||
<MudTh>Arguments</MudTh>
|
||||
<MudTh>Working Dir</MudTh>
|
||||
<MudTh>Primary</MudTh>
|
||||
<MudTh></MudTh>
|
||||
</HeaderContent>
|
||||
<Space Direction="DirectionVHType.Vertical" Size="@("large")" Style="width: 100%">
|
||||
<SpaceItem>
|
||||
<Table TItem="Data.Models.Action" DataSource="@OrderedActions" HidePagination="true" Style="border: 1px solid #f0f0f0">
|
||||
<PropertyColumn Property="a => a.Name">
|
||||
<Input Type="text" @bind-Value="context.Name" />
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="a => a.Path">
|
||||
<Space Style="display: flex">
|
||||
<SpaceItem Style="flex-grow: 1">
|
||||
<Input Type="text" @bind-Value="context.Path" />
|
||||
</SpaceItem>
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => BrowseForActionPath(context)" Type="@ButtonType.Primary" Icon="@IconType.Outline.FolderOpen" />
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="a => a.Arguments">
|
||||
<Input Type="text" @bind-Value="context.Arguments" />
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="a => a.WorkingDirectory" Title="Working Dir">
|
||||
<Input Type="text" @bind-Value="context.WorkingDirectory" />
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="a => a.PrimaryAction" Title="Primary" Style="text-align: center">
|
||||
<Checkbox @bind-Checked="context.PrimaryAction" />
|
||||
</PropertyColumn>
|
||||
<ActionColumn>
|
||||
<Space Style="display: flex; justify-content: end">
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => MoveUp(context)" Icon="@IconType.Outline.Up" Type="@ButtonType.Text" />
|
||||
<Button OnClick="() => MoveDown(context)" Icon="@IconType.Outline.Down" Type="@ButtonType.Text" />
|
||||
|
||||
<RowTemplate>
|
||||
<MudTd><MudTextField @bind-Value="context.Name" /></MudTd>
|
||||
<MudTd><MudTextField @bind-Value="context.Path" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Folder" OnAdornmentClick="() => BrowseForActionPath(context)" /></MudTd>
|
||||
<MudTd><MudTextField @bind-Value="context.Arguments" /></MudTd>
|
||||
<MudTd><MudTextField @bind-Value="context.WorkingDirectory" /></MudTd>
|
||||
<MudTd><MudCheckBox @bind-Checked="context.PrimaryAction" Color="Color.Primary" /></MudTd>
|
||||
<MudTd Class="d-flex flex-nowrap justify-end">
|
||||
<MudIconButton OnClick="() => MoveUp(context)" Icon="@Icons.Material.Filled.ArrowUpward"></MudIconButton>
|
||||
<MudIconButton OnClick="() => MoveDown(context)" Icon="@Icons.Material.Filled.ArrowDownward"></MudIconButton>
|
||||
<MudIconButton OnClick="() => RemoveAction(context)" Color="Color.Error" Icon="@Icons.Material.Filled.Close"></MudIconButton>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
<Popconfirm OnConfirm="() => RemoveAction(context)" Title="Are you sure you want to remove this action?">
|
||||
<Button Icon="@IconType.Outline.Close" Type="@ButtonType.Text" Danger />
|
||||
</Popconfirm>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
</SpaceItem>
|
||||
|
||||
<MudPaper Elevation="0" Class="d-flex justify-end mt-3">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="AddAction">Add</MudButton>
|
||||
</MudPaper>
|
||||
<SpaceItem>
|
||||
<GridRow Justify="end">
|
||||
<GridCol>
|
||||
<Button OnClick="AddAction" Type="@ButtonType.Primary">Add Action</Button>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<Data.Models.Action> Actions { get; set; }
|
||||
[Parameter] public Guid ArchiveId { get; set; }
|
||||
[Parameter] public Game Game { get; set; }
|
||||
|
||||
private List<Data.Models.Action> OrderedActions { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
OrderedActions = Actions.OrderBy(a => a.SortOrder).ToList();
|
||||
OrderedActions = Game.Actions.OrderBy(a => a.SortOrder).ToList();
|
||||
FixSortOrder();
|
||||
}
|
||||
|
||||
private void AddAction()
|
||||
private async Task AddAction()
|
||||
{
|
||||
if (OrderedActions == null)
|
||||
OrderedActions = new List<Data.Models.Action>();
|
||||
|
@ -54,12 +76,12 @@
|
|||
});
|
||||
}
|
||||
|
||||
private void RemoveAction(Data.Models.Action action)
|
||||
private async Task RemoveAction(Data.Models.Action action)
|
||||
{
|
||||
OrderedActions.Remove(action);
|
||||
}
|
||||
|
||||
private void MoveUp(Data.Models.Action action)
|
||||
private async Task MoveUp(Data.Models.Action action)
|
||||
{
|
||||
if (action.SortOrder > 0)
|
||||
OrderedActions.Move(action, action.SortOrder - 1);
|
||||
|
@ -67,7 +89,7 @@
|
|||
FixSortOrder();
|
||||
}
|
||||
|
||||
private void MoveDown(Data.Models.Action action)
|
||||
private async Task MoveDown(Data.Models.Action action)
|
||||
{
|
||||
if (action.SortOrder < OrderedActions.Count + 1)
|
||||
OrderedActions.Move(action, action.SortOrder + 1);
|
||||
|
@ -77,17 +99,27 @@
|
|||
|
||||
private async void BrowseForActionPath(Data.Models.Action action)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["ArchiveId"] = ArchiveId
|
||||
};
|
||||
var modalOptions = new ModalOptions()
|
||||
{
|
||||
Title = "Choose Action Executable",
|
||||
Maximizable = false,
|
||||
DefaultMaximized = true,
|
||||
Closable = true,
|
||||
OkText = "Select File"
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<ArchiveFileSelectorDialog>("File Selector", parameters);
|
||||
var result = await dialog.Result;
|
||||
var browserOptions = new ArchiveBrowserOptions()
|
||||
{
|
||||
ArchiveId = Game.Archives.FirstOrDefault().Id,
|
||||
Select = true,
|
||||
Multiple = false
|
||||
};
|
||||
|
||||
if (!result.Canceled)
|
||||
var modalRef = await ModalService.CreateModalAsync<ArchiveBrowserDialog, ArchiveBrowserOptions, IEnumerable<ZipArchiveEntry>>(modalOptions, browserOptions);
|
||||
|
||||
modalRef.OnOk = (results) =>
|
||||
{
|
||||
action.Path = result.Data as string;
|
||||
action.Path = results.FirstOrDefault().FullName;
|
||||
|
||||
var parts = action.Path.Split('/');
|
||||
|
||||
|
@ -98,7 +130,8 @@
|
|||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
private void FixSortOrder()
|
||||
|
@ -112,6 +145,6 @@
|
|||
i++;
|
||||
}
|
||||
|
||||
Actions = OrderedActions;
|
||||
Game.Actions = OrderedActions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,50 @@
|
|||
@using ByteSizeLib;
|
||||
@using AntDesign.TableModels;
|
||||
@using ByteSizeLib;
|
||||
@using LANCommander.Services;
|
||||
@using System.IO.Compression;
|
||||
@inject ArchiveService ArchiveService;
|
||||
|
||||
<MudStack Row="true" Style="max-height: 100%">
|
||||
<MudTreeView Items="Directories" Hover="true" @bind-SelectedValue="SelectedDirectory" T="ArchiveDirectory" Style="min-width: 18%">
|
||||
<ItemTemplate>
|
||||
<MudTreeViewItem Expanded="@context.IsExpanded" Value="@context" Items="@context.Children" Text="@context.Name" T="ArchiveDirectory" OnClick="() => ChangeDirectory(context)"></MudTreeViewItem>
|
||||
</ItemTemplate>
|
||||
</MudTreeView>
|
||||
<GridRow Style="position: fixed; height: calc(100vh - 55px - 53px); top: 55px; left: 0; width: 100%">
|
||||
<GridCol Span="6" Style="height: 100%; overflow-y: scroll; padding: 24px">
|
||||
<Tree TItem="ArchiveDirectory"
|
||||
DataSource="Directories"
|
||||
TitleExpression="x => x.DataItem.Name"
|
||||
ChildrenExpression="x => x.DataItem.Children"
|
||||
IsLeafExpression="x => !x.DataItem.HasChildren"
|
||||
OnClick="(args) => ChangeDirectory(args.Node.DataItem)">
|
||||
</Tree>
|
||||
</GridCol>
|
||||
|
||||
<MudTable Items="@CurrentPathEntries" Hover="true" Class="flex-grow-1 archive-browser" FixedHeader="true" Elevation="0" Height="calc(100vh - 64px)">
|
||||
<HeaderContent>
|
||||
<MudTh></MudTh>
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh>Size</MudTh>
|
||||
<MudTh>Modified</MudTh>
|
||||
@if (OnFileSelected.HasDelegate)
|
||||
<GridCol Span="18" Style="height: 100%">
|
||||
<Table
|
||||
@ref="FileTable"
|
||||
TItem="ZipArchiveEntry"
|
||||
DataSource="CurrentPathEntries"
|
||||
HidePagination="true"
|
||||
Loading="Entries == null"
|
||||
RowSelectable="@(x => x.FullName != null && !x.FullName.EndsWith('/'))"
|
||||
OnRowClick="OnRowClicked"
|
||||
SelectedRowsChanged="SelectedFilesChanged"
|
||||
ScrollY="calc(100vh - 55px - 55px - 53px)">
|
||||
|
||||
@if (Select)
|
||||
{
|
||||
<MudTh></MudTh>
|
||||
<Selection Key="@context.FullName" Type="@(Multiple ? "checkbox" : "radio")" Disabled="@(context.FullName != null && context.FullName.EndsWith('/'))" />
|
||||
}
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd><MudIcon Icon="@GetIcon(context)" /></MudTd>
|
||||
<MudTd>@GetFileName(context)</MudTd>
|
||||
<MudTd>@ByteSize.FromBytes(context.Length)</MudTd>
|
||||
<MudTd>@context.LastWriteTime</MudTd>
|
||||
@if (OnFileSelected.HasDelegate)
|
||||
{
|
||||
<MudTd><MudButton Class="select-file-button" Color="Color.Primary" Variant="Variant.Filled" OnClick="() => OnFileSelected.InvokeAsync(context.FullName)">Select</MudButton></MudTd>
|
||||
}
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
</MudStack>
|
||||
<Column TData="string" Width="32">
|
||||
<Icon Type="@GetIcon(context)" Theme="outline" />
|
||||
</Column>
|
||||
<PropertyColumn Property="e => e.FullName" Sortable Title="Name">
|
||||
@GetFileName(context)
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="e => e.Length" Sortable Title="Size">
|
||||
@ByteSize.FromBytes(context.Length)
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="e => e.LastWriteTime" Format="MM/dd/yyyy hh:mm" Sortable Title="Modified" />
|
||||
|
||||
</Table>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
|
||||
<style>
|
||||
.select-file-button {
|
||||
|
@ -47,9 +59,13 @@
|
|||
|
||||
@code {
|
||||
[Parameter] public Guid ArchiveId { get; set; }
|
||||
[Parameter] public Guid Archive { get; set; }
|
||||
[Parameter] public EventCallback<string> OnFileSelected { get; set; }
|
||||
[Parameter] public bool Select { get; set; }
|
||||
[Parameter] public bool Multiple { get; set; }
|
||||
|
||||
[Parameter] public IEnumerable<ZipArchiveEntry> SelectedFiles { get; set; }
|
||||
[Parameter] public EventCallback<IEnumerable<ZipArchiveEntry>> SelectedFilesChanged { get; set; }
|
||||
|
||||
ITable? FileTable;
|
||||
|
||||
private IEnumerable<ZipArchiveEntry> Entries { get; set; }
|
||||
private IEnumerable<ZipArchiveEntry> CurrentPathEntries { get; set; }
|
||||
|
@ -76,10 +92,14 @@
|
|||
ChangeDirectory(root);
|
||||
}
|
||||
|
||||
private void OnRowClicked(RowData<ZipArchiveEntry> row)
|
||||
{
|
||||
FileTable.SetSelection(new string[] { row.Data.FullName });
|
||||
}
|
||||
|
||||
private void ChangeDirectory(ArchiveDirectory selectedDirectory)
|
||||
{
|
||||
if (SelectedDirectory == null)
|
||||
SelectedDirectory = selectedDirectory;
|
||||
SelectedDirectory = selectedDirectory;
|
||||
|
||||
if (SelectedDirectory.FullName == "")
|
||||
CurrentPathEntries = Entries.Where(e => !e.FullName.TrimEnd('/').Contains('/'));
|
||||
|
@ -102,23 +122,23 @@
|
|||
switch (Path.GetExtension(entry.FullName))
|
||||
{
|
||||
case "":
|
||||
return Icons.Material.Filled.Folder;
|
||||
return "folder";
|
||||
|
||||
case ".exe":
|
||||
return Icons.Material.Filled.Terminal;
|
||||
return "code";
|
||||
|
||||
case ".zip":
|
||||
case ".rar":
|
||||
case ".7z":
|
||||
case ".gz":
|
||||
case ".tar":
|
||||
return Icons.Material.Filled.FolderZip;
|
||||
return "file-zip";
|
||||
|
||||
case ".wad":
|
||||
case ".pk3":
|
||||
case ".pak":
|
||||
case ".cab":
|
||||
return Icons.Material.Filled.Token;
|
||||
return "file-zip";
|
||||
|
||||
case ".txt":
|
||||
case ".cfg":
|
||||
|
@ -129,12 +149,12 @@
|
|||
case ".log":
|
||||
case ".doc":
|
||||
case ".nfo":
|
||||
return Icons.Custom.FileFormats.FileDocument;
|
||||
return "file-text";
|
||||
|
||||
case ".bat":
|
||||
case ".ps1":
|
||||
case ".json":
|
||||
return Icons.Custom.FileFormats.FileCode;
|
||||
return "code";
|
||||
|
||||
case ".bik":
|
||||
case ".avi":
|
||||
|
@ -146,26 +166,23 @@
|
|||
case ".mpg":
|
||||
case ".mpeg":
|
||||
case ".flv":
|
||||
return Icons.Custom.FileFormats.FileVideo;
|
||||
return "video-camera";
|
||||
|
||||
case ".dll":
|
||||
return Icons.Material.Filled.SettingsApplications;
|
||||
|
||||
case ".scm":
|
||||
return Icons.Material.Filled.Map;
|
||||
return "api";
|
||||
|
||||
case ".hlp":
|
||||
return Icons.Material.Filled.Help;
|
||||
return "file-unknown";
|
||||
|
||||
case ".png":
|
||||
case ".bmp":
|
||||
case ".jpeg":
|
||||
case ".jpg":
|
||||
case ".gif":
|
||||
return Icons.Custom.FileFormats.FileImage;
|
||||
return "file-image";
|
||||
|
||||
default:
|
||||
return Icons.Material.Filled.InsertDriveFile;
|
||||
return "file";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +1,14 @@
|
|||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6">
|
||||
Browse Archive
|
||||
</MudText>
|
||||
</TitleContent>
|
||||
@inherits FeedbackComponent<ArchiveBrowserOptions, IEnumerable<ZipArchiveEntry>>
|
||||
@using System.IO.Compression;
|
||||
@using LANCommander.Models;
|
||||
|
||||
<DialogContent>
|
||||
<ArchiveBrowser ArchiveId="ArchiveId" />
|
||||
</DialogContent>
|
||||
</MudDialog>
|
||||
<ArchiveBrowser ArchiveId="Options.ArchiveId" @bind-SelectedFiles="SelectedFiles" Select="Options.Select" Multiple="Options.Multiple" />
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
[Parameter] public Guid ArchiveId { get; set; }
|
||||
private IEnumerable<ZipArchiveEntry> SelectedFiles { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
public override async Task OnFeedbackOkAsync(ModalClosingEventArgs args)
|
||||
{
|
||||
MudDialog.Options.MaxWidth = MaxWidth.Large;
|
||||
MudDialog.Options.FullWidth = true;
|
||||
MudDialog.Options.FullScreen = true;
|
||||
MudDialog.Options.CloseButton = true;
|
||||
MudDialog.Options.CloseOnEscapeKey = true;
|
||||
|
||||
MudDialog.SetOptions(MudDialog.Options);
|
||||
await base.OkCancelRefWithResult!.OnOk(SelectedFiles);
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6">
|
||||
Select a File
|
||||
</MudText>
|
||||
</TitleContent>
|
||||
|
||||
<DialogContent>
|
||||
<ArchiveBrowser ArchiveId="ArchiveId" OnFileSelected="FileSelected" />
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
[Parameter] public Guid ArchiveId { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
MudDialog.Options.MaxWidth = MaxWidth.Large;
|
||||
MudDialog.Options.FullWidth = true;
|
||||
MudDialog.Options.FullScreen = true;
|
||||
|
||||
MudDialog.SetOptions(MudDialog.Options);
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
|
||||
private void FileSelected(string fileName)
|
||||
{
|
||||
MudDialog.Close(DialogResult.Ok(fileName));
|
||||
}
|
||||
}
|
|
@ -2,52 +2,88 @@
|
|||
@using System.Diagnostics;
|
||||
@inject HttpClient HttpClient
|
||||
@inject NavigationManager Navigator
|
||||
@inject ISnackbar Snackbar
|
||||
@inject ArchiveService ArchiveService
|
||||
@inject IMessageService MessageService
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudForm @bind-IsValid="@IsValid">
|
||||
<MudTextField T="string" @bind-Value="Archive.Version" Label="Version" Required="true" Disabled="Uploading" RequiredError="Version is required" />
|
||||
<MudTextField T="string" @bind-Value="Archive.Changelog" Label="Changelog" Required="false" Disabled="Uploading" Lines="6" />
|
||||
<Space Direction="DirectionVHType.Vertical" Style="width: 100%">
|
||||
<SpaceItem>
|
||||
<Table TItem="Archive" DataSource="@Game.Archives.OrderByDescending(a => a.CreatedOn)" HidePagination="true">
|
||||
<PropertyColumn Property="a => a.Version" />
|
||||
<PropertyColumn Property="a => a.CompressedSize">
|
||||
@ByteSizeLib.ByteSize.FromBytes(context.CompressedSize)
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="a => a.CreatedBy">
|
||||
@context.CreatedBy?.UserName
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="a => a.CreatedOn" Format="MM/dd/yyyy hh:mm" />
|
||||
<ActionColumn Title="">
|
||||
<Space Style="display: flex; justify-content: end">
|
||||
<SpaceItem>
|
||||
<Popconfirm Title="Are you sure you want to delete this archive?" OnConfirm="() => Delete(context)">
|
||||
<Button Icon="@IconType.Outline.Close" Type="@ButtonType.Text" Danger />
|
||||
</Popconfirm>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
</SpaceItem>
|
||||
|
||||
<MudFileUpload T="IBrowserFile" OnFilesChanged="FileSelected" Class="flex-1"
|
||||
InputClass="absolute mud-width-full mud-height-full overflow-hidden z-20 d-block" InputStyle="opacity: 0;"
|
||||
@ondragenter="@SetDragClass" @ondragleave="@ClearDragClass" @ondragend="@ClearDragClass">
|
||||
<ButtonTemplate>
|
||||
<MudPaper Height="200px" Outlined="true" Class="@DragClass">
|
||||
<MudText Typo="Typo.h6">Drop files here or click to browse</MudText>
|
||||
<SpaceItem>
|
||||
<GridRow Justify="end">
|
||||
<GridCol>
|
||||
<Button OnClick="AddArchive" Type="@ButtonType.Primary">Upload Archive</Button>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
|
||||
@if (File != null)
|
||||
{
|
||||
<MudChip Color="Color.Dark" Text="@File.Name" />
|
||||
}
|
||||
</MudPaper>
|
||||
</ButtonTemplate>
|
||||
</MudFileUpload>
|
||||
@{
|
||||
RenderFragment Footer =
|
||||
@<Template>
|
||||
<Button OnClick="UploadArchive" Disabled="@(File == null || Uploading)" Type="@ButtonType.Primary">Upload</Button>
|
||||
<Button OnClick="Clear" Disabled="File == null || Uploading" Danger>Clear</Button>
|
||||
<Button OnClick="Cancel">Cancel</Button>
|
||||
</Template>;
|
||||
}
|
||||
|
||||
<MudProgressLinear Color="Color.Primary" Striped="Uploading" Size="Size.Large" Value="Progress" Class="mt-4" />
|
||||
<Modal Visible="@ModalVisible" Title="Upload Archive" OnOk="UploadArchive" OnCancel="Cancel" Footer="@Footer">
|
||||
<Form Model="@Archive" Layout="@FormLayout.Vertical">
|
||||
<FormItem Label="Version">
|
||||
<Input @bind-Value="@context.Version" />
|
||||
</FormItem>
|
||||
|
||||
<MudText>@ByteSizeLib.ByteSize.FromBytes(Speed)/s</MudText>
|
||||
<FormItem Label="Changelog">
|
||||
<TextArea @bind-Value="@context.Changelog" MaxLength=500 ShowCount />
|
||||
</FormItem>
|
||||
|
||||
<MudToolBar DisableGutters="true" Class="gap-4">
|
||||
<MudButton OnClick="UploadArchive" Disabled="@(!IsValid || File == null || Uploading)" Color="Color.Primary" Variant="Variant.Filled">Upload</MudButton>
|
||||
<MudButton OnClick="Clear" Disabled="File == null || Uploading" Color="Color.Error" Variant="Variant.Filled">Clear</MudButton>
|
||||
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||
</MudToolBar>
|
||||
</MudForm>
|
||||
</DialogContent>
|
||||
</MudDialog>
|
||||
<FormItem>
|
||||
<InputFile id="FileInput" OnChange="FileSelected" hidden />
|
||||
|
||||
<Upload Name="files" FileList="FileList">
|
||||
<label class="ant-btn" for="FileInput">
|
||||
<Icon Type="upload" />
|
||||
Select Archive
|
||||
</label>
|
||||
</Upload>
|
||||
</FormItem>
|
||||
|
||||
<FormItem>
|
||||
<Progress Percent="Progress" />
|
||||
<Text>@ByteSizeLib.ByteSize.FromBytes(Speed)/s</Text>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
[Parameter] public Guid GameId { get; set; }
|
||||
[Parameter] public Game Game { get; set; }
|
||||
|
||||
Archive Archive;
|
||||
|
||||
IBrowserFile File { get; set; }
|
||||
List<UploadFileItem> FileList = new List<UploadFileItem>();
|
||||
|
||||
bool IsValid = false;
|
||||
bool ModalVisible = false;
|
||||
|
||||
private static string DefaultDragClass = "relative rounded-lg border-2 border-dashed pa-4 mt-4 mud-width-full mud-height-full z-10";
|
||||
private string DragClass = DefaultDragClass;
|
||||
|
@ -63,42 +99,49 @@
|
|||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
MudDialog.Options.MaxWidth = MaxWidth.Large;
|
||||
MudDialog.Options.FullWidth = true;
|
||||
MudDialog.Options.CloseButton = false;
|
||||
MudDialog.Options.CloseOnEscapeKey = false;
|
||||
MudDialog.Options.DisableBackdropClick = true;
|
||||
|
||||
MudDialog.SetOptions(MudDialog.Options);
|
||||
|
||||
HttpClient.BaseAddress = new Uri(Navigator.BaseUri);
|
||||
|
||||
Archive = new Archive()
|
||||
{
|
||||
GameId = GameId,
|
||||
GameId = Game.Id,
|
||||
Id = Guid.NewGuid()
|
||||
};
|
||||
}
|
||||
|
||||
private void SetDragClass()
|
||||
private void AddArchive()
|
||||
{
|
||||
DragClass = $"{DefaultDragClass} mud-border-primary";
|
||||
Archive = new Archive()
|
||||
{
|
||||
GameId = Game.Id,
|
||||
Id = Guid.NewGuid()
|
||||
};
|
||||
|
||||
ModalVisible = true;
|
||||
}
|
||||
|
||||
private void ClearDragClass()
|
||||
private async Task Delete(Archive archive)
|
||||
{
|
||||
DragClass = DefaultDragClass;
|
||||
try
|
||||
{
|
||||
await ArchiveService.Delete(archive);
|
||||
|
||||
await MessageService.Success("Archive deleted!");
|
||||
}
|
||||
catch
|
||||
{
|
||||
await MessageService.Error("Archive could not be deleted.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
File = null;
|
||||
ClearDragClass();
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
File = null;
|
||||
ModalVisible = false;
|
||||
}
|
||||
|
||||
private void FileSelected(InputFileChangeEventArgs args)
|
||||
|
@ -159,7 +202,7 @@
|
|||
{
|
||||
Watch.Stop();
|
||||
Uploading = false;
|
||||
UploadComplete();
|
||||
await UploadComplete();
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
@ -172,9 +215,10 @@
|
|||
Archive.ObjectKey = Archive.Id.ToString();
|
||||
Archive.CompressedSize = File.Size;
|
||||
|
||||
ArchiveService.Add(Archive);
|
||||
await ArchiveService.Add(Archive);
|
||||
|
||||
MudDialog.Close();
|
||||
Snackbar.Add("Archive uploaded!", Severity.Success);
|
||||
ModalVisible = false;
|
||||
|
||||
await MessageService.Success("Archive uploaded!");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +1,66 @@
|
|||
@using LANCommander.Data.Enums
|
||||
@using AntDesign.TableModels;
|
||||
@using LANCommander.Data.Enums
|
||||
@using LANCommander.Models
|
||||
@using LANCommander.PCGamingWiki
|
||||
@inject IGDBService IGDBService
|
||||
@inject CompanyService CompanyService
|
||||
@inject GenreService GenreService
|
||||
@inject TagService TagService
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6">
|
||||
<MudIcon Icon="@Icons.Material.Filled.DeleteForever" Class="mr-3 mb-n1" />
|
||||
Results for @GameTitle
|
||||
</MudText>
|
||||
</TitleContent>
|
||||
<DialogContent>
|
||||
@if (Results == null)
|
||||
{
|
||||
<MudProgressCircular Color="Color.Primary" Indeterminate="true" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudTable Items="@Results" Hover="true">
|
||||
<HeaderContent>
|
||||
<MudTh>Title</MudTh>
|
||||
<MudTh>Released</MudTh>
|
||||
<MudTh>Developers</MudTh>
|
||||
<MudTh></MudTh>
|
||||
</HeaderContent>
|
||||
@{
|
||||
RenderFragment Footer =
|
||||
@<Template>
|
||||
<Button OnClick="SelectGame" Disabled="@(Results == null || Results.Count() == 0)" Type="@ButtonType.Primary">Select</Button>
|
||||
<Button OnClick="() => ModalVisible= false">Cancel</Button>
|
||||
</Template>;
|
||||
}
|
||||
|
||||
<RowTemplate>
|
||||
<MudTh>@context.Title</MudTh>
|
||||
<MudTh>@context.ReleasedOn?.ToString("MM/dd/yyyy")</MudTh>
|
||||
<MudTh>@String.Join(", ", context.Developers?.Select(d => d.Name))</MudTh>
|
||||
<MudTh>
|
||||
<MudButton OnClick="() => SelectGame(context)">Select</MudButton>
|
||||
</MudTh>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
<Modal Visible="ModalVisible" Title="Game Metadata Lookup" Footer="@Footer">
|
||||
<Table
|
||||
@ref="ResultsTable"
|
||||
TItem="Game"
|
||||
DataSource="Results"
|
||||
HidePagination="true"
|
||||
Loading="Results == null"
|
||||
OnRowClick="OnRowClicked"
|
||||
@bind-SelectedRows="SelectedResults"
|
||||
ScrollY="calc(100vh - 55px - 55px - 53px)">
|
||||
|
||||
<Selection Key="@context.IGDBId.ToString()" Type="radio" />
|
||||
<PropertyColumn Property="g => g.Title" Title="Title" />
|
||||
<PropertyColumn Property="g => g.ReleasedOn" Format="MM/dd/yyyy" Title="Released" />
|
||||
<PropertyColumn Property="g => g.Developers">
|
||||
@String.Join(", ", context.Developers?.Select(d => d.Name))
|
||||
</PropertyColumn>
|
||||
</Table>
|
||||
</Modal>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
[Parameter] public EventCallback<GameLookupResult> OnResultSelected { get; set; }
|
||||
|
||||
[Parameter] public string GameTitle { get; set; }
|
||||
ITable? ResultsTable;
|
||||
|
||||
private IEnumerable<Game> Results { get; set; }
|
||||
private PCGamingWikiClient PCGamingWikiClient { get; set; }
|
||||
IEnumerable<Game> Results { get; set; }
|
||||
IEnumerable<Game> SelectedResults { get; set; }
|
||||
PCGamingWikiClient PCGamingWikiClient { get; set; }
|
||||
bool ModalVisible { get; set; } = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
PCGamingWikiClient = new PCGamingWikiClient();
|
||||
|
||||
await SearchForGame(GameTitle);
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
private void OnRowClicked(RowData<Game> row)
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
ResultsTable.SetSelection(new string[] { row.Data.IGDBId.ToString() });
|
||||
}
|
||||
|
||||
public async Task SearchForGame(string title)
|
||||
{
|
||||
var results = await IGDBService.Search(GameTitle, "involved_companies.*", "involved_companies.company.*");
|
||||
ModalVisible = true;
|
||||
Results = null;
|
||||
|
||||
var results = await IGDBService.Search(title, "involved_companies.*", "involved_companies.company.*");
|
||||
|
||||
if (results == null)
|
||||
Results = new List<Game>();
|
||||
|
@ -96,62 +89,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
private async Task SelectGame(Game game)
|
||||
private async Task SelectGame()
|
||||
{
|
||||
Results = null;
|
||||
var result = new GameLookupResult();
|
||||
|
||||
var result = await IGDBService.Get(game.IGDBId.GetValueOrDefault(), "genres.*", "game_modes.*", "multiplayer_modes.*", "release_dates.*", "platforms.*", "keywords.*", "involved_companies.*", "involved_companies.company.*", "cover.*");
|
||||
result.IGDBMetadata = await IGDBService.Get(SelectedResults.First().IGDBId.GetValueOrDefault(), "genres.*", "game_modes.*", "multiplayer_modes.*", "release_dates.*", "platforms.*", "keywords.*", "involved_companies.*", "involved_companies.company.*", "cover.*");
|
||||
result.MultiplayerModes = await GetMultiplayerModes(result.IGDBMetadata.Name);
|
||||
|
||||
game.Title = result.Name;
|
||||
game.Description = result.Summary;
|
||||
game.ReleasedOn = result.FirstReleaseDate.GetValueOrDefault().UtcDateTime;
|
||||
game.MultiplayerModes = await GetMultiplayerModes(result.Name);
|
||||
game.Developers = new List<Company>();
|
||||
game.Publishers = new List<Company>();
|
||||
game.Genres = new List<Genre>();
|
||||
game.Tags = new List<Tag>();
|
||||
await OnResultSelected.InvokeAsync(result);
|
||||
|
||||
if (result.GameModes != null && result.GameModes.Values != null)
|
||||
game.Singleplayer = result.GameModes.Values.Any(gm => gm.Name == "Singleplayer");
|
||||
|
||||
if (result.InvolvedCompanies != null && result.InvolvedCompanies.Values != null)
|
||||
{
|
||||
// Make sure companie
|
||||
var developers = result.InvolvedCompanies.Values.Where(c => c.Developer.GetValueOrDefault()).Select(c => c.Company.Value.Name);
|
||||
var publishers = result.InvolvedCompanies.Values.Where(c => c.Publisher.GetValueOrDefault()).Select(c => c.Company.Value.Name);
|
||||
|
||||
foreach (var developer in developers)
|
||||
{
|
||||
game.Developers.Add(await CompanyService.AddMissing(c => c.Name == developer, new Company { Name = developer }));
|
||||
}
|
||||
|
||||
foreach (var publisher in publishers)
|
||||
{
|
||||
game.Publishers.Add(await CompanyService.AddMissing(c => c.Name == publisher, new Company { Name = publisher }));
|
||||
}
|
||||
}
|
||||
|
||||
if (result.Genres != null && result.Genres.Values != null)
|
||||
{
|
||||
var genres = result.Genres.Values.Select(g => g.Name);
|
||||
|
||||
foreach (var genre in genres)
|
||||
{
|
||||
game.Genres.Add(await GenreService.AddMissing(g => g.Name == genre, new Genre { Name = genre }));
|
||||
}
|
||||
}
|
||||
|
||||
if (result.Keywords != null && result.Keywords.Values != null)
|
||||
{
|
||||
var tags = result.Keywords.Values.Select(t => t.Name).Take(20);
|
||||
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
game.Tags.Add(await TagService.AddMissing(t => t.Name == tag, new Tag { Name = tag }));
|
||||
}
|
||||
}
|
||||
|
||||
MudDialog.Close(DialogResult.Ok(game));
|
||||
ModalVisible = false;
|
||||
}
|
||||
|
||||
private async Task<ICollection<MultiplayerMode>> GetMultiplayerModes(string gameTitle)
|
||||
|
|
123
LANCommander/Components/KeysEditor.razor
Normal file
123
LANCommander/Components/KeysEditor.razor
Normal file
|
@ -0,0 +1,123 @@
|
|||
@inject KeyService KeyService
|
||||
@inject IMessageService MessageService
|
||||
|
||||
<Row>
|
||||
<Col Span="8">
|
||||
<Statistic Title="Available" Value="Game.Keys.Count - AllocatedKeys" Style="text-align: center;" />
|
||||
</Col>
|
||||
<Col Span="8">
|
||||
<Statistic Title="Allocated" Value="AllocatedKeys" Style="text-align: center;" />
|
||||
</Col>
|
||||
<Col Span="8">
|
||||
<Statistic Title="Total" Value="Game.Keys.Count" Style="text-align: center;" />
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Modal Title="View Keys" Visible="ViewModalVisible" Maximizable="false" DefaultMaximized="true" OnCancel="() => ViewModalVisible = false" OnOk="() => ViewModalVisible = false">
|
||||
<Table TItem="Key" DataSource="@Game.Keys" Bordered>
|
||||
<PropertyColumn Property="k => k.Value">
|
||||
<InputPassword @bind-Value="@context.Value" />
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="k => k.AllocationMethod" />
|
||||
<Column TData="string">
|
||||
@switch (context.AllocationMethod)
|
||||
{
|
||||
case KeyAllocationMethod.MacAddress:
|
||||
<text>@context.ClaimedByMacAddress</text>
|
||||
break;
|
||||
|
||||
case KeyAllocationMethod.UserAccount:
|
||||
<text>@context.ClaimedByUser?.UserName</text>
|
||||
break;
|
||||
}
|
||||
</Column>
|
||||
<PropertyColumn Property="g => g.ClaimedOn" Format="MM/dd/yyyy hh:mm" Sortable />
|
||||
<ActionColumn Title="">
|
||||
<Space>
|
||||
<SpaceItem>
|
||||
@if (context.IsAllocated())
|
||||
{
|
||||
<Button OnClick="() => Release(context)">Release</Button>
|
||||
}
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
</Modal>
|
||||
|
||||
<Modal Title="Edit Keys" Visible="EditModalVisible" Maximizable="false" DefaultMaximized="true" OnCancel="() => EditModalVisible = false" OnOk="Save">
|
||||
<StandaloneCodeEditor @ref="Editor" Id="editor" ConstructionOptions="EditorConstructionOptions" />
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.monaco-editor-container {
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
[Parameter] public Game Game { get; set; }
|
||||
|
||||
int AllocatedKeys;
|
||||
|
||||
bool ViewModalVisible = false;
|
||||
bool EditModalVisible = false;
|
||||
|
||||
private StandaloneCodeEditor? Editor;
|
||||
|
||||
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
|
||||
{
|
||||
return new StandaloneEditorConstructionOptions
|
||||
{
|
||||
AutomaticLayout = true,
|
||||
Language = "text",
|
||||
Value = String.Join('\n', Game.Keys.Select(k => k.Value)),
|
||||
Theme = "vs-dark",
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
AllocatedKeys = Game.Keys.Count(k => k.IsAllocated());
|
||||
}
|
||||
|
||||
public void Edit()
|
||||
{
|
||||
EditModalVisible = true;
|
||||
}
|
||||
|
||||
public void View()
|
||||
{
|
||||
ViewModalVisible = true;
|
||||
}
|
||||
|
||||
private async Task Release(Key key)
|
||||
{
|
||||
key = await KeyService.Release(key);
|
||||
|
||||
await MessageService.Success("Key was unallocated!");
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
var value = await Editor.GetValue();
|
||||
var keys = value.Split("\n").Select(k => k.Trim()).Where(k => !String.IsNullOrWhiteSpace(k));
|
||||
|
||||
var keysDeleted = Game.Keys.Where(k => !keys.Contains(k.Value));
|
||||
var keysAdded = keys.Where(k => !Game.Keys.Any(gk => gk.Value == k));
|
||||
|
||||
foreach (var key in keysDeleted)
|
||||
KeyService.Delete(key);
|
||||
|
||||
foreach (var key in keysAdded)
|
||||
await KeyService.Add(new Key()
|
||||
{
|
||||
Game = Game,
|
||||
Value = key
|
||||
});
|
||||
|
||||
EditModalVisible = false;
|
||||
|
||||
await MessageService.Success("Keys updated!");
|
||||
}
|
||||
}
|
|
@ -1,36 +1,39 @@
|
|||
@using LANCommander.Data.Enums
|
||||
@using LANCommander.Data.Models
|
||||
|
||||
<MudTable Items="@Game.MultiplayerModes" Elevation="0" Dense="true">
|
||||
<HeaderContent>
|
||||
<MudTh>Type</MudTh>
|
||||
<MudTh>Min Players</MudTh>
|
||||
<MudTh>Max Players</MudTh>
|
||||
<MudTh>Description</MudTh>
|
||||
<MudTh></MudTh>
|
||||
</HeaderContent>
|
||||
<Space Direction="DirectionVHType.Vertical" Size="@("large")" Style="width: 100%">
|
||||
<SpaceItem>
|
||||
<Table TItem="MultiplayerMode" DataSource="@Game.MultiplayerModes" HidePagination="true">
|
||||
<PropertyColumn Property="m => m.Type">
|
||||
<Select @bind-Value="context.Type" TItem="MultiplayerType" TItemValue="MultiplayerType" DataSource="Enum.GetValues<MultiplayerType>()" />
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="m => m.MinPlayers">
|
||||
<AntDesign.InputNumber @bind-Value="context.MinPlayers" DefaultValue="2" Min="2" />
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="m => m.MaxPlayers">
|
||||
<AntDesign.InputNumber @bind-Value="context.MaxPlayers" DefaultValue="2" Min="2" />
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="m => m.Description">
|
||||
<Input Type="text" @bind-Value="context.Description" />
|
||||
</PropertyColumn>
|
||||
<ActionColumn>
|
||||
<Space Style="display: flex; justify-content: end">
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => RemoveMode(context)" Type="@ButtonType.Text" Danger Icon="@IconType.Outline.Close" />
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
</SpaceItem>
|
||||
|
||||
<RowTemplate>
|
||||
<MudTd>
|
||||
<MudSelect @bind-Value="context.Type" Margin="0">
|
||||
@foreach (MultiplayerType type in Enum.GetValues(typeof(MultiplayerType)))
|
||||
{
|
||||
<MudSelectItem Value="@((MultiplayerType)type)">@type</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudTd>
|
||||
<MudTd><MudNumericField @bind-Value="context.MinPlayers" Margin="0" /></MudTd>
|
||||
<MudTd><MudNumericField @bind-Value="context.MaxPlayers" Margin="0" /></MudTd>
|
||||
<MudTd><MudTextField @bind-Value="context.Description" Margin="0" /></MudTd>
|
||||
<MudTd Class="d-flex justify-end">
|
||||
<MudIconButton Color="Color.Error" OnClick="() => RemoveMode(context)" Icon="@Icons.Material.Filled.Close"></MudIconButton>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
<MudPaper Elevation="0" Class="d-flex justify-end mt-3">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="AddMode">Add</MudButton>
|
||||
</MudPaper>
|
||||
<SpaceItem>
|
||||
<GridRow Justify="end">
|
||||
<GridCol>
|
||||
<Button OnClick="AddMode" Type="@ButtonType.Primary">Add Mode</Button>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
|
||||
@code {
|
||||
[Parameter] public Game Game { get; set; }
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<MudNavMenu>
|
||||
<MudNavLink Href="/" Match="NavLinkMatch.All">Dashboard</MudNavLink>
|
||||
<MudNavLink Href="/Games" Match="NavLinkMatch.Prefix">Games</MudNavLink>
|
||||
</MudNavMenu>
|
8
LANCommander/Components/RedirectToLogin.razor
Normal file
8
LANCommander/Components/RedirectToLogin.razor
Normal file
|
@ -0,0 +1,8 @@
|
|||
@inject NavigationManager NavigationManager
|
||||
|
||||
@code {
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
NavigationManager.NavigateTo("/Identity/Account/Login");
|
||||
}
|
||||
}
|
190
LANCommander/Components/ScriptEditor.razor
Normal file
190
LANCommander/Components/ScriptEditor.razor
Normal file
|
@ -0,0 +1,190 @@
|
|||
@using LANCommander.Data.Enums;
|
||||
@using LANCommander.Models
|
||||
@using LANCommander.Services
|
||||
@using System.IO.Compression;
|
||||
@inject ScriptService ScriptService
|
||||
@inject ModalService ModalService
|
||||
@inject IMessageService MessageService
|
||||
|
||||
<Modal Visible="ModalVisible" OnOk="Save" OnCancel="() => ModalVisible = false" Title="@(Script == null ? "Add Script" : "Edit Script")" OkText="@("Save")" Maximizable="false" DefaultMaximized="true">
|
||||
<Form Model="@Script" Layout="@FormLayout.Vertical">
|
||||
<FormItem>
|
||||
@foreach (var group in Snippets.Select(s => s.Group).Distinct())
|
||||
{
|
||||
<Dropdown>
|
||||
<Overlay>
|
||||
<Menu>
|
||||
@foreach (var snippet in Snippets.Where(s => s.Group == group))
|
||||
{
|
||||
<MenuItem OnClick="() => InsertSnippet(snippet)">
|
||||
@snippet.Name
|
||||
</MenuItem>
|
||||
}
|
||||
</Menu>
|
||||
</Overlay>
|
||||
|
||||
<ChildContent>
|
||||
<Button Type="@ButtonType.Primary">@group</Button>
|
||||
</ChildContent>
|
||||
</Dropdown>
|
||||
}
|
||||
|
||||
<Button Icon="@IconType.Outline.FolderOpen" OnClick="BrowseForPath" Type="@ButtonType.Text">Browse</Button>
|
||||
</FormItem>
|
||||
|
||||
<FormItem>
|
||||
<StandaloneCodeEditor @ref="Editor" Id="editor" ConstructionOptions="EditorConstructionOptions" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="Type">
|
||||
<Select @bind-Value="Script.Type" TItem="ScriptType" TItemValue="ScriptType" DataSource="Enum.GetValues<ScriptType>()" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem>
|
||||
<Checkbox @bind-Checked="Script.RequiresAdmin">Requires Admin</Checkbox>
|
||||
</FormItem>
|
||||
|
||||
<FormItem Label="Description">
|
||||
<TextArea @bind-Value="Script.Description" MaxLength=500 ShowCount />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Space Direction="DirectionVHType.Vertical" Size="@("large")" Style="width: 100%">
|
||||
<SpaceItem>
|
||||
<Table TItem="Script" DataSource="@Game.Scripts" HidePagination="true">
|
||||
<PropertyColumn Property="s => s.Type" />
|
||||
<PropertyColumn Property="s => s.CreatedBy">
|
||||
@context.CreatedBy?.UserName
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="s => s.CreatedOn" Format="MM/dd/yyyy hh:mm" />
|
||||
<ActionColumn Title="">
|
||||
<Space Style="display: flex; justify-content: end">
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => Edit(context)" Icon="@IconType.Outline.Edit" Type="@ButtonType.Text" />
|
||||
|
||||
<Popconfirm OnConfirm="() => Delete(context)" Title="Are you sure you want to delete this script?">
|
||||
<Button Icon="@IconType.Outline.Close" Type="@ButtonType.Text" Danger />
|
||||
</Popconfirm>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
</SpaceItem>
|
||||
|
||||
<SpaceItem>
|
||||
<GridRow Justify="end">
|
||||
<GridCol>
|
||||
<Button OnClick="() => Edit()" Type="@ButtonType.Primary">Add Script</Button>
|
||||
</GridCol>
|
||||
</GridRow>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
|
||||
<style>
|
||||
.monaco-editor-container {
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
[Parameter] public Game Game { get; set; }
|
||||
|
||||
Script Script;
|
||||
|
||||
bool ModalVisible = false;
|
||||
|
||||
IEnumerable<Snippet> Snippets { get; set; }
|
||||
StandaloneCodeEditor Editor;
|
||||
|
||||
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
|
||||
{
|
||||
return new StandaloneEditorConstructionOptions
|
||||
{
|
||||
AutomaticLayout = true,
|
||||
Language = "powershell",
|
||||
Value = Script.Contents,
|
||||
Theme = "vs-dark",
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Snippets = ScriptService.GetSnippets();
|
||||
|
||||
if (Script == null)
|
||||
Script = new Script();
|
||||
}
|
||||
|
||||
private async void Edit(Script script = null)
|
||||
{
|
||||
if (Script == null)
|
||||
Script = new Script();
|
||||
else
|
||||
Script = script;
|
||||
|
||||
if (Editor != null)
|
||||
await Editor.SetValue(Script.Contents);
|
||||
|
||||
ModalVisible = true;
|
||||
}
|
||||
|
||||
private async void Delete(Script script = null)
|
||||
{
|
||||
if (script != null)
|
||||
await ScriptService.Delete(script);
|
||||
|
||||
await MessageService.Success("Script deleted!");
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
var value = await Editor.GetValue();
|
||||
|
||||
await ScriptService.Update(Script);
|
||||
|
||||
await MessageService.Success("Script saved!");
|
||||
}
|
||||
|
||||
private async void InsertSnippet(Snippet snippet)
|
||||
{
|
||||
await Editor.Trigger("keyboard", "type", new
|
||||
{
|
||||
text = snippet.Content
|
||||
});
|
||||
}
|
||||
|
||||
private async void BrowseForPath()
|
||||
{
|
||||
var modalOptions = new ModalOptions()
|
||||
{
|
||||
Title = "Choose Reference",
|
||||
Maximizable = false,
|
||||
DefaultMaximized = true,
|
||||
Closable = true,
|
||||
OkText = "Insert File Path"
|
||||
};
|
||||
|
||||
var browserOptions = new ArchiveBrowserOptions()
|
||||
{
|
||||
ArchiveId = Game.Archives.FirstOrDefault().Id,
|
||||
Select = true,
|
||||
Multiple = false
|
||||
};
|
||||
|
||||
var modalRef = await ModalService.CreateModalAsync<ArchiveBrowserDialog, ArchiveBrowserOptions, IEnumerable<ZipArchiveEntry>>(modalOptions, browserOptions);
|
||||
|
||||
modalRef.OnOk = (results) =>
|
||||
{
|
||||
var path = results.FirstOrDefault().FullName;
|
||||
|
||||
Editor.Trigger("keyboard", "type", new
|
||||
{
|
||||
text = $"$InstallDir\\{path.Replace('/', '\\')}"
|
||||
});
|
||||
|
||||
StateHasChanged();
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
@using LANCommander.Data.Enums;
|
||||
@using LANCommander.Models
|
||||
@using LANCommander.Services
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IDialogService DialogService
|
||||
@inject ScriptService ScriptService
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudPaper Elevation="0" Class="gap-4 d-flex flex-nowrap justify-content-start mb-2">
|
||||
@foreach (var group in Snippets.Select(s => s.Group).Distinct())
|
||||
{
|
||||
<MudMenu EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@group" Color="Color.Primary" Variant="Variant.Filled">
|
||||
@foreach (var snippet in Snippets.Where(s => s.Group == group))
|
||||
{
|
||||
<MudMenuItem OnClick="() => InsertSnippet(snippet)">@snippet.Name</MudMenuItem>
|
||||
}
|
||||
</MudMenu>
|
||||
}
|
||||
|
||||
<MudTooltip Text="Browse Archive For Path">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Folder" OnClick="BrowseForPath" Class="align-self-end" />
|
||||
</MudTooltip>
|
||||
</MudPaper>
|
||||
|
||||
<StandaloneCodeEditor @ref="Editor" Id="editor" ConstructionOptions="EditorConstructionOptions" />
|
||||
|
||||
<MudSelect @bind-Value="Script.Type" Label="Type">
|
||||
@foreach (ScriptType type in Enum.GetValues(typeof(ScriptType)))
|
||||
{
|
||||
<MudSelectItem Value="@((ScriptType)type)">@type</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudCheckBox @bind-Checked="Script.RequiresAdmin" Color="Color.Primary" Label="Requires Admin"></MudCheckBox>
|
||||
|
||||
<MudTextField @bind-Value="Script.Description" Lines="4" Label="Description" />
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Save" OnClick="Save">Save</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
<style>
|
||||
.monaco-editor-container {
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
[Parameter] public Script Script { get; set; }
|
||||
|
||||
IEnumerable<Snippet> Snippets { get; set; }
|
||||
StandaloneCodeEditor Editor;
|
||||
|
||||
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
|
||||
{
|
||||
return new StandaloneEditorConstructionOptions
|
||||
{
|
||||
AutomaticLayout = true,
|
||||
Language = "powershell",
|
||||
Value = Script.Contents,
|
||||
Theme = "vs-dark",
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
MudDialog.Options.MaxWidth = MaxWidth.ExtraLarge;
|
||||
MudDialog.Options.FullWidth = true;
|
||||
MudDialog.Options.CloseButton = true;
|
||||
MudDialog.Options.CloseOnEscapeKey = false;
|
||||
MudDialog.Options.DisableBackdropClick = true;
|
||||
|
||||
MudDialog.SetOptions(MudDialog.Options);
|
||||
|
||||
Snippets = ScriptService.GetSnippets();
|
||||
|
||||
if (Script == null)
|
||||
Script = new Script();
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
var value = await Editor.GetValue();
|
||||
|
||||
await ScriptService.Update(Script);
|
||||
|
||||
Snackbar.Add("Script saved!", Severity.Success);
|
||||
MudDialog.Close();
|
||||
}
|
||||
|
||||
private async void InsertSnippet(Snippet snippet)
|
||||
{
|
||||
Editor.Trigger("keyboard", "type", new
|
||||
{
|
||||
text = snippet.Content
|
||||
});
|
||||
}
|
||||
|
||||
private async void BrowseForPath()
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
["ArchiveId"] = Script.Game.Archives.OrderByDescending(a => a.CreatedOn).First().Id
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<ArchiveFileSelectorDialog>("File Selector", parameters);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled)
|
||||
{
|
||||
var path = result.Data as string;
|
||||
|
||||
Editor.Trigger("keyboard", "type", new
|
||||
{
|
||||
text = $"$InstallDir\\{path.Replace('/', '\\')}"
|
||||
});
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
@using LANCommander.Models;
|
||||
@using LANCommander.Services;
|
||||
@inject IJSRuntime JS
|
||||
|
||||
@foreach (var group in Snippets.Select(s => s.Group).Distinct())
|
||||
{
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
@group
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var snippet in Snippets.Where(s => s.Group == group))
|
||||
{
|
||||
<li><a class="dropdown-item" @onclick="() => InsertSnippet(snippet)">@snippet.Name</a></li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
public IEnumerable<Snippet> Snippets { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Snippets = ScriptService.GetSnippets();
|
||||
}
|
||||
|
||||
private async Task InsertSnippet(Snippet snippet) {
|
||||
await JS.InvokeVoidAsync("Editor.trigger", "keyboard", "type", new { text = snippet.Content });
|
||||
}
|
||||
}
|
40
LANCommander/Components/TagsInput.razor
Normal file
40
LANCommander/Components/TagsInput.razor
Normal file
|
@ -0,0 +1,40 @@
|
|||
@typeparam TItem where TItem : BaseModel
|
||||
|
||||
<Select Mode="tags" TItem="Guid" TItemValue="Guid" @bind-Values="@SelectedValues" OnSelectedItemsChanged="OnSelectedItemsChanged" EnableSearch>
|
||||
<SelectOptions>
|
||||
@foreach (var entity in Entities)
|
||||
{
|
||||
<SelectOption TItemValue="Guid" TItem="Guid" Value="@entity.Id" Label="@OptionLabelSelector.Invoke(entity)" />
|
||||
}
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
|
||||
@code {
|
||||
[Parameter] public Func<TItem, string> OptionLabelSelector { get; set; }
|
||||
[Parameter] public IEnumerable<TItem> Entities { get; set; }
|
||||
[Parameter] public ICollection<TItem> SelectedEntities { get; set; }
|
||||
|
||||
private IEnumerable<Guid> SelectedValues;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (SelectedEntities != null)
|
||||
SelectedValues = SelectedEntities.Select(e => e.Id);
|
||||
}
|
||||
|
||||
private void OnSelectedItemsChanged(IEnumerable<Guid> values)
|
||||
{
|
||||
var toAdd = values.Where(v => !SelectedEntities.Any(e => e.Id == v));
|
||||
var toRemove = SelectedEntities.Where(e => !values.Any(v => v == e.Id));
|
||||
|
||||
foreach (var value in toAdd)
|
||||
{
|
||||
SelectedEntities.Add(Entities.First(e => e.Id == value));
|
||||
}
|
||||
|
||||
foreach (var value in toRemove)
|
||||
{
|
||||
SelectedEntities.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,17 @@ namespace LANCommander.Data.Models
|
|||
public string? ClaimedByComputerName { get; set; }
|
||||
public virtual User? ClaimedByUser { get; set; }
|
||||
public DateTime? ClaimedOn { get; set; }
|
||||
|
||||
public bool IsAllocated()
|
||||
{
|
||||
if (AllocationMethod == KeyAllocationMethod.MacAddress && !String.IsNullOrWhiteSpace(ClaimedByMacAddress))
|
||||
return true;
|
||||
|
||||
if (AllocationMethod == KeyAllocationMethod.UserAccount && ClaimedByUser != null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum KeyAllocationMethod
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AntDesign" Version="0.14.3" />
|
||||
<PackageReference Include="Blazor-ApexCharts" Version="0.9.18-beta" />
|
||||
<PackageReference Include="BlazorMonaco" Version="3.0.0" />
|
||||
<PackageReference Include="ByteSize" Version="2.1.1" />
|
||||
<PackageReference Include="IGDB" Version="2.3.1" />
|
||||
|
@ -48,10 +50,11 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Areas\Archive\Pages\" />
|
||||
<Folder Include="bin\Debug\net6.0\" />
|
||||
<Folder Include="Data\Migrations\" />
|
||||
<Folder Include="Migrations\" />
|
||||
<Folder Include="Pages\Games\Archives\" />
|
||||
<Folder Include="Pages\Settings\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
9
LANCommander/Models/ArchiveBrowserOptions.cs
Normal file
9
LANCommander/Models/ArchiveBrowserOptions.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace LANCommander.Models
|
||||
{
|
||||
public class ArchiveBrowserOptions
|
||||
{
|
||||
public Guid ArchiveId { get; set; }
|
||||
public bool Select { get; set; }
|
||||
public bool Multiple { get; set; }
|
||||
}
|
||||
}
|
10
LANCommander/Models/GameLookupResult.cs
Normal file
10
LANCommander/Models/GameLookupResult.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using LANCommander.Data.Models;
|
||||
|
||||
namespace LANCommander.Models
|
||||
{
|
||||
public class GameLookupResult
|
||||
{
|
||||
public IGDB.Models.Game IGDBMetadata { get; set; }
|
||||
public IEnumerable<MultiplayerMode> MultiplayerModes { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
@page "/Dashboard"
|
||||
|
||||
@using System.Diagnostics;
|
||||
@using LANCommander.Models;
|
||||
|
||||
<MudGrid Justify="Justify.Center">
|
||||
<MudItem>
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">CPU Utilization (%)</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
@if (PerformanceChartData.ProcessorUtilization != null && PerformanceChartData.ProcessorUtilization.PerformanceCounter != null)
|
||||
{
|
||||
<MudChart ChartType="ChartType.Line" ChartSeries="@(PerformanceChartData.ProcessorUtilization.ToSeriesList("CPU %"))" Width="100%" Height="250px"></MudChart>
|
||||
}
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
|
||||
<MudItem>
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Upload Rate (MB/s)</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudChart ChartType="ChartType.Line" ChartSeries="@PerformanceChartData.NetworkUploadRate.Select(x => x.Value.ToSeries(x.Key)).ToList()" Width="100%" Height="250px"></MudChart>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
|
||||
<MudItem>
|
||||
<MudCard>
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Download Rate (MB/s)</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
<MudCardContent>
|
||||
<MudChart ChartType="ChartType.Line" ChartSeries="@PerformanceChartData.NetworkDownloadRate.Select(x => x.Value.ToSeries(x.Key)).ToList()" Width="100%" Height="250px"></MudChart>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
@code {
|
||||
int cpuTime = 0;
|
||||
|
||||
int RecordTime = 60;
|
||||
|
||||
private PerformanceChartData PerformanceChartData = new PerformanceChartData()
|
||||
{
|
||||
ProcessorUtilization = new PerformanceCounterData(),
|
||||
NetworkUploadRate = new Dictionary<string, PerformanceCounterData>(),
|
||||
NetworkDownloadRate = new Dictionary<string, PerformanceCounterData>()
|
||||
};
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var timer = new System.Timers.Timer();
|
||||
|
||||
timer.Interval = 1000;
|
||||
|
||||
timer.Elapsed += async (s, e) =>
|
||||
{
|
||||
RefreshLiveData();
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
};
|
||||
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void RefreshLiveData()
|
||||
{
|
||||
RefreshProcessorUtilization();
|
||||
RefreshNetworkUsage();
|
||||
}
|
||||
|
||||
private void RefreshProcessorUtilization()
|
||||
{
|
||||
if (PerformanceChartData.ProcessorUtilization.PerformanceCounter == null)
|
||||
PerformanceChartData.ProcessorUtilization.PerformanceCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
|
||||
|
||||
PerformanceChartData.ProcessorUtilization.Data = ShiftArrayAndInsert<double>(PerformanceChartData.ProcessorUtilization.Data, PerformanceChartData.ProcessorUtilization.PerformanceCounter.NextValue());
|
||||
}
|
||||
|
||||
private void RefreshNetworkUsage()
|
||||
{
|
||||
var category = new PerformanceCounterCategory("Network Interface");
|
||||
|
||||
foreach (var instance in category.GetInstanceNames())
|
||||
{
|
||||
if (!PerformanceChartData.NetworkUploadRate.ContainsKey(instance))
|
||||
PerformanceChartData.NetworkUploadRate[instance] = new PerformanceCounterData()
|
||||
{
|
||||
PerformanceCounter = new PerformanceCounter("Network Interface", "Bytes Sent/sec", instance),
|
||||
Data = new double[RecordTime]
|
||||
};
|
||||
|
||||
if (!PerformanceChartData.NetworkDownloadRate.ContainsKey(instance))
|
||||
PerformanceChartData.NetworkDownloadRate[instance] = new PerformanceCounterData()
|
||||
{
|
||||
PerformanceCounter = new PerformanceCounter("Network Interface", "Bytes Received/sec", instance),
|
||||
Data = new double[RecordTime]
|
||||
};
|
||||
|
||||
PerformanceChartData.NetworkUploadRate[instance].Data = ShiftArrayAndInsert<double>(PerformanceChartData.NetworkUploadRate[instance].Data, (double)PerformanceChartData.NetworkUploadRate[instance].PerformanceCounter.NextValue() / (1024 * 1024));
|
||||
PerformanceChartData.NetworkDownloadRate[instance].Data = ShiftArrayAndInsert<double>(PerformanceChartData.NetworkDownloadRate[instance].Data, (double)PerformanceChartData.NetworkDownloadRate[instance].PerformanceCounter.NextValue() / (1024 * 1024));
|
||||
}
|
||||
}
|
||||
|
||||
private T[] ShiftArrayAndInsert<T>(T[] array, T input)
|
||||
{
|
||||
if (array == null || array.Length < RecordTime)
|
||||
{
|
||||
array = new T[RecordTime];
|
||||
}
|
||||
|
||||
Array.Copy(array, 1, array, 0, array.Length - 1);
|
||||
|
||||
array[array.Length - 1] = input;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private PerformanceCounter[] GetCounters(string categoryName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var category = PerformanceCounterCategory.GetCategories().First(c => c.CategoryName == categoryName);
|
||||
|
||||
var instanceName = Process.GetCurrentProcess().ProcessName;
|
||||
|
||||
return category.GetCounters(instanceName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new PerformanceCounter[] { };
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,211 +1,138 @@
|
|||
@page "/Games/{id:guid}/Edit"
|
||||
@using LANCommander.Models;
|
||||
@using System.IO.Compression;
|
||||
@attribute [Authorize(Roles = "Administrator")]
|
||||
@inject GameService GameService
|
||||
@inject CompanyService CompanyService
|
||||
@inject GenreService GenreService
|
||||
@inject TagService TagService
|
||||
@inject ArchiveService ArchiveService
|
||||
@inject ScriptService ScriptService
|
||||
@inject IDialogService DialogService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IMessageService MessageService
|
||||
@inject ModalService ModalService
|
||||
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudPaper Class="pa-4">
|
||||
<MudForm @bind-IsValid="@Success" @bind-Errors="@Errors">
|
||||
<MudTextField @bind-Value="Game.Title" Label="Title" For="@(() => Game.Title)" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Search" OnAdornmentClick="LookupGameMetadata" />
|
||||
<MudTextField @bind-Value="Game.SortTitle" Label="Sort Title" For="@(() => Game.SortTitle)" />
|
||||
<MudTextField @bind-Value="Game.Icon" Label="Icon" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Folder" OnAdornmentClick="BrowseForIcon" />
|
||||
<MudTextField @bind-Value="Game.Description" Label="Description" For="@(() => Game.Description)" Lines="4" />
|
||||
<MudDatePicker @bind-Date="Game.ReleasedOn" Label="Released" />
|
||||
<MudCheckBox @bind-Checked="@Game.Singleplayer" Label="Singleplayer" Color="Color.Primary"></MudCheckBox>
|
||||
<Space Direction="DirectionVHType.Vertical" Size="@("large")" Style="width: 100%;">
|
||||
<SpaceItem>
|
||||
<Card Title="Game Details">
|
||||
<Body>
|
||||
<Form Model="@Game" Layout="@FormLayout.Vertical">
|
||||
<FormItem Label="Title">
|
||||
<GameMetadataLookup @ref="GameMetadataLookup" OnResultSelected="OnGameLookupResultSelected" />
|
||||
|
||||
<MudDivider Class="mt-4 mb-4" />
|
||||
<Space Style="display: flex">
|
||||
<SpaceItem Style="flex-grow: 1">
|
||||
<Input @bind-Value="@context.Title" />
|
||||
</SpaceItem>
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => GameMetadataLookup.SearchForGame(context.Title)" Type="@ButtonType.Primary">Lookup</Button>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</FormItem>
|
||||
<FormItem Label="Sort Title">
|
||||
<Input @bind-Value="@context.SortTitle" />
|
||||
</FormItem>
|
||||
<FormItem Label="Icon">
|
||||
<Space Style="display: flex">
|
||||
<SpaceItem Style="flex-grow: 1">
|
||||
<Input @bind-Value="@context.Icon" />
|
||||
</SpaceItem>
|
||||
<SpaceItem>
|
||||
<Button OnClick="BrowseForIcon" Type="@ButtonType.Primary">Browse</Button>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</FormItem>
|
||||
<FormItem Label="Description">
|
||||
<TextArea @bind-Value="@context.Description" MaxLength=500 ShowCount />
|
||||
</FormItem>
|
||||
<FormItem Label="Released On">
|
||||
<DatePicker TValue="DateTime?" @bind-Value="@context.ReleasedOn" Picker="@DatePickerType.Date" />
|
||||
</FormItem>
|
||||
<FormItem Label="Singleplayer">
|
||||
<Checkbox @bind-Checked="@context.Singleplayer" />
|
||||
</FormItem>
|
||||
<FormItem Label="Developers">
|
||||
<TagsInput Entities="Companies" SelectedEntities="Game.Developers" OptionLabelSelector="c => c.Name" TItem="Company" />
|
||||
</FormItem>
|
||||
<FormItem Label="Publishers">
|
||||
<TagsInput Entities="Companies" SelectedEntities="Game.Publishers" OptionLabelSelector="c => c.Name" TItem="Company" />
|
||||
</FormItem>
|
||||
<FormItem Label="Genres">
|
||||
<TagsInput Entities="Genres" SelectedEntities="Game.Genres" OptionLabelSelector="c => c.Name" TItem="Genre" />
|
||||
</FormItem>
|
||||
<FormItem Label="Tags">
|
||||
<TagsInput Entities="Tags" SelectedEntities="Game.Tags" OptionLabelSelector="c => c.Name" TItem="Data.Models.Tag" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button Type="@ButtonType.Primary" OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Body>
|
||||
</Card>
|
||||
</SpaceItem>
|
||||
|
||||
<MudText Typo="Typo.h6">Developers</MudText>
|
||||
<SpaceItem>
|
||||
<Card Title="Actions">
|
||||
<Body>
|
||||
<ActionEditor Game="Game" />
|
||||
</Body>
|
||||
</Card>
|
||||
</SpaceItem>
|
||||
|
||||
<MudChipSet>
|
||||
@foreach (var developer in Game.Developers)
|
||||
{
|
||||
<MudChip>@developer.Name</MudChip>
|
||||
}
|
||||
</MudChipSet>
|
||||
<SpaceItem>
|
||||
<Card Title="Multiplayer Modes">
|
||||
<Body>
|
||||
<MultiplayerModeEditor Game="Game" />
|
||||
</Body>
|
||||
</Card>
|
||||
</SpaceItem>
|
||||
|
||||
<MudDivider Class="mt-4 mb-4" />
|
||||
<SpaceItem>
|
||||
<Card Title="Keys">
|
||||
<Extra>
|
||||
<Button OnClick="() => KeysEditor.Edit()">Edit</Button>
|
||||
<Button OnClick="() => KeysEditor.View()" Type="@ButtonType.Primary">View</Button>
|
||||
</Extra>
|
||||
<Body>
|
||||
<KeysEditor @ref="KeysEditor" Game="Game" />
|
||||
</Body>
|
||||
</Card>
|
||||
</SpaceItem>
|
||||
|
||||
<MudText Typo="Typo.h6">Publishers</MudText>
|
||||
<SpaceItem>
|
||||
<Card Title="Scripts">
|
||||
<Body>
|
||||
<ScriptEditor Game="Game" />
|
||||
</Body>
|
||||
</Card>
|
||||
</SpaceItem>
|
||||
|
||||
<MudChipSet>
|
||||
@foreach (var publisher in Game.Publishers)
|
||||
{
|
||||
<MudChip>@publisher.Name</MudChip>
|
||||
}
|
||||
</MudChipSet>
|
||||
|
||||
<MudDivider Class="mt-4 mb-4" />
|
||||
|
||||
<MudText Typo="Typo.h6">Genres</MudText>
|
||||
|
||||
<MudChipSet>
|
||||
@foreach (var genre in Game.Genres)
|
||||
{
|
||||
<MudChip>@genre.Name</MudChip>
|
||||
}
|
||||
</MudChipSet>
|
||||
|
||||
<MudDivider Class="mt-4 mb-4" />
|
||||
|
||||
<MudText Typo="Typo.h6">Tags</MudText>
|
||||
|
||||
<MudChipSet>
|
||||
@foreach (var tags in Game.Tags)
|
||||
{
|
||||
<MudChip>@tags.Name</MudChip>
|
||||
}
|
||||
</MudChipSet>
|
||||
</MudForm>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<MudCard Class="mt-4">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Actions</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent>
|
||||
<ActionEditor Actions="Game.Actions" ArchiveId="Game.Archives.OrderByDescending(a => a.CreatedOn).First().Id" />
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudCard Class="mt-4">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Multiplayer Modes</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent>
|
||||
<MultiplayerModeEditor Game="Game" />
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudCard Class="mt-4">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Keys</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent>
|
||||
<MudGrid>
|
||||
<MudItem md="4" Class="mud-typography-align-center">
|
||||
<MudText Typo="Typo.overline">Available</MudText>
|
||||
<MudText Typo="Typo.body1">@KeysAvailable</MudText>
|
||||
</MudItem>
|
||||
|
||||
<MudItem md="4" Class="mud-typography-align-center">
|
||||
<MudText Typo="Typo.overline">Claimed</MudText>
|
||||
<MudText Typo="Typo.body1">@(Game.Keys.Count - KeysAvailable)</MudText>
|
||||
</MudItem>
|
||||
|
||||
<MudItem md="4" Class="mud-typography-align-center">
|
||||
<MudText Typo="Typo.overline">Total</MudText>
|
||||
<MudText Typo="Typo.body1">@Game.Keys.Count</MudText>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<MudPaper Class="d-flex justify-end" Elevation="0">
|
||||
<MudButton Href="@($"/Games/{Id}/Keys")" Color="Color.Primary" Variant="Variant.Filled">View</MudButton>
|
||||
</MudPaper>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudCard Class="mt-4">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Archives</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent>
|
||||
<MudTable Items="Game.Archives" Elevation="0">
|
||||
<HeaderContent>
|
||||
<MudTh>Version</MudTh>
|
||||
<MudTh>Uploaded By</MudTh>
|
||||
<MudTh>Uploaded On</MudTh>
|
||||
<MudTh>Size</MudTh>
|
||||
<MudTh></MudTh>
|
||||
</HeaderContent>
|
||||
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Version</MudTd>
|
||||
<MudTd>@context.CreatedBy?.UserName</MudTd>
|
||||
<MudTd>@context.CreatedOn</MudTd>
|
||||
<MudTd>
|
||||
@{
|
||||
long size = 0;
|
||||
var path = Path.Combine("Upload", context.ObjectKey);
|
||||
|
||||
if (File.Exists(path))
|
||||
size = new FileInfo(path).Length;
|
||||
}
|
||||
@ByteSizeLib.ByteSize.FromBytes(size)
|
||||
</MudTd>
|
||||
<MudTd Class="d-flex flex-nowrap justify-end">
|
||||
<MudIconButton OnClick="() => BrowseArchive(context)" Icon="@Icons.Material.Filled.Folder"></MudIconButton>
|
||||
<MudIconButton OnClick="() => DeleteArchive(context)" Color="Color.Error" Icon="@Icons.Material.Filled.Close"></MudIconButton>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
<MudPaper Class="d-flex justify-end" Elevation="0">
|
||||
<MudButton OnClick="() => UploadArchive()" StartIcon="@Icons.Material.Filled.Add" Color="Color.Primary" Variant="Variant.Filled">Add</MudButton>
|
||||
</MudPaper>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudCard Class="mt-4">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">Scripts</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent>
|
||||
<MudTable Items="Game.Scripts" Elevation="0">
|
||||
<HeaderContent>
|
||||
<MudTh>Type</MudTh>
|
||||
<MudTh>Created By</MudTh>
|
||||
<MudTh>Created On</MudTh>
|
||||
<MudTh></MudTh>
|
||||
</HeaderContent>
|
||||
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Type</MudTd>
|
||||
<MudTd>@context.CreatedBy?.UserName</MudTd>
|
||||
<MudTd>@context.CreatedOn</MudTd>
|
||||
<MudTd Class="d-flex flex-nowrap justify-end">
|
||||
<MudIconButton OnClick="() => EditScript(context)" Icon="@Icons.Material.Filled.Edit"></MudIconButton>
|
||||
<MudIconButton OnClick="() => DeleteScript(context)" Color="Color.Error" Icon="@Icons.Material.Filled.Close"></MudIconButton>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
<MudPaper Class="d-flex justify-end" Elevation="0">
|
||||
<MudButton OnClick="() => EditScript()" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled">Add</MudButton>
|
||||
</MudPaper>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudFab Color="Color.Primary" Disabled="@(!Success)" OnClick="Save" StartIcon="@Icons.Material.Filled.Save" Style="position: fixed; right: 32px; bottom: 32px;" />
|
||||
<SpaceItem>
|
||||
<Card Title="Archives">
|
||||
<ArchiveUploader Game="Game" />
|
||||
</Card>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
|
||||
@code {
|
||||
[Parameter] public Guid Id { get; set; }
|
||||
|
||||
bool Success;
|
||||
string[] Errors = { };
|
||||
MudForm Form;
|
||||
|
||||
private Game Game { get; set; }
|
||||
IEnumerable<Company> Companies;
|
||||
IEnumerable<Genre> Genres;
|
||||
IEnumerable<Data.Models.Tag> Tags;
|
||||
|
||||
ArchiveBrowserDialog ArchiveBrowserDialog;
|
||||
|
||||
Modal FileSelectorModal;
|
||||
|
||||
private string value = "blazor";
|
||||
private ConfirmRef _confirmRef;
|
||||
|
||||
private Game Game;
|
||||
private KeysEditor? KeysEditor;
|
||||
private GameMetadataLookup? GameMetadataLookup;
|
||||
|
||||
private int KeysAvailable { get {
|
||||
return Game.Keys.Count(k =>
|
||||
|
@ -219,6 +146,9 @@
|
|||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Game = await GameService.Get(Id);
|
||||
Companies = CompanyService.Get();
|
||||
Genres = GenreService.Get();
|
||||
Tags = TagService.Get();
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
|
@ -227,35 +157,105 @@
|
|||
{
|
||||
Game = await GameService.Update(Game);
|
||||
|
||||
Snackbar.Add("Game updated!", Severity.Success);
|
||||
await MessageService.Success("Game updated!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add("An unknown error occurred!", Severity.Error);
|
||||
await MessageService.Error("Could not save!");
|
||||
}
|
||||
}
|
||||
|
||||
private async void BrowseForIcon()
|
||||
private async Task BrowseForIcon()
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
var modalOptions = new ModalOptions()
|
||||
{
|
||||
["ArchiveId"] = Game.Archives.OrderByDescending(a => a.CreatedOn).First().Id
|
||||
Title = "Choose Icon",
|
||||
Maximizable = false,
|
||||
DefaultMaximized = true,
|
||||
Closable = true,
|
||||
OkText = "Select File"
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<ArchiveFileSelectorDialog>("File Selector", parameters);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled)
|
||||
var browserOptions = new ArchiveBrowserOptions()
|
||||
{
|
||||
Game.Icon = result.Data as string;
|
||||
ArchiveId = Game.Archives.FirstOrDefault().Id,
|
||||
Select = true,
|
||||
Multiple = false
|
||||
};
|
||||
|
||||
var modalRef = await ModalService.CreateModalAsync<ArchiveBrowserDialog, ArchiveBrowserOptions, IEnumerable<ZipArchiveEntry>>(modalOptions, browserOptions);
|
||||
|
||||
modalRef.OnOk = (results) =>
|
||||
{
|
||||
Game.Icon = results.FirstOrDefault().FullName;
|
||||
StateHasChanged();
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
private async Task OnGameLookupResultSelected(GameLookupResult result)
|
||||
{
|
||||
Game.Title = result.IGDBMetadata.Name;
|
||||
Game.Description = result.IGDBMetadata.Summary;
|
||||
Game.ReleasedOn = result.IGDBMetadata.FirstReleaseDate.GetValueOrDefault().UtcDateTime;
|
||||
Game.MultiplayerModes = result.MultiplayerModes.ToList();
|
||||
Game.Developers = new List<Company>();
|
||||
Game.Publishers = new List<Company>();
|
||||
Game.Genres = new List<Genre>();
|
||||
Game.Tags = new List<Data.Models.Tag>();
|
||||
|
||||
if (result.IGDBMetadata.GameModes != null && result.IGDBMetadata.GameModes.Values != null)
|
||||
Game.Singleplayer = result.IGDBMetadata.GameModes.Values.Any(gm => gm.Name == "Singleplayer");
|
||||
|
||||
if (result.IGDBMetadata.InvolvedCompanies != null && result.IGDBMetadata.InvolvedCompanies.Values != null)
|
||||
{
|
||||
// Make sure companie
|
||||
var developers = result.IGDBMetadata.InvolvedCompanies.Values.Where(c => c.Developer.GetValueOrDefault()).Select(c => c.Company.Value.Name);
|
||||
var publishers = result.IGDBMetadata.InvolvedCompanies.Values.Where(c => c.Publisher.GetValueOrDefault()).Select(c => c.Company.Value.Name);
|
||||
|
||||
foreach (var developer in developers)
|
||||
{
|
||||
Game.Developers.Add(await CompanyService.AddMissing(c => c.Name == developer, new Company { Name = developer }));
|
||||
}
|
||||
|
||||
foreach (var publisher in publishers)
|
||||
{
|
||||
Game.Publishers.Add(await CompanyService.AddMissing(c => c.Name == publisher, new Company { Name = publisher }));
|
||||
}
|
||||
}
|
||||
|
||||
if (result.IGDBMetadata.Genres != null && result.IGDBMetadata.Genres.Values != null)
|
||||
{
|
||||
var genres = result.IGDBMetadata.Genres.Values.Select(g => g.Name);
|
||||
|
||||
foreach (var genre in genres)
|
||||
{
|
||||
Game.Genres.Add(await GenreService.AddMissing(g => g.Name == genre, new Genre { Name = genre }));
|
||||
}
|
||||
}
|
||||
|
||||
if (result.IGDBMetadata.Keywords != null && result.IGDBMetadata.Keywords.Values != null)
|
||||
{
|
||||
var tags = result.IGDBMetadata.Keywords.Values.Select(t => t.Name).Take(20);
|
||||
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
Game.Tags.Add(await TagService.AddMissing(t => t.Name == tag, new Data.Models.Tag { Name = tag }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CloseModal()
|
||||
{
|
||||
if (_confirmRef != null)
|
||||
{
|
||||
await _confirmRef.CloseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async void LookupGameMetadata()
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
/*var parameters = new DialogParameters
|
||||
{
|
||||
["GameTitle"] = Game.Title
|
||||
};
|
||||
|
@ -278,12 +278,12 @@
|
|||
Game.Singleplayer = info.Singleplayer;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private async void UploadArchive()
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
/*var parameters = new DialogParameters
|
||||
{
|
||||
["GameId"] = Game.Id
|
||||
};
|
||||
|
@ -294,22 +294,22 @@
|
|||
|
||||
await GameService.Context.Entry(Game).Collection(nameof(Game.Archives)).LoadAsync();
|
||||
|
||||
StateHasChanged();
|
||||
StateHasChanged();*/
|
||||
}
|
||||
|
||||
private async void BrowseArchive(Archive archive)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
/*var parameters = new DialogParameters
|
||||
{
|
||||
["ArchiveId"] = archive.Id
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<ArchiveBrowserDialog>("Archive Browser", parameters);
|
||||
var dialog = await DialogService.ShowAsync<ArchiveBrowserDialog>("Archive Browser", parameters);*/
|
||||
}
|
||||
|
||||
private async void DeleteArchive(Archive archive)
|
||||
{
|
||||
bool? result = await DialogService.ShowMessageBox(
|
||||
/*bool? result = await DialogService.ShowMessageBox(
|
||||
"Delete Archive?",
|
||||
"Do you really want to delete this archive? You will not be able to recover it later.",
|
||||
"Delete",
|
||||
|
@ -319,12 +319,12 @@
|
|||
if (result == true)
|
||||
await ArchiveService.Delete(archive);
|
||||
|
||||
StateHasChanged();
|
||||
StateHasChanged();*/
|
||||
}
|
||||
|
||||
private async void EditScript(Script script = null)
|
||||
{
|
||||
if (script == null)
|
||||
/*if (script == null)
|
||||
script = new Script()
|
||||
{
|
||||
GameId = Game.Id,
|
||||
|
@ -342,12 +342,12 @@
|
|||
|
||||
await GameService.Context.Entry(Game).Collection(nameof(Game.Archives)).LoadAsync();
|
||||
|
||||
StateHasChanged();
|
||||
StateHasChanged();*/
|
||||
}
|
||||
|
||||
private async void DeleteScript(Script script)
|
||||
{
|
||||
bool? result = await DialogService.ShowMessageBox(
|
||||
/*bool? result = await DialogService.ShowMessageBox(
|
||||
"Delete Script",
|
||||
"Do you really want to delete this script? You will not be able to recover it later.",
|
||||
"Delete",
|
||||
|
@ -357,6 +357,6 @@
|
|||
if (result == true)
|
||||
await ScriptService.Delete(script);
|
||||
|
||||
StateHasChanged();
|
||||
StateHasChanged();*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +1,50 @@
|
|||
@page "/Games"
|
||||
@attribute [Authorize]
|
||||
@inject GameService GameService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<MudTable Items="@Games.Where(g => String.IsNullOrEmpty(Search) || g.Title.ToLower().Contains(Search.ToLower().Trim()))" RowsPerPage="25" Hover="true">
|
||||
<ToolBarContent>
|
||||
<MudText Typo="Typo.h6">Games</MudText>
|
||||
<MudSpacer />
|
||||
<MudTextField @bind-Value="Search" Placeholder="Search" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
|
||||
</ToolBarContent>
|
||||
|
||||
<HeaderContent>
|
||||
<MudTh></MudTh>
|
||||
<MudTh>Title</MudTh>
|
||||
<MudTh>Sort Title</MudTh>
|
||||
<MudTh>Released</MudTh>
|
||||
<MudTh>Created</MudTh>
|
||||
<MudTh>Created By</MudTh>
|
||||
<MudTh>Updated</MudTh>
|
||||
<MudTh>Updated By</MudTh>
|
||||
</HeaderContent>
|
||||
|
||||
<RowTemplate>
|
||||
<MudTd>
|
||||
<MudImage Src="@GetIcon(context)" Height="32" Width="32" />
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Title">@context.Title</MudTd>
|
||||
<MudTd DataLabel="Sort Title">@context.SortTitle</MudTd>
|
||||
<MudTd DataLabel="Released">@context.ReleasedOn?.ToString("MM/dd/yyyy")</MudTd>
|
||||
<MudTd DataLabel="Created">@context.CreatedOn</MudTd>
|
||||
<MudTd DataLabel="Created By">@context.CreatedBy?.UserName</MudTd>
|
||||
<MudTd DataLabel="Updated">@context.UpdatedOn</MudTd>
|
||||
<MudTd DataLabel="Updated By">@context.UpdatedBy?.UserName</MudTd>
|
||||
<MudTd Class="d-flex justify-end">
|
||||
<MudButton Href="@($"/Games/{context.Id}/Edit")">Edit</MudButton>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
|
||||
<PagerContent>
|
||||
<MudTablePager />
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
<Table TItem="Game" DataSource="@Games">
|
||||
<Column TData="string">
|
||||
<Image Src="@GetIcon(context)" Height="32" Width="32" Preview="false"></Image>
|
||||
</Column>
|
||||
<PropertyColumn Property="g => g.Title" Sortable />
|
||||
<PropertyColumn Property="g => g.SortTitle" Sortable />
|
||||
<PropertyColumn Property="g => g.ReleasedOn" Format="MM/dd/yyyy" Sortable />
|
||||
<PropertyColumn Property="g => g.CreatedOn" Format="MM/dd/yyyy hh:mm" Sortable />
|
||||
<PropertyColumn Property="g => g.CreatedBy" Sortable>
|
||||
@context.CreatedBy?.UserName
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="g => g.UpdatedOn" Format="MM/dd/yyyy hh:mm" Sortable />
|
||||
<PropertyColumn Property="g => g.UpdatedBy" Sortable>
|
||||
@context.UpdatedBy?.UserName
|
||||
</PropertyColumn>
|
||||
<ActionColumn Title="">
|
||||
<Space>
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => Edit(context)">Edit</Button>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
|
||||
@code {
|
||||
private ICollection<Game> Games { get; set; }
|
||||
IEnumerable<Game> Games { get; set; } = new List<Game>();
|
||||
private string Search { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Games = GameService.Get().OrderBy(g => String.IsNullOrWhiteSpace(g.SortTitle) ? g.Title : g.SortTitle).ToList();
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private string GetIcon(Game game)
|
||||
{
|
||||
return $"/api/Games/{game.Id}/Icon.png";
|
||||
}
|
||||
|
||||
private void Edit(Game game)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/Games/{game.Id}/Edit");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
@page "/Games/{id:guid}/Keys/Edit"
|
||||
@inject GameService GameService
|
||||
@inject KeyService KeyService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudPaper Class="pa-4">
|
||||
<StandaloneCodeEditor @ref="Editor" Id="editor" ConstructionOptions="EditorConstructionOptions" />
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<style>
|
||||
.monaco-editor-container {
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<MudFab Color="Color.Primary" OnClick="Save" StartIcon="@Icons.Material.Filled.Save" Style="position: fixed; right: 32px; bottom: 32px;" />
|
||||
|
||||
@code {
|
||||
[Parameter] public Guid Id { get; set; }
|
||||
|
||||
private Game Game { get; set; }
|
||||
|
||||
private StandaloneCodeEditor Editor;
|
||||
|
||||
private StandaloneEditorConstructionOptions EditorConstructionOptions(StandaloneCodeEditor editor)
|
||||
{
|
||||
return new StandaloneEditorConstructionOptions
|
||||
{
|
||||
AutomaticLayout = true,
|
||||
Language = "text",
|
||||
Value = String.Join('\n', Game.Keys.Select(k => k.Value)),
|
||||
Theme = "vs-dark",
|
||||
};
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Game = await GameService.Get(Id);
|
||||
}
|
||||
|
||||
private async Task Save()
|
||||
{
|
||||
var value = await Editor.GetValue();
|
||||
var keys = value.Split("\n").Select(k => k.Trim()).Where(k => !String.IsNullOrWhiteSpace(k));
|
||||
|
||||
var keysDeleted = Game.Keys.Where(k => !keys.Contains(k.Value));
|
||||
var keysAdded = keys.Where(k => !Game.Keys.Any(gk => gk.Value == k));
|
||||
|
||||
foreach (var key in keysDeleted)
|
||||
KeyService.Delete(key);
|
||||
|
||||
foreach (var key in keysAdded)
|
||||
await KeyService.Add(new Key()
|
||||
{
|
||||
Game = Game,
|
||||
Value = key
|
||||
});
|
||||
|
||||
NavigationManager.NavigateTo($"/Games/{Game.Id}/Keys");
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
@page "/Games/{id:guid}/Keys"
|
||||
@inject GameService GameService
|
||||
@inject KeyService KeyService
|
||||
|
||||
<MudTable Items="@Keys" Hover="true" Elevation="0">
|
||||
<ToolBarContent>
|
||||
<MudText Typo="Typo.h6">Keys</MudText>
|
||||
<MudSpacer />
|
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" Href="@($"/Games/{Id}/Keys/Edit")">Edit</MudButton>
|
||||
</ToolBarContent>
|
||||
|
||||
<HeaderContent>
|
||||
<MudTh>Key</MudTh>
|
||||
<MudTh>Allocation Method</MudTh>
|
||||
<MudTh>Claimed By</MudTh>
|
||||
<MudTh>Claimed On</MudTh>
|
||||
<MudTh></MudTh>
|
||||
</HeaderContent>
|
||||
|
||||
<RowTemplate>
|
||||
<MudTd>@context.Value</MudTd>
|
||||
<MudTd>@context.AllocationMethod</MudTd>
|
||||
<MudTd>
|
||||
@switch (context.AllocationMethod)
|
||||
{
|
||||
case KeyAllocationMethod.MacAddress:
|
||||
<text>@context.ClaimedByMacAddress</text>
|
||||
break;
|
||||
|
||||
case KeyAllocationMethod.UserAccount:
|
||||
<text>@context.ClaimedByUser?.UserName</text>
|
||||
break;
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd>@context.ClaimedOn</MudTd>
|
||||
<MudTd>
|
||||
@if ((context.AllocationMethod == KeyAllocationMethod.MacAddress && !String.IsNullOrWhiteSpace(context.ClaimedByMacAddress)) || (context.AllocationMethod == KeyAllocationMethod.UserAccount && context.ClaimedByUser != null))
|
||||
{
|
||||
<MudButton OnClick="() => Release(context)">Release</MudButton>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@code {
|
||||
[Parameter] public Guid Id { get; set; }
|
||||
|
||||
private ICollection<Key> Keys { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var game = await GameService.Get(Id);
|
||||
|
||||
Keys = game.Keys;
|
||||
}
|
||||
|
||||
private async Task Release(Key key)
|
||||
{
|
||||
key = await KeyService.Release(key);
|
||||
}
|
||||
}
|
|
@ -2,66 +2,44 @@
|
|||
@using LANCommander.Models;
|
||||
@layout SettingsLayout
|
||||
@inject SettingService SettingService
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IMessageService MessageService
|
||||
|
||||
<MudCard Elevation="0">
|
||||
<MudCardHeader>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.h6">General</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
|
||||
<MudCardContent>
|
||||
<MudText Typo="Typo.subtitle1">IGDB Credentials</MudText>
|
||||
<MudForm Model="@Settings" @ref="Form">
|
||||
<MudTextField @bind-Value="Settings.IGDBClientId" For="@(() => Settings.IGDBClientId)" Immediate="true" Label="Client ID" />
|
||||
<MudTextField @bind-Value="Settings.IGDBClientSecret"
|
||||
For="@(() => Settings.IGDBClientSecret)"
|
||||
Immediate="true"
|
||||
Label="Client Secret"
|
||||
InputType="@IGDBClientSecretInputType"
|
||||
Adornment="Adornment.End"
|
||||
AdornmentIcon="@IGDBClientSecretInputIcon"
|
||||
OnAdornmentClick="() => ToggleClientSecretInput()" />
|
||||
<MudText Typo="Typo.caption">In order to use IGDB metadata, you need a Twitch developer account. <MudLink Href="https://api-docs.igdb.com/#account-creation" Typo="Typo.caption" Target="_blank">Click here</MudLink> for more details.</MudText>
|
||||
</MudForm>
|
||||
</MudCardContent>
|
||||
|
||||
<MudCardActions>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Save" OnClick="Save">Save</MudButton>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
<Card Title="General">
|
||||
<Body>
|
||||
<Text>IGDB Credentials</Text>
|
||||
<Text>In order to use IGDB metadata, you need a Twitch developer account. <a href="https://api-docs.igdb.com/#account-creation" target="_blank">Click here</a> for more details.</Text>
|
||||
<Form Model="Settings">
|
||||
<FormItem Label="Client ID">
|
||||
<Input @bind-Value="context.IGDBClientId" />
|
||||
</FormItem>
|
||||
<FormItem Label="Client Secret">
|
||||
<InputPassword @bind-Value="context.IGDBClientSecret" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button OnClick="Save" Icon="@IconType.Fill.Save">Save</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Body>
|
||||
</Card>
|
||||
|
||||
@code {
|
||||
private MudForm Form;
|
||||
private LANCommanderSettings Settings;
|
||||
|
||||
private bool ShowIGDBClientSecret = false;
|
||||
private InputType IGDBClientSecretInputType = InputType.Password;
|
||||
private string IGDBClientSecretInputIcon = Icons.Material.Filled.Visibility;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Settings = SettingService.GetSettings();
|
||||
}
|
||||
|
||||
private void ToggleClientSecretInput()
|
||||
{
|
||||
ShowIGDBClientSecret = !ShowIGDBClientSecret;
|
||||
IGDBClientSecretInputIcon = ShowIGDBClientSecret ? Icons.Material.Filled.VisibilityOff : Icons.Material.Filled.Visibility;
|
||||
IGDBClientSecretInputType = ShowIGDBClientSecret ? InputType.Text : InputType.Password;
|
||||
}
|
||||
|
||||
private void Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
SettingService.SaveSettings(Settings);
|
||||
Snackbar.Add("Settings saved!", Severity.Success);
|
||||
MessageService.Success("Settings saved!");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Snackbar.Add("An unknown error occurred", Severity.Error);
|
||||
MessageService.Error("An unknown error occurred.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
@inherits LayoutComponentBase
|
||||
@layout MainLayout
|
||||
|
||||
<MudPaper>
|
||||
<MudGrid Style="width: 100%">
|
||||
<MudItem xs="12" sm="4" md="3" lg="2">
|
||||
<MudNavMenu Bordered="true">
|
||||
<MudText Typo="Typo.h6" Class="px-4">Settings</MudText>
|
||||
<MudDivider Class="my-2" />
|
||||
<MudNavLink Href="/SettingsNew/General" Match="NavLinkMatch.Prefix">General</MudNavLink>
|
||||
<MudNavLink Href="/SettingsNew/Users" Match="NavLinkMatch.Prefix">Users</MudNavLink>
|
||||
</MudNavMenu>
|
||||
</MudItem>
|
||||
<GridRow>
|
||||
<GridCol Span="6">
|
||||
<Menu Mode=@MenuMode.Vertical>
|
||||
<MenuItem RouterLink="/Settings/General">General</MenuItem>
|
||||
<MenuItem RouterLink="/Settings/Users">Users</MenuItem>
|
||||
</Menu>
|
||||
</GridCol>
|
||||
|
||||
<MudItem xs="12" sm="8" md="9" lg="10">
|
||||
@Body
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</MudPaper>
|
||||
<GridCol Span="18">
|
||||
@Body
|
||||
</GridCol>
|
||||
</GridRow>
|
|
@ -3,38 +3,35 @@
|
|||
@layout SettingsLayout
|
||||
@inject UserManager<User> UserManager
|
||||
@inject RoleManager<Role> RoleManager
|
||||
@inject ISnackbar Snackbar
|
||||
@inject IMessageService MessageService
|
||||
|
||||
<MudTable Items="@UserList.Where(u => String.IsNullOrEmpty(Search) || u.UserName.ToLower().Contains(Search.ToLower().Trim()))" RowsPerPage="25" Hover="true" Elevation="0">
|
||||
<ToolBarContent>
|
||||
<MudText Typo="Typo.h6">Users</MudText>
|
||||
<MudSpacer />
|
||||
<MudTextField @bind-Value="Search" Placeholder="Search" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
|
||||
</ToolBarContent>
|
||||
|
||||
<HeaderContent>
|
||||
<MudTh>Username</MudTh>
|
||||
<MudTh>Roles</MudTh>
|
||||
<MudTh>Saves</MudTh>
|
||||
<MudTh></MudTh>
|
||||
</HeaderContent>
|
||||
|
||||
<RowTemplate>
|
||||
<MudTd>@context.UserName</MudTd>
|
||||
<MudTd>@String.Join(", ", context.Roles)</MudTd>
|
||||
<MudTd>@ByteSizeLib.ByteSize.FromBytes(context.SavesSize)</MudTd>
|
||||
<MudTd>
|
||||
@if (!context.Roles.Any(r => r == "Administrator"))
|
||||
{
|
||||
<MudButton OnClick="() => PromoteUser(context)">Promote</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton OnClick="() => DemoteUser(context)">Demote</MudButton>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
<Card Title="Users">
|
||||
<Body>
|
||||
<Table TItem="UserViewModel" DataSource="@UserList">
|
||||
<PropertyColumn Property="u => u.UserName" />
|
||||
<PropertyColumn Property="u => u.Roles">
|
||||
@String.Join(", ", context.Roles)
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="u => u.SavesSize">
|
||||
@ByteSizeLib.ByteSize.FromBytes(context.SavesSize)
|
||||
</PropertyColumn>
|
||||
<ActionColumn>
|
||||
<Space>
|
||||
<SpaceItem>
|
||||
@if (!context.Roles.Any(r => r == "Administrator"))
|
||||
{
|
||||
<Button OnClick="() => PromoteUser(context)">Promote</Button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Button OnClick="() => DemoteUser(context)">Demote</Button>
|
||||
}
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
</Body>
|
||||
</Card>
|
||||
|
||||
@code {
|
||||
private ICollection<UserViewModel> UserList { get; set; }
|
||||
|
@ -73,14 +70,14 @@
|
|||
await UserManager.AddToRoleAsync(UserManager.Users.First(u => u.UserName == user.UserName), "Administrator");
|
||||
await RefreshUserList();
|
||||
|
||||
Snackbar.Add($"Promoted {user.UserName}!", Severity.Success);
|
||||
await MessageService.Success($"Promoted {user.UserName}!");
|
||||
}
|
||||
|
||||
private async Task DemoteUser(UserViewModel user)
|
||||
{
|
||||
if (UserList.SelectMany(u => u.Roles).Count(r => r == "Administrator") == 1)
|
||||
{
|
||||
Snackbar.Add("Cannot demote the only administrator!", Severity.Error);
|
||||
await MessageService.Error("Cannot demote the only administrator!");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<base href="~/" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
|
||||
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
|
||||
<link href="_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet" />
|
||||
<link href="~/css/site.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -21,6 +22,7 @@
|
|||
</div>
|
||||
|
||||
<script src="~/_content/MudBlazor/MudBlazor.min.js"></script>
|
||||
<script src="~/_content/AntDesign/js/ant-design-blazor.js"></script>
|
||||
<script src="~/_framework/blazor.server.js"></script>
|
||||
<script src="~/_content/BlazorMonaco/jsInterop.js"></script>
|
||||
<script src="~/_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
|
||||
|
|
|
@ -81,6 +81,8 @@ builder.Services.AddControllers().AddJsonOptions(x =>
|
|||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
builder.Services.AddAntDesign();
|
||||
|
||||
builder.Services.AddMudServices(config =>
|
||||
{
|
||||
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomLeft;
|
||||
|
|
|
@ -1,59 +1,17 @@
|
|||
@inherits LayoutComponentBase
|
||||
|
||||
<MudThemeProvider Theme="DarkTheme" />
|
||||
<MudDialogProvider />
|
||||
<MudSnackbarProvider />
|
||||
<Layout Class="layout">
|
||||
<Header>
|
||||
<div class="logo" />
|
||||
|
||||
<MudLayout Class="mb-16">
|
||||
<MudAppBar>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" />
|
||||
LANCommander
|
||||
</MudAppBar>
|
||||
<Menu Theme="MenuTheme.Dark" Mode="MenuMode.Horizontal">
|
||||
<MenuItem Key="1">Dashboard</MenuItem>
|
||||
<MenuItem Key="2">Games</MenuItem>
|
||||
<MenuItem Key="3">Settings</MenuItem>
|
||||
</Menu>
|
||||
</Header>
|
||||
|
||||
<MudDrawer @bind-Open="@_drawerOpen">
|
||||
<NavMenu />
|
||||
</MudDrawer>
|
||||
|
||||
<MudMainContent>
|
||||
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-8 mb-16">
|
||||
@Body
|
||||
</MudContainer>
|
||||
</MudMainContent>
|
||||
</MudLayout>
|
||||
|
||||
@code {
|
||||
bool _drawerOpen = true;
|
||||
|
||||
private static readonly MudTheme DarkTheme = new MudTheme()
|
||||
{
|
||||
Palette = new Palette()
|
||||
{
|
||||
Black = "#27272f",
|
||||
Background = "#32333d",
|
||||
BackgroundGrey = "#27272f",
|
||||
Surface = "#373740",
|
||||
DrawerBackground = "#27272f",
|
||||
DrawerText = "rgba(255,255,255, 0.50)",
|
||||
DrawerIcon = "rgba(255,255,255, 0.50)",
|
||||
AppbarBackground = "#27272f",
|
||||
AppbarText = "rgba(255,255,255, 0.70)",
|
||||
TextPrimary = "rgba(255,255,255, 0.70)",
|
||||
TextSecondary = "rgba(255,255,255, 0.50)",
|
||||
ActionDefault = "#adadb1",
|
||||
ActionDisabled = "rgba(255,255,255, 0.26)",
|
||||
ActionDisabledBackground = "rgba(255,255,255, 0.12)",
|
||||
Divider = "rgba(255,255,255, 0.12)",
|
||||
DividerLight = "rgba(255,255,255, 0.06)",
|
||||
TableLines = "rgba(255,255,255, 0.12)",
|
||||
LinesDefault = "rgba(255,255,255, 0.12)",
|
||||
LinesInputs = "rgba(255,255,255, 0.3)",
|
||||
TextDisabled = "rgba(255,255,255, 0.2)",
|
||||
TableStriped = "#3f3f45"
|
||||
}
|
||||
};
|
||||
|
||||
void DrawerToggle()
|
||||
{
|
||||
_drawerOpen = !_drawerOpen;
|
||||
}
|
||||
}
|
||||
<Content Style="padding: 24px;">
|
||||
@Body
|
||||
</Content>
|
||||
</Layout>
|
|
@ -1,10 +1,12 @@
|
|||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using Microsoft.JSInterop
|
||||
@using MudBlazor
|
||||
@using AntDesign
|
||||
@using BlazorMonaco
|
||||
@using BlazorMonaco.Editor
|
||||
@using LANCommander.Components
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
"target": "es6"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"wwwroot/lib"
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
.mud-table-cell .mud-input-control > .mud-input-control-input-container > div.mud-input.mud-input-text,
|
||||
.mud-table-cell .mud-input-control {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mud-dialog-fullscreen .mud-dialog-title + div > div,
|
||||
.mud-dialog-fullscreen .mud-dialog-content {
|
||||
max-height: calc(100vh - 64px);
|
||||
}
|
Loading…
Add table
Reference in a new issue