Updated WebUI with Radzen and tailwind css
This commit is contained in:
@@ -1,21 +1,59 @@
|
||||
@using WebUI.Components.Notification
|
||||
@inherits LayoutComponentBase
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<NotificationDisplay />
|
||||
<div class="page">
|
||||
<div class="sidebar">
|
||||
<NavMenu/>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<article class="content px-4">
|
||||
<RadzenLayout>
|
||||
<RadzenHeader>
|
||||
<MobileHeader />
|
||||
</RadzenHeader>
|
||||
|
||||
<RadzenSidebar @bind-Expanded="@_sidebarExpanded" class="!bg-gradient-to-b !from-indigo-950 !via-indigo-900 !to-purple-950 !border-0 shadow-2xl shadow-purple-900/20">
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Logo Section -->
|
||||
<div class="p-5 border-b border-white/10">
|
||||
<div class="flex items-center gap-3.5">
|
||||
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center shadow-lg shadow-primary/40 transition-transform hover:scale-105">
|
||||
<i class="bi bi-robot text-white text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-white font-bold text-lg m-0 tracking-tight">Seth Bot</h1>
|
||||
<p class="text-white/60 text-xs m-0">Discord Bot Manager</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="flex-1 py-4 px-2 overflow-y-auto scrollbar-thin scrollbar-thumb-white/10 scrollbar-track-transparent">
|
||||
<NavMenu />
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="p-4 border-t border-white/10 mt-auto">
|
||||
<SidebarFooter />
|
||||
</div>
|
||||
</div>
|
||||
</RadzenSidebar>
|
||||
|
||||
<RadzenBody class="!bg-dark-primary">
|
||||
<div class="p-6 md:p-8 min-h-screen">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
</RadzenBody>
|
||||
</RadzenLayout>
|
||||
|
||||
<!-- Setup Wizard -->
|
||||
<SetupWizardWrapper />
|
||||
|
||||
<!-- Radzen Components -->
|
||||
<RadzenDialog />
|
||||
<RadzenNotification />
|
||||
<RadzenTooltip />
|
||||
<RadzenContextMenu />
|
||||
|
||||
<div id="blazor-error-ui" class="fixed bottom-0 left-0 w-full bg-amber-500 text-black p-4 z-[9999] text-center hidden">
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload underline ml-2">Reload</a>
|
||||
<a class="dismiss absolute right-4 top-1/2 -translate-y-1/2 cursor-pointer">🗙</a>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
@code {
|
||||
private bool _sidebarExpanded = true;
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
.page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
justify-content: flex-end;
|
||||
height: 3.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.top-row ::deep a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 640.98px) {
|
||||
.top-row {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.page {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.top-row.auth ::deep a:first-child {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.top-row, article {
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 1.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
#blazor-error-ui {
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<div class="lg:hidden block p-3 bg-dark-secondary border-b border-slate-700/50">
|
||||
<div class="flex items-center justify-between">
|
||||
<button @onclick="ToggleMobileSidebar"
|
||||
class="p-2 rounded-lg text-slate-300 hover:text-white hover:bg-white/10 transition-colors">
|
||||
<RadzenIcon Icon="menu" />
|
||||
</button>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-8 h-8 rounded-lg bg-gradient-to-br from-primary to-accent flex items-center justify-center shadow-md shadow-primary/30">
|
||||
<i class="bi bi-robot text-white text-sm"></i>
|
||||
</div>
|
||||
<span class="font-semibold text-white">Seth Bot</span>
|
||||
</div>
|
||||
|
||||
<SidebarFooter Compact="true" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Sidebar Drawer -->
|
||||
@if (_sidebarOpen)
|
||||
{
|
||||
<div class="fixed inset-0 z-50 lg:hidden">
|
||||
<!-- Overlay -->
|
||||
<div class="fixed inset-0 bg-black/50 backdrop-blur-sm" @onclick="ToggleMobileSidebar"></div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="fixed left-0 top-0 h-full w-72 bg-gradient-to-b from-indigo-950 via-indigo-900 to-purple-950 shadow-2xl z-10">
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Logo Section -->
|
||||
<div class="p-5 border-b border-white/10">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center shadow-lg shadow-primary/40">
|
||||
<i class="bi bi-robot text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-white font-bold text-base m-0">Seth Bot</h1>
|
||||
<p class="text-white/60 text-xs m-0">Discord Bot Manager</p>
|
||||
</div>
|
||||
</div>
|
||||
<button @onclick="ToggleMobileSidebar"
|
||||
class="p-2 rounded-lg text-white/70 hover:text-white hover:bg-white/10 transition-colors">
|
||||
<RadzenIcon Icon="close" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="flex-1 py-4 px-2 overflow-y-auto">
|
||||
<NavMenu />
|
||||
</div>
|
||||
|
||||
<!-- Footer with Theme Toggle -->
|
||||
<div class="p-4 border-t border-white/10 mt-auto">
|
||||
<SidebarFooter />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _sidebarOpen;
|
||||
|
||||
private void ToggleMobileSidebar()
|
||||
{
|
||||
_sidebarOpen = !_sidebarOpen;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,11 @@
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid align-items-center">
|
||||
<a class="navbar-brand align-content-center" href="">Seth Discord Bot</a>
|
||||
</div>
|
||||
</div>
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler"/>
|
||||
<RadzenPanelMenu>
|
||||
<RadzenPanelMenuItem Text="Home" Icon="home" Path="" />
|
||||
<RadzenPanelMenuItem Text="Settings" Icon="settings" Path="settings" />
|
||||
<RadzenPanelMenuItem Text="Plugins" Icon="extension">
|
||||
<RadzenPanelMenuItem Text="Online Plugins" Icon="cloud_download" Path="plugins/online" />
|
||||
<RadzenPanelMenuItem Text="Local Plugins" Icon="folder" Path="plugins/local" />
|
||||
</RadzenPanelMenuItem>
|
||||
</RadzenPanelMenu>
|
||||
|
||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<nav class="flex-column">
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="settings">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Settings
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link d-flex align-items-center" href="plugins/online">
|
||||
<span class="bi bi-list" aria-hidden="true" style="
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
margin-right: 0.5rem;
|
||||
font-size: 1.2rem;
|
||||
"></span>
|
||||
Online Plugins
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="plugins/local">
|
||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Local Plugins
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -1,106 +0,0 @@
|
||||
.navbar-toggler {
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
width: 3.5rem;
|
||||
height: 2.5rem;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 1rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.navbar-toggler:checked {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
height: 3.5rem;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.bi {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
|
||||
.bi-house-door-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link {
|
||||
color: #d7d7d7;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-toggler:checked ~ .nav-scrollable {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
@using DiscordBotCore.Configuration
|
||||
@using DiscordBotCore.Logging
|
||||
|
||||
@inject IConfiguration Configuration
|
||||
@inject ILogger Logger
|
||||
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-gradient-to-br from-indigo-950 via-indigo-900 to-purple-950 overflow-hidden">
|
||||
<!-- Static background blobs (no animation) -->
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<div class="absolute top-[10%] left-[10%] w-[500px] h-[500px] rounded-full bg-primary/40 blur-[100px] opacity-60"></div>
|
||||
<div class="absolute bottom-[10%] right-[10%] w-[600px] h-[600px] rounded-full bg-pink-500/30 blur-[100px] opacity-60"></div>
|
||||
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[400px] h-[400px] rounded-full bg-blue-500/25 blur-[100px] opacity-60"></div>
|
||||
</div>
|
||||
|
||||
<!-- Setup Card -->
|
||||
<div class="relative w-full max-w-[480px] mx-4 bg-white/[0.08] backdrop-blur-2xl rounded-3xl border border-white/[0.12] overflow-hidden">
|
||||
<!-- Progress Bar -->
|
||||
<div class="h-1 bg-white/10">
|
||||
<div class="h-full bg-gradient-to-r from-primary to-accent transition-all duration-300 ease-out" style="width: @(_currentStep * 100 / TotalSteps)%"></div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="p-8">
|
||||
@switch (_currentStep)
|
||||
{
|
||||
case 1:
|
||||
<!-- Welcome Step -->
|
||||
<div class="flex flex-col items-center gap-6">
|
||||
<div class="w-20 h-20 rounded-2xl bg-gradient-to-br from-primary to-accent flex items-center justify-center shadow-2xl shadow-primary/40">
|
||||
<i class="bi bi-robot text-white text-3xl"></i>
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<h2 class="text-2xl font-bold text-white">Welcome to Seth Bot!</h2>
|
||||
<p class="text-white/70">Let's get your Discord bot up and running in just a few steps.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 mt-2">
|
||||
<div class="p-4 rounded-xl bg-white/[0.08] border border-white/10 text-center min-w-[100px]">
|
||||
<RadzenIcon Icon="bolt" class="text-amber-400 text-2xl" />
|
||||
<p class="text-white text-xs mt-2 font-medium">Fast Setup</p>
|
||||
</div>
|
||||
<div class="p-4 rounded-xl bg-white/[0.08] border border-white/10 text-center min-w-[100px]">
|
||||
<RadzenIcon Icon="extension" class="text-emerald-400 text-2xl" />
|
||||
<p class="text-white text-xs mt-2 font-medium">Plugins</p>
|
||||
</div>
|
||||
<div class="p-4 rounded-xl bg-white/[0.08] border border-white/10 text-center min-w-[100px]">
|
||||
<RadzenIcon Icon="security" class="text-blue-400 text-2xl" />
|
||||
<p class="text-white text-xs mt-2 font-medium">Secure</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
|
||||
case 2:
|
||||
<!-- Token Step -->
|
||||
<div class="flex flex-col items-center gap-6">
|
||||
<div class="w-20 h-20 rounded-2xl bg-gradient-to-br from-primary to-purple-500 flex items-center justify-center shadow-2xl shadow-primary/40">
|
||||
<RadzenIcon Icon="vpn_key" class="text-white text-3xl" />
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<h2 class="text-xl font-bold text-white">Bot Token</h2>
|
||||
<p class="text-white/70">Enter your Discord bot token to continue</p>
|
||||
</div>
|
||||
|
||||
<div class="w-full space-y-4">
|
||||
<RadzenPassword @bind-Value="@_token"
|
||||
Placeholder="Paste your bot token here..."
|
||||
class="!w-full !bg-white/[0.08] !border-white/15 hover:!border-primary focus-within:!border-primary !text-white !rounded-xl !placeholder:text-white/40" />
|
||||
|
||||
<div class="p-4 rounded-xl bg-blue-500/10 border border-blue-500/20">
|
||||
<p class="text-white text-sm">
|
||||
<span class="font-semibold">Where to find your token?</span><br/>
|
||||
<span class="text-white/70">Go to Discord Developer Portal → Your Application → Bot → Token</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
|
||||
case 3:
|
||||
<!-- Prefix Step -->
|
||||
<div class="flex flex-col items-center gap-6">
|
||||
<div class="w-20 h-20 rounded-2xl bg-gradient-to-br from-emerald-500 to-teal-500 flex items-center justify-center shadow-2xl shadow-emerald-500/40">
|
||||
<RadzenIcon Icon="chat" class="text-white text-3xl" />
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<h2 class="text-xl font-bold text-white">Command Prefix</h2>
|
||||
<p class="text-white/70">Choose a prefix for your bot commands</p>
|
||||
</div>
|
||||
|
||||
<div class="w-full space-y-4 flex flex-col items-center">
|
||||
<RadzenTextBox @bind-Value="@_prefix"
|
||||
Placeholder="!"
|
||||
MaxLength="5"
|
||||
class="!w-24 !text-center !text-2xl !font-mono !bg-white/[0.08] !border-white/15 hover:!border-primary focus-within:!border-primary !text-white !rounded-xl" />
|
||||
|
||||
<div class="flex gap-2 justify-center">
|
||||
@foreach (var prefix in new[] { "!", "?", ".", "$", "/" })
|
||||
{
|
||||
var p = prefix;
|
||||
<button type="button"
|
||||
@onclick="@(() => _prefix = p)"
|
||||
class="w-12 h-12 rounded-xl font-mono text-xl transition-colors @(_prefix == p ? "bg-gradient-to-r from-primary to-accent text-white shadow-lg shadow-primary/30" : "bg-white/[0.08] text-white/70 border border-white/15 hover:border-primary hover:text-white")">
|
||||
@p
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<p class="text-white/60 text-sm">
|
||||
Example: <code class="bg-white/10 px-2 py-0.5 rounded text-white font-mono">@(_prefix)help</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
|
||||
case 4:
|
||||
<!-- Server IDs Step -->
|
||||
<div class="flex flex-col items-center gap-6">
|
||||
<div class="w-20 h-20 rounded-2xl bg-gradient-to-br from-orange-500 to-amber-500 flex items-center justify-center shadow-2xl shadow-orange-500/40">
|
||||
<RadzenIcon Icon="dns" class="text-white text-3xl" />
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<h2 class="text-xl font-bold text-white">Server IDs</h2>
|
||||
<p class="text-white/70">Add the Discord server IDs where your bot will operate</p>
|
||||
</div>
|
||||
|
||||
<div class="w-full space-y-4">
|
||||
<RadzenTextArea @bind-Value="@_serverIds"
|
||||
Placeholder="Enter server IDs separated by commas..."
|
||||
Rows="3"
|
||||
class="!w-full !bg-white/[0.08] !border-white/15 hover:!border-primary focus-within:!border-primary !text-white !rounded-xl !placeholder:text-white/40" />
|
||||
|
||||
<div class="p-4 rounded-xl bg-amber-500/10 border border-amber-500/20">
|
||||
<p class="text-white text-sm">
|
||||
<span class="font-semibold">How to get Server ID?</span><br/>
|
||||
<span class="text-white/70">Enable Developer Mode in Discord Settings → Right-click your server → Copy Server ID</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="text-white/50 text-xs text-center">Optional: You can add this later in Settings</p>
|
||||
</div>
|
||||
</div>
|
||||
break;
|
||||
|
||||
case 5:
|
||||
<!-- Complete Step -->
|
||||
<div class="flex flex-col items-center gap-6">
|
||||
<div class="w-20 h-20 rounded-full bg-gradient-to-br from-emerald-500 to-teal-500 flex items-center justify-center shadow-2xl shadow-emerald-500/40">
|
||||
<RadzenIcon Icon="check" class="text-white text-4xl" />
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<h2 class="text-2xl font-bold text-white">You're All Set!</h2>
|
||||
<p class="text-white/70">Your bot is configured and ready to go.</p>
|
||||
</div>
|
||||
|
||||
<div class="w-full p-5 rounded-xl bg-white/[0.08] border border-white/10">
|
||||
<div class="flex items-center gap-2 mb-4 text-white">
|
||||
<RadzenIcon Icon="list" class="text-white/70" />
|
||||
<span class="font-semibold">Configuration Summary</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3 text-sm">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-white/60">Token</span>
|
||||
<code class="text-white bg-white/10 px-2 py-0.5 rounded font-mono">••••••••@(_token.Length > 8 ? _token.Substring(_token.Length - 4) : "••••")</code>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-white/60">Prefix</span>
|
||||
<code class="text-white bg-white/10 px-2 py-0.5 rounded font-mono">@_prefix</code>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-white/60">Server IDs</span>
|
||||
<span class="text-white">@(string.IsNullOrEmpty(_serverIds) ? "None specified" : _serverIds.Split(',').Length + " server(s)")</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-white/50 text-xs">You can change these settings anytime</p>
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
|
||||
<!-- Navigation Buttons -->
|
||||
<div class="flex justify-between mt-8 gap-4">
|
||||
@if (_currentStep > 1 && _currentStep < TotalSteps)
|
||||
{
|
||||
<button @onclick="PreviousStep"
|
||||
class="px-5 py-2.5 rounded-xl border border-white/30 text-white hover:bg-white/10 transition-all duration-200 flex items-center gap-2">
|
||||
<RadzenIcon Icon="arrow_back" class="text-sm" />
|
||||
Back
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div></div>
|
||||
}
|
||||
|
||||
@if (_currentStep < TotalSteps)
|
||||
{
|
||||
<RadzenButton Text="@(_currentStep == 1 ? "Get Started" : "Continue")"
|
||||
Icon="arrow_forward"
|
||||
IconPosition="ButtonIconPosition.Right"
|
||||
ButtonStyle="ButtonStyle.Primary"
|
||||
Disabled="@(!CanProceed())"
|
||||
Click="@NextStep"
|
||||
class="!bg-gradient-to-r !from-primary !to-accent hover:!shadow-lg hover:!shadow-primary/30 !transition-all !duration-300 !rounded-xl" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<RadzenButton Text="Launch Bot"
|
||||
Icon="rocket_launch"
|
||||
ButtonStyle="ButtonStyle.Success"
|
||||
Click="@FinishSetup"
|
||||
class="!bg-gradient-to-r !from-emerald-500 !to-teal-500 !rounded-xl" />
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Step Indicators -->
|
||||
<div class="flex justify-center gap-2 mt-6">
|
||||
@for (int i = 1; i <= TotalSteps; i++)
|
||||
{
|
||||
var step = i;
|
||||
<div class="w-2 h-2 rounded-full transition-all duration-300 @(step <= _currentStep ? "bg-white scale-125" : "bg-white/25")"></div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public EventCallback OnSetupComplete { get; set; }
|
||||
|
||||
private int _currentStep = 1;
|
||||
private const int TotalSteps = 5;
|
||||
|
||||
private string _token = string.Empty;
|
||||
private string _prefix = "!";
|
||||
private string _serverIds = string.Empty;
|
||||
|
||||
private void NextStep()
|
||||
{
|
||||
if (_currentStep < TotalSteps && CanProceed())
|
||||
{
|
||||
_currentStep++;
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviousStep()
|
||||
{
|
||||
if (_currentStep > 1)
|
||||
{
|
||||
_currentStep--;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanProceed()
|
||||
{
|
||||
return _currentStep switch
|
||||
{
|
||||
1 => true,
|
||||
2 => !string.IsNullOrWhiteSpace(_token),
|
||||
3 => !string.IsNullOrWhiteSpace(_prefix),
|
||||
4 => true,
|
||||
_ => true
|
||||
};
|
||||
}
|
||||
|
||||
private async Task FinishSetup()
|
||||
{
|
||||
// Save configuration
|
||||
Configuration.Set("token", _token);
|
||||
Configuration.Set("prefix", _prefix);
|
||||
|
||||
var serverIdList = _serverIds
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(id => ulong.TryParse(id.Trim(), out var v) ? v : 0)
|
||||
.Where(v => v != 0)
|
||||
.ToList();
|
||||
|
||||
Configuration.Set("ServerIds", serverIdList);
|
||||
await Configuration.SaveToFile();
|
||||
|
||||
Logger.Log("Initial setup completed successfully!", this);
|
||||
await OnSetupComplete.InvokeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
@using DiscordBotCore.Configuration
|
||||
@inject IJSRuntime JS
|
||||
@inject IConfiguration Configuration
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@if (_showSetupWizard)
|
||||
{
|
||||
<SetupWizard OnSetupComplete="HandleSetupComplete" />
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _showSetupWizard;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
// Check if token is configured - if not, show setup wizard
|
||||
var token = Configuration.Get<string>("token");
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
_showSetupWizard = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleSetupComplete()
|
||||
{
|
||||
await JS.InvokeVoidAsync("setupWizard.completeSetup");
|
||||
_showSetupWizard = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
@if (Compact)
|
||||
{
|
||||
<div class="p-2 rounded-lg text-white/80">
|
||||
<RadzenIcon Icon="dark_mode" />
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="flex items-center justify-between p-3 bg-white/5 rounded-xl border border-white/10">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-8 h-8 rounded-lg flex items-center justify-center bg-gradient-to-br from-indigo-500 to-primary">
|
||||
<RadzenIcon Icon="dark_mode" class="text-white text-sm" />
|
||||
</div>
|
||||
<span class="text-white/90 text-sm font-medium">Copyright @DateTime.Now.Year</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public bool Compact { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user