Merge: t-paliad-128 — /events Nur persönliche redefined as created_by=me (deadlines + appointments)

This commit is contained in:
m
2026-05-04 19:53:02 +02:00
6 changed files with 286 additions and 84 deletions

View File

@@ -303,7 +303,12 @@ async function loadSummary() {
try {
const params = new URLSearchParams();
params.set("type", currentType);
if (projectFilter && projectFilter !== PERSONAL) {
if (projectFilter === PERSONAL) {
// "Nur persönliche" → items the caller created. The backend
// applies this uniformly to both rails (deadlines + appointments)
// and ignores any project_id we'd send. See t-paliad-128.
params.set("personal_only", "true");
} else if (projectFilter) {
params.set("project_id", projectFilter);
}
const resp = await fetch(`/api/events/summary?${params.toString()}`);
@@ -360,7 +365,12 @@ async function loadList() {
// no-op on the appointment side.
params.set("status", statusFilter);
}
if (projectFilter && projectFilter !== PERSONAL) {
if (projectFilter === PERSONAL) {
// "Nur persönliche" → items the caller created (t-paliad-128).
// Applies uniformly to both rails server-side; project_id is
// intentionally not sent (the two are mutually exclusive).
params.set("personal_only", "true");
} else if (projectFilter) {
params.set("project_id", projectFilter);
}
if (currentType !== "appointment") {
@@ -382,13 +392,7 @@ async function loadList() {
hideTableAndCalendar();
return;
}
let data: EventListItem[] = await resp.json();
// "Personal only" is a UI shorthand for project_id IS NULL on the
// appointment side. Filter client-side because the backend doesn't
// expose a NULL-id query (matches current /appointments behaviour).
if (projectFilter === PERSONAL) {
data = data.filter((x) => x.type === "appointment" && !x.project_id);
}
const data: EventListItem[] = await resp.json();
allItems = data;
loadedOK = true;
render();
@@ -772,19 +776,8 @@ function applyTypeVisibility() {
// Termin-Typ is appointment-only.
toggleFilterPair("events-filter-appointment-type", !isDeadline);
// "Personal only" project option only makes sense with appointments in scope.
const projectSel = document.getElementById("events-filter-project") as HTMLSelectElement | null;
if (projectSel) {
const personalOpt = projectSel.querySelector<HTMLOptionElement>('option[value="__personal__"]');
if (personalOpt) {
personalOpt.hidden = isDeadline;
personalOpt.disabled = isDeadline;
if (isDeadline && projectFilter === PERSONAL) {
projectFilter = "";
projectSel.value = "";
}
}
}
// "Nur persönliche" applies uniformly to deadlines + appointments now
// (t-paliad-128) — items where the caller is the creator. Always available.
// Update chip active state.
document.querySelectorAll<HTMLButtonElement>('[data-event-type]').forEach((b) => {
@@ -836,6 +829,7 @@ function syncURLParams() {
url.searchParams.delete("view");
url.searchParams.delete("status");
url.searchParams.delete("project_id");
url.searchParams.delete("personal_only");
url.searchParams.delete("event_type");
url.searchParams.delete("type_filter");
// Default type per route comes from window.__PALIAD_EVENTS__; only
@@ -907,6 +901,9 @@ function initFilters() {
statusFilter = defaultStatusFor(currentType);
}
if (params.has("project_id")) projectFilter = params.get("project_id")!;
// ?personal_only=true is a bookmark-friendly alias for project_id=__personal__.
// Both URLs land on the same UI state (PERSONAL sentinel in the project select).
if (params.get("personal_only") === "true") projectFilter = PERSONAL;
if (params.has("type_filter")) appointmentTypeFilter = params.get("type_filter")!;
const status = document.getElementById("events-filter-status") as HTMLSelectElement;