From 0c2c5ee12c34d42d0a59ebf6fcec1597fc714cde Mon Sep 17 00:00:00 2001
From: Thomas JUND <tjund@entrouvert.com>
Date: Thu, 4 Feb 2021 13:42:39 +0100
Subject: [PATCH] isere-cd38: add calendar booking  template (#50311)

---
 static/isere-cd38/_custom.scss                |  91 +++++
 .../forms/widgets/select--ens-calendar.html   | 316 ++++++++++++++++++
 2 files changed, 407 insertions(+)
 create mode 100644 templates/variants/isere-cd38/qommon/forms/widgets/select--ens-calendar.html

diff --git a/static/isere-cd38/_custom.scss b/static/isere-cd38/_custom.scss
index f59f40ed..688d9498 100644
--- a/static/isere-cd38/_custom.scss
+++ b/static/isere-cd38/_custom.scss
@@ -575,6 +575,97 @@ div#rub_service {
 	}
 }
 
+// ENS CALENDAR WIDGET
+.ens-calendar {
+	$max-btn-size: 4rem;
+	display: flex;
+	max-width: $max-btn-size * 7;
+	position: relative;
+
+	&, &--month {
+		.is-hidden {
+			display: none !important;
+		}
+	}
+
+	table, thead, tbody {
+		display: block;
+	}
+
+	&--month {
+		flex: 0 0 100%;
+	}
+
+	&--head {
+		min-height: 3rem;
+		padding-left: 3rem;
+		padding-right: 3rem;
+		text-transform: uppercase;
+		font-weight: 600;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	&--weekday {
+		text-transform: uppercase;
+		font-weight: 500;
+	}
+
+	&--weekdays,
+	&--week {
+		display: flex;
+	}
+	&--weekday,
+	&--day {
+		flex: 1 1 0;
+		padding: 0.15rem;
+	}
+	div.gru-content &--day-btn {
+		margin: 0;
+		display: block;
+		width: 100%;
+		padding-left: 0;
+		padding-right: 0;
+		background-color: white;
+		border: 1px solid;
+
+		&[data-status=open] {
+			color: green;
+			&:hover, &.on {
+				background-color: green;
+				color: white;
+			}
+		}
+		&[data-status=partially-open] {
+			color: #D66909;
+			&:hover, &.on {
+				background-color: #D66909;
+				color: white;
+			}
+		}
+		&[data-status=close] {
+			color: red;
+			background-color: white;
+			border: 1px solid;
+			&:hover, &.on {
+				background-color: red;
+				color: white;
+			}
+		}
+	}
+	div.gru-content &--next, div.gru-content &--prev {
+		position: absolute;
+		top: 0;
+		margin: 0;
+	}
+	&--next {
+		right: 0;
+	}
+	&--prev {
+		left: 0;
+	}
+}
 
 
 //
diff --git a/templates/variants/isere-cd38/qommon/forms/widgets/select--ens-calendar.html b/templates/variants/isere-cd38/qommon/forms/widgets/select--ens-calendar.html
new file mode 100644
index 00000000..101fd25a
--- /dev/null
+++ b/templates/variants/isere-cd38/qommon/forms/widgets/select--ens-calendar.html
@@ -0,0 +1,316 @@
+{% extends "qommon/forms/widget.html" %}
+{% load qommon i18n %}
+{% block widget-control %}
+
+<select
+  id="form_{{widget.name}}"
+  name="{{widget.name}}"
+  {% for attr in widget.attrs.items %}{{attr.0}}="{{attr.1}}"{% endfor %}
+  style="display: none"
+>
+  <option value="">---</option>
+  {% for option in widget.get_options %}
+    {% with datetime=option.options.date|parse_datetime %}
+    <option
+        {% for attr in option.attrs.items %} {{attr.0}}="{{attr.1}}"{% endfor %}
+        data-weeknumber="{{ option.options.date_weeknumber }}"
+        data-daynumber="{{ option.options.date_number }}"
+        data-weekdayindex="{{ option.options.date_weekdayindex }}"
+        data-month="{{ option.options.date_month }}"
+        data-status="{{ option.options.status }}"
+      >
+        {{ option.description }}
+      </option>
+    {% endwith %}
+  {% endfor %}
+</select>
+<div id="form_{{widget.name}}_calendar" class="ens-calendar is-hidden">
+</div>
+
+<script>
+$(function() {
+  const prefix = "ens-calendar--";
+  const WEEKDAYS = ["{% trans "Monday" %}", "{% trans "Tuesday" %}",
+                    "{% trans "Wednesday" %}", "{% trans "Thursday" %}",
+                    "{% trans "Friday" %}", "{% trans "Saturday" %}",
+                    "{% trans "Sunday" %}", ];
+
+
+
+  const $select = $('#form_{{widget.name}}');
+  let $options = $select.find('option');
+  const $calendar = $('#form_{{widget.name}}_calendar');
+  const $prev_btn = $('<button class="'  + prefix + 'prev" role="button">←</button>');
+  const $next_btn = $('<button class="'  + prefix + 'next" role="button">→</button>');
+  const activated_day_class = "on";
+
+  let calendar_days;
+  let $month_calendars;
+  let $day_btns;
+  let active_index = 0;
+
+  // map available data
+  // if static, data = $options
+  // if live, data = json
+  const define_calendar_days = function(data) {
+    const is$ = data instanceof $;
+    let days = data;
+    if (is$) {
+      days = $.makeArray($options);
+    }
+    const json_datas = days.map(function(day, idx){
+      const date = {};
+      date.value = (is$) ? day.value : day.id;
+      date.label = (is$) ? day.text : day.details;
+      date.number = (is$) ? day.dataset.daynumber : day.date_number ;
+      date.weekdayindex = (is$) ? day.dataset.weekdayindex : day.date_weekdayindex;
+      date.weeknumber = (is$) ? day.dataset.weeknumber : day.date_weeknumber;
+      date.month = (is$) ? day.dataset.month : day.date_month;
+      date.idx = (is$) ? idx : idx + 1;
+      date.status = (is$) ? day.dataset.status : day.status;
+      return date;
+    }).filter(function(date){
+      return date.value;
+    });
+    return json_datas;
+  };
+
+  // render Calendar
+  const calendar_layout = function() {
+    // Return array of uniq key
+    const list_of = function (arr, key) {
+      return arr
+        .map(function(day){
+          return day[key];
+        })
+        .filter(function(day, idx, days) {
+          return days.indexOf(day) === idx;
+        });
+    }
+
+    const months = list_of(calendar_days, 'month');
+
+    // Generate thead markup of month table
+    const days_headers_tpl = function() {
+      let days = '';
+      const day = WEEKDAYS.forEach(function(el) {
+        days += ('<th class="' + prefix + 'weekday">' + el.slice(0, 3) + '</th>');
+      });
+      return '<thead>'+
+                '<tr class="' + prefix + 'weekdays">' + days + '</tr>' +
+              '</thead>';
+    };
+
+    // Generate tbody markup of month table
+    const days_body_tpl = function(month) {
+
+      const day_tpl = function(day) {
+        let day_markup = "";
+        if (day) {
+          const disabled = (day.status === "close") ? 'disabled' : "";
+          let selectedClass = "";
+          if ($options[day.idx].selected) {
+            selectedClass = activated_day_class;
+            // Change active_index to display correct month
+            active_index = months.indexOf(month);
+          }
+          day_markup += '<td class="' + prefix + 'day">' +
+                          '<button ' +
+                            'role="button"'+
+                            'title="' + day.label + '"' +
+                            'class="' + prefix + 'day-btn ' + selectedClass +'"' +
+                            'data-option-idx="' + day.idx + '"' +
+                            'data-status="' + day.status + '"' +
+                            disabled + '>' +
+                            day.number +
+                          '</button>' +
+                        '</td>';
+        } else {
+          day_markup += '<td class="' + prefix +'day is-empty"></td>'
+        }
+        return day_markup;
+      };
+
+      const days_of_month = calendar_days.filter(function(day) {
+          return day.month === month;
+      });
+      const weeks_of_month = list_of(days_of_month, "weeknumber");
+
+      let weeks_markup = '';
+
+      weeks_of_month.forEach(function(weeknumber) {
+        let days_markup = "";
+        const days_of_week = days_of_month.filter(function(day) {
+          return day.weeknumber === weeknumber;
+        });
+        // empty days before
+        const first_position = parseInt(days_of_week[0].weekdayindex);
+        for (let i = 1; i < first_position; i++) {
+          days_markup += day_tpl();
+        }
+        // days of week
+        days_of_week.forEach(function(day) {
+          days_markup += day_tpl(day);
+        });
+        // empty days after
+        const last_position = parseInt(days_of_week[days_of_week.length - 1].weekdayindex);
+        if (last_position)  {
+          for (let i = last_position; i < 7; i++) {
+            days_markup += day_tpl();
+          }
+        }
+        // week raw
+        weeks_markup += '<tr class="' + prefix + 'week">' + days_markup + '</tr>';
+      });
+      return '<tbody>' + weeks_markup +'</tbody>';
+    };
+
+    // render month
+    const $month_tpl = function (month) {
+      const $month_calendar = $('<div class="ens-calendar--month is-hidden">' +
+          '<strong class="ens-calendar--head">' + month + '</strong>' +
+          '<table>' +
+            days_headers_tpl() +
+            days_body_tpl(month) +
+          '</table>' +
+        '</div>');
+
+      $month_calendars = $month_calendars.add($month_calendar);
+      $month_day_btns = $month_calendar.find('.' + prefix + 'day-btn');
+      $day_btns = $day_btns.add($month_day_btns);
+
+      return $month_calendar;
+    };
+
+    // render calendar
+    const render_calendar = function() {
+      $calendar.empty();
+      months.forEach(function(month){
+        $month_tpl(month).appendTo($calendar);
+        $prev_btn.add($next_btn).appendTo($calendar);
+      });
+    };
+
+    return render_calendar();
+  };
+
+  // Go to
+  const goto = function(index) {
+    $month_calendars.addClass('is-hidden');
+    $prev_btn.add($next_btn).prop('disabled', null);
+    max = $month_calendars.length - 1;
+
+    switch (index) {
+      case 0:
+        $prev_btn.prop('disabled', true); break;
+      case max :
+        $next_btn.prop('disabled', true); break;
+    }
+
+    $month_calendars.eq(index).removeClass('is-hidden');
+    active_index = index;
+  };
+
+  const init = function() {
+    $calendar.addClass('is-hidden');
+    $month_calendars = $();
+    $day_btns = $();
+    calendar_layout();
+    goto(active_index);
+    $calendar.removeClass('is-hidden');
+    // events
+    $prev_btn.click(function(e){
+      e.preventDefault();
+      goto(active_index - 1);
+    });
+
+    $next_btn.click(function(e){
+      e.preventDefault();
+      goto(active_index + 1);
+    });
+
+    $day_btns.click(function(e){
+      e.preventDefault();
+      $day_btns.removeClass(activated_day_class);
+      $options.eq(this.dataset.optionIdx).prop('selected', true);
+      $(this).addClass(activated_day_class);
+    });
+  };
+
+  // if Static
+  if ($options.length > 1) {
+    calendar_days = define_calendar_days($options);
+    init();
+  }
+
+  // if $select is filled live
+  $select.on('wcs:options-change', function(ev, data) {
+    if (!data) return;
+    calendar_days = define_calendar_days(data.items);
+    $('<option value=""></option>').prependTo($select);
+    $options = $select.find('option');
+    init();
+  });
+
+});
+</script>
+{% if request.quixote_request.is_in_backoffice %}
+<style>
+.ens-calendar.is-hidden,
+.ens-calendar--month.is-hidden {
+  display: none !important;
+}
+.ens-calendar {
+  position: relative;
+  width: 350px;
+}
+.ens-calendar--head {
+  text-align: center;
+  display: block;
+  height: 3em;
+  padding-left: 3em;
+  padding-right: 3em;
+}
+.ens-calendar--day-btn[data-status=open] {
+  color: green;
+}
+.ens-calendar--day-btn[data-status=open]:hover,
+.ens-calendar--day-btn[data-status=open].on {
+  background-color: green;
+  color: white;
+}
+.ens-calendar--day-btn[data-status=partially-open] {
+  color: orange;
+}
+.ens-calendar--day-btn[data-status=partially-open]:hover,
+.ens-calendar--day-btn[data-status=partially-open]:on {
+  background-color: orange;
+  color: white;
+}
+.ens-calendar--day-btn[data-status=close] {
+  color: red;
+}
+.ens-calendar--day-btn[data-status=close]:hover,
+.ens-calendar--day-btn[data-status=close].on {
+  background-color: red;
+  color: white;
+}
+
+.ens-calendar--next, .ens-calendar--prev {
+  position: absolute;
+  top: 0;
+  margin: 0;
+}
+.ens-calendar--next {
+  right: 0;
+}
+.ens-calendar--prev {
+  left: 0;
+}
+</style>
+{% endif %}
+{% endblock %}
+
+{% block widget-hint %}
+{% if widget.hint %}<div class="hint">{{widget.hint}}</div>{% endif %}
+{% endblock %}
-- 
2.20.1

