FolderBox now delegates deletion to parent via OnDeleteRequested callback instead of deleting directly. Settings.razor shows a ConfirmDialog before removing the folder.
200 lines
7.7 KiB
Plaintext
200 lines
7.7 KiB
Plaintext
@page "/Settings"
|
|
@using Butter.Dtos.Folder
|
|
@using Butter.Dtos.Settings
|
|
@using Butter.Settings
|
|
@using Butter.Types
|
|
@using MilkStream.Client.Components.SettingBoxes
|
|
@using System.Linq
|
|
|
|
@inject NavigationManager NavigationManager
|
|
@inject LoginService LoginService
|
|
@inject SettingsService SettingsService
|
|
@inject FoldersService FoldersService
|
|
|
|
<PageTitle>Settings</PageTitle>
|
|
|
|
<div class="settings-layout">
|
|
<nav class="settings-sidebar">
|
|
@foreach (var group in _groups) {
|
|
<button class="settings-nav-item @(_activeGroup == group.Key ? "active" : "")"
|
|
@onclick="() => SelectGroup(group.Key)">
|
|
<i class="bi @group.Value.Icon"></i>
|
|
<span>@group.Key</span>
|
|
</button>
|
|
}
|
|
</nav>
|
|
|
|
<main class="settings-content">
|
|
@if (_isLoading) {
|
|
<div class="d-flex justify-content-center align-items-center h-100">
|
|
<LoadSpinner/>
|
|
</div>
|
|
} else if (_activeGroup == "Folders") {
|
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
|
<h4 class="m-0"><i class="bi bi-folder2-open"></i> Folders</h4>
|
|
<button class="btn btn-primary btn-sm" @onclick="OnAddFolder">
|
|
<i class="bi bi-folder-plus"></i> Add folder
|
|
</button>
|
|
</div>
|
|
@if (_folders.Any()) {
|
|
@foreach (var folder in _folders) {
|
|
<FolderBox Folder="folder" OnDeleteRequested="RequestFolderDelete"/>
|
|
}
|
|
} else {
|
|
<div class="alert alert-secondary">No folders configured yet.</div>
|
|
}
|
|
} else {
|
|
<h4 class="mb-3"><i class="bi @_groups[_activeGroup].Icon"></i> @_activeGroup</h4>
|
|
@if (_groupedSettings.TryGetValue(_activeGroup, out var settings) && settings.Count > 0) {
|
|
<div class="settings-grid">
|
|
@{ settings.Sort((dto, other) => string.Compare(dto.Name, other.Name, StringComparison.OrdinalIgnoreCase)); }
|
|
@foreach (var setting in settings) {
|
|
@switch (setting.DisplayType) {
|
|
case DisplayType.Switch:
|
|
<SettingSwitch Setting="setting"/>
|
|
|
|
break;
|
|
case DisplayType.IntNumber:
|
|
case DisplayType.FloatNumber:
|
|
<SettingNumber Setting="setting"/>
|
|
|
|
break;
|
|
case DisplayType.Text:
|
|
case DisplayType.Password:
|
|
<SettingString Setting="setting"/>
|
|
|
|
break;
|
|
case DisplayType.TimePicker:
|
|
<SettingRange Setting="setting"
|
|
MinValue="Convert.ToInt32(setting.Options?[0])"
|
|
MaxValue="Convert.ToInt32(setting.Options?[1])"
|
|
Step="Convert.ToInt32(setting.Options?[2])"/>
|
|
|
|
break;
|
|
case DisplayType.Po2W:
|
|
<SettingRange Setting="setting" IsPowerOfTwo="true"
|
|
MinPowerValue="Convert.ToInt32(setting.Options?[0])"
|
|
MaxPowerValue="Convert.ToInt32(setting.Options?[1])"
|
|
Step="Convert.ToInt32(setting.Options?[2])"/>
|
|
|
|
break;
|
|
case DisplayType.UserSelect:
|
|
<SettingUserSelect Setting="setting"/>
|
|
|
|
break;
|
|
default:
|
|
<div class="alert alert-warning py-1 small">Unsupported: @setting.DisplayType</div>
|
|
break;
|
|
}
|
|
}
|
|
</div>
|
|
} else {
|
|
<div class="alert alert-secondary">No settings in this category.</div>
|
|
}
|
|
}
|
|
</main>
|
|
</div>
|
|
|
|
<ConfirmDialog Show="_showDeleteConfirm"
|
|
Title="Confirm Deletion"
|
|
Message="@_deleteConfirmMessage"
|
|
ConfirmText="Delete"
|
|
ConfirmButtonClass="btn-danger"
|
|
OnConfirm="ExecuteFolderDelete"
|
|
OnCancel="() => _showDeleteConfirm = false" />
|
|
|
|
@code {
|
|
private record GroupInfo(string Icon, string[] SettingNames);
|
|
|
|
private static readonly Dictionary<string, GroupInfo> _groups = new() {
|
|
["General"] = new("bi-gear-wide-connected", ["User Registration Enabled"]),
|
|
["Scanning"] = new("bi-arrow-repeat", ["Folder Scan Enabled", "Folder Scan Interval", "System Uploader Id"]),
|
|
["Upload"] = new("bi-upload", ["File Upload Enabled", "File Upload MaxSize"]),
|
|
["Paths"] = new("bi-folder2", ["Thumbnail Path", "Preview Path"]),
|
|
["Jobs"] = new("bi-kanban-fill", ["Thumbnail Size", "Preview Size", "Thumbnail Quality", "Preview Quality", "Max Concurrent Jobs", "Job Batch Size", "Job Retention Days"]),
|
|
["Folders"] = new("bi-folder2-open", [])
|
|
};
|
|
|
|
private Dictionary<string, List<SettingDto>> _groupedSettings = new();
|
|
private List<FolderFullDto> _folders = new();
|
|
private string _activeGroup = "General";
|
|
private bool _isLoading = true;
|
|
bool _showDeleteConfirm;
|
|
string _deleteConfirmMessage = "";
|
|
Guid _pendingDeleteFolderId;
|
|
|
|
protected override async Task OnInitializedAsync() {
|
|
if (!LoginService.IsLoggedIn) {
|
|
NavigationManager.NavigateToLogin();
|
|
return;
|
|
}
|
|
if (!LoginService.IsAdmin) {
|
|
NavigationManager.NavigateTo("/");
|
|
return;
|
|
}
|
|
await LoadData();
|
|
}
|
|
|
|
async Task LoadData() {
|
|
_isLoading = true;
|
|
StateHasChanged();
|
|
|
|
var settingsTask = SettingsService.GetAllSettings();
|
|
var foldersTask = FoldersService.GetAllFolders();
|
|
|
|
await Task.WhenAll(settingsTask, foldersTask);
|
|
|
|
var settings = settingsTask.Result;
|
|
var folders = foldersTask.Result;
|
|
|
|
if (settings != null) {
|
|
_groupedSettings = GroupSettings(settings);
|
|
}
|
|
|
|
if (folders != null) {
|
|
_folders = folders;
|
|
}
|
|
|
|
_isLoading = false;
|
|
StateHasChanged();
|
|
}
|
|
|
|
static Dictionary<string, List<SettingDto>> GroupSettings(List<SettingDto> settings) {
|
|
var lookup = _groups
|
|
.Where(g => g.Key != "Folders")
|
|
.SelectMany(g => g.Value.SettingNames.Select(name => (Group: g.Key, Name: name)))
|
|
.ToDictionary(x => x.Name, x => x.Group);
|
|
|
|
return settings
|
|
.GroupBy(s => lookup.GetValueOrDefault(s.Name, "Other"))
|
|
.ToDictionary(g => g.Key, g => g.ToList());
|
|
}
|
|
|
|
void SelectGroup(string group) {
|
|
_activeGroup = group;
|
|
}
|
|
|
|
async Task OnAddFolder() {
|
|
await FoldersService.CreateFolderAsync(new FolderCreateDto { BasePath = "New Folder" });
|
|
await LoadData();
|
|
}
|
|
|
|
void RequestFolderDelete(Guid id) {
|
|
var folder = _folders.FirstOrDefault(f => f.Id == id);
|
|
_deleteConfirmMessage = $"Delete folder '{folder?.BasePath}'? This cannot be undone.";
|
|
_pendingDeleteFolderId = id;
|
|
_showDeleteConfirm = true;
|
|
}
|
|
|
|
async Task ExecuteFolderDelete() {
|
|
_showDeleteConfirm = false;
|
|
await FoldersService.RemoveFolderAsync(_pendingDeleteFolderId);
|
|
HandleFolderDeleted(_pendingDeleteFolderId);
|
|
}
|
|
|
|
void HandleFolderDeleted(Guid id) {
|
|
_folders.RemoveAll(f => f.Id == id);
|
|
StateHasChanged();
|
|
}
|
|
}
|