💄 Restyled web pages

This commit is contained in:
2025-07-10 01:53:44 +08:00
parent 08b5ffa02f
commit ed2961a5d5
16 changed files with 2758 additions and 1332 deletions

View File

@ -6,53 +6,44 @@
@if (Model.Account != null) @if (Model.Account != null)
{ {
<div class="h-full bg-gray-100 dark:bg-gray-900 py-8 px-4"> <div class="p-4 sm:p-8 bg-base-200">
<div class="max-w-6xl mx-auto"> <div class="max-w-6xl mx-auto">
<!-- Header --> <!-- Header -->
<div class="mb-8"> <div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">Profile Settings</h1> <h1 class="text-3xl font-bold">Profile Settings</h1>
<p class="text-gray-600 dark:text-gray-400 mt-2">Manage your account information and preferences</p> <p class="text-base-content/70 mt-2">Manage your account information and preferences</p>
</div> </div>
<!-- Two Column Layout --> <!-- Two Column Layout -->
<div class="flex flex-col md:flex-row gap-8"> <div class="flex flex-col md:flex-row gap-8">
<!-- Left Pane - Profile Card --> <!-- Left Pane - Profile Card -->
<div class="w-full md:w-1/3 lg:w-1/4"> <div class="w-full md:w-1/3 lg:w-1/4">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 sticky top-8"> <div class="card bg-base-100 shadow-xl sticky top-8">
<div class="flex flex-col items-center text-center"> <div class="card-body items-center text-center">
<!-- Avatar --> <!-- Avatar -->
<div <div class="avatar avatar-placeholder mb-4">
class="w-32 h-32 rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center mb-4 overflow-hidden"> <div class="bg-neutral text-neutral-content rounded-full w-32">
<span class="text-4xl text-gray-500 dark:text-gray-400"> <span class="text-4xl">@Model.Account.Name?[..1].ToUpper()</span>
@Model.Account.Name?.Substring(0, 1).ToUpper() </div>
</span>
</div> </div>
<!-- Basic Info --> <!-- Basic Info -->
<h2 class="text-xl font-semibold text-gray-900 dark:text-white"> <h2 class="card-title">@Model.Account.Nick</h2>
@Model.Account.Nick <p class="font-mono text-sm">@@@Model.Account.Name</p>
</h2>
<p class="text-gray-600 dark:text-gray-400">@Model.Account.Name</p>
<!-- Stats --> <!-- Stats -->
<div <div class="stats stats-vertical shadow mt-4">
class="mt-4 flex justify-around w-full border-t border-gray-200 dark:border-gray-700 pt-4"> <div class="stat">
<div class="text-center"> <div class="stat-title">Level</div>
<div <div class="stat-value">@Model.Account.Profile.Level</div>
class="text-lg font-semibold text-gray-900 dark:text-white">@Model.Account.Profile.Level</div>
<div class="text-sm text-gray-500 dark:text-gray-400">Level</div>
</div> </div>
<div class="text-center"> <div class="stat">
<div <div class="stat-title">XP</div>
class="text-lg font-semibold text-gray-900 dark:text-white">@Model.Account.Profile.Experience</div> <div class="stat-value">@Model.Account.Profile.Experience</div>
<div class="text-sm text-gray-500 dark:text-gray-400">XP</div>
</div> </div>
<div class="text-center"> <div class="stat">
<div <div class="stat-title">Member since</div>
class="text-lg font-semibold text-gray-900 dark:text-white"> <div class="stat-value">@Model.Account.CreatedAt.ToDateTimeUtc().ToString("yyyy/MM")</div>
@Model.Account.CreatedAt.ToDateTimeUtc().ToString("yyyy/MM/dd")
</div>
<div class="text-sm text-gray-500 dark:text-gray-400">Member since</div>
</div> </div>
</div> </div>
</div> </div>
@ -61,181 +52,107 @@
<!-- Right Pane - Tabbed Content --> <!-- Right Pane - Tabbed Content -->
<div class="flex-1"> <div class="flex-1">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden"> <div role="tablist" class="tabs tabs-lift w-full">
<!-- Tabs --> <input type="radio" name="profile-tabs" role="tab" class="tab" aria-label="Profile" checked />
<div class="border-b border-gray-200 dark:border-gray-700"> <div role="tabpanel" class="tab-content bg-base-100 border-base-300 p-6">
<nav class="flex -mb-px"> <h2 class="text-xl font-semibold mb-6">Profile Information</h2>
<button type="button"
class="tab-button active py-4 px-6 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-200" <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
data-tab="profile"> <div>
Profile <h3 class="text-lg font-medium mb-4">Basic Information</h3>
</button> <dl class="space-y-4">
<button type="button" <div>
class="tab-button py-4 px-6 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-200" <dt class="text-sm font-medium text-base-content/70">Full Name</dt>
data-tab="security"> <dd class="mt-1 text-sm">@($"{Model.Account.Profile.FirstName} {Model.Account.Profile.MiddleName} {Model.Account.Profile.LastName}".Trim())</dd>
Security </div>
</button> <div>
<button type="button" <dt class="text-sm font-medium text-base-content/70">Username</dt>
class="tab-button py-4 px-6 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-200" <dd class="mt-1 text-sm">@Model.Account.Name</dd>
data-tab="sessions"> </div>
Sessions <div>
</button> <dt class="text-sm font-medium text-base-content/70">Nickname</dt>
</nav> <dd class="mt-1 text-sm">@Model.Account.Nick</dd>
</div>
<div>
<dt class="text-sm font-medium text-base-content/70">Gender</dt>
<dd class="mt-1 text-sm">@Model.Account.Profile.Gender</dd>
</div>
</dl>
</div>
<div>
<h3 class="text-lg font-medium mb-4">Additional Details</h3>
<dl class="space-y-4">
<div>
<dt class="text-sm font-medium text-base-content/70">Location</dt>
<dd class="mt-1 text-sm">@Model.Account.Profile.Location</dd>
</div>
<div>
<dt class="text-sm font-medium text-base-content/70">Birthday</dt>
<dd class="mt-1 text-sm">@Model.Account.Profile.Birthday?.ToString("MMMM d, yyyy", System.Globalization.CultureInfo.InvariantCulture)</dd>
</div>
<div>
<dt class="text-sm font-medium text-base-content/70">Bio</dt>
<dd class="mt-1 text-sm">@(string.IsNullOrEmpty(Model.Account.Profile.Bio) ? "No bio provided" : Model.Account.Profile.Bio)</dd>
</div>
</dl>
</div>
</div>
</div> </div>
<!-- Tab Content --> <input type="radio" name="profile-tabs" role="tab" class="tab" aria-label="Security" />
<div class="p-6"> <div role="tabpanel" class="tab-content bg-base-100 border-base-300 p-6">
<!-- Profile Tab --> <h2 class="text-xl font-semibold mb-2">Security Settings</h2>
<div id="profile-tab" class="tab-content">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">Profile
Information</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> <div class="space-y-6">
<div> <div class="card bg-base-300 shadow-xl">
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-4">Basic <div class="card-body">
Information</h3> <h3 class="card-title">Access Token</h3>
<dl class="space-y-4"> <p>Use this token to authenticate with the API</p>
<div> <div class="form-control">
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Full <div class="join">
Name <input type="password" id="accessToken" value="@Model.AccessToken" readonly class="input input-bordered join-item flex-grow" />
</dt> <button onclick="copyAccessToken()" class="btn join-item">Copy</button>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
@($"{Model.Account.Profile.FirstName} {Model.Account.Profile.MiddleName} {Model.Account.Profile.LastName}".Trim())
</dd>
</div> </div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
Username
</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">@Model.Account.Name</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
Nickname
</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">@Model.Account.Nick</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
Gender
</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">@Model.Account.Profile.Gender</dd>
</div>
</dl>
</div>
<div>
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-4">Additional
Details</h3>
<dl class="space-y-4">
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
Location
</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">@Model.Account.Profile.Location</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">
Birthday
</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
@Model.Account.Profile.Birthday?.ToString("MMMM d, yyyy", System.Globalization.CultureInfo.InvariantCulture)
</dd>
</div>
<div>
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Bio
</dt>
<dd class="mt-1 text-sm text-gray-900 dark:text-white">
@(string.IsNullOrEmpty(Model.Account.Profile.Bio) ? "No bio provided" : Model.Account.Profile.Bio)
</dd>
</div>
</dl>
</div>
</div>
<div class="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
<button type="button"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Edit Profile
</button>
</div>
</div>
<!-- Security Tab -->
<div id="security-tab" class="tab-content hidden">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">Security
Settings</h2>
<div class="space-y-6">
<div class="bg-white dark:bg-gray-800 shadow overflow-hidden sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">
Access Token</h3>
<p class="mt-1 max-w-2xl text-sm text-gray-500 dark:text-gray-400">Use this
token to authenticate with the API</p>
</div>
<div class="border-t border-gray-200 dark:border-gray-700 px-4 py-5 sm:px-6">
<div class="flex items-center">
<input type="password" id="accessToken" value="@Model.AccessToken"
readonly
class="form-input flex-grow rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50 dark:bg-gray-700 dark:border-gray-600 dark:text-white py-2 px-4"/>
<button onclick="copyAccessToken()"
class="ml-4 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
Copy
</button>
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Keep this token secure and do not share it with anyone.
</p>
</div> </div>
<p class="text-sm text-base-content/70 mt-2">Keep this token secure and do not share it with anyone.</p>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Sessions Tab --> <input type="radio" name="profile-tabs" role="tab" class="tab" aria-label="Sessions" />
<div id="sessions-tab" class="tab-content hidden"> <div role="tabpanel" class="tab-content bg-base-100 border-base-300 p-6">
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">Active <h2 class="text-xl font-semibold">Active Sessions</h2>
Sessions</h2> <p class="text-base-content/70 mb-3">This is a list of devices that have logged into your account. Revoke any sessions that you do not recognize.</p>
<p class="text-gray-600 dark:text-gray-400 mb-6">This is a list of devices that have
logged into your account. Revoke any sessions that you do not recognize.</p>
<div class="bg-white dark:bg-gray-800 shadow overflow-hidden sm:rounded-lg"> <div class="card bg-base-300 shadow-xl">
<ul class="divide-y divide-gray-200 dark:divide-gray-700"> <div class="card-body">
<li class="px-4 py-4 sm:px-6"> <div class="overflow-x-auto">
<div class="flex items-center justify-between"> <table class="table">
<div class="flex items-center"> <tbody>
<div <tr>
class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 dark:bg-blue-900 flex items-center justify-center"> <td>
<svg class="h-6 w-6 text-blue-600 dark:text-blue-400" <div class="flex items-center gap-3">
fill="currentColor" viewBox="0 0 20 20"> <div class="avatar">
<path fill-rule="evenodd" <div class="mask mask-squircle w-12 h-12">
d="M4 4a2 2 0 012-2h8a2 2 0 012 2v12a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 0v12h8V4H6z" <svg class="h-full w-full text-blue-600 dark:text-blue-400" fill="currentColor" viewBox="0 0 20 20">
clip-rule="evenodd"/> <path fill-rule="evenodd" d="M4 4a2 2 0 012-2h8a2 2 0 012 2v12a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 0v12h8V4H6z" clip-rule="evenodd" />
</svg> </svg>
</div> </div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900 dark:text-white">
Current Session
</div> </div>
<div class="text-sm text-gray-500 dark:text-gray-400"> <div>
@($"{Request.Headers["User-Agent"]} • {DateTime.Now:MMMM d, yyyy 'at' h:mm tt}") <div class="font-bold">Current Session</div>
<div class="text-sm opacity-50">@($"{Request.Headers["User-Agent"]} • {DateTime.Now:MMMM d, yyyy 'at' h:mm tt}")</div>
</div> </div>
</div> </div>
</div> </td>
<div class="ml-4 flex-shrink-0"> </tr>
<span </tbody>
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"> </table>
Active now </div>
</span> <div class="card-actions justify-end mt-4">
</div> <button type="button" class="btn btn-error">Sign out all other sessions</button>
</div>
</li>
</ul>
<div class="bg-gray-50 dark:bg-gray-800 px-4 py-4 sm:px-6 text-right">
<button type="button"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
Sign out all other sessions
</button>
</div> </div>
</div> </div>
</div> </div>
@ -245,10 +162,7 @@
<!-- Logout Button --> <!-- Logout Button -->
<div class="mt-6 flex justify-end"> <div class="mt-6 flex justify-end">
<form method="post" asp-page-handler="Logout"> <form method="post" asp-page-handler="Logout">
<button type="submit" <button type="submit" class="btn btn-error">Sign out</button>
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
Sign out
</button>
</form> </form>
</div> </div>
</div> </div>
@ -258,54 +172,22 @@
} }
else else
{ {
<div class="min-h-screen flex items-center justify-center bg-gray-100 dark:bg-gray-900"> <div class="hero min-h-screen bg-base-200">
<div class="max-w-md w-full p-8 bg-white dark:bg-gray-800 rounded-lg shadow-md text-center"> <div class="hero-content text-center">
<div class="text-red-500 text-5xl mb-4"> <div class="max-w-md">
<i class="fas fa-exclamation-circle"></i> <div class="text-error text-5xl mb-4">
<i class="fas fa-exclamation-circle"></i>
</div>
<h1 class="text-5xl font-bold">Profile Not Found</h1>
<p class="py-6">User profile not found. Please log in to continue.</p>
<a href="/auth/login" class="btn btn-primary">Go to Login</a>
</div> </div>
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Profile Not Found</h2>
<p class="text-gray-600 dark:text-gray-400 mb-6">User profile not found. Please log in to continue.</p>
<a href="/auth/login"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Go to Login
</a>
</div> </div>
</div> </div>
} }
@section Scripts { @section Scripts {
<script> <script>
// Tab functionality
document.addEventListener('DOMContentLoaded', function () {
// Get all tab buttons and content
const tabButtons = document.querySelectorAll('.tab-button');
const tabContents = document.querySelectorAll('.tab-content');
// Add click event listeners to tab buttons
tabButtons.forEach(button => {
button.addEventListener('click', function () {
const tabId = this.getAttribute('data-tab');
// Update active tab button
tabButtons.forEach(btn => btn.classList.remove('border-blue-500', 'text-blue-600', 'dark:text-blue-400'));
this.classList.add('border-blue-500', 'text-blue-600', 'dark:text-blue-400');
// Show corresponding tab content
tabContents.forEach(content => {
content.classList.add('hidden');
if (content.id === `${tabId}-tab`) {
content.classList.remove('hidden');
}
});
});
});
// Show first tab by default
if (tabButtons.length > 0) {
tabButtons[0].click();
}
});
// Copy access token to clipboard // Copy access token to clipboard
function copyAccessToken() { function copyAccessToken() {
const copyText = document.getElementById("accessToken"); const copyText = document.getElementById("accessToken");

View File

@ -4,10 +4,10 @@
ViewData["Title"] = "Authorize Application"; ViewData["Title"] = "Authorize Application";
} }
<div class="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 transition-colors duration-200 py-12 px-4 sm:px-6 lg:px-8"> <div class="min-h-screen flex items-center justify-center bg-base-200 py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-md w-full space-y-8 bg-white dark:bg-gray-800 p-8 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 transition-colors duration-200"> <div class="card w-full max-w-md bg-base-100 shadow-xl">
<div class="text-center"> <div class="card-body">
<h2 class="mt-6 text-3xl font-extrabold text-gray-900 dark:text-white"> <h2 class="card-title justify-center text-2xl font-bold">
Authorize Application Authorize Application
</h2> </h2>
@if (!string.IsNullOrEmpty(Model.AppName)) @if (!string.IsNullOrEmpty(Model.AppName))
@ -16,21 +16,17 @@
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
@if (!string.IsNullOrEmpty(Model.AppLogo)) @if (!string.IsNullOrEmpty(Model.AppLogo))
{ {
<div class="relative h-16 w-16 flex-shrink-0"> <div class="avatar">
<img class="h-16 w-16 rounded-lg object-cover border border-gray-200 dark:border-gray-700" <div class="w-16 rounded">
src="@Model.AppLogo" <img src="@Model.AppLogo" alt="@Model.AppName logo" />
alt="@Model.AppName logo"
onerror="this.onerror=null; this.src='data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzczz0idy02IGgtNiB0ZXh0LWdyYXktNDAwIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xOS42MDMgMy4wMDRjMS4xNTMgMCAyLjI2LjE4IDMuMzI5LjUxM2EuNzUuNzUgMCAwMS0uMzI4IDEuNDQ1IDE2LjkzIDE2LjkzIDAgMDAtNi4wMS0xuMTAzLjc1Ljc1IDAgMDEtLjY2OC0uNzQ4VjMuNzVBLjc1Ljc1IDAgMDExNi41IDNoMy4xMDN6TTMxLjUgMjEuMDA4Yy0uU0UuNzUgNzUuNzUgMCAwMTEuOTQ4LjA1MWMuMDgxLjkxOC4wNTIgMS44MzgtLjA4NiAyLjczNGEuNzUuNzUgMCAwMS0xLjQ5LS4xNTkgMjEuMzg2IDIxLjM4NiAwIDAwLjA2LTIuNTc1Ljc1Ljc1IDAgMDExLjU3OC0uMDQzem0tMy4wMi0xOC4wMmMuMDYgMS4wOTIuMDQgMi4xODctLjA1NiAzLjI3MmEuNzUuNzUgMCAwMS0xLjQ5Mi0uMTY0Yy4wOTItLjg3NC4xMDctMS43NTYuMDUxLTIuNjA3YS43NS43NSAwIDAxMS40OTctLjEwMXpNNS42MzcgNS42MzJjLS4zMzQuMS0uNjc2LjE4NS0xLjAyNi4yNTdhLjc1Ljc1IDAgMDEuMTQ5LTEuNDljLjQyNS0uMDg1Ljg1Mi0uMTg5IDEuMjY4LS4zMDRhLjc1Ljc1IDAgMDEuMzYgMS40Mzd6TTMuMzMgMTkuNjUzYy0uMTY1LS4zNS0uMzA4LS43MDctNDIuNjUzLjc1Ljc1IDAgMS4zODgtLjU0MiAxLjQ5LTEuMjg1LjA0Ni0uMzI2LjEwNi0uNjUyLjE4LS45NzZhLjc1Ljc1IDAgMTExLjQ2LS41M2MuMTA2LjQzNy4xODkuODgxLjI0NSAxLjM0OGEuNzUuNzUgMCAwMS0xLjQ5LjIzM3pNTEuMzUzIDIuNzY3YS43NS43NSAwIDAxLjc1LS4wMTdsLjAwMS4wMDFoLjAwMWwuMDAyLjAwMWwuMDA3LjAwM2wuMDI0LjAxM2MuMDIuMDEuMDQ1LjAyNS4wNzkuMDQ2LjA2Ny4wNDIuMTYxLjEwMi4yNzUuMTc4LjIzMi4xNTEuNTUuMzgzLjg1Ni42NjdsLjAyNy4wMjRjLjYxNi41NTggMS4yMTIgMS4xNzYgMS43MzMgMS44NDNhLjc1Ljc1IDAgMDEtMS4yNC44N2MtLjQ3LS42NzEtMS4wMzEtMS4yOTItMS42LFsxLjYxNi0xLjYxNmEzLjc1IDMuNzUgMCAwMC01LjMwNS01LjMwNGwtMS4wNi0xuMDZBNy4yNDMgNy4yNDMgMCAwMTUxLjM1MyAyLjc2N3pNNDQuMzc5IDkuNjRsLTEuNTYgMS41NmE2Ljk5IDYuOTkgMCAwMTIuMjMgNC4zMzcgNi45OSA2Ljk5IDAgMDEtMi4yMyA1LjE3NmwtMS41Ni0xLjU2QTguNDkgOC40OSAwIDAwNDUuNSAxNS41YzAtMi4yOTYtLjg3NC00LjQzLTIuMTIxLTYuMDF6bS0zLjUzLTMuNTNsLTEuMDYxLTEuMDZhNy4yNDMgNy4yNDMgMCAwMTkuMTkyIDkuE2x0LTEuMDYgMS4wNjFhNS43NDkgNS43NDkgMCAwMC04LjEzLTguMTN6TTM0LjUgMTUuNWE4Ljk5IDguOTkgMCAwMC0yLjYzMSA2LjM2OS43NS43NSAwIDExLTEuNDk0LS43MzlDNzIuMzkgMjAuMDYgMzMuNSAxNy41NzUgMzMuNSAxNS41YzAtMi4zNzYgMS4wOTktNC40MzggMi44MTEtNS44MTJsLS4zOTYtLjM5NmEuNzUuNzUgMCAwMTEuMDYtMS4wNkwzNy41IDkuNDRWMmgtL4wMDJhLjc1Ljc1IDAgMDEtLjc0OC0uNzVWMS41YS43NS43NSAwIDAxLjc1LS43NWg2YS43NS43NSAwIDAxLjc1Ljc1di4yNWEwIC43NS0uNzUuNzVoLS4wMDF2Ny40NGwzLjUzNy0zLjUzN2EuNzUuNzUgMCAwMTEuMDYgMS4wNmwtLjM5Ni4zOTZDMzUuNDAxIDExLjA2MiAzNC41IDEzLjEyNCAzNC41IDE1LjV6TTM5IDIuMjV2Ni4wMy0uMDAyYTEuNSAxLjUgMCAwMS0uNDQ0IDEuMDZsLTEuMDYxIDEuMDZBOC40OSA4LjQ5IDAgMDAzOSAxNS41YzAgMi4yOTYtLjg3NCA0LjQzLTIuMTIxIDYuMDFsMS41NiAxLjU2QTYuOTkgNi45OSAwIDAwNDIgMTUuNWE2Ljk5IDYuOTkgMCAwMC0yLjIzLTUuMTc2bC0xLjU2LTEuNTZhMS41IDEuNSAwIDAxLS40NC0xLjA2VjIuMjVoLTF6TTI0IDkuNzVhLjc1Ljc1IDAgMDEtLjc1Ljc1di0uNWMwLS40MTQuMzM2LS43NS43NS0uNzVoLjVjLjQxNCAwIC43NS4zMzYuNzUuNzV2LjVhLjc1Ljc1IDAgMDEtLjc1Ljc1aC0uNXpNMTkuNSAxM2EuNzUuNzUgMCAwMS0uNzUtLjc1di0uNWMwLS40MTQuMzM2LS43NS43NS0uNzVoLjVjLjQxNCAwIC43NS4zMzYuNzUuNzV2LjVhLjc1Ljc1IDAgMDEtLjc1Ljc1aC0uNXpNMTUgMTYuMjVhLjc1Ljc1IDAgMDEuNzUtLjc1aC41Yy40MTQgMCAuNzUuMzM2Ljc1Ljc1di41YS43NS43NSAwIDAxLS43NS43NWgtLjVhLjc1Ljc1IDAgMDEtLjc1LS43NXYtLjV6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=';">
<div class="absolute inset-0 flex items-center justify-center bg-gray-100 dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600">
<span class="text-xs font-medium text-gray-500 dark:text-gray-300">@Model.AppName?[0]</span>
</div> </div>
</div> </div>
} }
<div class="ml-4 text-left"> <div class="ml-4 text-left">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">@Model.AppName</h3> <h3 class="text-lg font-medium">@Model.AppName</h3>
@if (!string.IsNullOrEmpty(Model.AppUri)) @if (!string.IsNullOrEmpty(Model.AppUri))
{ {
<a href="@Model.AppUri" class="text-sm text-blue-600 dark:text-blue-400 hover:text-blue-500 dark:hover:text-blue-300 transition-colors duration-200" target="_blank" rel="noopener noreferrer"> <a href="@Model.AppUri" class="text-sm link link-primary" target="_blank" rel="noopener noreferrer">
@Model.AppUri @Model.AppUri
</a> </a>
} }
@ -38,77 +34,69 @@
</div> </div>
</div> </div>
} }
<p class="mt-6 text-sm text-gray-600 dark:text-gray-300"> <p class="mt-6 text-sm text-center">
wants to access your account with the following permissions: wants to access your account with the following permissions:
</p> </p>
</div>
<div class="mt-6"> <div class="mt-6">
<ul class="border border-gray-200 dark:border-gray-700 rounded-lg divide-y divide-gray-200 dark:divide-gray-700 overflow-hidden"> <ul class="menu bg-base-200 rounded-box">
@if (Model.Scope != null) @if (Model.Scope != null)
{
var scopeDescriptions = new Dictionary<string, (string Name, string Description)>
{ {
["openid"] = ("OpenID", "Read your basic profile information"), var scopeDescriptions = new Dictionary<string, (string Name, string Description)>
["profile"] = ("Profile", "View your basic profile information"), {
["email"] = ("Email", "View your email address"), ["openid"] = ("OpenID", "Read your basic profile information"),
["offline_access"] = ("Offline Access", "Access your data while you're not using the application") ["profile"] = ("Profile", "View your basic profile information"),
}; ["email"] = ("Email", "View your email address"),
["offline_access"] = ("Offline Access", "Access your data while you're not using the application")
};
foreach (var scope in Model.Scope.Split(' ').Where(s => !string.IsNullOrWhiteSpace(s))) foreach (var scope in Model.Scope.Split(' ').Where(s => !string.IsNullOrWhiteSpace(s)))
{ {
var scopeInfo = scopeDescriptions.GetValueOrDefault(scope, (scope, scope.Replace('_', ' '))); var scopeInfo = scopeDescriptions.GetValueOrDefault(scope, (scope, scope.Replace('_', ' ')));
<li class="px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-150"> <li>
<div class="flex items-start"> <a>
<div class="flex-shrink-0 pt-0.5"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-success" viewBox="0 0 20 20" fill="currentColor">
<svg class="h-5 w-5 text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" /> <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
</svg> </svg>
</div> <div>
<div class="ml-3"> <p class="font-medium">@scopeInfo.Item1</p>
<p class="text-sm font-medium text-gray-900 dark:text-white">@scopeInfo.Item1</p> <p class="text-xs text-base-content/70">@scopeInfo.Item2</p>
<p class="text-xs text-gray-500 dark:text-gray-400">@scopeInfo.Item2</p> </div>
</div> </a>
</div> </li>
</li> }
} }
} </ul>
</ul>
<div class="mt-4 text-xs text-gray-500 dark:text-gray-400"> <div class="mt-4 text-xs text-base-content/70">
<p>By authorizing, you allow this application to access your information on your behalf.</p> <p>By authorizing, you allow this application to access your information on your behalf.</p>
</div>
</div> </div>
<form method="post" class="mt-8 space-y-4">
<input type="hidden" asp-for="ClientIdString" />
<input type="hidden" asp-for="ResponseType" name="response_type" />
<input type="hidden" asp-for="RedirectUri" name="redirect_uri" />
<input type="hidden" asp-for="Scope" name="scope" />
<input type="hidden" asp-for="State" name="state" />
<input type="hidden" asp-for="Nonce" name="nonce" />
<input type="hidden" asp-for="ReturnUrl" name="returnUrl" />
<input type="hidden" name="code_challenge" value="@HttpContext.Request.Query["code_challenge"]" />
<input type="hidden" name="code_challenge_method" value="@HttpContext.Request.Query["code_challenge_method"]" />
<input type="hidden" name="response_mode" value="@HttpContext.Request.Query["response_mode"]" />
<div class="card-actions justify-center">
<button type="submit" name="allow" value="true" class="btn btn-primary">Allow</button>
<button type="submit" name="allow" value="false" class="btn btn-ghost">Deny</button>
</div>
<div class="mt-4 pt-4 border-t border-base-300">
<p class="text-xs text-center text-base-content/70">
You can change these permissions later in your account settings.
</p>
</div>
</form>
</div> </div>
<form method="post" class="mt-8 space-y-4">
<input type="hidden" asp-for="ClientIdString" />
<input type="hidden" asp-for="ResponseType" name="response_type" />
<input type="hidden" asp-for="RedirectUri" name="redirect_uri" />
<input type="hidden" asp-for="Scope" name="scope" />
<input type="hidden" asp-for="State" name="state" />
<input type="hidden" asp-for="Nonce" name="nonce" />
<input type="hidden" asp-for="ReturnUrl" name="returnUrl" />
<input type="hidden" name="code_challenge" value="@HttpContext.Request.Query["code_challenge"]" />
<input type="hidden" name="code_challenge_method" value="@HttpContext.Request.Query["code_challenge_method"]" />
<input type="hidden" name="response_mode" value="@HttpContext.Request.Query["response_mode"]" />
<div class="flex flex-col space-y-3">
<button type="submit" name="allow" value="true"
class="w-full flex justify-center py-2.5 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-offset-gray-800 transition-colors duration-200">
Allow
</button>
<button type="submit" name="allow" value="false"
class="w-full flex justify-center py-2.5 px-4 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-offset-gray-800 transition-colors duration-200">
Deny
</button>
</div>
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<p class="text-xs text-center text-gray-500 dark:text-gray-400">
You can change these permissions later in your account settings.
</p>
</div>
</form>
</div> </div>
</div> </div>

View File

@ -5,10 +5,12 @@
Layout = "_Layout"; Layout = "_Layout";
} }
<div class="h-full flex items-center justify-center"> <div class="hero min-h-full bg-base-200">
<div class="max-w-lg w-full mx-auto p-6 text-center"> <div class="hero-content text-center">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Authentication Successful</h1> <div class="max-w-md">
<p class="mb-6 text-gray-900 dark:text-white">You can now close this window and return to the application.</p> <h1 class="text-5xl font-bold">Authentication Successful</h1>
<p class="py-6">You can now close this window and return to the application.</p>
</div>
</div> </div>
</div> </div>

View File

@ -6,8 +6,11 @@
Response.Redirect($"/web/auth/challenge/{Model.Id}/select-factor"); Response.Redirect($"/web/auth/challenge/{Model.Id}/select-factor");
} }
<div class="h-full flex items-center justify-center bg-gray-100 dark:bg-gray-900"> <div class="hero min-h-full bg-base-200">
<div class="bg-white dark:bg-gray-800 p-8 rounded-lg shadow-md w-full max-w-md text-center"> <div class="hero-content text-center">
<p>Redirecting to authentication page...</p> <div class="max-w-md">
<span class="loading loading-spinner loading-lg"></span>
<p class="py-6">Redirecting to authentication page...</p>
</div>
</div> </div>
</div> </div>

View File

@ -5,32 +5,32 @@
var returnUrl = Model.ReturnUrl ?? ""; var returnUrl = Model.ReturnUrl ?? "";
} }
<div class="h-full flex items-center justify-center bg-gray-100 dark:bg-gray-900"> <div class="hero min-h-full bg-base-200">
<div class="bg-white dark:bg-gray-800 p-8 rounded-lg shadow-md w-full max-w-md"> <div class="hero-content w-full max-w-md">
<h1 class="text-2xl font-bold text-center text-gray-900 dark:text-white mb-6">Login</h1> <div class="card w-full bg-base-100 shadow-xl">
<div class="card-body px-8 py-7">
<form method="post"> <h1 class="card-title justify-center text-2xl font-bold">Welcome back!</h1>
<input type="hidden" asp-for="ReturnUrl" value="@returnUrl" /> <p class="text-center">Login to your Solar Network account to continue.</p>
<div class="mb-4"> <form method="post" class="mt-4">
<label asp-for="Username" <input type="hidden" asp-for="ReturnUrl" value="@returnUrl"/>
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"></label> <div class="form-control">
<input asp-for="Username" <label class="label" asp-for="Username">
class="form-input mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50 dark:bg-gray-700 dark:border-gray-600 dark:text-white px-4 py-2"/> <span class="label-text">Username</span>
<span asp-validation-for="Username" class="text-red-500 text-sm mt-1"></span> </label>
<input asp-for="Username" class="input input-bordered w-full"/>
<span asp-validation-for="Username" class="text-error text-sm mt-1"></span>
</div>
<div class="form-control mt-6">
<button type="submit" class="btn btn-primary w-full">Next</button>
</div>
<div class="text-sm text-center mt-4">
<span class="text-base-content/70">Have no account?</span> <br/>
<a href="https://solian.app/#/auth/create-account" class="link link-primary">
Create a new account →
</a>
</div>
</form>
</div> </div>
<button type="submit"
class="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
Next
</button>
</form>
<div class="mt-8 flex flex-col text-sm text-center">
<span class="text-gray-900 dark:text-white opacity-80">Have no account?</span>
<a href="https://solian.app/#/auth/create-account"
class="text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300">
Create a new account →
</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,54 +5,92 @@
ViewData["Title"] = "Select Authentication Method"; ViewData["Title"] = "Select Authentication Method";
} }
<div class="h-full flex items-center justify-center bg-gray-100 dark:bg-gray-900"> <div class="hero min-h-full bg-base-200">
<div class="bg-white dark:bg-gray-800 p-8 rounded-lg shadow-md w-full max-w-md"> <div class="hero-content w-full max-w-md">
<h1 class="text-2xl font-bold text-center text-gray-900 dark:text-white mb-6">Select Authentication Method</h1> <div class="card w-full bg-base-100 shadow-xl">
<div class="card-body">
<h1 class="card-title justify-center text-2xl font-bold">Select Authentication Method</h1>
@if (Model.AuthChallenge == null) @if (Model.AuthChallenge == null)
{
<p class="text-red-500 text-center">Challenge not found or expired.</p>
}
else if (Model.AuthChallenge.StepRemain == 0)
{
<p class="text-green-600 dark:text-green-400 text-center">Challenge completed. Redirecting...</p>
}
else
{
<p class="text-gray-700 dark:text-gray-300 mb-4">Please select an authentication method:</p>
<div class="space-y-4">
@foreach (var factor in Model.AuthFactors)
{ {
<div class="mb-4"> <div class="alert alert-error">
<form method="post" asp-page-handler="SelectFactor" class="w-full" id="factor-@factor.Id"> <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none"
<input type="hidden" name="SelectedFactorId" value="@factor.Id"/> viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>Challenge not found or expired.</span>
</div>
}
else if (Model.AuthChallenge.StepRemain == 0)
{
<div class="alert alert-success">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>Challenge completed. Redirecting...</span>
</div>
}
else
{
<p class="text-center">Please select an authentication method:</p>
@if (factor.Type == AccountAuthFactorType.EmailCode) <div class="space-y-4">
{ @foreach (var factor in Model.AuthFactors)
<div class="mb-3"> {
<label for="hint-@factor.Id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> <form method="post" asp-page-handler="SelectFactor" class="w-full" id="factor-@factor.Id">
Email to send code to <input type="hidden" name="SelectedFactorId" value="@factor.Id"/>
</label>
<input type="email"
id="hint-@factor.Id"
name="hint"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
placeholder="Enter your email"
required>
</div>
}
<button type="submit" @if (factor.Type == AccountAuthFactorType.EmailCode)
class="w-full text-left p-4 bg-gray-50 dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 rounded-lg transition-colors"> {
<div class="font-medium text-gray-900 dark:text-white">@GetFactorDisplayName(factor.Type)</div> <div class="card w-full bg-base-200 card-sm shadow-sm rounded-md">
<div class="text-sm text-gray-500 dark:text-gray-400">@GetFactorDescription(factor.Type)</div> <div class="py-4 px-5 align-items-center">
</button> <div>
</form> <h2 class="card-title">@GetFactorDisplayName(factor.Type)</h2>
<p>@GetFactorDescription(factor.Type)</p>
</div>
<div class="join w-full mt-2">
<div class="flex-1">
<label class="input join-item input-sm">
<input id="hint-@factor.Id" type="email"
placeholder="mail@site.com" required/>
</label>
</div>
<button class="btn btn-primary join-item btn-sm">
<span class="material-symbols-outlined">
arrow_right_alt
</span>
</button>
</div>
</div>
</div>
}
else
{
<div class="card w-full bg-base-200 card-sm shadow-sm rounded-md">
<div class="flex py-4 px-5 align-items-center">
<div class="flex-1">
<h2 class="card-title">@GetFactorDisplayName(factor.Type)</h2>
<p>@GetFactorDescription(factor.Type)</p>
</div>
<div class="justify-end card-actions">
<button type="submit" class="btn btn-primary btn-sm">
<span class="material-symbols-outlined">
arrow_right_alt
</span>
</button>
</div>
</div>
</div>
}
</form>
}
</div> </div>
} }
</div> </div>
} </div>
</div> </div>
</div> </div>

View File

@ -5,70 +5,79 @@
ViewData["Title"] = "Verify Your Identity"; ViewData["Title"] = "Verify Your Identity";
} }
<div class="h-full flex items-center justify-center bg-gray-100 dark:bg-gray-900"> <div class="hero min-h-full bg-base-200">
<div class="bg-white dark:bg-gray-800 px-8 pt-8 pb-4 rounded-lg shadow-md w-full max-w-md"> <div class="hero-content w-full max-w-md">
<h1 class="text-2xl font-bold text-center text-gray-900 dark:text-white mb-2">Verify Your Identity</h1> <div class="card w-full bg-base-100 shadow-xl">
<p class="text-center text-gray-600 dark:text-gray-300 mb-6"> <div class="card-body px-8 py-7">
@switch (Model.FactorType) <h1 class="card-title justify-center text-2xl font-bold">Verify Your Identity</h1>
{ <p class="text-center">
case AccountAuthFactorType.EmailCode: @switch (Model.FactorType)
<span>We've sent a verification code to your email.</span> {
break; case AccountAuthFactorType.EmailCode:
case AccountAuthFactorType.InAppCode: <span>We've sent a verification code to your email.</span>
<span>Enter the code from your authenticator app.</span> break;
break; case AccountAuthFactorType.InAppCode:
case AccountAuthFactorType.TimedCode: <span>Enter the code from your authenticator app.</span>
<span>Enter your time-based verification code.</span> break;
break; case AccountAuthFactorType.TimedCode:
case AccountAuthFactorType.PinCode: <span>Enter your time-based verification code.</span>
<span>Enter your PIN code.</span> break;
break; case AccountAuthFactorType.PinCode:
case AccountAuthFactorType.Password: <span>Enter your PIN code.</span>
<span>Enter your password.</span> break;
break; case AccountAuthFactorType.Password:
default: <span>Enter your password.</span>
<span>Please verify your identity.</span> break;
break; default:
} <span>Please verify your identity.</span>
</p> break;
}
</p>
@if (Model.AuthChallenge == null) @if (Model.AuthChallenge == null)
{ {
<p class="text-red-500 text-center">Challenge not found or expired.</p> <div class="alert alert-error">
} <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
else if (Model.AuthChallenge.StepRemain == 0) <span>Challenge not found or expired.</span>
{ </div>
<p class="text-green-600 dark:text-green-400 text-center">Verification successful. Redirecting...</p> }
} else if (Model.AuthChallenge.StepRemain == 0)
else {
{ <div class="alert alert-success">
<form method="post" class="space-y-4"> <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<div asp-validation-summary="ModelOnly" class="text-red-500 text-sm"></div> <span>Verification successful. Redirecting...</span>
</div>
}
else
{
<form method="post" class="space-y-4">
<div asp-validation-summary="ModelOnly" class="text-error text-sm"></div>
<div class="mb-4"> <div class="form-control">
<label asp-for="Code" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"> <label asp-for="Code" class="label">
@(Model.FactorType == AccountAuthFactorType.Password ? "Use your password" : "Verification Code") <span class="label-text">@(Model.FactorType == AccountAuthFactorType.Password ? "Use your password" : "Verification Code")</span>
</label> </label>
<input asp-for="Code" <input asp-for="Code"
class="form-input mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50 dark:bg-gray-700 dark:border-gray-600 dark:text-white px-4 py-2" class="input input-bordered w-full"
autocomplete="one-time-code" autocomplete="one-time-code"
type="password" type="password"
autofocus /> autofocus />
<span asp-validation-for="Code" class="text-red-500 text-sm mt-1"></span> <span asp-validation-for="Code" class="text-error text-sm mt-1"></span>
</div> </div>
<button type="submit" <div class="form-control mt-6">
class="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"> <button type="submit" class="btn btn-primary w-full">Verify</button>
Verify </div>
</button>
<div class="text-center mt-4"> <div class="text-center mt-4">
<a asp-page="SelectFactor" asp-route-id="@Model.Id" class="text-sm text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300"> <a asp-page="SelectFactor" asp-route-id="@Model.Id" class="link link-primary text-sm">
← Back to authentication methods ← Back to authentication methods
</a> </a>
</div> </div>
</form> </form>
} }
</div>
</div>
</div> </div>
</div> </div>

View File

@ -38,72 +38,72 @@
</script> </script>
} }
<div class="h-full flex items-center justify-center"> <div class="hero min-h-full bg-base-200">
<div class="max-w-lg w-full mx-auto p-6"> <div class="hero-content text-center">
<div class="text-center"> <div class="max-w-md">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Security Check</h1> <div class="card bg-base-100 shadow-xl">
<p class="mb-6 text-gray-900 dark:text-white">Please complete the contest below to confirm you're not a robot</p> <div class="card-body">
<h1 class="card-title">Security Check</h1>
<p>Please complete the contest below to confirm you're not a robot</p>
<div class="flex justify-center mb-8"> <div class="flex justify-center my-8">
@switch (provider) @switch (provider)
{ {
case "cloudflare": case "cloudflare":
<div class="cf-turnstile" <div class="cf-turnstile"
data-sitekey="@apiKey" data-sitekey="@apiKey"
data-callback="onSuccess"> data-callback="onSuccess">
</div> </div>
break; break;
case "recaptcha": case "recaptcha":
<div class="g-recaptcha" <div class="g-recaptcha"
data-sitekey="@apiKey" data-sitekey="@apiKey"
data-callback="onSuccess"> data-callback="onSuccess">
</div> </div>
break; break;
case "hcaptcha": case "hcaptcha":
<div class="h-captcha" <div class="h-captcha"
data-sitekey="@apiKey" data-sitekey="@apiKey"
data-callback="onSuccess"> data-callback="onSuccess">
</div> </div>
break; break;
default: default:
<div class="p-4 bg-yellow-100 dark:bg-yellow-900 rounded-lg"> <div class="alert alert-warning">
<p class="text-yellow-800 dark:text-yellow-200"> <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
Captcha provider not configured correctly. <span>Captcha provider not configured correctly.</span>
</p> </div>
</div> break;
break; }
} </div>
</div>
</div>
<div class="mt-8 text-center text-sm"> <div class="text-center text-sm">
<div class="font-semibold text-gray-700 dark:text-gray-300 mb-1">Solar Network Anti-Robot</div> <div class="font-semibold mb-1">Solar Network Anti-Robot</div>
<div class="text-gray-600 dark:text-gray-400"> <div class="text-base-content/70">
Powered by Powered by
@switch (provider) @switch (provider)
{ {
case "cloudflare": case "cloudflare":
<a href="https://www.cloudflare.com/turnstile/" <a href="https://www.cloudflare.com/turnstile/" class="link link-hover">
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"> Cloudflare Turnstile
Cloudflare Turnstile </a>
</a> break;
break; case "recaptcha":
case "recaptcha": <a href="https://www.google.com/recaptcha/" class="link link-hover">
<a href="https://www.google.com/recaptcha/" Google reCaptcha
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"> </a>
Google reCaptcha break;
</a> default:
break; <span>Nothing</span>
default: break;
<span>Nothing</span> }
break; <br/>
} Hosted by
<br/> <a href="https://github.com/Solsynth/DysonNetwork" class="link link-hover">
Hosted by DysonNetwork.Sphere
<a href="https://github.com/Solsynth/DysonNetwork" </a>
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"> </div>
DysonNetwork.Sphere </div>
</a> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,26 +4,20 @@
ViewData["Title"] = "The Solar Network"; ViewData["Title"] = "The Solar Network";
} }
<div class="container-default h-full text-center flex flex-col justify-center items-center"> <div class="hero min-h-full bg-base-200">
<div class="mx-auto max-w-2xl"> <div class="hero-content text-center">
<h1 class="text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-6xl"> <div class="max-w-md">
Solar Network <h1 class="text-5xl font-bold">Solar Network</h1>
</h1> <p class="py-6">This Solar Network instance is up and running.</p>
<p class="mt-6 text-lg leading-8 text-gray-600 dark:text-gray-300"> <a href="https://sn.solsynth.dev" target="_blank" class="btn btn-primary">Get started</a>
This Solar Network instance is up and running. <div class="flex items-center justify-center gap-x-6 mt-6">
</p> <a href="/swagger" target="_blank" class="btn btn-ghost">
<div class="mt-10 flex items-center justify-center gap-x-6"> <span aria-hidden="true">λ&nbsp;</span> API Docs
<a href="https://sn.solsynth.dev" target="_blank" class="btn-primary"> </a>
Get started <a href="https://kb.solsynth.dev" target="_blank" class="btn btn-ghost">
</a> Learn more <span aria-hidden="true">→</span>
</div> </a>
<div class="flex items-center justify-center gap-x-6 mt-6"> </div>
<a href="/swagger" target="_blank" class="btn-text">
<span aria-hidden="true">λ&nbsp;</span> API Docs
</a>
<a href="https://kb.solsynth.dev" target="_blank" class="btn-text">
Learn more <span aria-hidden="true">→</span>
</a>
</div>
</div> </div>
</div>
</div> </div>

View File

@ -6,6 +6,5 @@ public class IndexModel : PageModel
{ {
public void OnGet() public void OnGet()
{ {
// Add any page initialization logic here
} }
} }

View File

@ -5,34 +5,44 @@
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>@ViewData["Title"]</title> <title>@ViewData["Title"]</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+Mono:wght@100..900&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="~/css/styles.css" asp-append-version="true"/> <link rel="stylesheet" href="~/css/styles.css" asp-append-version="true"/>
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
rel="stylesheet"
/>
</head> </head>
<body class="h-[calc(100dvh-118px)] mt-[64px] bg-white dark:bg-gray-900"> <body class="h-full bg-base-200">
<header class="bg-white dark:bg-gray-800 shadow-sm fixed left-0 right-0 top-0 z-50"> <header class="navbar bg-base-100/35 backdrop-blur-md shadow-xl fixed left-0 right-0 top-0 z-50 px-5">
<nav class="container-default"> <div class="flex-1">
<div class="flex justify-between h-16 items-center"> <a class="btn btn-ghost text-xl">Solar Network</a>
<div class="flex"> </div>
<a href="/" class="text-xl font-bold text-gray-900 dark:text-white">Solar Network</a> <div class="flex-none">
</div> <ul class="menu menu-horizontal px-1">
<div class="flex items-center ml-auto"> @if (Context.Request.Cookies.TryGetValue(AuthConstants.CookieTokenName, out _))
@if (Context.Request.Cookies.TryGetValue(AuthConstants.CookieTokenName, out _)) {
{ <li><a href="/web/account/profile">Profile</a></li>
<a href="/web/account/profile" class="text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300 px-3 py-2 rounded-md text-sm font-medium">Profile</a> <li>
<form method="post" asp-page="/Account/Profile" asp-page-handler="Logout" class="inline"> <form method="post" asp-page="/Account/Profile" asp-page-handler="Logout">
<button type="submit" class="text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300 px-3 py-2 rounded-md text-sm font-medium">Logout</button> <button type="submit">Logout</button>
</form> </form>
} </li>
else }
{ else
<a href="/web/auth/login" class="text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-300 px-3 py-2 rounded-md text-sm font-medium">Login</a> {
} <li>
</div> <a href="/web/auth/login"><span class="material-symbols-outlined">login</span></a>
</div> </li>
</nav> }
</ul>
</div>
</header> </header>
@* The header 64px *@ <main class="h-full pt-16">
<main class="h-full">
@RenderBody() @RenderBody()
</main> </main>

View File

@ -6,95 +6,85 @@
ViewData["Title"] = "Magic Spell"; ViewData["Title"] = "Magic Spell";
} }
<div class="h-full flex items-center justify-center"> <div class="hero min-h-full bg-base-200">
<div class="max-w-lg w-full mx-auto p-6"> <div class="hero-content text-center">
<div class="text-center"> <div class="max-w-md">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-4">Magic Spell</h1> <h1 class="text-5xl font-bold mb-4">Magic Spell</h1>
@if (Model.IsSuccess) @if (Model.IsSuccess)
{ {
<div class="p-4 bg-green-100 dark:bg-green-900 rounded-lg mb-6"> <div class="alert alert-success">
<p class="text-green-800 dark:text-green-200">The spell was applied successfully!</p> <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<p class="text-green-800 dark:text-green-200 opacity-80">Now you can close this page.</p> <span>The spell was applied successfully!</span>
<p>Now you can close this page.</p>
</div> </div>
} }
else if (Model.CurrentSpell == null) else if (Model.CurrentSpell == null)
{ {
<div class="p-4 bg-yellow-100 dark:bg-yellow-900 rounded-lg"> <div class="alert alert-warning">
<p class="text-yellow-800 dark:text-yellow-200">The spell was expired or does not exist.</p> <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
<span>The spell was expired or does not exist.</span>
</div> </div>
} }
else else
{ {
<div <div class="card bg-base-100 shadow-xl">
class="px-4 py-12 bg-white dark:bg-gray-800 text-gray-900 dark:text-white shadow-lg rounded-lg mb-6"> <div class="card-body">
<div class="mb-2"> <h2 class="card-title">
<p> @System.Text.RegularExpressions.Regex.Replace(Model.CurrentSpell!.Type.ToString(), "([a-z])([A-Z])", "$1 $2")
<span class="font-medium">The spell is for </span> </h2>
<span <p>for @@ @Model.CurrentSpell.Account?.Name</p>
class="font-bold">@System.Text.RegularExpressions.Regex.Replace(Model.CurrentSpell!.Type.ToString(), "([a-z])([A-Z])", "$1 $2")</span> <div class="text-sm opacity-80">
</p> @if (Model.CurrentSpell.ExpiresAt.HasValue)
<p><span class="font-medium">for @@</span>@Model.CurrentSpell.Account?.Name</p> {
</div> <p>Available until @Model.CurrentSpell.ExpiresAt.Value.ToDateTimeUtc().ToString("g")</p>
<div class="text-sm opacity-80"> }
@if (Model.CurrentSpell.ExpiresAt.HasValue) @if (Model.CurrentSpell.AffectedAt.HasValue)
{ {
<p>Available until @Model.CurrentSpell.ExpiresAt.Value.ToDateTimeUtc().ToString("g")</p> <p>Available after @Model.CurrentSpell.AffectedAt.Value.ToDateTimeUtc().ToString("g")</p>
} }
@if (Model.CurrentSpell.AffectedAt.HasValue)
{
<p>Available after @Model.CurrentSpell.AffectedAt.Value.ToDateTimeUtc().ToString("g")</p>
}
</div>
<p class="text-sm opacity-80">Would you like to apply this spell?</p>
</div>
<form method="post" class="mt-4">
<input type="hidden" asp-for="CurrentSpell!.Id"/>
@if (Model.CurrentSpell?.Type == MagicSpellType.AuthPasswordReset)
{
<div class="mb-4">
<label
asp-for="NewPassword"
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
>
New Password
</label>
<input type="password"
asp-for="NewPassword"
required
minlength="8"
style="padding: 0.5rem 1rem"
placeholder="Your new password"
class="w-full border-2 border-gray-300 dark:border-gray-600 rounded-lg
focus:ring-2 focus:ring-blue-400
dark:text-white bg-gray-100 dark:bg-gray-800"/>
</div> </div>
} <p class="text-sm opacity-80">Would you like to apply this spell?</p>
<button type="submit" <form method="post" class="mt-4">
class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors <input type="hidden" asp-for="CurrentSpell!.Id"/>
transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-400">
Apply @if (Model.CurrentSpell?.Type == MagicSpellType.AuthPasswordReset)
</button> {
</form> <div class="form-control w-full max-w-xs">
<label class="label" asp-for="NewPassword">
<span class="label-text">New Password</span>
</label>
<input type="password"
asp-for="NewPassword"
required
minlength="8"
placeholder="Your new password"
class="input input-bordered w-full max-w-xs"/>
</div>
}
<div class="card-actions justify-end mt-4">
<button type="submit" class="btn btn-primary">Apply</button>
</div>
</form>
</div>
</div>
} }
</div>
<div class="mt-8 text-center text-sm"> <div class="mt-8 text-center text-sm">
<div class="font-semibold text-gray-700 dark:text-gray-300 mb-1">Solar Network</div> <div class="font-semibold mb-1">Solar Network</div>
<div class="text-gray-600 dark:text-gray-400"> <div class="text-base-content/70">
<a href="https://solsynth.dev" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"> <a href="https://solsynth.dev" class="link link-hover">
Solsynth LLC Solsynth LLC
</a> </a>
&copy; @DateTime.Now.Year &copy; @DateTime.Now.Year
<br/> <br/>
Powered by Powered by
<a href="https://github.com/Solsynth/DysonNetwork" <a href="https://github.com/Solsynth/DysonNetwork" class="link link-hover">
class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors"> DysonNetwork.Sphere
DysonNetwork.Sphere </a>
</a> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -6,7 +6,8 @@
"css:build": "npx @tailwindcss/cli -i ./wwwroot/css/site.css -o ./wwwroot/css/styles.css" "css:build": "npx @tailwindcss/cli -i ./wwwroot/css/site.css -o ./wwwroot/css/styles.css"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/cli": "^4.1.7",
"@tailwindcss/postcss": "^4.1.7", "@tailwindcss/postcss": "^4.1.7",
"@tailwindcss/cli": "^4.1.7" "daisyui": "^5.0.46"
} }
} }

View File

@ -1,10 +1,88 @@
@import "tailwindcss"; @import "tailwindcss";
@plugin "daisyui";
@layer theme, base, components, utilities; @layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme); @import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base); @import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css" layer(utilities); @import "tailwindcss/utilities.css" layer(utilities);
@theme {
--font-sans: "Nunito", sans-serif;
--font-mono: "Noto Sans Mono", monospace;
}
@plugin "daisyui/theme" {
name: "light";
default: true;
prefersdark: false;
color-scheme: "light";
--color-base-100: oklch(100% 0 0);
--color-base-200: oklch(98% 0 0);
--color-base-300: oklch(95% 0 0);
--color-base-content: oklch(21% 0.006 285.885);
--color-primary: oklch(62% 0.0873 281deg);
--color-primary-content: oklch(93% 0.034 272.788);
--color-secondary: oklch(62% 0.214 259.815);
--color-secondary-content: oklch(94% 0.028 342.258);
--color-accent: oklch(77% 0.152 181.912);
--color-accent-content: oklch(38% 0.063 188.416);
--color-neutral: oklch(14% 0.005 285.823);
--color-neutral-content: oklch(92% 0.004 286.32);
--color-info: oklch(82% 0.111 230.318);
--color-info-content: oklch(29% 0.066 243.157);
--color-success: oklch(79% 0.209 151.711);
--color-success-content: oklch(37% 0.077 168.94);
--color-warning: oklch(82% 0.189 84.429);
--color-warning-content: oklch(41% 0.112 45.904);
--color-error: oklch(71% 0.194 13.428);
--color-error-content: oklch(27% 0.105 12.094);
--radius-selector: 0.5rem;
--radius-field: 0.5rem;
--radius-box: 1rem;
--size-selector: 0.28125rem;
--size-field: 0.28125rem;
--border: 1px;
--depth: 1;
--noise: 1;
}
@plugin "daisyui/theme" {
name: "dark";
default: false;
prefersdark: true;
color-scheme: "dark";
--color-base-100: oklch(0% 0 0);
--color-base-200: oklch(20% 0.016 285.938);
--color-base-300: oklch(25% 0.013 285.805);
--color-base-content: oklch(97.807% 0.029 256.847);
--color-primary: oklch(50% 0.0873 281deg);
--color-primary-content: oklch(96% 0.018 272.314);
--color-secondary: oklch(62% 0.214 259.815);
--color-secondary-content: oklch(94% 0.028 342.258);
--color-accent: oklch(77% 0.152 181.912);
--color-accent-content: oklch(38% 0.063 188.416);
--color-neutral: oklch(21% 0.006 285.885);
--color-neutral-content: oklch(92% 0.004 286.32);
--color-info: oklch(82% 0.111 230.318);
--color-info-content: oklch(29% 0.066 243.157);
--color-success: oklch(79% 0.209 151.711);
--color-success-content: oklch(37% 0.077 168.94);
--color-warning: oklch(82% 0.189 84.429);
--color-warning-content: oklch(41% 0.112 45.904);
--color-error: oklch(64% 0.246 16.439);
--color-error-content: oklch(27% 0.105 12.094);
--radius-selector: 0.5rem;
--radius-field: 0.5rem;
--radius-box: 1rem;
--size-selector: 0.28125rem;
--size-field: 0.28125rem;
--border: 1px;
--depth: 1;
--noise: 1;
}
@layer base { @layer base {
html, body { html, body {
padding: 0; padding: 0;
@ -12,6 +90,10 @@
box-sizing: border-box; box-sizing: border-box;
} }
.material-symbols-outlined {
font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48;
}
/* For Firefox. */ /* For Firefox. */
* { * {
scrollbar-width: none; scrollbar-width: none;
@ -23,14 +105,6 @@
} }
} }
.btn-primary {
@apply px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors dark:bg-blue-600 dark:hover:bg-blue-700;
}
.btn-text {
@apply text-sm font-semibold leading-6 text-gray-900 dark:text-gray-100 hover:text-gray-700 dark:hover:text-gray-300;
}
.container-default { .container-default {
@apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8; @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8;
} }

File diff suppressed because it is too large Load Diff

View File

@ -70,6 +70,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APutObjectArgs_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F6efe388c7585d5dd5587416a55298550b030c2a107edf45f988791297c3ffa_003FPutObjectArgs_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APutObjectArgs_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F6efe388c7585d5dd5587416a55298550b030c2a107edf45f988791297c3ffa_003FPutObjectArgs_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AQueryable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F42d8f09d6a294d00a6f49efc989927492fe00_003F4e_003F26d1ee34_003FQueryable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AQueryable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F42d8f09d6a294d00a6f49efc989927492fe00_003F4e_003F26d1ee34_003FQueryable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AQueryable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcbafb95b4df34952928f87356db00c8f2fe00_003F9b_003F8ba036bb_003FQueryable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AQueryable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcbafb95b4df34952928f87356db00c8f2fe00_003F9b_003F8ba036bb_003FQueryable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARazorPage_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F81d2924a2bbd4b0c864a1d23cbf5f0893d200_003F5f_003Fc110be1c_003FRazorPage_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AResizeOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fef3339e864a448e2b1ec6fa7bbf4c6661fee00_003F48_003F0209e410_003FResizeOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AResizeOptions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fef3339e864a448e2b1ec6fa7bbf4c6661fee00_003F48_003F0209e410_003FResizeOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AResourceManagerStringLocalizerFactory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb62f365d06c44ad695ff75960cdf97a2a800_003Fe4_003Ff6ba93b7_003FResourceManagerStringLocalizerFactory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AResourceManagerStringLocalizerFactory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb62f365d06c44ad695ff75960cdf97a2a800_003Fe4_003Ff6ba93b7_003FResourceManagerStringLocalizerFactory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARSA_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fee4f989f6b8042b59b2654fdc188e287243600_003F8b_003F44e5f855_003FRSA_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARSA_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fee4f989f6b8042b59b2654fdc188e287243600_003F8b_003F44e5f855_003FRSA_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>