Redesign header menu: separate account dropdown from dashboard nav
The profile dropdown was doing three jobs at once (account actions, the job-seeker panel menu, and the admin panel menu) and a stray inline @if for the notification badge leaked into the markup as literal text. - Profile dropdown is now account-only: identity card (avatar + name + phone), one role-aware dashboard entry, edit profile, logout. This removes the leaked @if and de-clutters the menu. - Dashboard menu is centralized in _PanelNav and auto-rendered by the layout on every logged-in panel page (/Admin, /Me, /Employer, /Preferences) instead of being duplicated in the dropdown and pages. - Drop the now-duplicate manual <partial name="_PanelNav" /> from Overview, Ingested, Me/Index, Employer/Index. - CSS: identity-card (.pd-id) styles + mobile tweaks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,8 +8,6 @@
|
|||||||
$"<a class=\"ing-pill {(Model.Status == key || (Model.Status is null && key == "all") ? "active" : "")}\" href=\"?status={key}\">{label} ({P(count)})</a>";
|
$"<a class=\"ing-pill {(Model.Status == key || (Model.Status is null && key == "all") ? "active" : "")}\" href=\"?status={key}\">{label} ({P(count)})</a>";
|
||||||
}
|
}
|
||||||
|
|
||||||
<partial name="_PanelNav" />
|
|
||||||
|
|
||||||
<div class="page-head">
|
<div class="page-head">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>نتایج جمعآوری</h1>
|
<h1>نتایج جمعآوری</h1>
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
string P(int n) => JalaliDate.ToPersianDigits(n.ToString());
|
string P(int n) => JalaliDate.ToPersianDigits(n.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
<partial name="_PanelNav" />
|
|
||||||
|
|
||||||
<div class="page-head">
|
<div class="page-head">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>داشبورد مدیریت</h1>
|
<h1>داشبورد مدیریت</h1>
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
<partial name="_PanelNav" />
|
|
||||||
|
|
||||||
<div class="page-head">
|
<div class="page-head">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>پنل مرکز درمانی</h1>
|
<h1>پنل مرکز درمانی</h1>
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
<partial name="_PanelNav" />
|
|
||||||
|
|
||||||
<div class="page-head">
|
<div class="page-head">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>پنل کارجو</h1>
|
<h1>پنل کارجو</h1>
|
||||||
|
|||||||
@@ -6,20 +6,26 @@
|
|||||||
var title = ViewData["Title"] as string;
|
var title = ViewData["Title"] as string;
|
||||||
int unreadCount = 0;
|
int unreadCount = 0;
|
||||||
int meId = 0;
|
int meId = 0;
|
||||||
string? meName = null;
|
string? meFullName = null;
|
||||||
|
string? mePhone = null;
|
||||||
bool meHasAvatar = false;
|
bool meHasAvatar = false;
|
||||||
if (User.Identity?.IsAuthenticated == true && int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier), out meId))
|
if (User.Identity?.IsAuthenticated == true && int.TryParse(User.FindFirstValue(ClaimTypes.NameIdentifier), out meId))
|
||||||
{
|
{
|
||||||
unreadCount = await Notifications.UnreadCountAsync(meId);
|
unreadCount = await Notifications.UnreadCountAsync(meId);
|
||||||
var info = await Db.Users.Where(u => u.Id == meId)
|
var info = await Db.Users.Where(u => u.Id == meId)
|
||||||
.Select(u => new { u.FullName, u.Phone, HasAvatar = u.Avatar != null }).FirstOrDefaultAsync();
|
.Select(u => new { u.FullName, u.Phone, HasAvatar = u.Avatar != null }).FirstOrDefaultAsync();
|
||||||
meName = string.IsNullOrWhiteSpace(info?.FullName) ? info?.Phone : info!.FullName;
|
meFullName = string.IsNullOrWhiteSpace(info?.FullName) ? null : info!.FullName!.Trim();
|
||||||
|
mePhone = info?.Phone;
|
||||||
meHasAvatar = info?.HasAvatar ?? false;
|
meHasAvatar = info?.HasAvatar ?? false;
|
||||||
}
|
}
|
||||||
// Person glyph when there's no real name yet (avoid showing a phone digit like "0").
|
// Avatar glyph/label: prefer a real name; never show a bare phone digit like "0".
|
||||||
var meInitial = (!string.IsNullOrWhiteSpace(meName) && !char.IsDigit(meName!.Trim()[0]))
|
var meInitial = meFullName is not null ? meFullName.Substring(0, 1) : "👤";
|
||||||
? meName!.Trim().Substring(0, 1) : "👤";
|
var meLabel = meFullName ?? "حساب من";
|
||||||
var meLabel = (!string.IsNullOrWhiteSpace(meName) && !char.IsDigit(meName!.Trim()[0])) ? meName! : "حساب من";
|
|
||||||
|
// Single, role-aware dashboard entry — the full menu lives in the panel sub-nav (_PanelNav).
|
||||||
|
var dashUrl = "/Me/Index"; var dashLabel = "داشبورد من"; var dashIcon = "🗂️";
|
||||||
|
if (User.IsInRole("Admin")) { dashUrl = "/Admin/Overview"; dashLabel = "پنل مدیریت"; dashIcon = "🛠️"; }
|
||||||
|
else if (User.IsInRole("FacilityAdmin")) { dashUrl = "/Employer/Index"; dashLabel = "پنل کارفرما"; dashIcon = "🏥"; }
|
||||||
|
|
||||||
// --- SEO context ---
|
// --- SEO context ---
|
||||||
var baseUrl = $"{Context.Request.Scheme}://{Context.Request.Host}";
|
var baseUrl = $"{Context.Request.Scheme}://{Context.Request.Host}";
|
||||||
@@ -33,6 +39,11 @@
|
|||||||
string[] noindexPrefixes = { "/Admin", "/Me", "/Employer", "/Account", "/Preferences" };
|
string[] noindexPrefixes = { "/Admin", "/Me", "/Employer", "/Account", "/Preferences" };
|
||||||
var noIndex = (ViewData["NoIndex"] as bool? ?? false)
|
var noIndex = (ViewData["NoIndex"] as bool? ?? false)
|
||||||
|| noindexPrefixes.Any(p => path.StartsWith(p, StringComparison.OrdinalIgnoreCase));
|
|| noindexPrefixes.Any(p => path.StartsWith(p, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
// Show the centralized dashboard sub-nav on any logged-in panel page.
|
||||||
|
string[] panelPrefixes = { "/Admin", "/Me", "/Employer", "/Preferences" };
|
||||||
|
var showPanelNav = User.Identity?.IsAuthenticated == true
|
||||||
|
&& panelPrefixes.Any(p => path.StartsWith(p, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fa" dir="rtl">
|
<html lang="fa" dir="rtl">
|
||||||
@@ -126,22 +137,26 @@
|
|||||||
<span class="avatar-caret">▾</span>
|
<span class="avatar-caret">▾</span>
|
||||||
</label>
|
</label>
|
||||||
<nav class="profile-dropdown">
|
<nav class="profile-dropdown">
|
||||||
<div class="pd-head">@meName</div>
|
<div class="pd-id">
|
||||||
<a asp-page="/Me/Profile">👤 ویرایش پروفایل</a>
|
@if (meHasAvatar)
|
||||||
<a asp-page="/Me/Index" data-tour="panel">🗂️ پنل کارجو</a>
|
|
||||||
<a asp-page="/Me/Alerts">🔎 هشدارهای شغلی</a>
|
|
||||||
<a asp-page="/Preferences/Index">⭐ علاقهمندیها</a>
|
|
||||||
<a asp-page="/Me/Notifications">🔔 اعلانها@if (unreadCount > 0) {<span class="bell-badge" style="position:static; margin-inline-start:6px;">@JalaliDate.ToPersianDigits(unreadCount > 99 ? "99+" : unreadCount.ToString())</span>}</a>
|
|
||||||
@if (User.IsInRole("FacilityAdmin"))
|
|
||||||
{
|
{
|
||||||
<a asp-page="/Employer/Index">🏥 پنل کارفرما</a>
|
<img class="avatar-img" src="/avatar/@meId" alt="" />
|
||||||
}
|
}
|
||||||
@if (User.IsInRole("Admin"))
|
else
|
||||||
{
|
{
|
||||||
|
<span class="avatar-fallback">@meInitial</span>
|
||||||
|
}
|
||||||
|
<div class="pd-id-text">
|
||||||
|
<strong>@(meFullName ?? "کاربر همکادر")</strong>
|
||||||
|
@if (mePhone is not null)
|
||||||
|
{
|
||||||
|
<span class="muted" dir="ltr">@mePhone</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="pd-sep"></div>
|
<div class="pd-sep"></div>
|
||||||
<a asp-page="/Admin/Overview">🛠️ پنل مدیریت</a>
|
<a href="@dashUrl" data-tour="panel">@dashIcon @dashLabel</a>
|
||||||
<a asp-page="/Admin/Settings">⚙️ تنظیمات</a>
|
<a asp-page="/Me/Profile">👤 ویرایش پروفایل</a>
|
||||||
}
|
|
||||||
<div class="pd-sep"></div>
|
<div class="pd-sep"></div>
|
||||||
<form method="post" asp-page="/Account/Logout">
|
<form method="post" asp-page="/Account/Logout">
|
||||||
<button type="submit" class="pd-logout">🚪 خروج</button>
|
<button type="submit" class="pd-logout">🚪 خروج</button>
|
||||||
@@ -159,6 +174,10 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main role="main">
|
<main role="main">
|
||||||
|
@if (showPanelNav)
|
||||||
|
{
|
||||||
|
<partial name="_PanelNav" />
|
||||||
|
}
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ a { color: inherit; text-decoration: none; }
|
|||||||
.profile-dropdown a:hover, .pd-logout:hover { background: var(--primary-soft); color: var(--primary-dark); }
|
.profile-dropdown a:hover, .pd-logout:hover { background: var(--primary-soft); color: var(--primary-dark); }
|
||||||
.profile-dropdown form { margin: 0; }
|
.profile-dropdown form { margin: 0; }
|
||||||
.pd-head { padding: 8px 12px; font-weight: 800; color: var(--muted); font-size: 13px; }
|
.pd-head { padding: 8px 12px; font-weight: 800; color: var(--muted); font-size: 13px; }
|
||||||
|
.pd-id { display: flex; align-items: center; gap: 10px; padding: 8px 10px 12px; }
|
||||||
|
.pd-id .avatar-img, .pd-id .avatar-fallback { width: 42px; height: 42px; font-size: 17px; }
|
||||||
|
.pd-id-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.45; }
|
||||||
|
.pd-id-text strong { font-size: 14px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.pd-id-text .muted { font-size: 12px; }
|
||||||
.pd-sep { height: 1px; background: var(--line); margin: 4px 0; }
|
.pd-sep { height: 1px; background: var(--line); margin: 4px 0; }
|
||||||
.pd-logout { color: var(--danger); }
|
.pd-logout { color: var(--danger); }
|
||||||
|
|
||||||
@@ -483,6 +488,7 @@ label { font-size: 13px; }
|
|||||||
.profile-dropdown { position: static; display: block; box-shadow: none; border: none; padding: 0; min-width: 0; }
|
.profile-dropdown { position: static; display: block; box-shadow: none; border: none; padding: 0; min-width: 0; }
|
||||||
.profile-dropdown a, .pd-logout { padding: 12px 6px; font-size: 15px; }
|
.profile-dropdown a, .pd-logout { padding: 12px 6px; font-size: 15px; }
|
||||||
.pd-head { display: none; }
|
.pd-head { display: none; }
|
||||||
|
.pd-id { padding: 10px 6px; border-bottom: 1px solid var(--line); margin-bottom: 4px; }
|
||||||
.pd-sep { display: none; }
|
.pd-sep { display: none; }
|
||||||
|
|
||||||
.cal { border-spacing: 4px; }
|
.cal { border-spacing: 4px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user