Skip to content

Commit 2354359

Browse files
committed
(feat) icalendar links
1 parent 69e4d99 commit 2354359

File tree

16 files changed

+256
-15
lines changed

16 files changed

+256
-15
lines changed

app/controllers/public_reservation_ical_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ def type_ics
88
# send_data cal.to_ical, type: 'text/calendar', disposition: 'inline', filename: "#{params[:reservable_type]}_reservations.ics"
99
render plain: cal.to_ical
1010
end
11-
end
11+
end

app/frontend/src/javascript/controllers/calendar.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,56 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
130130
$scope.filterAvailabilities(filter, $scope);
131131
};
132132

133+
$scope.openICalLinksModal = function () {
134+
const modalInstance = $uibModal.open({
135+
animation: true,
136+
templateUrl: '/calendar/ical_links.html',
137+
size: 'md',
138+
controller: ['$scope', '$uibModalInstance', function ($scope, $uibModalInstance) {
139+
$scope.icalLinks = [];
140+
if (Fablab.machinesModule) {
141+
$scope.icalLinks.push({
142+
type: 'Machine',
143+
name: _t('app.shared.calendar.machine_reservations'),
144+
url: `webcal://${window.location.host}/ical/reservations/machine`
145+
});
146+
}
147+
if (Fablab.trainingsModule) {
148+
$scope.icalLinks.push({
149+
type: 'Training',
150+
name: _t('app.shared.calendar.training_reservations'),
151+
url: `webcal://${window.location.host}/ical/availabilities/training`
152+
});
153+
}
154+
if (Fablab.spacesModule) {
155+
$scope.icalLinks.push({
156+
type: 'Space',
157+
name: _t('app.shared.calendar.space_reservations'),
158+
url: `webcal://${window.location.host}/ical/availabilities/space`
159+
});
160+
}
161+
$scope.icalLinks.push({
162+
type: 'Event',
163+
name: _t('app.shared.calendar.event_reservations'),
164+
url: `webcal://${window.location.host}/ical/reservations/event`
165+
});
166+
167+
$scope.copyToClipboard = function (url) {
168+
navigator.clipboard.writeText(url).then(function () {
169+
growl.success(_t('app.shared.calendar.link_copied'));
170+
}).catch(function () {
171+
growl.error(_t('app.shared.calendar.copy_failed'));
172+
});
173+
};
174+
175+
$scope.close = function () {
176+
$uibModalInstance.dismiss();
177+
};
178+
}]
179+
});
180+
return modalInstance;
181+
};
182+
133183
$scope.openFilterAside = () =>
134184
$aside.open({
135185
templateUrl: '/calendar/filterAside.html',

app/frontend/templates/calendar/calendar.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ <h1 translate>{{ 'app.public.calendar.calendar' }}</h1>
1818
</button>
1919
<button type="button" class="btn btn-default m-t m-b m-r-sm" ng-click="triggerAutoRefresh()">
2020
<span class="fas fa-redo"></span>
21+
<span ng-if="autoRefresh">{{ 'app.shared.calendar.auto_refresh_on' | translate }}</span>
22+
<span ng-if="!autoRefresh">{{ 'app.shared.calendar.auto_refresh_off' | translate }}</span>
23+
</button>
24+
<button type="button" class="btn btn-default m-t m-b m-r-sm" ng-click="openICalLinksModal()">
25+
<span class="fas fa-calendar-alt"></span> {{ 'app.shared.calendar.ical_links' | translate }}
2126
</button>
2227
<button type="button" class="btn btn-default m-t m-b" ng-click="triggerOnlyCalendarViewMode()">
2328
<span class="fas fa-expand"></span>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<div class="modal-header">
2+
<h3 class="modal-title">{{ 'app.shared.calendar.ical_links' | translate }}</h3>
3+
</div>
4+
5+
<div class="modal-body">
6+
<p class="text-muted">{{ 'app.shared.calendar.ical_description' | translate }}</p>
7+
8+
<div class="list-group">
9+
<div class="list-group-item" ng-repeat="link in icalLinks">
10+
<div class="row">
11+
<div class="col-md-8">
12+
<h5 class="list-group-item-heading m-b">
13+
<i class="fas fa-calendar" ng-class="{'text-primary': link.type === 'Machine', 'text-success': link.type === 'Training', 'text-warning': link.type === 'Event', 'text-info': link.type === 'Space'}"></i>
14+
{{ link.name }}
15+
</h5>
16+
<p class="list-group-item-text text-muted small">{{ link.url }}</p>
17+
</div>
18+
<div class="col-md-4 text-right">
19+
<button type="button" class="btn btn-outline-primary btn-sm" ng-click="copyToClipboard(link.url)">
20+
<i class="fas fa-copy"></i> {{ 'app.shared.calendar.copy_link' | translate }}
21+
</button>
22+
</div>
23+
</div>
24+
</div>
25+
</div>
26+
27+
<div class="alert alert-info" role="alert">
28+
<i class="fas fa-info-circle"></i>
29+
{{ 'app.shared.calendar.ical_usage_info' | translate }}
30+
</div>
31+
</div>
32+
33+
<div class="modal-footer">
34+
<button type="button" class="btn btn-default" ng-click="close()">{{ 'app.shared.buttons.close' | translate }}</button>
35+
</div>

app/services/reservation_calendar_service.rb

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,32 +51,47 @@ def slot_icalendar_event(cal)
5151
reservations = load_reservations
5252

5353
reservations.find_each do |reservation|
54-
reservation.grouped_slots.each do |_date, daily_groups|
55-
daily_groups.each do |start_time, group_slots|
56-
cal.event do |e|
57-
e.dtstart = start_time
58-
e.dtend = group_slots.last[:end_at]
59-
e.summary = reservation.reservable.name
60-
e.description = user_name(reservation)
61-
e.uid = "#{reservable_type}-#{reservation.reservable.id}-#{start_time.to_i}@fabmanager"
62-
e.status = group_slots.first.slots_reservations.pluck(&:canceled_at).include?(nil) ? 'IN-PROCESS' : 'CANCELLED'
63-
end
54+
reservation.slots_reservations.each do |slots_reservation|
55+
cal.event do |e|
56+
e.dtstart = slots_reservation.slot.start_at
57+
e.dtend = slots_reservation.slot.end_at
58+
e.summary = slots_reservation.reservation.reservable.name
59+
e.description = user_name(slots_reservation.reservation)
60+
e.uid = "#{reservable_type}-#{slots_reservation.reservation.reservable.id}-#{slots_reservation.slot.start_at.to_i}@fabmanager"
61+
e.status = slots_reservation.canceled_at ? 'CANCELLED' : 'CONFIRMED'
6462
end
6563
end
64+
# reservation.grouped_slots.each do |_date, daily_groups|
65+
# daily_groups.each do |start_time, group_slots|
66+
# slots_not_canceled = Slot.where(id: group_slots).includes(:slots_reservations)
67+
# .where(slots_reservations: { canceled_at: nil }).order(:start_at)
68+
# start_at = slots_not_canceled.present? ? slots_not_canceled.first.start_at : start_time
69+
# cal.event do |e|
70+
# e.dtstart = start_at
71+
# e.dtend = slots_not_canceled.present? ? slots_not_canceled.last.end_at : group_slots.last[:end_at]
72+
# e.summary = reservation.reservable.name
73+
# e.description = user_name(reservation)
74+
# e.uid = "#{reservable_type}-#{reservation.reservable.id}-#{start_at.to_i}@fabmanager"
75+
# e.status = slots_not_canceled.present? ? 'CONFIRMED' : 'CANCELLED'
76+
# end
77+
# end
78+
# end
6679
end
6780
end
6881

6982
def training_icalendar_event(cal)
7083
reservations_grouped = load_reservations.group_by(&:reservable_id)
7184
reservations_grouped.each_value do |reservations|
7285
reservation = reservations.first
86+
reservations_not_canceled = Reservation.where(id: reservations).includes(:slots_reservations)
87+
.where(slots_reservations: { canceled_at: nil })
7388
cal.event do |e|
7489
e.dtstart = reservation.slots.first.start_at
7590
e.dtend = reservation.slots.last.end_at
7691
e.summary = reservation.reservable.name
77-
e.description = reservations.map { |r| user_name(r) }.join('\n')
92+
e.description = reservations_not_canceled.map { |r| user_name(r) }.join('\n')
7893
e.uid = "#{reservable_type}-#{reservation.reservable_id}-#{reservation.slots.first.start_at.to_i}@fabmanager"
79-
e.status = reservation.slots_reservations.first.canceled_at ? 'CANCELLED' : 'IN-PROCESS'
94+
e.status = reservations_not_canceled.present? ? 'CONFIRMED' : 'CANCELLED'
8095
end
8196
end
8297
end
@@ -85,13 +100,15 @@ def event_icalendar_event(cal)
85100
reservations_grouped = load_reservations.group_by(&:reservable_id)
86101
reservations_grouped.each_value do |reservations|
87102
reservation = reservations.first
103+
reservations_not_canceled = Reservation.where(id: reservations).includes(:slots_reservations)
104+
.where(slots_reservations: { canceled_at: nil })
88105
cal.event do |e|
89106
e.dtstart = reservation.slots.first.start_at
90107
e.dtend = reservation.slots.last.end_at
91108
e.summary = reservation.reservable.title
92-
e.description = build_event_description(reservations)
109+
e.description = build_event_description(reservations_not_canceled)
93110
e.uid = "#{reservable_type}-#{reservation.reservable_id}-#{reservation.slots.first.start_at.to_i}@fabmanager"
94-
e.status = reservation.slots_reservations.first.canceled_at ? 'CANCELLED' : 'IN-PROCESS'
111+
e.status = reservations_not_canceled.present? ? 'CONFIRMED' : 'CANCELLED'
95112
end
96113
end
97114
end

config/locales/app.shared.de.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,18 @@ de:
548548
events: "Events"
549549
externals: "Other calendars"
550550
show_reserved_uniq: "Show only slots with reservations"
551+
auto_refresh_on: "Auto-Aktualisierung aktiviert"
552+
auto_refresh_off: "Auto-Aktualisierung deaktiviert"
553+
ical_links: "iCal-Links"
554+
ical_description: "Verwenden Sie diese Links, um Reservierungskalender in Ihrer bevorzugten Kalender-App zu abonnieren."
555+
copy_link: "Link kopieren"
556+
ical_usage_info: "Kopieren Sie diese webcal://-Links in Ihre Kalender-App, um automatische Updates zu abonnieren."
557+
link_copied: "Link in die Zwischenablage kopiert!"
558+
copy_failed: "Fehler beim Kopieren des Links"
559+
machine_reservations: "Maschinenreservierungen"
560+
training_reservations: "Schulungsreservierungen"
561+
event_reservations: "Veranstaltungsreservierungen"
562+
space_reservations: "Raumreservierungen"
551563
machine:
552564
machine_uncategorized: "Uncategorized machines"
553565
form_unsaved_list:

config/locales/app.shared.en.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,19 @@ en:
548548
events: "Events"
549549
externals: "Other calendars"
550550
show_reserved_uniq: "Show only slots with reservations"
551+
auto_refresh_on: "Auto refresh: ON"
552+
auto_refresh_off: "Auto refresh: OFF"
553+
ical_links: "iCal links"
554+
ical_description: "Use the following links to subscribe to reservation calendars in your calendar application."
555+
copy_link: "Copy Link"
556+
subscribe: "Subscribe"
557+
ical_usage_info: "Add these links to your calendar application (such as Google Calendar, Outlook, etc.) to automatically sync reservation information."
558+
link_copied: "Link copied to clipboard"
559+
copy_failed: "Copy failed, please copy the link manually"
560+
machine_reservations: "Machine Reservations"
561+
training_reservations: "Training Reservations"
562+
event_reservations: "Event Reservations"
563+
space_reservations: "Space Reservations"
551564
machine:
552565
machine_uncategorized: "Uncategorized machines"
553566
form_unsaved_list:

config/locales/app.shared.es-MX.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,18 @@ es-MX:
548548
events: "Eventos"
549549
externals: "Otros calendarios"
550550
show_reserved_uniq: "Mostrar sólo franjas horarias con reserva"
551+
auto_refresh_on: "Actualización automática activada"
552+
auto_refresh_off: "Actualización automática desactivada"
553+
ical_links: "Enlaces iCal"
554+
ical_description: "Usa estos enlaces para suscribirte a calendarios de reservas en tu aplicación de calendario preferida."
555+
copy_link: "Copiar enlace"
556+
ical_usage_info: "Copia estos enlaces webcal:// a tu aplicación de calendario para suscribirte a actualizaciones automáticas."
557+
link_copied: "¡Enlace copiado al portapapeles!"
558+
copy_failed: "Error al copiar enlace"
559+
machine_reservations: "Reservas de máquinas"
560+
training_reservations: "Reservas de capacitaciones"
561+
event_reservations: "Reservas de eventos"
562+
space_reservations: "Reservas de espacios"
551563
machine:
552564
machine_uncategorized: "Máquinas sin categoría"
553565
form_unsaved_list:

config/locales/app.shared.es.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,18 @@ es:
548548
events: "Eventos"
549549
externals: "Otros calendarios"
550550
show_reserved_uniq: "Mostrar sólo franjas horarias con reserva"
551+
auto_refresh_on: "Actualización automática activada"
552+
auto_refresh_off: "Actualización automática desactivada"
553+
ical_links: "Enlaces iCal"
554+
ical_description: "Use estos enlaces para suscribirse a calendarios de reservas en su aplicación de calendario preferida."
555+
copy_link: "Copiar enlace"
556+
ical_usage_info: "Copie estos enlaces webcal:// en su aplicación de calendario para suscribirse a actualizaciones automáticas."
557+
link_copied: "¡Enlace copiado al portapapeles!"
558+
copy_failed: "Error al copiar el enlace"
559+
machine_reservations: "Reservas de máquinas"
560+
training_reservations: "Reservas de capacitaciones"
561+
event_reservations: "Reservas de eventos"
562+
space_reservations: "Reservas de espacios"
551563
machine:
552564
machine_uncategorized: "Máquinas sin categoría"
553565
form_unsaved_list:

config/locales/app.shared.fr.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,18 @@ fr:
548548
events: "Événements"
549549
externals: "Autres calendriers"
550550
show_reserved_uniq: "Afficher uniquement les créneaux avec réservation"
551+
auto_refresh_on: "Actualisation auto activée"
552+
auto_refresh_off: "Actualisation auto désactivée"
553+
ical_links: "Liens iCal"
554+
ical_description: "Utilisez ces liens pour vous abonner aux calendriers de réservation dans votre application de calendrier préférée."
555+
copy_link: "Copier le lien"
556+
ical_usage_info: "Copiez ces liens webcal:// dans votre application de calendrier pour vous abonner aux mises à jour automatiques."
557+
link_copied: "Lien copié dans le presse-papiers !"
558+
copy_failed: "Échec de la copie du lien"
559+
machine_reservations: "Réservations de machines"
560+
training_reservations: "Réservations de formations"
561+
event_reservations: "Réservations d'événements"
562+
space_reservations: "Réservations d'espaces"
551563
machine:
552564
machine_uncategorized: "Machines non classés"
553565
form_unsaved_list:

0 commit comments

Comments
 (0)