improve UI/UX of the User Area

#496
This commit is contained in:
Hamid Reza Mohammadi 2026-08-05 13:23:37 +03:30
parent 714c05b162
commit ada2dc5028
4 changed files with 191 additions and 84 deletions

View File

@ -6,13 +6,50 @@
ViewData["Title"] = "پیشخان کاربر";
await GanjooRazor.Utils.GanjoorSessionChecker.ApplyPermissionsToViewData(Request, Response, ViewData);
}
<h1>@ViewData["Title"]</h1>
<div class="up-page-header">
<h1>@ViewData["Title"]</h1>
@{
var displayName = !string.IsNullOrWhiteSpace(Model.UserInfo?.NickName) ? Model.UserInfo.NickName : Model.UserInfo?.FirstName;
}
@if (!string.IsNullOrWhiteSpace(displayName))
{
<div class="up-page-subtitle">خوش آمدید، @displayName</div>
}
</div>
@if (Model.LastError != "")
{
<h2>@Model.LastError</h2>
<div class="up-alert up-alert--danger">@Model.LastError</div>
}
else
{
<div class="up-quick-links">
<a class="up-card up-quick-link" asp-area="User" asp-page="/Edits">
<strong>ویرایش‌های من</strong>
<span class="up-text-muted">پیگیری وضعیت ویرایش‌های پیشنهادی شما</span>
</a>
<a class="up-card up-quick-link" asp-area="User" asp-page="/MyComments">
<strong>حاشیه‌های من</strong>
<span class="up-text-muted">حاشیه‌هایی که نوشته‌اید</span>
</a>
<a class="up-card up-quick-link" asp-area="User" asp-page="/MyBookmarks">
<strong>نشان‌شده‌های من</strong>
<span class="up-text-muted">شعرهایی که نشان کرده‌اید</span>
</a>
<a class="up-card up-quick-link" asp-area="User" asp-page="/Notifications">
<strong>اعلان‌های من</strong>
<span class="up-text-muted">پاسخ‌ها و رویدادهای مرتبط با شما</span>
</a>
<a class="up-card up-quick-link" asp-area="User" asp-page="/History">
<strong>ادامهٔ مطالعه</strong>
<span class="up-text-muted">از جایی که مانده بودید ادامه دهید</span>
</a>
<a class="up-card up-quick-link" asp-area="User" asp-page="/MySuggestedQuotes">
<strong>مشق‌های پیشنهادی من</strong>
<span class="up-text-muted">اشعاری که به عنوان مشق پیشنهاد داده‌اید</span>
</a>
</div>
<div class="up-card">
<form method="post" asp-page-handler="SetMyPass">
<table style="width:100%" class="noborder">
<tr>
@ -50,12 +87,14 @@ else
@Model.PasswordChanged
</td>
<td>
<button type="submit" name="approve" id="approve" style="width:100%;color:white;background-color:green">تغییر گذرواژه</button>
<button type="submit" name="approve" id="approve" class="up-btn up-btn--success up-btn--block">تغییر گذرواژه</button>
</td>
</tr>
</table>
</form>
</div>
<div class="up-card">
<form method="post" asp-page-handler="SetMyInfo">
<table style="width:100%" class="noborder">
<tr>
@ -126,7 +165,7 @@ else
<input type="hidden" asp-for="UserInfo.RImageId" />
</td>
<td>
<button type="submit" name="approve" id="approve" style="width:100%;color:white;background-color:green">ذخیره</button>
<button type="submit" name="approve" id="approveProfile" class="up-btn up-btn--success up-btn--block">ذخیره</button>
</td>
</tr>
<tr>
@ -137,10 +176,11 @@ else
<tr>
<td colspan="2">
<a role="button" style="cursor:pointer;width:100%;color:white;background-color:red;display:block" href="/User/DeleteAccount">حذف حساب کاربری</a>
<a role="button" class="up-btn up-btn--danger up-btn--block" href="/User/DeleteAccount">حذف حساب کاربری</a>
</td>
</tr>
</table>
</form>
</div>
}

View File

@ -81,99 +81,86 @@
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
@{
bool hasModerationTools =
ViewData.ContainsKey("ganjoor-songrevu") ||
ViewData.ContainsKey("ganjoor-moderate") ||
ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ArtifactEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName}") ||
ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.GanjoorEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModeratePoetPhotos}") ||
ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.FAQEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModerateOperationShortName}") ||
ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.GanjoorEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModifyOperationShortName}");
}
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/Index">خانه</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/MyComments">حاشیه‌های من</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/MyBookmarks">نشان‌شده‌های من</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/Notifications">اعلان‌‌های من</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/UpVotedRecitations">خوانش‌های نشان شده</a>
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="upContribsDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">مشارکت‌های من</a>
<div class="dropdown-menu" aria-labelledby="upContribsDropdown">
<a class="dropdown-item" asp-area="User" asp-page="/Edits">ویرایش‌‌های من</a>
<a class="dropdown-item" asp-area="User" asp-page="/SectionEdits">ویرایش‌‌های قطعات من</a>
<a class="dropdown-item" asp-area="User" asp-page="/CatEdits">ویرایش‌‌های بخش‌های من</a>
<a class="dropdown-item" asp-area="User" asp-page="/MySuggestedQuotes">مشق‌های پیشنهادی من</a>
<a class="dropdown-item" asp-area="User" asp-page="/MySuggestedSongs">آهنگ‌های پیشنهادی من</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/History">ادامهٔ مطالعه</a>
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="upEngagementDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">تعامل من</a>
<div class="dropdown-menu" aria-labelledby="upEngagementDropdown">
<a class="dropdown-item" asp-area="User" asp-page="/MyComments">حاشیه‌های من</a>
<a class="dropdown-item" asp-area="User" asp-page="/MyBookmarks">نشان‌شده‌های من</a>
<a class="dropdown-item" asp-area="User" asp-page="/UpVotedRecitations">خوانش‌های نشان شده</a>
<a class="dropdown-item" asp-area="User" asp-page="/History">ادامهٔ مطالعه</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/MySuggestedSongs">آهنگ‌های پیشنهادی من</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/Edits">ویرایش‌‌های من</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/SectionEdits">ویرایش‌‌های قطعات من</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/CatEdits">ویرایش‌‌های بخش‌های من</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/MySuggestedQuotes">مشق‌های پیشنهادی من</a>
</li>
@if (ViewData.ContainsKey("ganjoor-songrevu"))
@if (hasModerationTools)
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/ReviewSongs">آهنگ‌ها</a>
</li>
}
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="upModerationDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
ابزارهای مدیریت
<span class="up-badge up-badge--primary">ویژه</span>
</a>
<div class="dropdown-menu" aria-labelledby="upModerationDropdown">
@if (ViewData.ContainsKey("ganjoor-songrevu"))
{
<a class="dropdown-item" asp-area="User" asp-page="/ReviewSongs">آهنگ‌ها</a>
}
@if (ViewData.ContainsKey("ganjoor-moderate"))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/ReportedComments">حاشیه‌های گزارش شده</a>
</li>
@if (ViewData.ContainsKey("ganjoor-moderate"))
{
<a class="dropdown-item" asp-area="User" asp-page="/ReportedComments">حاشیه‌های گزارش شده</a>
<a class="dropdown-item" asp-area="User" asp-page="/AwaitingComments">حاشیه‌های در انتظار تأیید</a>
}
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/AwaitingComments">حاشیه‌های در انتظار تأیید</a>
</li>
}
@if (ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ArtifactEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName}"))
{
<a class="dropdown-item" asp-area="User" asp-page="/FinalizeImages">نهایی‌سازی تصاویر</a>
}
@if (ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ArtifactEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ReviewGanjoorLinksOperationShortName}"))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/FinalizeImages">نهایی‌سازی تصاویر</a>
</li>
}
@if (ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.GanjoorEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModeratePoetPhotos}"))
{
<a class="dropdown-item" asp-area="User" asp-page="/SuggestedPoetSpecLines">نکات پیشنهادی</a>
<a class="dropdown-item" asp-area="User" asp-page="/SuggestedPoetPhotos">تصاویر پیشنهادی</a>
}
@if (ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.FAQEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModerateOperationShortName}"))
{
<a class="dropdown-item" asp-area="User" asp-page="/FAQItems">FAQ</a>
}
@if (ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.GanjoorEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModeratePoetPhotos}"))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/SuggestedPoetSpecLines">نکات پیشنهادی</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/SuggestedPoetPhotos">تصاویر پیشنهادی</a>
</li>
}
@if (ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.FAQEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModerateOperationShortName}"))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="User" asp-page="/FAQItems">FAQ</a>
</li>
}
@if (ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.GanjoorEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModifyOperationShortName}"))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Admin" asp-page="/ReviewEdits">مدیریت</a>
@if (ViewData.ContainsKey($"{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.GanjoorEntityShortName}-{RMuseum.Models.Auth.Memory.RMuseumSecurableItem.ModifyOperationShortName}"))
{
<div class="dropdown-divider"></div>
<a class="dropdown-item" asp-area="Admin" asp-page="/ReviewEdits">مدیریت</a>
}
</div>
</li>
}
</ul>

View File

@ -444,8 +444,57 @@ body.up-modal-open {
overflow: hidden;
}
/* ---------- dashboard quick links ---------- */
.up-quick-links {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: var(--up-space-3);
margin-bottom: var(--up-space-4);
}
.up-quick-link {
display: flex;
flex-direction: column;
gap: var(--up-space-1);
text-decoration: none;
color: var(--up-text);
transition: transform 0.1s ease, box-shadow 0.15s ease;
}
.up-quick-link:hover {
text-decoration: none;
color: var(--up-text);
box-shadow: var(--up-shadow);
transform: translateY(-2px);
}
/* ---------- small utilities ---------- */
.up-mt-0 { margin-top: 0; }
.up-text-muted { color: var(--up-text-muted); }
.up-text-center { text-align: center; }
/* ---------- navbar dropdowns ---------- */
/* Bootstrap 4's default (non-RTL) build anchors dropdown-menu to the left
and left-aligns text, which reads wrong on this RTL page. */
.navbar .dropdown-menu {
right: 0;
left: auto;
text-align: right;
border-radius: var(--up-radius);
border-color: var(--up-border);
box-shadow: var(--up-shadow-sm);
}
.navbar .dropdown-item {
text-align: right;
}
.navbar .up-badge {
margin-inline-start: 6px;
vertical-align: middle;
font-size: 0.65rem;
padding: 1px 8px;
}

View File

@ -1,9 +1,40 @@
# GanjoorService
Ganjoor museum and ganjoor.net own backend (ASP.NET Core Web API) and frontend (Razor Pages) code
# User panel Phase 0 + Phase 1 — file package
این کد وب سرویس [گنجینهٔ گنجور](https://museum.ganjoor.net) و [گنجور](https://ganjoor.net) و همچنین کد سایت گنجور است.
This contains the FULL, current content of every file touched by Phase 0
(shared components/foundations) and Phase 1 (nav grouping + dashboard)
combined. Since you already have Phase 0 applied, copying these over is
a no-op for the Phase-0-only files and just adds the Phase 1 changes.
[فهرست توابع در دسترس](https://api.ganjoor.net)
Copy each file below into the same relative path under your
`GanjoorService/GanjooRazor/` folder, overwriting what's there.
![https://api.ganjoor.net](https://user-images.githubusercontent.com/582212/91652208-14a63c00-eaaa-11ea-89c2-5acabdfda7de.png)
## New files (Phase 0)
- wwwroot/css/user-panel.css
- wwwroot/js/user-panel.js
- Areas/User/Pages/Shared/_Pagination.cshtml
- Areas/User/Pages/Shared/_EmptyState.cshtml
- Areas/User/Pages/Shared/_Toasts.cshtml
- Areas/User/Pages/Shared/_ConfirmModal.cshtml
## Modified files (Phase 0 + Phase 1, combined into their final state)
- Pages/Shared/_UserPanelLayout.cshtml
(Phase 0: loads user-panel.css/js, includes toast/modal hosts.
Phase 1: navbar regrouped into dropdowns.)
- Areas/User/Pages/Notifications.cshtml
(Phase 0 demo: uses the new pagination/empty-state partials,
upConfirm/upToast instead of confirm()/alert().)
- Areas/User/Pages/Index.cshtml
(Phase 1: dashboard greeting, quick-links grid, forms wrapped in cards.)
## After copying
1. Diff against what you have now (or just eyeball it — none of these
files are huge) to make sure nothing local got clobbered.
2. Build.
3. Load `/User/Index` and `/User/Notifications` and click around:
nav dropdowns, the confirm-modal on delete, toasts on notification
actions, the quick-links grid.
4. Commit and push however you normally would.
No backend/API/.csproj changes in either phase — these are all under
`wwwroot/` or `Areas/User/Pages/` (Razor views + one stylesheet + one
script).