Agentur: Eintraege gehoeren allen -- und Events bekommen ein eigenes Formular
Gemessen am 07.09.2026, bevor irgendetwas geaendert wurde: Von drei Agentur-Eintraegen sah eine Creatorin genau EINEN -- den, der ihr zugeordnet war. Ein Event fuer alle traf weder `creator_id = ich` noch `erstellt_von = ich`; die Agentur-Seite war fuer jeden Creator leer. Nicht kaputt, nicht fehlerhaft: leer, so wie eine Seite aussieht, auf der noch nichts steht. Die Pruefung dazu war gruen. Sie legte ihre Testeintraege mit `creator_id: idLuna` an und pruefte damit einen Fall, den es im Alltag nicht gibt. - Bereichseinstellung `fuerAlle` + `ohneCreatorBezug`, daraus abgeleitet sichtbarEintrag(). BEWUSST neben sichtbar() statt darin: an derselben Funktion haengen Aufgaben, Dateien, Termine und Calls -- wer dort "1=1" einschleust, gibt nebenbei fremde Akten frei. Eine Gegenprobe mit einer zweiten Creatorin haelt das fest. - Die Zuordnung bietet nur noch "Agentur" an. Der Server verwirft eine Zuordnung ausserdem selbst -- inklusive des frei getippten Namens, und zwar NACH externPruefen: davor haette der Name den Riegel wieder aufgemacht. - "Event & Kampagne" heisst jetzt "Agentur-Events" und hat ein eigenes Formular: Von/Bis, Titel, Beschreibung, Aufgaben (Punkte und Preise), Regeln. Die Karte zeigt den Zustand als WORT (laeuft bis / startet / vorbei seit), nicht nur als Farbe. - Drei neue Spalten -- und sie stehen auch im Tabellenneubau vom 06.09. Der laeuft NACH dem Spaltennachtrag und haette sie samt Inhalt weggeworfen, ohne Fehler und mit stimmender Zeilenzahl. - Creator sehen weiterhin alles und tragen weiterhin nichts ein (403). Die Unterzeile sagt jetzt "alles, was hier steht" statt "alles, was zu dir gehoert" -- eine vollstaendige Liste soll sich nicht wie ein Ausschnitt lesen. pruef-agentur: 61 Pruefungen (vorher 40), alle gruen. Zwei eigene Fehler nebenbei gefunden und behoben: eine Beschriftung mit 11,2 px (Grenze 11,5) und ein Testdatum aus UTC statt Ortszeit. Zusaetzlich gruen: css-klassen, struktur, formulare, barrierefrei-workspace, handy. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+166
-15
@@ -51,6 +51,32 @@
|
||||
const melde = (t) => { $('fehler').textContent = t || ''; };
|
||||
const datum = (iso) => (iso ? iso.slice(0, 10).split('-').reverse().join('.') : '—');
|
||||
|
||||
/* ---------- Agentur-Events ---------------------------------------------
|
||||
Ein Event ist kein Eintrag mit Ueberschrift und Text, sondern eine
|
||||
Aktion mit Zeitraum, Aufgaben und Regeln. Welche Art das ist, sagt
|
||||
der Server (die Arten kommen aus workspace-bereiche.js) -- die
|
||||
Kennung steht hier trotzdem woertlich, weil das Formular je Art
|
||||
anders aussieht und es dafuer keine allgemeine Regel gibt. Sie
|
||||
steht an EINER Stelle, damit sie sich nicht verteilt. */
|
||||
const EVENT_BEREICH = 'agentur';
|
||||
const EVENT_ART = 'kampagne';
|
||||
const istEventArt = (art) => bereich === EVENT_BEREICH && art === EVENT_ART;
|
||||
|
||||
/* Laeuft das Event noch, kommt es erst, ist es vorbei?
|
||||
Der Vergleich laeuft auf den ISO-Zeichenketten, nicht auf Date --
|
||||
"2026-09-12" < "2026-09-30" ist als Text schon richtig, und ein
|
||||
Date() haette hier eine Zeitzone erfunden, die niemand gemeint hat.
|
||||
Ohne Ende gilt der Beginntag als der ganze Zeitraum. */
|
||||
function eventStand(e) {
|
||||
const heute = window.heuteLokal ? window.heuteLokal() : new Date().toISOString().slice(0, 10);
|
||||
const von = (e.datum || '').slice(0, 10);
|
||||
const bis = (e.event_ende || von).slice(0, 10);
|
||||
if (!von) return null;
|
||||
if (heute < von) return { schluessel: 'kommt', text: 'startet ' + datum(von) };
|
||||
if (heute > bis) return { schluessel: 'vorbei', text: 'vorbei seit ' + datum(bis) };
|
||||
return { schluessel: 'laeuft', text: von === bis ? 'heute' : 'läuft bis ' + datum(bis) };
|
||||
}
|
||||
|
||||
async function hole(pfad, optionen = {}) {
|
||||
const a = await fetch(pfad, { credentials: 'same-origin', ...optionen });
|
||||
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
|
||||
@@ -91,10 +117,45 @@
|
||||
}
|
||||
k.append(kopf);
|
||||
|
||||
/* ---- Agentur-Event: Zeitraum, Aufgaben, Regeln -------------------
|
||||
Die Reihenfolge ist nicht beliebig. Wer auf ein Event schaut,
|
||||
fragt in genau dieser Folge: Laeuft das ueberhaupt noch? Was
|
||||
muss ich tun? Was gilt dabei? Steht der Zeitraum unten im Fuss
|
||||
-- da, wo bei allen anderen Karten das Datum steht --, liest man
|
||||
erst die Aufgaben und danach, dass es seit einer Woche vorbei
|
||||
ist. */
|
||||
const stand = istEventArt(e.art) ? eventStand(e) : null;
|
||||
if (stand) {
|
||||
k.dataset.event = stand.schluessel;
|
||||
const zeile = el('p', 'event-zeitraum');
|
||||
zeile.dataset.stand = stand.schluessel;
|
||||
zeile.append(el('span', 'event-zeitraum__marke', stand.text));
|
||||
zeile.append(el('span', 'event-zeitraum__spanne',
|
||||
e.event_ende && e.event_ende !== e.datum
|
||||
? `${datum(e.datum)} – ${datum(e.event_ende)}`
|
||||
: datum(e.datum)));
|
||||
k.append(zeile);
|
||||
}
|
||||
|
||||
if (e.text) k.append(el('p', 'eintrag-text', e.text));
|
||||
|
||||
if (stand) {
|
||||
for (const [feld, name] of [['event_aufgaben', 'Aufgaben'], ['event_regeln', 'Regeln']]) {
|
||||
if (!e[feld]) continue;
|
||||
const block = el('div', 'event-block');
|
||||
block.append(el('h4', 'event-block__schild', name));
|
||||
/* Zeilenumbrueche bleiben stehen (white-space: pre-line im CSS).
|
||||
Eine Aufzaehlung, die zu einem Absatz zusammenlaeuft, ist
|
||||
genau das, was diese beiden Felder verhindern sollen. */
|
||||
block.append(el('p', 'event-block__text', e[feld]));
|
||||
k.append(block);
|
||||
}
|
||||
}
|
||||
|
||||
const fuss = el('div', 'eintrag-fuss');
|
||||
fuss.append(el('span', null, datum(e.datum)));
|
||||
/* Beim Event steht der Zeitraum schon oben -- das Datum hier waere
|
||||
dieselbe Zahl ein zweites Mal. */
|
||||
if (!stand) fuss.append(el('span', null, datum(e.datum)));
|
||||
if (LEITUNG.has(ich.rolle) && e.creator_name) fuss.append(el('span', null, '· ' + e.creator_name));
|
||||
if (e.erstellt_name) fuss.append(el('span', null, '· von ' + e.erstellt_name));
|
||||
if (e.status === 'erledigt') fuss.append(el('span', null, '· erledigt'));
|
||||
@@ -276,6 +337,44 @@
|
||||
|
||||
/* ---------- Neuer Eintrag ---------------------------------------------- */
|
||||
|
||||
/* Das Formular sieht bei einem Agentur-Event anders aus als bei allem
|
||||
anderen -- nicht "zusaetzlich", sondern anders: Aus "Überschrift"
|
||||
wird der Titel des Events, aus "Text" die Beschreibung, aus dem
|
||||
Datum ein Zeitraum, und Dringlichkeit hat hier nichts zu suchen
|
||||
(ein Event ist nicht dringend, es hat einen Termin).
|
||||
|
||||
Umgeschaltet wird bei JEDEM Artwechsel und beim Oeffnen. Nur beim
|
||||
Oeffnen zu schalten waere der bequeme Fehler: Man oeffnet mit
|
||||
"Schulung", waehlt "Agentur-Events" und fuellt ein Formular aus,
|
||||
das noch nach der alten Art fragt. */
|
||||
function formularAnpassen() {
|
||||
const event = istEventArt($('f-art').value);
|
||||
|
||||
$('feld-ende').hidden = !event;
|
||||
$('feld-aufgaben').hidden = !event;
|
||||
$('feld-regeln').hidden = !event;
|
||||
if (einstellung?.dringlichkeit) $('feld-dringlich').hidden = event;
|
||||
|
||||
$('schild-datum').textContent = event ? 'Von' : 'Datum';
|
||||
$('schild-titel').textContent = event ? 'Titel des Events' : 'Überschrift';
|
||||
/* textContent wuerde das <span class="leise"> mit erschlagen --
|
||||
deshalb nur der erste Textknoten, das kleine "(optional)" bleibt
|
||||
stehen. */
|
||||
const textSchild = $('schild-text').firstChild;
|
||||
if (textSchild) textSchild.nodeValue = event ? 'Beschreibung ' : 'Text ';
|
||||
|
||||
/* Leeren, was gerade unsichtbar wird. Sonst reicht ein Wechsel von
|
||||
"Agentur-Events" auf "Schulung", und das Ende des Zeitraums
|
||||
faehrt unsichtbar mit -- der Server nimmt es an, weil es zum
|
||||
Bereich gehoert, und an der Schulung stuende ein Enddatum, das
|
||||
niemand eingetragen hat und niemand sieht. */
|
||||
if (!event) {
|
||||
$('f-ende').value = '';
|
||||
$('f-aufgaben').value = '';
|
||||
$('f-regeln').value = '';
|
||||
}
|
||||
}
|
||||
|
||||
const umschalten = (auf) => {
|
||||
/* Auch hier die Regel, nicht nur einmal beim Aufbau: Diese Funktion
|
||||
ist die einzige Stelle, die das Formular je wieder sichtbar macht.
|
||||
@@ -288,11 +387,17 @@
|
||||
};
|
||||
$('neu-oeffnen').addEventListener('click', () => {
|
||||
$('f-datum').value = window.heuteLokal();
|
||||
formularAnpassen();
|
||||
umschalten(true);
|
||||
});
|
||||
$('neu-abbrechen').addEventListener('click', () => {
|
||||
$('neu').reset(); $('neu-fehler').textContent = ''; umschalten(false);
|
||||
$('neu').reset(); $('neu-fehler').textContent = ''; formularAnpassen(); umschalten(false);
|
||||
});
|
||||
/* "change" und nicht "input": Die Auswahl wird ueber wahl.js zu einer
|
||||
Knopfliste umgebaut, und die meldet ihre Wahl als change-Ereignis
|
||||
am urspruenglichen <select> -- ohne dieses Ereignis merkte das
|
||||
Formular vom Artwechsel nichts. */
|
||||
$('f-art').addEventListener('change', formularAnpassen);
|
||||
|
||||
$('neu').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
@@ -304,9 +409,19 @@
|
||||
text: $('f-text').value,
|
||||
datum: $('f-datum').value,
|
||||
};
|
||||
if (einstellung.dringlichkeit) daten.dringlichkeit = $('f-dringlich').value;
|
||||
const event = istEventArt(daten.art);
|
||||
if (einstellung.dringlichkeit && !event) daten.dringlichkeit = $('f-dringlich').value;
|
||||
if (einstellung.bewertung) daten.bewertung = $('f-bewertung').value || null;
|
||||
if (LEITUNG.has(ich.rolle)) {
|
||||
if (event) {
|
||||
daten.event_ende = $('f-ende').value || null;
|
||||
daten.event_aufgaben = $('f-aufgaben').value || null;
|
||||
daten.event_regeln = $('f-regeln').value || null;
|
||||
}
|
||||
/* Im Agentur-Bereich gehoert ein Eintrag allen -- es gibt nichts
|
||||
zuzuordnen, also wird auch nichts mitgeschickt. Der Server setzt
|
||||
beides ohnehin auf leer (ohneCreatorBezug); hier zu schweigen
|
||||
spart die stille Annahme, das Formular haette etwas zu sagen. */
|
||||
if (LEITUNG.has(ich.rolle) && !einstellung.ohneCreatorBezug) {
|
||||
daten.creator_id = $('f-creator').value || null;
|
||||
daten.creator_extern = $('f-creator').dataset.extern || null;
|
||||
}
|
||||
@@ -365,8 +480,16 @@
|
||||
if (!darfEintragen()) {
|
||||
$('neu-oeffnen').hidden = true;
|
||||
$('neu').hidden = true;
|
||||
$('unterzeile').textContent =
|
||||
(UNTERZEILE[bereich] || '') + ' Hier trägt deine Betreuung ein – du siehst alles, was zu dir gehört.';
|
||||
/* Zwei verschiedene Saetze, weil es zwei verschiedene Wahrheiten
|
||||
sind. In den Betreuungsbereichen sieht ein Creator, was ZU IHM
|
||||
gehoert -- in der Agentur sieht er ALLES. Stuende hier weiter
|
||||
"was zu dir gehört", laese sich eine vollstaendige Liste wie
|
||||
ein Ausschnitt, und die naheliegende Frage waere: Was sehe ich
|
||||
hier eigentlich nicht? */
|
||||
$('unterzeile').textContent = (UNTERZEILE[bereich] || '')
|
||||
+ (einstellung.fuerAlle
|
||||
? ' Eingetragen wird von der Agentur – du siehst alles, was hier steht.'
|
||||
: ' Hier trägt deine Betreuung ein – du siehst alles, was zu dir gehört.');
|
||||
}
|
||||
|
||||
for (const [wert, name] of Object.entries(einstellung.arten)) {
|
||||
@@ -379,16 +502,44 @@
|
||||
|
||||
if (LEITUNG.has(ich.rolle)) {
|
||||
$('feld-creator').hidden = false;
|
||||
try {
|
||||
const { personen } = await (await hole('/workspace/api/personen')).json();
|
||||
for (const p of personen.filter((p) => p.rolle === 'creator')) {
|
||||
const o = document.createElement('option');
|
||||
o.value = String(p.id); o.textContent = p.name;
|
||||
$('f-creator').append(o);
|
||||
}
|
||||
} catch { /* Zuordnung bleibt leer */ }
|
||||
window.personenwahl?.('f-creator', { hinweis: "Kein Konto im Workspace – der Eintrag gehört dann zu keiner Person und taucht in personenbezogenen Auswertungen nicht auf. Sichtbar bleibt er für dich." });
|
||||
|
||||
/* AGENTUR: EINE EINZIGE WAHL, UND DIE HEISST "AGENTUR" (07.09.2026).
|
||||
|
||||
Hier stand dieselbe Creator-Liste wie ueberall -- und sie war
|
||||
eine Falle. Wer ein Event fuer alle anlegt und dabei aus
|
||||
Gewohnheit einen Namen anklickt, hat es damit UNSICHTBAR
|
||||
gemacht: fuer alle anderen Creator, ohne Warnung, ohne dass es
|
||||
der Liste danach anzusehen waere.
|
||||
|
||||
Kein Feld auszublenden waere die halbe Loesung gewesen: Dann
|
||||
fragt man sich, wem der Eintrag gehoert. Eine Liste mit genau
|
||||
einer Wahl beantwortet die Frage, statt sie wegzunehmen.
|
||||
|
||||
Die Personenwahl (freier Name, Suchfeld) wird hier bewusst
|
||||
NICHT aufgesetzt -- sie erlaubt getippte Namen, und genau das
|
||||
soll es hier nicht geben. */
|
||||
if (einstellung.ohneCreatorBezug) {
|
||||
$('schild-creator').textContent = 'Gilt für';
|
||||
$('f-creator').textContent = '';
|
||||
const o = document.createElement('option');
|
||||
o.value = ''; o.textContent = 'Agentur';
|
||||
$('f-creator').append(o);
|
||||
const hinweis = $('hinweis-creator');
|
||||
hinweis.textContent = 'Agentur-Einträge gehören allen – jeder im Workspace sieht sie.';
|
||||
hinweis.hidden = false;
|
||||
} else {
|
||||
try {
|
||||
const { personen } = await (await hole('/workspace/api/personen')).json();
|
||||
for (const p of personen.filter((p) => p.rolle === 'creator')) {
|
||||
const o = document.createElement('option');
|
||||
o.value = String(p.id); o.textContent = p.name;
|
||||
$('f-creator').append(o);
|
||||
}
|
||||
} catch { /* Zuordnung bleibt leer */ }
|
||||
window.personenwahl?.('f-creator', { hinweis: "Kein Konto im Workspace – der Eintrag gehört dann zu keiner Person und taucht in personenbezogenen Auswertungen nicht auf. Sichtbar bleibt er für dich." });
|
||||
}
|
||||
}
|
||||
formularAnpassen();
|
||||
|
||||
/* Kommt jemand vom Bericht mit "nur die offenen", wird der Filter
|
||||
VOR dem ersten Laden gesetzt -- sonst baut die Seite alles auf,
|
||||
|
||||
Reference in New Issue
Block a user