Add database models and migrations for redistributables
parent
80bd7dc66c
commit
a00d0b3b42
|
@ -37,13 +37,13 @@ namespace LANCommander.Data
|
|||
builder.Entity<Game>()
|
||||
.HasMany(g => g.Archives)
|
||||
.WithOne(g => g.Game)
|
||||
.IsRequired(true)
|
||||
.IsRequired(false)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<Game>()
|
||||
.HasMany(g => g.Scripts)
|
||||
.WithOne(s => s.Game)
|
||||
.IsRequired(true)
|
||||
.IsRequired(false)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<Game>()
|
||||
|
@ -104,6 +104,18 @@ namespace LANCommander.Data
|
|||
.WithOne(sl => sl.Server)
|
||||
.IsRequired(true)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<Redistributable>()
|
||||
.HasMany(r => r.Archives)
|
||||
.WithOne(a => a.Redistributable)
|
||||
.IsRequired(false)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.Entity<Redistributable>()
|
||||
.HasMany(r => r.Scripts)
|
||||
.WithOne(s => s.Redistributable)
|
||||
.IsRequired(false)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
|
||||
public DbSet<Game>? Games { get; set; }
|
||||
|
@ -123,5 +135,7 @@ namespace LANCommander.Data
|
|||
public DbSet<Server>? Servers { get; set; }
|
||||
|
||||
public DbSet<ServerConsole>? ServerConsoles { get; set; }
|
||||
|
||||
public DbSet<Redistributable>? Redistributables { get; set; }
|
||||
}
|
||||
}
|
|
@ -13,12 +13,18 @@ namespace LANCommander.Data.Models
|
|||
[Required]
|
||||
public string Version { get; set; }
|
||||
|
||||
public Guid GameId { get; set; }
|
||||
public Guid? GameId { get; set; }
|
||||
[JsonIgnore]
|
||||
[ForeignKey(nameof(GameId))]
|
||||
[InverseProperty("Archives")]
|
||||
public virtual Game? Game { get; set; }
|
||||
|
||||
public Guid? RedistributableId { get; set; }
|
||||
[JsonIgnore]
|
||||
[ForeignKey(nameof(RedistributableId))]
|
||||
[InverseProperty("Archives")]
|
||||
public virtual Redistributable? Redistributable { get; set; }
|
||||
|
||||
[Display(Name = "Last Version")]
|
||||
public virtual Archive? LastVersion { get; set; }
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace LANCommander.Data.Models
|
||||
{
|
||||
[Table("Redistributables")]
|
||||
public class Redistributable : BaseModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
|
||||
public virtual ICollection<Archive>? Archives { get; set; }
|
||||
public virtual ICollection<Script>? Scripts { get; set; }
|
||||
}
|
||||
}
|
|
@ -18,5 +18,11 @@ namespace LANCommander.Data.Models
|
|||
[ForeignKey(nameof(GameId))]
|
||||
[InverseProperty("Scripts")]
|
||||
public virtual Game? Game { get; set; }
|
||||
|
||||
public Guid? RedistributableId { get; set; }
|
||||
[JsonIgnore]
|
||||
[ForeignKey(nameof(RedistributableId))]
|
||||
[InverseProperty("Scripts")]
|
||||
public virtual Redistributable? Redistributable { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LANCommander.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddRedistributables : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "GameId",
|
||||
table: "Scripts",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "RedistributableId",
|
||||
table: "Scripts",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "GameId",
|
||||
table: "Archive",
|
||||
type: "TEXT",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT");
|
||||
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "RedistributableId",
|
||||
table: "Archive",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Redistributables",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Description = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Notes = table.Column<string>(type: "TEXT", nullable: true),
|
||||
CreatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
CreatedById = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
UpdatedById = table.Column<Guid>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Redistributables", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Redistributables_AspNetUsers_CreatedById",
|
||||
column: x => x.CreatedById,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_Redistributables_AspNetUsers_UpdatedById",
|
||||
column: x => x.UpdatedById,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Scripts_RedistributableId",
|
||||
table: "Scripts",
|
||||
column: "RedistributableId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Archive_RedistributableId",
|
||||
table: "Archive",
|
||||
column: "RedistributableId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Redistributables_CreatedById",
|
||||
table: "Redistributables",
|
||||
column: "CreatedById");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Redistributables_UpdatedById",
|
||||
table: "Redistributables",
|
||||
column: "UpdatedById");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Archive_Redistributables_RedistributableId",
|
||||
table: "Archive",
|
||||
column: "RedistributableId",
|
||||
principalTable: "Redistributables",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Scripts_Redistributables_RedistributableId",
|
||||
table: "Scripts",
|
||||
column: "RedistributableId",
|
||||
principalTable: "Redistributables",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Archive_Redistributables_RedistributableId",
|
||||
table: "Archive");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Scripts_Redistributables_RedistributableId",
|
||||
table: "Scripts");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Redistributables");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Scripts_RedistributableId",
|
||||
table: "Scripts");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Archive_RedistributableId",
|
||||
table: "Archive");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RedistributableId",
|
||||
table: "Scripts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RedistributableId",
|
||||
table: "Archive");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "GameId",
|
||||
table: "Scripts",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "GameId",
|
||||
table: "Archive",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "TEXT",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -165,7 +165,7 @@ namespace LANCommander.Migrations
|
|||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("GameId")
|
||||
b.Property<Guid?>("GameId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("LastVersionId")
|
||||
|
@ -175,6 +175,9 @@ namespace LANCommander.Migrations
|
|||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("RedistributableId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("UncompressedSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
@ -196,6 +199,8 @@ namespace LANCommander.Migrations
|
|||
|
||||
b.HasIndex("LastVersionId");
|
||||
|
||||
b.HasIndex("RedistributableId");
|
||||
|
||||
b.HasIndex("UpdatedById");
|
||||
|
||||
b.ToTable("Archive");
|
||||
|
@ -505,6 +510,43 @@ namespace LANCommander.Migrations
|
|||
b.ToTable("MultiplayerModes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("CreatedById")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("UpdatedById")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UpdatedOn")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("UpdatedById");
|
||||
|
||||
b.ToTable("Redistributables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Role", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
@ -591,13 +633,15 @@ namespace LANCommander.Migrations
|
|||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("GameId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid?>("RedistributableId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("RequiresAdmin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
@ -616,6 +660,8 @@ namespace LANCommander.Migrations
|
|||
|
||||
b.HasIndex("GameId");
|
||||
|
||||
b.HasIndex("RedistributableId");
|
||||
|
||||
b.HasIndex("UpdatedById");
|
||||
|
||||
b.ToTable("Scripts");
|
||||
|
@ -1074,13 +1120,17 @@ namespace LANCommander.Migrations
|
|||
b.HasOne("LANCommander.Data.Models.Game", "Game")
|
||||
.WithMany("Archives")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.Archive", "LastVersion")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastVersionId");
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.Redistributable", "Redistributable")
|
||||
.WithMany("Archives")
|
||||
.HasForeignKey("RedistributableId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("UpdatedById");
|
||||
|
@ -1091,6 +1141,8 @@ namespace LANCommander.Migrations
|
|||
|
||||
b.Navigation("LastVersion");
|
||||
|
||||
b.Navigation("Redistributable");
|
||||
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
|
@ -1243,6 +1295,21 @@ namespace LANCommander.Migrations
|
|||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("CreatedById");
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("UpdatedById");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.SavePath", b =>
|
||||
{
|
||||
b.HasOne("LANCommander.Data.Models.User", "CreatedBy")
|
||||
|
@ -1273,8 +1340,12 @@ namespace LANCommander.Migrations
|
|||
b.HasOne("LANCommander.Data.Models.Game", "Game")
|
||||
.WithMany("Scripts")
|
||||
.HasForeignKey("GameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.Redistributable", "Redistributable")
|
||||
.WithMany("Scripts")
|
||||
.HasForeignKey("RedistributableId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("LANCommander.Data.Models.User", "UpdatedBy")
|
||||
.WithMany()
|
||||
|
@ -1284,6 +1355,8 @@ namespace LANCommander.Migrations
|
|||
|
||||
b.Navigation("Game");
|
||||
|
||||
b.Navigation("Redistributable");
|
||||
|
||||
b.Navigation("UpdatedBy");
|
||||
});
|
||||
|
||||
|
@ -1426,6 +1499,13 @@ namespace LANCommander.Migrations
|
|||
b.Navigation("Servers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
|
||||
{
|
||||
b.Navigation("Archives");
|
||||
|
||||
b.Navigation("Scripts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LANCommander.Data.Models.Server", b =>
|
||||
{
|
||||
b.Navigation("ServerConsoles");
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
@page "/Redistributables"
|
||||
@using Microsoft.EntityFrameworkCore;
|
||||
@attribute [Authorize]
|
||||
@inject RedistributableService RedistributableService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IMessageService MessageService
|
||||
|
||||
<PageHeader Title="Redistributables">
|
||||
<PageHeaderExtra>
|
||||
<Space Direction="DirectionVHType.Horizontal">
|
||||
<SpaceItem>
|
||||
<Search Placeholder="Search" @bind-Value="Search" BindOnInput DebounceMilliseconds="150" OnChange="() => LoadData()" />
|
||||
</SpaceItem>
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => Add()" Type="@ButtonType.Primary">Add Redistributable</Button>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</PageHeaderExtra>
|
||||
</PageHeader>
|
||||
|
||||
<TableColumnPicker @ref="Picker" Key="Redistributables" @bind-Visible="ColumnPickerVisible" />
|
||||
|
||||
<Table TItem="Redistributable" DataSource="@Redistributables" Loading="@Loading" PageSize="25" Responsive>
|
||||
<PropertyColumn Property="r => r.Name" Sortable Hidden="@(Picker.IsColumnHidden("Name"))" />
|
||||
<PropertyColumn Property="s => s.CreatedOn" Format="MM/dd/yyyy hh:mm tt" Sortable Hidden="@(Picker.IsColumnHidden("Created On"))" />
|
||||
<PropertyColumn Property="s => s.CreatedBy" Sortable Hidden="@(Picker.IsColumnHidden("Created By"))">
|
||||
@context.CreatedBy?.UserName
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="g => g.UpdatedOn" Format="MM/dd/yyyy hh:mm tt" Sortable Hidden="@(Picker.IsColumnHidden("Updated On"))" />
|
||||
<PropertyColumn Property="g => g.UpdatedBy" Sortable Hidden="@(Picker.IsColumnHidden("Updated By"))">
|
||||
@context.UpdatedBy?.UserName
|
||||
</PropertyColumn>
|
||||
<ActionColumn Title="" Style="text-align: right; white-space: nowrap">
|
||||
<TitleTemplate>
|
||||
<div style="text-align: right">
|
||||
<Button Icon="@IconType.Outline.Edit" Type="@ButtonType.Text" OnClick="() => OpenColumnPicker()" />
|
||||
</div>
|
||||
</TitleTemplate>
|
||||
<ChildContent>
|
||||
<Space Direction="DirectionVHType.Horizontal">
|
||||
<SpaceItem>
|
||||
<Button OnClick="() => Edit(context)">Edit</Button>
|
||||
</SpaceItem>
|
||||
<SpaceItem>
|
||||
<Popconfirm OnConfirm="() => Delete(context)" Title="Are you sure you want to delete this redistributable?">
|
||||
<Button Icon="@IconType.Outline.Close" Type="@ButtonType.Text" Danger />
|
||||
</Popconfirm>
|
||||
</SpaceItem>
|
||||
</Space>
|
||||
</ChildContent>
|
||||
</ActionColumn>
|
||||
</Table>
|
||||
|
||||
@code {
|
||||
IEnumerable<Redistributable> Redistributables { get; set; } = new List<Redistributable>();
|
||||
|
||||
bool Loading = true;
|
||||
|
||||
string Search = "";
|
||||
|
||||
TableColumnPicker Picker;
|
||||
bool ColumnPickerVisible = false;
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
LoadData();
|
||||
|
||||
Loading = false;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadData()
|
||||
{
|
||||
var fuzzySearch = Search.ToLower().Trim();
|
||||
|
||||
Redistributables = await RedistributableService.Get(r => r.Name.ToLower().Contains(fuzzySearch)).OrderBy(r => r.Name).ToListAsync();
|
||||
}
|
||||
|
||||
private void Add()
|
||||
{
|
||||
NavigationManager.NavigateTo("/Redistributables/Add");
|
||||
}
|
||||
|
||||
private void Edit(Redistributable redistributable)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/Redistributables/{redistributable.Id}/General");
|
||||
}
|
||||
|
||||
private async Task Delete(Redistributable redistributable)
|
||||
{
|
||||
Redistributables = new List<Redistributable>();
|
||||
|
||||
Loading = true;
|
||||
|
||||
await RedistributableService.Delete(redistributable);
|
||||
|
||||
Redistributables = await RedistributableService.Get(x => true).OrderBy(r => r.Name).ToListAsync();
|
||||
|
||||
Loading = false;
|
||||
}
|
||||
|
||||
private async Task OpenColumnPicker()
|
||||
{
|
||||
ColumnPickerVisible = true;
|
||||
}
|
||||
|
||||
private async Task CloseColumnPicker()
|
||||
{
|
||||
ColumnPickerVisible = false;
|
||||
}
|
||||
}
|
|
@ -141,6 +141,7 @@ namespace LANCommander
|
|||
builder.Services.AddScoped<ServerService>();
|
||||
builder.Services.AddScoped<ServerConsoleService>();
|
||||
builder.Services.AddScoped<GameSaveService>();
|
||||
builder.Services.AddScoped<RedistributableService>();
|
||||
|
||||
builder.Services.AddSingleton<ServerProcessService>();
|
||||
builder.Services.AddSingleton<IPXRelayService>();
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
using LANCommander.Data;
|
||||
using LANCommander.Data.Models;
|
||||
|
||||
namespace LANCommander.Services
|
||||
{
|
||||
public class RedistributableService : BaseDatabaseService<Redistributable>
|
||||
{
|
||||
public RedistributableService(DatabaseContext dbContext, IHttpContextAccessor httpContextAccessor) : base(dbContext, httpContextAccessor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
@if (User != null && User.IsInRole("Administrator"))
|
||||
{
|
||||
<MenuItem RouterLink="/Games">Games</MenuItem>
|
||||
<MenuItem RouterLink="/Redistributables">Redistributables</MenuItem>
|
||||
<MenuItem RouterLink="/Servers">Servers</MenuItem>
|
||||
<MenuItem RouterLink="/Files">Files</MenuItem>
|
||||
<MenuItem RouterLink="/Settings">Settings</MenuItem>
|
||||
|
|
Loading…
Reference in New Issue