Skip to content
Merged

fix #33

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 27 additions & 12 deletions Api/wwwroot/js/kanban.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ function renderEmpty(message) {
function renderKanban(board) {
kanbanTitle.textContent = board.title || "Kanban";
kanbanBoard.innerHTML = "";
const canRenameColumns = isActiveProjectTeamAdmin();
const canManageColumns = isActiveProjectTeamAdmin();

if (!board.id) {
renderEmpty("У проекта пока нет канбанов");
Expand Down Expand Up @@ -337,23 +337,28 @@ function renderKanban(board) {
const editColumnBtn = columnEl.querySelector(".column-edit-btn");
const columnTitle = columnEl.querySelector("[data-column-title]");

if (!canRenameColumns) {
if (!canManageColumns) {
deleteColumnBtn?.remove();
editColumnBtn?.remove();
}

deleteColumnBtn.addEventListener("click", (event) => {
event.stopPropagation();
deleteColumn(column);
});
} else {
deleteColumnBtn?.addEventListener("click", (event) => {
event.stopPropagation();
deleteColumn(column);
});

editColumnBtn?.addEventListener("click", (event) => {
event.stopPropagation();
startEditColumnTitle(columnTitle, column);
});
editColumnBtn?.addEventListener("click", (event) => {
event.stopPropagation();
startEditColumnTitle(columnTitle, column);
});
}

kanbanBoard.appendChild(columnEl);
});

if (!canManageColumns) {
return;
}

const addColumn = document.createElement("button");
addColumn.className = "add-column-btn";
addColumn.type = "button";
Expand All @@ -369,6 +374,11 @@ function renderKanban(board) {
========================= */

async function addColumnToBoard() {
if (!isActiveProjectTeamAdmin()) {
showToast("Создавать колонки может только админ команды");
return;
}

if (!state.board?.id) {
showToast("Сначала выбери канбан");
return;
Expand Down Expand Up @@ -478,6 +488,11 @@ function startEditColumnTitle(titleEl, column) {
}

async function deleteColumn(column) {
if (!isActiveProjectTeamAdmin()) {
showToast("Удалять колонки может только админ команды");
return;
}

const confirmed = confirm(
`Удалить колонку «${column.title}» вместе с задачами: ${column.tasks.length}?`
);
Expand Down
83 changes: 62 additions & 21 deletions Infrastructure/Workers/NotificationWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,31 +69,72 @@ private async Task HandleMessageAsync(string eventType, string payload, Cancella
var (name, message) = BuildNotificationMessage(eventType);
var notifications = scope.ServiceProvider.GetRequiredService<INotificationRepository>();
var unitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork>();

var notificationSender = scope.ServiceProvider.GetRequiredService<INotificationSender>();
var notification = await notifications.AddAsync(
userId,
taskId,
var recipientUserIds = await ResolveRecipientUserIdsAsync(
scope.ServiceProvider,
kanbanId,
name,
message,
cancellationToken);
await unitOfWork.SaveChangesAsync(cancellationToken);
await notificationSender.SendToUserAsync(
userId,
new
{
id = notification.Id,
userId = userId,
taskId = taskId,
kanbanId = kanbanId,
type = eventType,
name = name,
message = message,
isRead = false,
createdAt = DateTime.UtcNow
},
cancellationToken);

var createdNotifications = new List<(Guid UserId, Guid NotificationId, DateTime CreatedAt)>();

foreach (var recipientUserId in recipientUserIds)
{
var notification = await notifications.AddAsync(
recipientUserId,
taskId,
kanbanId,
name,
message,
cancellationToken);

createdNotifications.Add((recipientUserId, notification.Id, notification.CreatedAt));
}

await unitOfWork.SaveChangesAsync(cancellationToken);

foreach (var createdNotification in createdNotifications)
{
await notificationSender.SendToUserAsync(
createdNotification.UserId,
new
{
id = createdNotification.NotificationId,
userId = createdNotification.UserId,
taskId = taskId,
kanbanId = kanbanId,
type = eventType,
name = name,
message = message,
isRead = false,
createdAt = createdNotification.CreatedAt
},
cancellationToken);
}
}

private static async Task<IReadOnlyCollection<Guid>> ResolveRecipientUserIdsAsync(
IServiceProvider serviceProvider,
Guid kanbanId,
Guid fallbackUserId,
CancellationToken cancellationToken)
{
var kanbans = serviceProvider.GetRequiredService<IKanbanRepository>();
var members = serviceProvider.GetRequiredService<ITeamMemberRepository>();

var kanban = await kanbans.GetByIdWithProjectAsync(kanbanId, cancellationToken);
if (kanban?.Project is null)
return [fallbackUserId];

var teamMembers = await members.GetMembersByTeamIdAsync(kanban.Project.TeamId, cancellationToken);
var recipientUserIds = teamMembers
.Select(member => member.UserId)
.Distinct()
.ToArray();

return recipientUserIds.Length > 0
? recipientUserIds
: [fallbackUserId];
}

private static (string name, string message) BuildNotificationMessage(string eventType)
Expand Down