- Add design-token stylesheet (wwwroot/css/user-panel.css): CSS variables derived from the existing p8.css palette (brand maroon, success/danger colors, radius, shadow), plus reusable component classes (card, button variants, alert, badge, empty state, pagination, list item, toast, confirm modal). Includes a prefers-color-scheme dark block compatible with the existing toggleColorScheme() mechanism in bk.js. - Add wwwroot/js/user-panel.js: upConfirm() and upToast() helpers as styled, promise-based replacements for window.confirm()/alert(), falling back to the native dialogs if the modal/toast markup isn't present on a page. - Add Areas/User/Pages/Shared/ partials: _Pagination (wraps the List<NameIdUrlImage> pattern already used by every list page), _EmptyState, _Toasts and _ConfirmModal (hosts, included once by the layout). - Wire the new stylesheet/script and the toast/modal hosts into _UserPanelLayout.cshtml. - Refactor Notifications.cshtml as a first demonstration: replaces the hand-rolled pagination loop, manual gray/lightsteelblue row alternation, and confirm()/alert() calls with the new components. No backend/API changes. Rest of the User area is untouched pending Phase 1+ (nav/dashboard, forms, remaining list pages, moderation queues, editors).
130 lines
5.7 KiB
Plaintext
130 lines
5.7 KiB
Plaintext
@page
|
|
@model GanjooRazor.Areas.User.Pages.NotificationsModel
|
|
@using DNTPersianUtils.Core
|
|
@{
|
|
Layout = "_UserPanelLayout";
|
|
ViewData["Title"] = "اعلانهای من";
|
|
await GanjooRazor.Utils.GanjoorSessionChecker.ApplyPermissionsToViewData(Request, Response, ViewData);
|
|
}
|
|
<div class="up-page-header">
|
|
<h1>@ViewData["Title"]</h1>
|
|
</div>
|
|
@if (Model.LastError != "")
|
|
{
|
|
<div class="up-alert up-alert--danger">@Model.LastError</div>
|
|
}
|
|
else
|
|
{
|
|
<script>
|
|
async function deleteNotification(notificationId) {
|
|
var ok = await upConfirm('آیا از حذف این اعلان اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
$.ajax({
|
|
type: "DELETE",
|
|
url: '?handler=Notification',
|
|
data: { id: notificationId },
|
|
success: function () {
|
|
$('#notification-' + notificationId).remove();
|
|
upToast('اعلان حذف شد.', 'success');
|
|
},
|
|
error: function (e) {
|
|
upToast(e.responseText || 'حذف اعلان با خطا مواجه شد.', 'error');
|
|
}
|
|
});
|
|
}
|
|
|
|
function markReadNotification(notificationId) {
|
|
$.ajax({
|
|
type: "PUT",
|
|
url: '?handler=MarkRead',
|
|
data: { id: notificationId },
|
|
success: function () {
|
|
var item = document.getElementById('notification-' + notificationId);
|
|
if (item) item.classList.remove('up-list-item--unread');
|
|
var btn = document.getElementById('unread-' + notificationId);
|
|
if (btn) btn.remove();
|
|
},
|
|
error: function (e) {
|
|
upToast(e.responseText || 'ثبت خوانده شدن اعلان با خطا مواجه شد.', 'error');
|
|
}
|
|
});
|
|
}
|
|
|
|
async function markAllRead() {
|
|
var ok = await upConfirm('آیا از علامتگذاری همهٔ اعلانها به عنوان خوانده شده اطمینان دارید؟', { danger: false, okText: 'بله، علامتگذاری کن' });
|
|
if (!ok) return;
|
|
|
|
$.ajax({
|
|
type: "PUT",
|
|
url: '?handler=MarkAllRead',
|
|
error: function (e) {
|
|
upToast(e.responseText || 'خطایی رخ داد.', 'error');
|
|
},
|
|
success: function () {
|
|
location.reload();
|
|
},
|
|
});
|
|
}
|
|
|
|
async function deleteAllRead() {
|
|
var ok = await upConfirm('آیا از حذف همهٔ اعلانهای خوانده شده اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
$.ajax({
|
|
type: "DELETE",
|
|
url: '?handler=AllRead',
|
|
error: function (e) {
|
|
upToast(e.responseText || 'خطایی رخ داد.', 'error');
|
|
},
|
|
success: function () {
|
|
location.reload();
|
|
},
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<partial name="_Pagination" model="Model.PaginationLinks" />
|
|
|
|
@if (Model.Notifications.Count == 0)
|
|
{
|
|
<partial name="_EmptyState" model="@("در حال حاضر اعلانی وجود ندارد. در صورتی که کاربری با زدن دکمهٔ «پاسخگویی» برای حاشیهٔ شما پاسخی بنویسد یا شما با پیشنهاد آهنگ مرتبط با شعر یا روش دیگری در گنجور مشارکت کنید و مشارکت شما بررسی شود در قسمت «اعلانهای من» با دریافت یک اعلان از این رویدادها مطلع میشوید.")" />
|
|
}
|
|
else
|
|
{
|
|
<div class="up-btn-group up-mt-0" style="margin-bottom: var(--up-space-3);">
|
|
<a role="button" onclick="markAllRead()" class="up-btn up-btn--ghost">همه را خواندم</a>
|
|
<a role="button" onclick="deleteAllRead()" class="up-btn up-btn--ghost">حذف همهٔ خوانده شدهها</a>
|
|
</div>
|
|
|
|
<div class="up-list">
|
|
@foreach (var notification in Model.Notifications)
|
|
{
|
|
bool unread = notification.Status == RSecurityBackend.Models.Notification.NotificationStatus.Unread;
|
|
<div class="up-list-item @(unread ? "up-list-item--unread" : "")" id="notification-@notification.Id">
|
|
<div>
|
|
<strong>@notification.Subject</strong>
|
|
<span class="up-list-item__meta">@notification.DateTime.ToFriendlyPersianDateTextify()</span>
|
|
</div>
|
|
<div id="notification-text-@notification.Id">
|
|
@Html.Raw(notification.HtmlText)
|
|
</div>
|
|
<div class="up-list-item__actions">
|
|
<a role="button" onclick="deleteNotification('@notification.Id')" class="up-icon-btn" title="حذف" aria-label="حذف این اعلان">
|
|
<i class="noindent-info-button delete-icon"></i>
|
|
</a>
|
|
@if (unread)
|
|
{
|
|
<a role="button" onclick="markReadNotification('@notification.Id')" class="up-icon-btn" title="خواندم" aria-label="علامتگذاری به عنوان خوانده شده" id="unread-@notification.Id">
|
|
<i class="noindent-info-button done-icon"></i>
|
|
</a>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<partial name="_Pagination" model="Model.PaginationLinks" />
|
|
}
|
|
}
|