feat: add shared task assignments and quick win sorting
This commit is contained in:
@@ -473,6 +473,9 @@ p {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.status-badge--due_today,
|
||||
.status-badge--due_tomorrow,
|
||||
.status-badge--due_day_after_tomorrow,
|
||||
.status-badge--soon {
|
||||
background: #fff3d6;
|
||||
color: #b45309;
|
||||
@@ -514,6 +517,16 @@ p {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.task-assignee__avatars {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.task-assignee__avatars .avatar + .avatar {
|
||||
margin-left: -10px;
|
||||
border: 2px solid var(--surface-strong);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
@@ -903,6 +916,9 @@ p {
|
||||
border-left: 4px solid #2563eb;
|
||||
}
|
||||
|
||||
.calendar-task--due_today,
|
||||
.calendar-task--due_tomorrow,
|
||||
.calendar-task--due_day_after_tomorrow,
|
||||
.calendar-task--soon {
|
||||
border-left: 4px solid #f59e0b;
|
||||
}
|
||||
@@ -1066,6 +1082,11 @@ p {
|
||||
|
||||
.quick-win-manage-card {
|
||||
align-items: stretch;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.quick-win-manage-card.is-dragging {
|
||||
opacity: 0.72;
|
||||
}
|
||||
|
||||
.quick-win-manage-form {
|
||||
@@ -1073,6 +1094,14 @@ p {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.quick-win-manage-card__drag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.quick-win-manage-card__actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
@@ -1427,6 +1456,9 @@ p {
|
||||
color: #8db7ff;
|
||||
}
|
||||
|
||||
.status-badge--due_today,
|
||||
.status-badge--due_tomorrow,
|
||||
.status-badge--due_day_after_tomorrow,
|
||||
.status-badge--soon {
|
||||
background: rgba(245, 158, 11, 0.18);
|
||||
color: #ffd38a;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const dialogForm = document.getElementById("completeDialogForm");
|
||||
const dialogChoice = document.getElementById("completeDialogChoice");
|
||||
const dialogText = document.getElementById("completeDialogText");
|
||||
const dialogChoices = document.getElementById("completeDialogChoices");
|
||||
const closeButton = document.getElementById("completeDialogClose");
|
||||
const quickTaskDialog = document.getElementById("quickTaskDialog");
|
||||
const quickTaskOpen = document.getElementById("quickTaskOpen");
|
||||
@@ -13,23 +14,55 @@
|
||||
const quickWinCustomFields = document.getElementById("quickWinCustomFields");
|
||||
const quickWinTitle = document.getElementById("quick-title");
|
||||
const quickWinEffort = document.getElementById("quick-effort");
|
||||
const currentUserId = document.body.dataset.currentUserId;
|
||||
const currentUserName = document.body.dataset.currentUserName;
|
||||
const quickWinSortList = document.querySelector("[data-quick-win-sort-list]");
|
||||
const quickWinSortToken = document.getElementById("quickWinSortToken");
|
||||
let draggedQuickWin = null;
|
||||
|
||||
function buildCompletionOptions(button) {
|
||||
const options = [];
|
||||
const assignedPairs = [
|
||||
[button.dataset.assignedPrimaryId, button.dataset.assignedPrimaryName],
|
||||
[button.dataset.assignedSecondaryId, button.dataset.assignedSecondaryName],
|
||||
];
|
||||
|
||||
assignedPairs.forEach(([id, label]) => {
|
||||
if (id && label && !options.some((option) => option.value === id)) {
|
||||
options.push({ value: id, label });
|
||||
}
|
||||
});
|
||||
|
||||
if (currentUserId && currentUserName && !options.some((option) => option.value === currentUserId)) {
|
||||
options.push({ value: currentUserId, label: "Ich" });
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
document.querySelectorAll("[data-complete-action]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
if (!dialog || !dialogForm || !dialogChoice || !dialogText) {
|
||||
if (!dialog || !dialogForm || !dialogChoice || !dialogText || !dialogChoices) {
|
||||
return;
|
||||
}
|
||||
dialogForm.action = button.dataset.completeAction;
|
||||
dialogText.textContent = `Die Aufgabe "${button.dataset.completeTitle}" war ${button.dataset.completeAssigned} zugewiesen. Wer hat sie erledigt?`;
|
||||
dialog.showModal();
|
||||
});
|
||||
});
|
||||
dialogChoices.innerHTML = "";
|
||||
|
||||
document.querySelectorAll("[data-complete-choice]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
dialogChoice.value = button.dataset.completeChoice || "me";
|
||||
dialog.close();
|
||||
dialogForm.submit();
|
||||
buildCompletionOptions(button).forEach((option, index) => {
|
||||
const choiceButton = document.createElement("button");
|
||||
choiceButton.type = "button";
|
||||
choiceButton.className = index === 0 ? "button button--secondary" : "button";
|
||||
choiceButton.dataset.completeChoice = option.value;
|
||||
choiceButton.textContent = option.label;
|
||||
choiceButton.addEventListener("click", () => {
|
||||
dialogChoice.value = option.value;
|
||||
dialog.close();
|
||||
dialogForm.submit();
|
||||
});
|
||||
dialogChoices.appendChild(choiceButton);
|
||||
});
|
||||
dialog.showModal();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,6 +120,64 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function persistQuickWinSort() {
|
||||
if (!quickWinSortList || !quickWinSortToken) {
|
||||
return;
|
||||
}
|
||||
const ids = [...quickWinSortList.querySelectorAll("[data-quick-win-sort-item]")]
|
||||
.map((item) => item.dataset.quickWinSortItem)
|
||||
.filter(Boolean);
|
||||
|
||||
await fetch("/settings/quick-wins/reorder", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRFToken": quickWinSortToken.value,
|
||||
},
|
||||
body: JSON.stringify({ ids }),
|
||||
});
|
||||
}
|
||||
|
||||
if (quickWinSortList) {
|
||||
quickWinSortList.querySelectorAll("[data-quick-win-sort-item]").forEach((item) => {
|
||||
item.addEventListener("dragstart", () => {
|
||||
draggedQuickWin = item;
|
||||
item.classList.add("is-dragging");
|
||||
});
|
||||
|
||||
item.addEventListener("dragend", () => {
|
||||
item.classList.remove("is-dragging");
|
||||
draggedQuickWin = null;
|
||||
});
|
||||
|
||||
item.addEventListener("dragover", (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
item.addEventListener("drop", async (event) => {
|
||||
event.preventDefault();
|
||||
if (!draggedQuickWin || draggedQuickWin === item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const items = [...quickWinSortList.querySelectorAll("[data-quick-win-sort-item]")];
|
||||
const draggedIndex = items.indexOf(draggedQuickWin);
|
||||
const targetIndex = items.indexOf(item);
|
||||
if (draggedIndex < targetIndex) {
|
||||
item.after(draggedQuickWin);
|
||||
} else {
|
||||
item.before(draggedQuickWin);
|
||||
}
|
||||
|
||||
try {
|
||||
await persistQuickWinSort();
|
||||
} catch (error) {
|
||||
console.error("Quick-Win-Sortierung konnte nicht gespeichert werden", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const pushButton = document.getElementById("pushToggle");
|
||||
const pushHint = document.getElementById("pushHint");
|
||||
const vapidKey = document.body.dataset.pushKey;
|
||||
|
||||
Reference in New Issue
Block a user