226 lines
12 KiB
Plaintext
226 lines
12 KiB
Plaintext
@page "/web/account/profile"
|
|
@model DysonNetwork.Sphere.Pages.Account.ProfileModel
|
|
@{
|
|
ViewData["Title"] = "Profile";
|
|
}
|
|
|
|
@if (Model.Account != null)
|
|
{
|
|
<div class="p-4 sm:p-8 bg-base-200">
|
|
<div class="max-w-6xl mx-auto">
|
|
<!-- Header -->
|
|
<div class="mb-8">
|
|
<h1 class="text-3xl font-bold">Profile Settings</h1>
|
|
<p class="text-base-content/70 mt-2">Manage your account information and preferences</p>
|
|
</div>
|
|
|
|
<!-- Two Column Layout -->
|
|
<div class="flex flex-col md:flex-row gap-6">
|
|
<!-- Left Pane - Profile Card -->
|
|
<div class="w-full md:w-1/3 lg:w-1/4">
|
|
<div class="card bg-base-100 shadow-xl sticky top-8">
|
|
<div class="card-body items-center text-center">
|
|
<!-- Avatar -->
|
|
<div class="avatar avatar-placeholder mb-4">
|
|
<div class="bg-neutral text-neutral-content rounded-full w-32">
|
|
<span class="text-4xl">@Model.Account.Name?[..1].ToUpper()</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Basic Info -->
|
|
<h2 class="card-title">@Model.Account.Nick</h2>
|
|
<p class="font-mono text-sm">@@@Model.Account.Name</p>
|
|
|
|
<!-- Stats -->
|
|
<div class="stats stats-vertical shadow mt-4">
|
|
<div class="stat">
|
|
<div class="stat-title">Level</div>
|
|
<div class="stat-value">@Model.Account.Profile.Level</div>
|
|
</div>
|
|
<div class="stat">
|
|
<div class="stat-title">XP</div>
|
|
<div class="stat-value">@Model.Account.Profile.Experience</div>
|
|
</div>
|
|
<div class="stat">
|
|
<div class="stat-title">Member since</div>
|
|
<div class="stat-value">@Model.Account.CreatedAt.ToDateTimeUtc().ToString("yyyy/MM")</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Pane - Tabbed Content -->
|
|
<div class="flex-1">
|
|
<div role="tablist" class="tabs tabs-lift w-full">
|
|
<input type="radio" name="profile-tabs" role="tab" class="tab" aria-label="Profile" checked />
|
|
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 p-6">
|
|
<h2 class="text-xl font-semibold mb-6">Profile Information</h2>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<h3 class="text-lg font-medium mb-4">Basic Information</h3>
|
|
<dl class="space-y-4">
|
|
<div>
|
|
<dt class="text-sm font-medium text-base-content/70">Full Name</dt>
|
|
<dd class="mt-1 text-sm">@($"{Model.Account.Profile.FirstName} {Model.Account.Profile.MiddleName} {Model.Account.Profile.LastName}".Trim())</dd>
|
|
</div>
|
|
<div>
|
|
<dt class="text-sm font-medium text-base-content/70">Username</dt>
|
|
<dd class="mt-1 text-sm">@Model.Account.Name</dd>
|
|
</div>
|
|
<div>
|
|
<dt class="text-sm font-medium text-base-content/70">Nickname</dt>
|
|
<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>
|
|
|
|
<input type="radio" name="profile-tabs" role="tab" class="tab" aria-label="Security" />
|
|
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 p-6">
|
|
<h2 class="text-xl font-semibold mb-2">Security Settings</h2>
|
|
|
|
<div class="space-y-6">
|
|
<div class="card bg-base-300 shadow-xl">
|
|
<div class="card-body">
|
|
<h3 class="card-title">Access Token</h3>
|
|
<p>Use this token to authenticate with the API</p>
|
|
<div class="form-control">
|
|
<div class="join">
|
|
<input type="password" id="accessToken" value="@Model.AccessToken" readonly class="input input-bordered join-item flex-grow" />
|
|
<button onclick="copyAccessToken()" class="btn join-item">Copy</button>
|
|
</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>
|
|
|
|
<input type="radio" name="profile-tabs" role="tab" class="tab" aria-label="Sessions" />
|
|
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 p-6">
|
|
<h2 class="text-xl font-semibold">Active 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>
|
|
|
|
<div class="card bg-base-300 shadow-xl">
|
|
<div class="card-body">
|
|
<div class="overflow-x-auto">
|
|
<table class="table">
|
|
<tbody>
|
|
<tr>
|
|
<td>
|
|
<div class="flex items-center gap-3">
|
|
<div class="avatar">
|
|
<div class="mask mask-squircle w-12 h-12">
|
|
<svg class="h-full w-full text-blue-600 dark:text-blue-400" fill="currentColor" viewBox="0 0 20 20">
|
|
<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>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<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>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="card-actions justify-end mt-4">
|
|
<button type="button" class="btn btn-error">Sign out all other sessions</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logout Button -->
|
|
<div class="mt-6 flex justify-end">
|
|
<form method="post" asp-page-handler="Logout">
|
|
<button type="submit" class="btn btn-error">Sign out</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="hero min-h-screen bg-base-200">
|
|
<div class="hero-content text-center">
|
|
<div class="max-w-md">
|
|
<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>
|
|
</div>
|
|
}
|
|
|
|
@section Scripts {
|
|
<script>
|
|
// Copy access token to clipboard
|
|
function copyAccessToken() {
|
|
const copyText = document.getElementById("accessToken");
|
|
copyText.select();
|
|
copyText.setSelectionRange(0, 99999);
|
|
document.execCommand("copy");
|
|
|
|
// Show tooltip or notification
|
|
const originalText = event.target.innerHTML;
|
|
event.target.innerHTML = '<i class="fas fa-check mr-1"></i> Copied!';
|
|
event.target.disabled = true;
|
|
|
|
setTimeout(() => {
|
|
event.target.innerHTML = originalText;
|
|
event.target.disabled = false;
|
|
}, 2000);
|
|
}
|
|
|
|
// Toggle password visibility
|
|
function togglePasswordVisibility(inputId) {
|
|
const input = document.getElementById(inputId);
|
|
const icon = document.querySelector(`[onclick="togglePasswordVisibility('${inputId}')"] i`);
|
|
|
|
if (input.type === 'password') {
|
|
input.type = 'text';
|
|
icon.classList.remove('fa-eye');
|
|
icon.classList.add('fa-eye-slash');
|
|
} else {
|
|
input.type = 'password';
|
|
icon.classList.remove('fa-eye-slash');
|
|
icon.classList.add('fa-eye');
|
|
}
|
|
}
|
|
</script>
|
|
}
|