Removed old architecture. Added new base project structure

This commit is contained in:
Masu-Baumgartner
2024-09-30 17:52:14 +02:00
parent c05ea18513
commit 73bf27d222
819 changed files with 6257 additions and 56097 deletions

View File

@@ -0,0 +1,61 @@
using Moonlight.Client.Interfaces;
using Moonlight.Client.Models;
namespace Moonlight.Client.Implementations;
public class DefaultSidebarItemProvider : ISidebarItemProvider
{
public SidebarItem[] Get()
{
return
[
// User
new SidebarItem()
{
Icon = "bi bi-columns",
Name = "Overview",
Path = "/",
Priority = 0,
RequiresExactMatch = true
},
// Admin
new SidebarItem()
{
Icon = "bi bi-columns",
Name = "Overview",
Group = "Admin",
Path = "/admin",
Priority = 0,
RequiresExactMatch = true
},
new SidebarItem()
{
Icon = "bi bi-people",
Name = "Users",
Group = "Admin",
Path = "/admin/users",
Priority = 1,
RequiresExactMatch = false
},
new SidebarItem()
{
Icon = "bi bi-key",
Name = "API",
Group = "Admin",
Path = "/admin/api",
Priority = 2,
RequiresExactMatch = false
},
new SidebarItem()
{
Icon = "bi bi-gear",
Name = "System",
Group = "Admin",
Path = "/admin/system",
Priority = 3,
RequiresExactMatch = false
},
];
}
}

View File

@@ -0,0 +1,8 @@
using Moonlight.Client.Models;
namespace Moonlight.Client.Interfaces;
public interface ISidebarItemProvider
{
public SidebarItem[] Get();
}

View File

@@ -0,0 +1,11 @@
namespace Moonlight.Client.Models;
public class SidebarItem
{
public string Icon { get; set; }
public string Name { get; set; }
public string? Group { get; set; }
public string Path { get; set; }
public int Priority { get; set; }
public bool RequiresExactMatch { get; set; } = false;
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.6"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.6" PrivateAssets="all"/>
<PackageReference Include="MoonCore" Version="1.5.7" />
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.1" />
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Moonlight.Shared\Moonlight.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Helpers\" />
<Folder Include="Services\" />
<Folder Include="UI\Components\" />
<Folder Include="wwwroot\css\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,60 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MoonCore.Blazor.Tailwind.Extensions;
using MoonCore.Extensions;
using MoonCore.Helpers;
using MoonCore.PluginFramework.Services;
using Moonlight.Client.Implementations;
using Moonlight.Client.Interfaces;
using Moonlight.Client.UI;
// Build pre run logger
var providers = LoggerBuildHelper.BuildFromConfiguration(configuration =>
{
configuration.Console.Enable = true;
configuration.Console.EnableAnsiMode = true;
configuration.FileLogging.Enable = false;
});
using var loggerFactory = new LoggerFactory(providers);
var logger = loggerFactory.CreateLogger("Startup");
// Fancy start console output... yes very fancy :>
Console.Write("Running ");
var rainbow = new Crayon.Rainbow(0.5);
foreach (var c in "Moonlight")
{
Console.Write(
rainbow
.Next()
.Bold()
.Text(c.ToString())
);
}
Console.WriteLine();
// Building app
var builder = WebAssemblyHostBuilder.CreateDefault(args);
// Configure application logging
builder.Logging.ClearProviders();
builder.Logging.AddProviders(providers);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped(sp => new HttpApiClient(sp.GetRequiredService<HttpClient>()));
builder.Services.AddMoonCoreBlazorTailwind();
// Implementation service
var implementationService = new ImplementationService();
implementationService.Register<ISidebarItemProvider, DefaultSidebarItemProvider>();
builder.Services.AddSingleton(implementationService);
await builder.Build().RunAsync();

View File

@@ -0,0 +1,14 @@
{
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5165",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,80 @@
/* Buttons */
.btn,
.btn-lg,
.btn-sm,
.btn-xs {
@apply font-medium text-sm inline-flex items-center justify-center border border-transparent rounded-lg leading-5 shadow-sm transition;
}
.btn {
@apply px-3 py-2;
}
.btn-lg {
@apply px-4 py-3;
}
.btn-sm {
@apply px-2 py-1;
}
.btn-xs {
@apply px-2 py-0.5;
}
/* Colors */
.btn-primary {
@apply bg-primary-600 hover:bg-primary-500 focus-visible:outline-primary-600;
}
.btn-secondary {
@apply bg-secondary-800 hover:bg-secondary-700 focus-visible:outline-secondary-800;
}
.btn-tertiary {
@apply bg-tertiary-600 hover:bg-tertiary-500 focus-visible:outline-tertiary-600;
}
.btn-danger {
@apply bg-danger-600 hover:bg-danger-500 focus-visible:outline-danger-600;
}
.btn-warning {
@apply bg-warning-500 hover:bg-warning-400 focus-visible:outline-warning-500;
}
.btn-info {
@apply bg-info-600 hover:bg-info-500 focus-visible:outline-info-600;
}
.btn-success {
@apply bg-success-600 hover:bg-success-500 focus-visible:outline-success-600;
}
/* Outline */
.btn-outline-primary {
@apply bg-gray-800 hover:border-gray-600 text-primary-500;
}
.btn-outline-tertiary {
@apply bg-gray-800 hover:border-gray-600 text-tertiary-500;
}
.btn-outline-danger {
@apply bg-gray-800 hover:border-gray-600 text-danger-500;
}
.btn-outline-warning {
@apply bg-gray-800 hover:border-gray-600 text-warning-400;
}
.btn-outline-info {
@apply bg-gray-800 hover:border-gray-600 text-info-500;
}
.btn-outline-success {
@apply bg-gray-800 hover:border-gray-600 text-success-500;
}

View File

@@ -0,0 +1,19 @@
.card {
@apply flex flex-col bg-gray-800 shadow-sm rounded-xl;
}
.card-header {
@apply px-5 py-4 border-b border-gray-700/60 flex items-center;
}
.card-title {
@apply font-semibold text-gray-100;
}
.card-body {
@apply px-5 py-5;
}
.card-footer {
@apply pt-3 pb-3 border-t border-gray-700/60 mt-auto;
}

View File

@@ -0,0 +1,3 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback');
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap');
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css");

View File

@@ -0,0 +1,77 @@
/* Forms */
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration {
-webkit-appearance: none;
}
.form-input,
.form-textarea,
.form-multiselect,
.form-select,
.form-checkbox,
.form-radio {
@apply bg-gray-700/60 border-2 focus:ring-0 focus:ring-offset-0 disabled:bg-gray-700/30 disabled:border-gray-700 disabled:hover:border-gray-700;
}
.form-checkbox {
@apply rounded;
}
.form-input,
.form-textarea,
.form-multiselect,
.form-select {
@apply text-sm text-gray-100 leading-5 py-2 px-3 border-gray-700 focus:border-primary-500 shadow-sm rounded-lg;
}
.form-input,
.form-textarea {
@apply placeholder-gray-700;
}
.form-select {
@apply pr-10;
}
.form-checkbox,
.form-radio {
@apply text-primary-500 checked:bg-primary-500 checked:border-transparent border border-gray-700/60 focus:border-primary-500/50;
}
/* Switch element */
.form-switch {
@apply relative select-none;
width: 44px;
}
.form-switch label {
@apply block overflow-hidden cursor-pointer h-6 rounded-full;
}
.form-switch label > span:first-child {
@apply absolute block rounded-full;
width: 20px;
height: 20px;
top: 2px;
left: 2px;
right: 50%;
transition: all .15s ease-out;
}
.form-switch input[type="checkbox"]:checked + label {
@apply bg-primary-600;
}
.form-switch input[type="checkbox"]:checked + label > span:first-child {
left: 22px;
}
.form-switch input[type="checkbox"]:disabled + label {
@apply cursor-not-allowed bg-gray-700/20 border border-gray-700/60;
}
.form-switch input[type="checkbox"]:disabled + label > span:first-child {
@apply bg-gray-600;
}

View File

@@ -0,0 +1,25 @@
.progress {
@apply bg-gray-800 rounded-full overflow-hidden;
}
.progress-bar {
@apply bg-primary-500 rounded-full h-3;
transition: width 0.6s ease;
}
.progress-bar.progress-intermediate {
animation: progress-animation 1s infinite linear;
transform-origin: 0 50%
}
@keyframes progress-animation {
0% {
transform: translateX(0) scaleX(0);
}
40% {
transform: translateX(0) scaleX(0.4);
}
100% {
transform: translateX(100%) scaleX(0.5);
}
}

View File

@@ -0,0 +1,9 @@
* {
scrollbar-width: thin;
scrollbar-color: #64748b transparent;
}
.no-scrollbar {
scrollbar-width: none;
scrollbar-color: transparent transparent;
}

View File

@@ -0,0 +1 @@
npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css --watch

View File

@@ -0,0 +1,2 @@
#! /bin/bash
npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css --watch

View File

@@ -0,0 +1,403 @@
[
"-m-1",
"-m-1.5",
"-m-2.5",
"-m-3",
"-mx-2",
"-mx-4",
"-translate-x-1/2",
"-translate-x-full",
"absolute",
"animate-spin",
"backdrop-blur",
"bg-danger-200",
"bg-danger-600",
"bg-gradient-to-t",
"bg-gray-100",
"bg-gray-200",
"bg-gray-400",
"bg-gray-50",
"bg-gray-700",
"bg-gray-750",
"bg-gray-800",
"bg-gray-800/60",
"bg-gray-800/80",
"bg-gray-900",
"bg-gray-950",
"bg-info-100",
"bg-opacity-50",
"bg-opacity-75",
"bg-red-50",
"bg-slate-900",
"bg-success-100",
"bg-tertiary-500",
"bg-transparent",
"bg-warning-100",
"bg-warning-400",
"bg-white",
"block",
"border",
"border-0",
"border-b-2",
"border-gray-100/10",
"border-gray-700",
"border-gray-700/60",
"border-primary-500",
"border-red-600",
"border-slate-700",
"border-t",
"border-transparent",
"bottom-0",
"bottom-full",
"col-span-1",
"col-span-3",
"cursor-default",
"cursor-not-allowed",
"dark:bg-gray-700",
"dark:bg-gray-700/60",
"dark:disabled:bg-gray-800",
"dark:disabled:border-gray-700",
"dark:disabled:placeholder:text-gray-600",
"dark:disabled:text-gray-600",
"dark:group-hover:text-gray-400",
"dark:text-gray-100",
"dark:text-gray-500",
"disabled:bg-gray-100",
"disabled:bg-gray-800",
"disabled:border-gray-200",
"disabled:border-gray-700",
"disabled:cursor-not-allowed",
"disabled:text-gray-400",
"disabled:text-gray-600",
"divide-gray-700/60",
"divide-y",
"duration-100",
"duration-200",
"duration-300",
"duration-75",
"ease-in",
"ease-in-out",
"ease-linear",
"ease-out",
"fill-current",
"fill-primary-600",
"filter",
"first:pl-4",
"fixed",
"flex",
"flex-1",
"flex-col",
"flex-nowrap",
"flex-row",
"flex-shrink-0",
"flex-wrap",
"focus:outline-none",
"focus:ring-0",
"focus:ring-2",
"focus:ring-indigo-500",
"focus:ring-indigo-600",
"focus:ring-offset-0",
"focus:ring-offset-2",
"font-bold",
"font-inter",
"font-medium",
"font-normal",
"font-scp",
"font-semibold",
"form-checkbox",
"form-input",
"form-radio",
"form-select",
"from-gray-700",
"from-primary-700",
"gap-2",
"gap-5",
"gap-x-3",
"gap-x-4",
"gap-x-6",
"gap-y-5",
"gap-y-7",
"gap-y-8",
"grid",
"grid-cols-1",
"grid-cols-3",
"grid-flow-col",
"group-hover:text-gray-500",
"group-hover:text-white",
"grow",
"h-10",
"h-12",
"h-16",
"h-20",
"h-4",
"h-5",
"h-6",
"h-8",
"h-px",
"hidden",
"hover:bg-gray-700",
"hover:bg-gray-800",
"hover:bg-primary-600",
"hover:border-b-2",
"hover:border-gray-600",
"hover:border-primary-500",
"hover:text-gray-100",
"hover:text-gray-500",
"hover:text-info-400",
"hover:text-white",
"inline",
"inline-flex",
"inset-0",
"italic",
"items-center",
"items-end",
"items-start",
"justify-between",
"justify-center",
"justify-end",
"justify-start",
"justify-stretch",
"last:mr-0",
"last:pr-4",
"leading-5",
"leading-6",
"leading-7",
"left-1/2",
"left-auto",
"left-full",
"lg:-mx-8",
"lg:bg-gray-900/10",
"lg:block",
"lg:first:pl-8",
"lg:fixed",
"lg:flex",
"lg:flex-col",
"lg:gap-x-6",
"lg:h-6",
"lg:hidden",
"lg:inset-y-0",
"lg:items-center",
"lg:last:pr-8",
"lg:max-w-5xl",
"lg:pl-72",
"lg:px-8",
"lg:w-72",
"lg:w-px",
"lg:z-50",
"list-disc",
"m-1",
"m-3",
"max-h-56",
"max-w-sm",
"max-w-xs",
"mb-1",
"mb-2",
"mb-3",
"mb-4",
"mb-5",
"mb-6",
"mb-8",
"md:flex-row",
"md:grid-cols-3",
"md:items-center",
"md:space-y-0",
"md:text-3xl",
"me-1",
"me-2",
"min-h-full",
"min-w-60",
"ml-2",
"ml-3",
"ml-4",
"ml-auto",
"mr-16",
"mr-2",
"mr-3",
"mr-6",
"ms-0.5",
"ms-1",
"ms-3",
"mt-1",
"mt-10",
"mt-2",
"mt-2.5",
"mt-3",
"mt-5",
"mt-8",
"mt-auto",
"mx-5",
"mx-auto",
"my-1",
"my-3",
"my-4",
"my-8",
"opacity-0",
"opacity-100",
"origin-top-right",
"overflow-auto",
"overflow-hidden",
"overflow-x-auto",
"overflow-x-scroll",
"overflow-y-auto",
"p-0",
"p-1.5",
"p-2",
"p-2.5",
"p-4",
"p-5",
"pb-1",
"pb-3",
"pb-4",
"pl-12",
"pl-2",
"pl-3",
"pl-5",
"pl-9",
"pointer-events-auto",
"pointer-events-none",
"pr-3",
"pr-8",
"pt-0.5",
"pt-4",
"pt-5",
"pt-6",
"px-2",
"px-3",
"px-4",
"px-5",
"px-6",
"py-1",
"py-10",
"py-2",
"py-3",
"py-6",
"py-8",
"relative",
"right-0",
"right-auto",
"ring-1",
"ring-black",
"ring-gray-900/5",
"ring-opacity-5",
"ring-white/10",
"rounded",
"rounded-b-lg",
"rounded-full",
"rounded-lg",
"rounded-md",
"rounded-t-lg",
"scale-100",
"scale-95",
"select-none",
"shadow-lg",
"shadow-none",
"shadow-sm",
"shadow-xl",
"shrink-0",
"sm:-mx-6",
"sm:auto-cols-max",
"sm:col-span-1",
"sm:col-span-2",
"sm:col-span-3",
"sm:col-span-4",
"sm:col-span-5",
"sm:col-span-6",
"sm:first:pl-6",
"sm:flex",
"sm:gap-x-6",
"sm:grid-cols-6",
"sm:items-center",
"sm:items-end",
"sm:justify-between",
"sm:justify-end",
"sm:last:pr-6",
"sm:max-w-lg",
"sm:mb-0",
"sm:mt-5",
"sm:mt-6",
"sm:my-8",
"sm:p-0",
"sm:p-6",
"sm:pb-4",
"sm:px-6",
"sm:text-sm",
"sm:w-full",
"space-x-1",
"space-x-2",
"space-x-5",
"space-y-1",
"space-y-2",
"space-y-3",
"space-y-4",
"space-y-8",
"sr-only",
"static",
"sticky",
"table",
"table-auto",
"text-2xl",
"text-4xl",
"text-[0.625rem]",
"text-base",
"text-center",
"text-danger-500",
"text-gray-100",
"text-gray-200",
"text-gray-300",
"text-gray-400",
"text-gray-500",
"text-gray-600",
"text-gray-700",
"text-gray-800",
"text-gray-900",
"text-green-500",
"text-indigo-600",
"text-info-400",
"text-info-500",
"text-left",
"text-lg",
"text-primary-500",
"text-red-400",
"text-red-500",
"text-red-700",
"text-red-800",
"text-slate-400",
"text-slate-600",
"text-sm",
"text-success-400",
"text-success-500",
"text-warning-400",
"text-white",
"text-xs",
"to-gray-800",
"to-primary-600",
"top-0",
"transform",
"transition",
"transition-all",
"transition-opacity",
"translate-x-0",
"translate-y-0",
"translate-y-2",
"truncate",
"uppercase",
"w-0",
"w-10",
"w-12",
"w-16",
"w-20",
"w-24",
"w-32",
"w-4",
"w-5",
"w-6",
"w-8",
"w-auto",
"w-full",
"w-px",
"w-screen",
"whitespace-nowrap",
"z-10",
"z-40",
"z-50"
]

1294
Moonlight.Client/Styles/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
{
"devDependencies": {
"tailwindcss": "^3.4.11"
},
"dependencies": {
"@tailwindcss/forms": "^0.5.9"
}
}

View File

@@ -0,0 +1,15 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "additions/fonts.css";
@import "additions/buttons.css";
@import "additions/cards.css";
@import "additions/forms.css";
@import "additions/progress.css";
@import "additions/scrollbar.css";
@import "tailwindcss/utilities";
#blazor-error-ui {
display: none;
}

View File

@@ -0,0 +1,124 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'../**/*.razor',
'mappings/*.json'
],
theme: {
extend: {
fontFamily: {
inter: ['Inter', 'sans-serif'],
scp: ['Source Code Pro', 'mono'],
},
colors: {
primary: {
50: '#eef2ff',
100: '#e0e7ff',
200: '#c7d2fe',
300: '#a5b4fc',
400: '#818cf8',
500: '#6366f1',
600: '#4f46e5',
700: '#4338ca',
800: '#3730a3',
900: '#312e81',
950: '#1e1b4b'
},
secondary: {
100: '#F9F9F9',
200: '#F1F1F2',
300: '#DBDFE9',
400: '#B5B5C3',
500: '#99A1B7',
600: '#78829D',
700: '#4B5675',
800: '#252F4A',
900: '#071437',
950: '#030712',
},
tertiary: {
50: '#f5f3ff',
100: '#ede9fe',
200: '#ddd6fe',
300: '#c4b5fd',
400: '#a78bfa',
500: '#8b5cf6',
600: '#7c3aed',
700: '#6d28d9',
800: '#5b21b6',
900: '#4c1d95',
950: '#2e1065'
},
warning: {
50: '#fefce8',
100: '#fef9c3',
200: '#fef08a',
300: '#fde047',
400: '#facc15',
500: '#eab308',
600: '#ca8a04',
700: '#a16207',
800: '#854d0e',
900: '#713f12',
950: '#422006'
},
danger: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
950: '#450a0a'
},
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
950: '#052e16'
},
info: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554'
},
gray: {
100: '#F9F9F9',
200: '#F1F1F2',
300: '#DBDFE9',
400: '#B5B5C3',
500: '#99A1B7',
600: '#78829D',
700: '#4B5675',
750: '#323c59',
800: '#252F4A',
900: '#071437',
950: '#030b1f',
}
}
},
},
plugins: [
require('@tailwindcss/forms')
],
}

View File

@@ -0,0 +1,13 @@
@using Moonlight.Client.UI.Layouts
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>

View File

@@ -0,0 +1,35 @@
@using MoonCore.Helpers
@using Moonlight.Client.UI.Partials
@inherits LayoutComponentBase
<div>
<AppSidebar Layout="this" />
<div class="lg:pl-72">
<AppHeader Layout="this"/>
<ErrorHandler>
<main class="py-10">
<div class="px-4 sm:px-6 lg:px-8">
@Body
</div>
</main>
</ErrorHandler>
<ToastLauncher />
<ModalLauncher />
</div>
</div>
@code
{
public event Func<Task> OnStateChanged;
public bool ShowMobileNavigation { get; private set; } = false;
public async Task ToggleMobileNavigation()
{
ShowMobileNavigation = !ShowMobileNavigation;
await OnStateChanged();
}
}

View File

@@ -0,0 +1,82 @@
@using Moonlight.Client.UI.Layouts
<div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 bg-gray-800/60 backdrop-blur px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8">
@if (Layout.ShowMobileNavigation)
{
<button @onclick="Layout.ToggleMobileNavigation" type="button" class="-m-2.5 p-2.5 text-gray-700 lg:hidden">
<span class="sr-only">Close sidebar</span>
<i class="bi bi-x-lg text-xl"></i>
</button>
}
else
{
<button @onclick="Layout.ToggleMobileNavigation" type="button" class="-m-2.5 p-2.5 text-gray-700 lg:hidden">
<span class="sr-only">Open sidebar</span>
<i class="bi bi-list text-xl"></i>
</button>
}
<div class="h-6 w-px bg-gray-800 lg:hidden" aria-hidden="true"></div>
<div class="flex justify-between gap-x-4 lg:gap-x-6 w-full">
<div></div>
<div class="flex items-center gap-x-4 lg:gap-x-6">
@*
<button type="button" class="-m-2.5 p-2.5 text-gray-200 hover:text-gray-100">
<span class="sr-only">View notifications</span>
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0"/>
</svg>
</button>
*@
<!-- Separator -->
<div class="hidden lg:block lg:h-6 lg:w-px lg:bg-gray-900/10" aria-hidden="true"></div>
<!-- Profile dropdown -->
<div class="relative">
<button type="button" class="-m-1.5 flex items-center p-1.5" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
<span class="sr-only">Open user menu</span>
<img class="h-8 w-8 rounded-full" src="https://masuowo.xyz/assets/images/avatar.png" alt="">
<span class="hidden lg:flex lg:items-center">
<span class="ml-4 text-sm font-semibold leading-6 text-gray-100" aria-hidden="true">
@@masuowo
</span>
<svg class="ml-2 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"/>
</svg>
</span>
</button>
<!--
Dropdown menu, show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div class="hidden absolute right-0 z-10 mt-2.5 w-32 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
<!-- Active: "bg-gray-50", Not Active: "" -->
<a href="#" class="block px-3 py-1 text-sm leading-6 text-gray-900" role="menuitem" tabindex="-1" id="user-menu-item-0">Your profile</a>
<a href="#" class="block px-3 py-1 text-sm leading-6 text-gray-900" role="menuitem" tabindex="-1" id="user-menu-item-1">Sign out</a>
</div>
</div>
</div>
</div>
</div>
@code
{
[Parameter] public MainLayout Layout { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(!firstRender)
return;
Layout.OnStateChanged += () => InvokeAsync(StateHasChanged);
}
}

View File

@@ -0,0 +1,194 @@
@using MoonCore.PluginFramework.Services
@using Moonlight.Client.Interfaces
@using Moonlight.Client.Models
@using Moonlight.Client.UI.Layouts
@inject NavigationManager Navigation
@inject ImplementationService ImplementationService
@{
var url = new Uri(Navigation.Uri);
}
<div class="relative z-40 lg:hidden transition-opacity @(Layout.ShowMobileNavigation ? "opacity-100" : "opacity-0")" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-gray-800/80"></div>
<div class="fixed inset-0 flex justify-center bg-gray-900">
<!--
Off-canvas menu, show/hide based on off-canvas menu state.
Entering: "transition ease-in-out duration-300 transform"
From: "-translate-x-full"
To: "translate-x-0"
Leaving: "transition ease-in-out duration-300 transform"
From: "translate-x-0"
To: "-translate-x-full"
-->
<div class="relative flex w-full max-w-xs flex-1">
<!--
Close button, show/hide based on off-canvas menu state.
Entering: "ease-in-out duration-300"
From: "opacity-0"
To: "opacity-100"
Leaving: "ease-in-out duration-300"
From: "opacity-100"
To: "opacity-0"
-->
<!-- Sidebar component, swap this element with another sidebar if you like -->
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-gray-900 px-6 pb-4">
<div class="flex h-16 shrink-0 items-center">
<img class="h-8 w-auto" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company">
</div>
<nav class="flex flex-1 flex-col">
<ul role="list" class="flex flex-1 flex-col gap-y-7">
@foreach (var group in Items)
{
<li>
@if (!string.IsNullOrEmpty(group.Key))
{
<div class="text-xs font-semibold leading-6 text-gray-400">
@group.Key
</div>
}
<ul role="list" class="-mx-2 space-y-1">
@foreach (var item in group.Value)
{
var isMatch = item.RequiresExactMatch
? url.LocalPath == item.Path
: url.LocalPath.StartsWith(item.Path);
<li>
@if (isMatch)
{
<a href="@item.Path" class="bg-gray-800 text-white group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
@item.Name
</a>
}
else
{
<a href="@item.Path" class="text-gray-300 hover:text-white hover:bg-gray-800 group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
@item.Name
</a>
}
</li>
}
</ul>
</li>
}
<li class="mt-auto">
<a href="#" class="group -mx-2 flex gap-x-3 rounded-md p-2 text-sm font-semibold leading-6 text-gray-400 hover:bg-gray-800 hover:text-white">
<svg class="h-6 w-6 shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
Settings
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
<!-- Static sidebar for desktop -->
<div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col">
<!-- Sidebar component, swap this element with another sidebar if you like -->
<div class="flex grow flex-col gap-y-5 overflow-y-auto bg-gray-800/60 px-6 pb-4">
<div class="flex h-16 shrink-0 items-center">
<img class="h-8 w-auto" src="https://gamecp.masuowo.xyz/api/core/asset/Core/svg/logo.svg" alt="Your Company">
</div>
<nav class="flex flex-1 flex-col">
<ul role="list" class="flex flex-1 flex-col gap-y-7">
@foreach (var group in Items)
{
<li>
@if (!string.IsNullOrEmpty(group.Key))
{
<div class="text-xs font-semibold leading-6 text-gray-400">
@group.Key
</div>
}
<ul role="list" class="-mx-2 space-y-1">
@foreach (var item in group.Value)
{
var isMatch = item.RequiresExactMatch
? url.LocalPath == item.Path
: url.LocalPath.StartsWith(item.Path);
<li>
@if (isMatch)
{
<a href="@item.Path" class="bg-gray-800 text-white group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
@item.Name
</a>
}
else
{
<a href="@item.Path" class="text-gray-300 hover:text-white hover:bg-gray-800 group flex gap-x-3 rounded-md p-2 text-sm leading-6 items-center">
<i class="ms-1 text-lg shrink-0 @item.Icon"></i>
@item.Name
</a>
}
</li>
}
</ul>
</li>
}
<li class="mt-auto">
<a href="#" class="group -mx-2 flex gap-x-3 rounded-md p-2 text-sm font-semibold leading-6 text-gray-400 hover:bg-gray-800 hover:text-white">
<svg class="h-6 w-6 shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"/>
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
Settings
</a>
</li>
</ul>
</nav>
</div>
</div>
@code
{
[Parameter] public MainLayout Layout { get; set; }
private Dictionary<string, SidebarItem[]> Items = new();
protected override void OnInitialized()
{
Items = ImplementationService.Get<ISidebarItemProvider>()
.SelectMany(x => x.Get())
//.Where(x => x.Permission == null || (x.Permission != null && IdentityService.HasPermission(x.Permission)))
.GroupBy(x => x.Group ?? "")
.OrderByDescending(x => string.IsNullOrEmpty(x.Key))
.ToDictionary(x => x.Key, x => x.OrderBy(y => y.Priority).ToArray());
}
protected override Task OnAfterRenderAsync(bool firstRender)
{
if (!firstRender)
return Task.CompletedTask;
Layout.OnStateChanged += () => InvokeAsync(StateHasChanged);
Navigation.LocationChanged += async (_, _) =>
{
if (!Layout.ShowMobileNavigation)
return;
await Layout.ToggleMobileNavigation();
};
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,3 @@
@page "/admin"
<h1>Elo testy</h1>

View File

@@ -0,0 +1,5 @@
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.

View File

@@ -0,0 +1,18 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using Moonlight.Client
@using MoonCore.Blazor.Tailwind.Components
@using MoonCore.Blazor.Tailwind.Alerts
@using MoonCore.Blazor.Tailwind.Crud
@using MoonCore.Blazor.Tailwind.Forms
@using MoonCore.Blazor.Tailwind.Helpers
@using MoonCore.Blazor.Tailwind.Modals
@using MoonCore.Blazor.Tailwind.Services
@using MoonCore.Blazor.Tailwind.Toasts

3222
Moonlight.Client/wwwroot/css/style.min.css vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Moonlight.Client</title>
<base href="/" />
<link rel="stylesheet" href="/css/style.min.css" />
<link href="manifest.webmanifest" rel="manifest" />
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
</head>
<body class="bg-gray-950 text-white font-inter">
<div id="app">
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="/_framework/blazor.webassembly.js"></script>
<script>navigator.serviceWorker.register('service-worker.js');</script>
</body>
</html>

View File

@@ -0,0 +1,22 @@
{
"name": "Moonlight.Client",
"short_name": "Moonlight.Client",
"id": "./",
"start_url": "./",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#03173d",
"prefer_related_applications": false,
"icons": [
{
"src": "icon-512.png",
"type": "image/png",
"sizes": "512x512"
},
{
"src": "icon-192.png",
"type": "image/png",
"sizes": "192x192"
}
]
}

View File

@@ -0,0 +1,4 @@
// In development, always fetch from the network and do not enable offline support.
// This is because caching would make development more difficult (changes would not
// be reflected on the first load after each change).
self.addEventListener('fetch', () => { });

View File

@@ -0,0 +1,55 @@
// Caution! Be sure you understand the caveats before publishing an application with
// offline support. See https://aka.ms/blazor-offline-considerations
self.importScripts('./service-worker-assets.js');
self.addEventListener('install', event => event.waitUntil(onInstall(event)));
self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
const cacheNamePrefix = 'offline-cache-';
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
const offlineAssetsExclude = [ /^service-worker\.js$/ ];
// Replace with your base path if you are hosting on a subfolder. Ensure there is a trailing '/'.
const base = "/";
const baseUrl = new URL(base, self.origin);
const manifestUrlList = self.assetsManifest.assets.map(asset => new URL(asset.url, baseUrl).href);
async function onInstall(event) {
console.info('Service worker: Install');
// Fetch and cache all matching items from the assets manifest
const assetsRequests = self.assetsManifest.assets
.filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
.filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
.map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }));
await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
}
async function onActivate(event) {
console.info('Service worker: Activate');
// Delete unused caches
const cacheKeys = await caches.keys();
await Promise.all(cacheKeys
.filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
.map(key => caches.delete(key)));
}
async function onFetch(event) {
let cachedResponse = null;
if (event.request.method === 'GET') {
// For all navigation requests, try to serve index.html from cache,
// unless that request is for an offline resource.
// If you need some URLs to be server-rendered, edit the following check to exclude those URLs
const shouldServeIndexHtml = event.request.mode === 'navigate'
&& !manifestUrlList.some(url => url === event.request.url);
const request = shouldServeIndexHtml ? 'index.html' : event.request;
const cache = await caches.open(cacheName);
cachedResponse = await cache.match(request);
}
return cachedResponse || fetch(event.request);
}