Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
09588af
feat(statistics): evaluate responses and attendance across appointments
claude Aug 8, 2026
795deb2
refactor(statistics): act on the cleanup review
claude Aug 9, 2026
de90c60
fix(statistics): read the new permission and capability in the client
claude Aug 9, 2026
7d31004
fix(dev): reset see_statistics along with the other permissions
claude Aug 9, 2026
8f18b8d
feat(statistics): own row only without the permission, and highlight …
claude Aug 9, 2026
ce0a97f
fix(statistics): name every bar, and take the chart colours from the …
claude Aug 9, 2026
bc5954a
feat(statistics): render the drill-down like the appointment detail page
claude Aug 9, 2026
7b37c44
test(statistics): assert the drill-down gate before the click
claude Aug 9, 2026
78ddd16
feat(statistics): switch grouping and level of detail
claude Aug 9, 2026
13ac190
refactor(statistics): act on the cleanup review
claude Aug 9, 2026
dd783a0
refactor(statistics): put the table's own controls with the table
claude Aug 9, 2026
37c88dd
fix(statistics): derive the attendance colour, and align the page hea…
claude Aug 9, 2026
a6e146f
fix(statistics): keep the view switches on one line
claude Aug 9, 2026
a79b048
feat(statistics): show the acceptance rate in the compact view too
claude Aug 9, 2026
be25052
fix: clear the navigation toggle once, for every view
claude Aug 9, 2026
4055fbd
refactor(statistics): act on the second cleanup review
claude Aug 9, 2026
474d99d
fix: put the toggle clearance on an element that exists
claude Aug 9, 2026
2c7d972
fix(statistics): keep the grouping switch off the charts
claude Aug 9, 2026
25f0e31
fix(statistics): align the chart legends with the plot area
claude Aug 9, 2026
deb4547
docs(l10n): explain the statistics strings a translator cannot place
claude Aug 9, 2026
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
7 changes: 7 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
['name' => 'page#appointment', 'url' => '/appointment/{id}', 'verb' => 'GET'],
// Legacy shape produced by <=1.42, redirects to the canonical URL.
['name' => 'page#legacyAllAppointment', 'url' => '/all/appointment/{id}', 'verb' => 'GET'],
['name' => 'page#statistics', 'url' => '/statistics', 'verb' => 'GET'],
['name' => 'page#checkin', 'url' => '/checkin/{id}', 'verb' => 'GET'],
['name' => 'page#create', 'url' => '/create', 'verb' => 'GET'],
['name' => 'page#edit', 'url' => '/edit/{id}', 'verb' => 'GET'],
Expand Down Expand Up @@ -86,6 +87,12 @@
// Export
['name' => 'appointment#export', 'url' => '/api/export', 'verb' => 'POST'],

// Statistics
// NOTE: Specific routes must come BEFORE wildcard {targetUserId} routes
['name' => 'statistics#index', 'url' => '/api/statistics', 'verb' => 'GET'],
['name' => 'statistics#export', 'url' => '/api/statistics/export', 'verb' => 'POST'],
['name' => 'statistics#person', 'url' => '/api/statistics/person/{targetUserId}', 'verb' => 'GET'],

// iCal feed
['name' => 'ical#getToken', 'url' => '/api/ical/token', 'verb' => 'GET'],
['name' => 'ical#regenerateToken', 'url' => '/api/ical/token/regenerate', 'verb' => 'POST'],
Expand Down
56 changes: 54 additions & 2 deletions l10n/de.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,6 @@ OC.L10N.register(
"Someone has to create the appointments people reply to. Usually that is a small group — a board, a team of trainers, the office." : "Jemand muss die Termine anlegen, auf die geantwortet wird. Meist ist das ein kleiner Kreis — ein Vorstand, ein Trainerteam, das Büro.",
"At the appointment itself, check-in records who really showed up. The check-in screen shows every attendee with a single tick per person." : "Beim Termin selbst hält der Check-in fest, wer wirklich da war. Die Check-in-Ansicht zeigt jeden Teilnehmer mit einem einzigen Haken pro Person.",
"Responses" : "Antworten",
"Who gets to see the replies of everyone else? The detailed summary shows names, the counts show only how many said yes, no or maybe." : "Wer darf die Antworten der anderen sehen? Die ausführliche Übersicht zeigt Namen, die Zahlen zeigen nur, wie viele mit Ja, Nein oder Vielleicht geantwortet haben.",
"Audit log" : "Auditprotokoll",
"Attendance can record every reply, change, withdrawal and check-in — who did what, when, and from where. The timeline shows up on each appointment, and it is what lets managers get a push notification when somebody changes their mind." : "Anwesenheit kann jede Antwort, Änderung, Rücknahme und jeden Check-in festhalten — wer was wann und von wo aus getan hat. Der Verlauf erscheint bei jedem Termin und ist die Grundlage dafür, dass Verantwortliche eine Push-Benachrichtigung bekommen, wenn jemand es sich anders überlegt.",
"Response summary groups" : "Gruppen in Antwortübersicht",
Expand Down Expand Up @@ -879,6 +878,59 @@ OC.L10N.register(
"_{count} attendee not yet checked in_::_{count} attendees not yet checked in_" : ["{count} Teilnehmer noch nicht eingecheckt","{count} Teilnehmer noch nicht eingecheckt"],
"_{count} reminder sent_::_{count} reminders sent_" : ["{count} Erinnerung gesendet","{count} Erinnerungen gesendet"],
"_This appointment is part of a series of {count} appointment. How would you like to apply this change?_::_This appointment is part of a series of {count} appointments. How would you like to apply this change?_" : ["Dieser Termin ist Teil einer Reihe von {count} Termin. Worauf soll diese Änderung angewendet werden?","Dieser Termin ist Teil einer Reihe von {count} Terminen. Worauf soll diese Änderung angewendet werden?"],
"_Do you want to set {count} user to {action}?_::_Do you want to set {count} users to {action}?_" : ["Möchtest du {count} Benutzer auf {action} festlegen?","Möchtest du {count} Benutzer auf {action} festlegen?"]
"_Do you want to set {count} user to {action}?_::_Do you want to set {count} users to {action}?_" : ["Möchtest du {count} Benutzer auf {action} festlegen?","Möchtest du {count} Benutzer auf {action} festlegen?"],
"Statistics" : "Statistik",
"Acceptance rate" : "Zusagequote",
"Attendance rate" : "Anwesenheitsquote",
"Response rate" : "Antwortquote",
"No-show" : "Nicht erschienen",
"Not recorded" : "Nicht erfasst",
"Said yes but was recorded as absent" : "Hat zugesagt, wurde aber als abwesend erfasst",
"Said no but attended" : "Hat abgesagt, war aber da",
"Total" : "Gesamt",
"Name" : "Name",
"Guest" : "Gast",
"People" : "Personen",
"Groups" : "Gruppen",
"Teams" : "Teams",
"Group by" : "Gruppieren nach",
"Period" : "Zeitraum",
"Range" : "Zeitspanne",
"Year" : "Jahr",
"Half-year" : "Halbjahr",
"Quarter" : "Quartal",
"Custom" : "Benutzerdefiniert",
"Q{quarter} {year}" : "Q{quarter} {year}",
"H{half} {year}" : "H{half} {year}",
"Over time" : "Zeitlicher Verlauf",
"By group" : "Nach Gruppe",
"By team" : "Nach Team",
"By category" : "Nach Kategorie",
"Without category" : "Ohne Kategorie",
"Search people" : "Personen suchen",
"No appointments in this period" : "Keine Termine in diesem Zeitraum",
"Too many appointments in this period" : "Zu viele Termine in diesem Zeitraum",
"Attendance recorded for {recorded} of {past} past appointments" : "Anwesenheit bei {recorded} von {past} vergangenen Terminen erfasst",
"People in several groups are counted in each of them. The total row counts every person once." : "Personen in mehreren Gruppen zählen in jeder davon mit. Die Gesamtzeile zählt jede Person einmal.",
"Failed to load statistics" : "Statistik konnte nicht geladen werden",
"Failed to export statistics" : "Statistik konnte nicht exportiert werden",
"May see everyone's statistics" : "Darf die Statistik aller sehen",
"See the evaluation of responses and attendance across all appointments, per person." : "Die Auswertung von Antworten und Anwesenheit über alle Termine hinweg sehen, pro Person.",
"Every user can see how often each person responded and attended." : "Jeder Nutzer kann sehen, wie oft jede Person geantwortet hat und anwesend war.",
"Who may see the evaluation across all appointments" : "Wer die Auswertung über alle Termine sieht",
"Who gets to see the replies of everyone else? The detailed summary shows names, the counts show only how many said yes, no or maybe, and the statistics evaluate both across all appointments." : "Wer darf die Antworten der anderen sehen? Die ausführliche Übersicht zeigt Namen, die Zahlen zeigen nur, wie viele mit Ja, Nein oder Vielleicht geantwortet haben, und die Statistik wertet beides über alle Termine hinweg aus.",
"Your numbers" : "Deine Zahlen",
"_%n appointment_::_%n appointments_" : ["%n Termin","%n Termine"],
"_%n appointment in this period_::_%n appointments in this period_" : ["%n Termin in diesem Zeitraum","%n Termine in diesem Zeitraum"],
"_%n person_::_%n people_" : ["%n Person","%n Personen"],
"_The evaluation handles up to %n appointment at a time. Please choose a shorter period._::_The evaluation handles up to %n appointments at a time. Please choose a shorter period._" : ["Die Auswertung verarbeitet höchstens %n Termin auf einmal. Bitte einen kürzeren Zeitraum wählen.","Die Auswertung verarbeitet höchstens %n Termine auf einmal. Bitte einen kürzeren Zeitraum wählen."],
"Without this, users only see their own numbers." : "Ohne diese Berechtigung sind nur die eigenen Zahlen sichtbar.",
"You were not invited to any appointment in this period" : "Du warst in diesem Zeitraum zu keinem Termin eingeladen",
"Grouping" : "Gruppierung",
"Grouped" : "Gruppiert",
"Ungrouped" : "Ohne Gruppierung",
"Detail" : "Detailgrad",
"Compact" : "Kompakt",
"Full" : "Vollständig"
},
"nplurals=2; plural=(n != 1);");
56 changes: 54 additions & 2 deletions l10n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,6 @@
"Someone has to create the appointments people reply to. Usually that is a small group — a board, a team of trainers, the office." : "Jemand muss die Termine anlegen, auf die geantwortet wird. Meist ist das ein kleiner Kreis — ein Vorstand, ein Trainerteam, das Büro.",
"At the appointment itself, check-in records who really showed up. The check-in screen shows every attendee with a single tick per person." : "Beim Termin selbst hält der Check-in fest, wer wirklich da war. Die Check-in-Ansicht zeigt jeden Teilnehmer mit einem einzigen Haken pro Person.",
"Responses" : "Antworten",
"Who gets to see the replies of everyone else? The detailed summary shows names, the counts show only how many said yes, no or maybe." : "Wer darf die Antworten der anderen sehen? Die ausführliche Übersicht zeigt Namen, die Zahlen zeigen nur, wie viele mit Ja, Nein oder Vielleicht geantwortet haben.",
"Audit log" : "Auditprotokoll",
"Attendance can record every reply, change, withdrawal and check-in — who did what, when, and from where. The timeline shows up on each appointment, and it is what lets managers get a push notification when somebody changes their mind." : "Anwesenheit kann jede Antwort, Änderung, Rücknahme und jeden Check-in festhalten — wer was wann und von wo aus getan hat. Der Verlauf erscheint bei jedem Termin und ist die Grundlage dafür, dass Verantwortliche eine Push-Benachrichtigung bekommen, wenn jemand es sich anders überlegt.",
"Response summary groups" : "Gruppen in Antwortübersicht",
Expand Down Expand Up @@ -877,6 +876,59 @@
"_{count} attendee not yet checked in_::_{count} attendees not yet checked in_" : ["{count} Teilnehmer noch nicht eingecheckt","{count} Teilnehmer noch nicht eingecheckt"],
"_{count} reminder sent_::_{count} reminders sent_" : ["{count} Erinnerung gesendet","{count} Erinnerungen gesendet"],
"_This appointment is part of a series of {count} appointment. How would you like to apply this change?_::_This appointment is part of a series of {count} appointments. How would you like to apply this change?_" : ["Dieser Termin ist Teil einer Reihe von {count} Termin. Worauf soll diese Änderung angewendet werden?","Dieser Termin ist Teil einer Reihe von {count} Terminen. Worauf soll diese Änderung angewendet werden?"],
"_Do you want to set {count} user to {action}?_::_Do you want to set {count} users to {action}?_" : ["Möchtest du {count} Benutzer auf {action} festlegen?","Möchtest du {count} Benutzer auf {action} festlegen?"]
"_Do you want to set {count} user to {action}?_::_Do you want to set {count} users to {action}?_" : ["Möchtest du {count} Benutzer auf {action} festlegen?","Möchtest du {count} Benutzer auf {action} festlegen?"],
"Statistics" : "Statistik",
"Acceptance rate" : "Zusagequote",
"Attendance rate" : "Anwesenheitsquote",
"Response rate" : "Antwortquote",
"No-show" : "Nicht erschienen",
"Not recorded" : "Nicht erfasst",
"Said yes but was recorded as absent" : "Hat zugesagt, wurde aber als abwesend erfasst",
"Said no but attended" : "Hat abgesagt, war aber da",
"Total" : "Gesamt",
"Name" : "Name",
"Guest" : "Gast",
"People" : "Personen",
"Groups" : "Gruppen",
"Teams" : "Teams",
"Group by" : "Gruppieren nach",
"Period" : "Zeitraum",
"Range" : "Zeitspanne",
"Year" : "Jahr",
"Half-year" : "Halbjahr",
"Quarter" : "Quartal",
"Custom" : "Benutzerdefiniert",
"Q{quarter} {year}" : "Q{quarter} {year}",
"H{half} {year}" : "H{half} {year}",
"Over time" : "Zeitlicher Verlauf",
"By group" : "Nach Gruppe",
"By team" : "Nach Team",
"By category" : "Nach Kategorie",
"Without category" : "Ohne Kategorie",
"Search people" : "Personen suchen",
"No appointments in this period" : "Keine Termine in diesem Zeitraum",
"Too many appointments in this period" : "Zu viele Termine in diesem Zeitraum",
"Attendance recorded for {recorded} of {past} past appointments" : "Anwesenheit bei {recorded} von {past} vergangenen Terminen erfasst",
"People in several groups are counted in each of them. The total row counts every person once." : "Personen in mehreren Gruppen zählen in jeder davon mit. Die Gesamtzeile zählt jede Person einmal.",
"Failed to load statistics" : "Statistik konnte nicht geladen werden",
"Failed to export statistics" : "Statistik konnte nicht exportiert werden",
"May see everyone's statistics" : "Darf die Statistik aller sehen",
"See the evaluation of responses and attendance across all appointments, per person." : "Die Auswertung von Antworten und Anwesenheit über alle Termine hinweg sehen, pro Person.",
"Every user can see how often each person responded and attended." : "Jeder Nutzer kann sehen, wie oft jede Person geantwortet hat und anwesend war.",
"Who may see the evaluation across all appointments" : "Wer die Auswertung über alle Termine sieht",
"Who gets to see the replies of everyone else? The detailed summary shows names, the counts show only how many said yes, no or maybe, and the statistics evaluate both across all appointments." : "Wer darf die Antworten der anderen sehen? Die ausführliche Übersicht zeigt Namen, die Zahlen zeigen nur, wie viele mit Ja, Nein oder Vielleicht geantwortet haben, und die Statistik wertet beides über alle Termine hinweg aus.",
"Your numbers" : "Deine Zahlen",
"_%n appointment_::_%n appointments_" : ["%n Termin","%n Termine"],
"_%n appointment in this period_::_%n appointments in this period_" : ["%n Termin in diesem Zeitraum","%n Termine in diesem Zeitraum"],
"_%n person_::_%n people_" : ["%n Person","%n Personen"],
"_The evaluation handles up to %n appointment at a time. Please choose a shorter period._::_The evaluation handles up to %n appointments at a time. Please choose a shorter period._" : ["Die Auswertung verarbeitet höchstens %n Termin auf einmal. Bitte einen kürzeren Zeitraum wählen.","Die Auswertung verarbeitet höchstens %n Termine auf einmal. Bitte einen kürzeren Zeitraum wählen."],
"Without this, users only see their own numbers." : "Ohne diese Berechtigung sind nur die eigenen Zahlen sichtbar.",
"You were not invited to any appointment in this period" : "Du warst in diesem Zeitraum zu keinem Termin eingeladen",
"Grouping" : "Gruppierung",
"Grouped" : "Gruppiert",
"Ungrouped" : "Ohne Gruppierung",
"Detail" : "Detailgrad",
"Compact" : "Kompakt",
"Full" : "Vollständig"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
Loading
Loading