From d4b5668c1adf7430b5b53c8600ed89f672d0c76b Mon Sep 17 00:00:00 2001
From: Siddarth Gajjar <sga@odoo.com>
Date: Tue, 1 May 2018 15:45:49 +0530
Subject: [PATCH] [IMP] calendar: generic improvements

We need the calendar view to be consistent in the usage of time formats
currently the week column (on the far left) is not consistent with what
we display in the meeting time slot.

It is currently not ovious that you can add and view anybody's calendar
so we need to add a placeholder.

In order to create a meeting you have to click on create which is not
explicit.

Task #1838108
Closes #24675
---
 .../js/views/calendar/calendar_controller.js  |   5 +-
 .../src/js/views/calendar/calendar_model.js   |   1 +
 .../views/calendar/calendar_quick_create.js   |  22 +-
 .../js/views/calendar/calendar_renderer.js    |   6 +-
 .../web/static/tests/views/calendar_tests.js  | 297 +++++++++++++++++-
 5 files changed, 308 insertions(+), 23 deletions(-)

diff --git a/addons/web/static/src/js/views/calendar/calendar_controller.js b/addons/web/static/src/js/views/calendar/calendar_controller.js
index d49c7c33f5cb..b699879472ab 100644
--- a/addons/web/static/src/js/views/calendar/calendar_controller.js
+++ b/addons/web/static/src/js/views/calendar/calendar_controller.js
@@ -240,7 +240,10 @@ var CalendarController = AbstractController.extend({
             }
         }
 
-        var options = _.extend({}, this.options, event.options, {context: context});
+        var options = _.extend({}, this.options, event.options, {
+            context: context,
+            title: _.str.sprintf(_t('Create: %s'), (this.displayName || this.renderer.arch.attrs.string))
+        });
 
         if (this.quick != null) {
             this.quick.destroy();
diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js
index 63a35171a305..1f8c9423cac0 100644
--- a/addons/web/static/src/js/views/calendar/calendar_model.js
+++ b/addons/web/static/src/js/views/calendar/calendar_model.js
@@ -405,6 +405,7 @@ return AbstractModel.extend({
             dayNames: moment.weekdays(),
             dayNamesShort: moment.weekdaysShort(),
             firstDay: _t.database.parameters.week_start,
+            slotLabelFormat: _t.database.parameters.time_format.search("%H") != -1 ? 'H:mm': 'h(:mm)a',
         };
     },
     /**
diff --git a/addons/web/static/src/js/views/calendar/calendar_quick_create.js b/addons/web/static/src/js/views/calendar/calendar_quick_create.js
index 698c5d8895a6..8329ca83bd81 100644
--- a/addons/web/static/src/js/views/calendar/calendar_quick_create.js
+++ b/addons/web/static/src/js/views/calendar/calendar_quick_create.js
@@ -39,7 +39,7 @@ var QuickCreate = Dialog.extend({
 
         var self = this;
         this._super(parent, {
-            title: this._getTitle(),
+            title: options.title,
             size: 'small',
             buttons: this._buttons ? [
                 {text: _t("Create"), classes: 'btn-primary', click: function () {
@@ -68,22 +68,9 @@ var QuickCreate = Dialog.extend({
     },
 
     //--------------------------------------------------------------------------
-    // Public
+    // Private
     //--------------------------------------------------------------------------
 
-    /**
-     * @returns {string}
-     */
-    _getTitle: function () {
-        var parent = this.getParent();
-        if (_.isUndefined(parent)) {
-            return _t("Create");
-        }
-        var title = (_.isUndefined(parent.field_widget)) ?
-                (parent.title || parent.string || parent.name) :
-                (parent.field_widget.string || parent.field_widget.name || '');
-        return _t("Create: ") + title;
-    },
     /**
      * Gathers data from the quick create dialog a launch quick_create(data) method
      */
@@ -93,6 +80,11 @@ var QuickCreate = Dialog.extend({
         dataCalendar.title = val;
         return (val)? this.trigger_up('quickCreate', {data: dataCalendar, options: this.options}) : false;
     },
+
+    //--------------------------------------------------------------------------
+    // Handlers
+    //--------------------------------------------------------------------------
+
     /**
      * @private
      * @param {keyEvent} event
diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js
index a8d5d07ed0d6..4b4050b67ccf 100644
--- a/addons/web/static/src/js/views/calendar/calendar_renderer.js
+++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js
@@ -78,6 +78,9 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, {
                     {
                         mode: 'edit',
                         can_create: false,
+                        attrs: {
+                            'placeholder': _.str.sprintf(_t("Add %s"), self.title),
+                        },
                     });
             });
             defs.push(def);
@@ -388,7 +391,8 @@ return AbstractRenderer.extend({
                 if (!event.allDay) {
                     var start = event.r_start || event.start;
                     var end = event.r_end || event.end;
-                    display_hour = start.format('HH:mm') + ' - ' + end.format('HH:mm');
+                    var timeFormat = _t.database.parameters.time_format.search("%H") != -1 ? 'HH:mm': 'h:mma';
+                    display_hour = start.format(timeFormat) + ' - ' + end.format(timeFormat);
                     if (display_hour === '00:00 - 00:00') {
                         display_hour = _t('All day');
                     }
diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js
index 9728d8a7c74b..0a7ca319c2f3 100644
--- a/addons/web/static/tests/views/calendar_tests.js
+++ b/addons/web/static/tests/views/calendar_tests.js
@@ -439,7 +439,7 @@ QUnit.module('Views', {
         testUtils.triggerMouseEvent($cell, "mousedown");
         testUtils.triggerMouseEvent($cell, "mouseup");
 
-        assert.strictEqual($('.modal-dialog.modal-sm .modal-title').text(), 'Create',
+        assert.strictEqual($('.modal-dialog.modal-sm .modal-title').text(), 'Create: Events',
             "should open the quick create dialog");
 
         $('.modal-body input:first').val('new event in quick create').trigger('input');
@@ -456,7 +456,7 @@ QUnit.module('Views', {
         calendar.destroy();
     });
 
-    QUnit.test('create event with timezone in week mode', function (assert) {
+    QUnit.test('create event with timezone in week mode European locale', function (assert) {
         assert.expect(5);
 
         this.data.event.records = [];
@@ -486,6 +486,9 @@ QUnit.module('Views', {
                     return 120;
                 },
             },
+            translateParameters: { // Avoid issues due to localization formats
+                time_format: "%H:%M:%S",
+            },
             mockRPC: function (route, args) {
                 if (args.method === "create") {
                     assert.deepEqual(args.kwargs.context, {
@@ -504,7 +507,7 @@ QUnit.module('Views', {
         var $view = $('#qunit-fixture').contents();
         $view.prependTo('body'); // => select with click position
 
-        var top = calendar.$('.fc-axis:contains(8am)').offset().top + 5;
+        var top = calendar.$('.fc-axis:contains(8:00)').offset().top + 5;
         var left = calendar.$('.fc-day:eq(2)').offset().left + 5;
 
         try {
@@ -550,7 +553,7 @@ QUnit.module('Views', {
         $view.remove();
     });
 
-    QUnit.test('create event with timezone in week mode with formViewDialog', function (assert) {
+    QUnit.test('create event with timezone in week mode with formViewDialog European locale', function (assert) {
         assert.expect(8);
 
         this.data.event.records = [];
@@ -589,6 +592,9 @@ QUnit.module('Views', {
                     return 120;
                 },
             },
+            translateParameters: { // Avoid issues due to localization formats
+                time_format: "%H:%M:%S",
+            },
             mockRPC: function (route, args) {
                 if (args.method === "create") {
                     assert.deepEqual(args.kwargs.context, {
@@ -610,7 +616,7 @@ QUnit.module('Views', {
         var $view = $('#qunit-fixture').contents();
         $view.prependTo('body'); // => select with click position
 
-        var top = calendar.$('.fc-axis:contains(8am)').offset().top + 5;
+        var top = calendar.$('.fc-axis:contains(8:00)').offset().top + 5;
         var left = calendar.$('.fc-day:eq(2)').offset().left + 5;
 
         try {
@@ -699,6 +705,282 @@ QUnit.module('Views', {
         $view.remove();
     });
 
+    QUnit.test('create event with timezone in week mode American locale', function (assert) {
+        assert.expect(5);
+
+        this.data.event.records = [];
+
+        var calendar = createView({
+            View: CalendarView,
+            model: 'event',
+            data: this.data,
+            arch:
+            '<calendar class="o_calendar_test" '+
+                'event_open_popup="true" '+
+                'date_start="start" '+
+                'date_stop="stop" '+
+                'all_day="allday" '+
+                'mode="week" '+
+                'readonly_form_view_id="1">'+
+                    '<field name="name"/>'+
+                    '<field name="start"/>'+
+                    '<field name="allday"/>'+
+            '</calendar>',
+            archs: archs,
+            viewOptions: {
+                initialDate: initialDate,
+            },
+            session: {
+                getTZOffset: function () {
+                    return 120;
+                },
+            },
+            translateParameters: { // Avoid issues due to localization formats
+                time_format: "%I:%M:%S",
+            },
+            mockRPC: function (route, args) {
+                if (args.method === "create") {
+                    assert.deepEqual(args.kwargs.context, {
+                        "default_name": null,
+                        "default_start": "2016-12-13 06:00:00",
+                        "default_stop": "2016-12-13 08:00:00",
+                        "default_allday": null
+                    },
+                    "should send the context to create events");
+                }
+                return this._super(route, args);
+            },
+        });
+
+
+        var $view = $('#qunit-fixture').contents();
+        $view.prependTo('body'); // => select with click position
+
+        var top = calendar.$('.fc-axis:contains(8am)').offset().top + 5;
+        var left = calendar.$('.fc-day:eq(2)').offset().left + 5;
+
+        try {
+            testUtils.triggerPositionalMouseEvent(left, top, "mousedown");
+        } catch (e) {
+            calendar.destroy();
+            $view.remove();
+            throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.');
+        }
+
+        testUtils.triggerPositionalMouseEvent(left, top + 60, "mousemove");
+
+        assert.strictEqual(calendar.$('.fc-content .fc-time').text(), "8:00am - 10:00am",
+            "should display the time in the calendar sticker");
+
+        testUtils.triggerPositionalMouseEvent(left, top + 60, "mouseup");
+        $('.modal input:first').val('new event').trigger('input');
+        $('.modal button.btn:contains(Create)').trigger('click');
+        var $newevent = calendar.$('.fc-event:contains(new event)');
+
+        assert.strictEqual($newevent.text().replace(/[\s\n\r]+/g, ''), "8:00am-10:00amnewevent12/13/201608:00:00False",
+            "should display the new event with time, title and additional fields");
+
+        assert.deepEqual($newevent.data('fcSeg').event.record,
+            {
+                display_name: "new event",
+                start: fieldUtils.parse.datetime("2016-12-13 06:00:00", this.data.event.fields.start, {isUTC: true}),
+                stop: fieldUtils.parse.datetime("2016-12-13 08:00:00", this.data.event.fields.stop, {isUTC: true}),
+                allday: false,
+                name: "new event",
+                id: 1
+            },
+            "the new record should have the utc datetime (quickCreate)");
+
+        // delete record
+
+        $newevent.trigger('click');
+        $('.modal button.btn-default:contains(Delete)').trigger('click');
+        $('.modal button.btn-primary:contains(Ok)').trigger('click');
+        assert.strictEqual(calendar.$('.fc-content').length, 0, "should delete the record");
+
+        calendar.destroy();
+        $view.remove();
+    });
+
+    QUnit.test('create event with timezone in week mode with formViewDialog American locale', function (assert) {
+        assert.expect(8);
+
+        this.data.event.records = [];
+        this.data.event.onchanges = {
+            allday: function (obj) {
+                if (obj.allday) {
+                    obj.start_date = obj.start && obj.start.split(' ')[0] || obj.start_date;
+                    obj.stop_date = obj.stop && obj.stop.split(' ')[0] || obj.stop_date || obj.start_date;
+                } else {
+                    obj.start = obj.start_date && (obj.start_date + ' 00:00:00') || obj.start;
+                    obj.stop = obj.stop_date && (obj.stop_date + ' 00:00:00') || obj.stop || obj.start;
+                }
+            }
+        };
+
+        var calendar = createView({
+            View: CalendarView,
+            model: 'event',
+            data: this.data,
+            arch:
+            '<calendar class="o_calendar_test" '+
+                'event_open_popup="true" '+
+                'date_start="start" '+
+                'date_stop="stop" '+
+                'all_day="allday" '+
+                'mode="week" '+
+                'readonly_form_view_id="1">'+
+                    '<field name="name"/>'+
+            '</calendar>',
+            archs: archs,
+            viewOptions: {
+                initialDate: initialDate,
+            },
+            session: {
+                getTZOffset: function () {
+                    return 120;
+                },
+            },
+            translateParameters: { // Avoid issues due to localization formats
+                time_format: "%I:%M:%S",
+            },
+            mockRPC: function (route, args) {
+                if (args.method === "create") {
+                    assert.deepEqual(args.kwargs.context, {
+                        "default_name": "new event",
+                        "default_start": "2016-12-13 06:00:00",
+                        "default_stop": "2016-12-13 08:00:00",
+                        "default_allday": null
+                    },
+                    "should send the context to create events");
+                }
+                if (args.method === "write") {
+                    assert.deepEqual(args.args[1], expectedEvent,
+                        "should move the event");
+                }
+                return this._super(route, args);
+            },
+        });
+
+        var $view = $('#qunit-fixture').contents();
+        $view.prependTo('body'); // => select with click position
+
+        var top = calendar.$('.fc-axis:contains(8am)').offset().top + 5;
+        var left = calendar.$('.fc-day:eq(2)').offset().left + 5;
+
+        try {
+            testUtils.triggerPositionalMouseEvent(left, top, "mousedown");
+        } catch (e) {
+            calendar.destroy();
+            $view.remove();
+            throw new Error('The test fails to simulate a click in the screen. Your screen is probably too small or your dev tools is open.');
+        }
+        testUtils.triggerPositionalMouseEvent(left, top + 60, "mousemove");
+        testUtils.triggerPositionalMouseEvent(left, top + 60, "mouseup");
+        $('.modal input:first').val('new event').trigger('input');
+        $('.modal button.btn:contains(Edit)').trigger('click');
+
+        assert.strictEqual($('.o_field_widget[name="start"] input').val(), "12/13/2016 08:00:00",
+            "should display the datetime");
+
+        $('.modal-lg .o_field_boolean[name="allday"] input').trigger('click');
+
+        assert.strictEqual($('.o_field_widget[name="start_date"] input').val(), "12/13/2016",
+            "should display the date");
+
+        $('.modal-lg .o_field_boolean[name="allday"] input').trigger('click');
+
+        assert.strictEqual($('.o_field_widget[name="start"] input').val(), "12/13/2016 02:00:00",
+            "should display the datetime from the date with the timezone");
+
+        // use datepicker to enter a date: 12/13/2016 08:00:00
+        $('.o_field_widget[name="start"] input').trigger('click');
+        $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]').trigger('click');
+        $('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour').trigger('click');
+        $('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(08)').trigger('click');
+        $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]').trigger('click');
+
+        // use datepicker to enter a date: 12/13/2016 10:00:00
+        $('.o_field_widget[name="stop"] input').trigger('click');
+        $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="togglePicker"]').trigger('click');
+        $('.bootstrap-datetimepicker-widget .timepicker .timepicker-hour').trigger('click');
+        $('.bootstrap-datetimepicker-widget .timepicker-hours td.hour:contains(10)').trigger('click');
+        $('.bootstrap-datetimepicker-widget .picker-switch a[data-action="close"]').trigger('click');
+
+        $('.modal-lg button.btn:contains(Save)').trigger('click');
+        var $newevent = calendar.$('.fc-event:contains(new event)');
+
+        assert.strictEqual($newevent.text().replace(/[\s\n\r]+/g, ''), "8:00am-10:00amnewevent",
+            "should display the new event with time and title");
+
+        assert.deepEqual($newevent.data('fcSeg').event.record,
+            {
+                display_name: "new event",
+                start: fieldUtils.parse.datetime("2016-12-13 06:00:00", this.data.event.fields.start, {isUTC: true}),
+                stop: fieldUtils.parse.datetime("2016-12-13 08:00:00", this.data.event.fields.stop, {isUTC: true}),
+                allday: false,
+                name: "new event",
+                id: 1
+            },
+            "the new record should have the utc datetime (formViewDialog)");
+
+        var pos = calendar.$('.fc-content').offset();
+        left = pos.left + 5;
+        top = pos.top + 5;
+
+        // Mode this event to another day
+        var expectedEvent = {
+          "allday": false,
+          "start": "2016-12-12 06:00:00",
+          "stop": "2016-12-12 08:00:00"
+        };
+        testUtils.triggerPositionalMouseEvent(left, top, "mousedown");
+        left = calendar.$('.fc-day:eq(1)').offset().left + 5;
+        testUtils.triggerPositionalMouseEvent(left, top, "mousemove");
+        testUtils.triggerPositionalMouseEvent(left, top, "mouseup");
+
+        // Move to "All day"
+        expectedEvent = {
+          "allday": true,
+          "start": "2016-12-12 00:00:00",
+          "stop": "2016-12-12 00:00:00"
+        };
+        testUtils.triggerPositionalMouseEvent(left, top, "mousedown");
+        top = calendar.$('.fc-day:eq(1)').offset().top + 5;
+        testUtils.triggerPositionalMouseEvent(left, top, "mousemove");
+        testUtils.triggerPositionalMouseEvent(left, top, "mouseup");
+
+        calendar.destroy();
+        $view.remove();
+    });
+
+    QUnit.test('check calendar week column timeformat and event content timeformat', function (assert) {
+        assert.expect(2);
+
+        var calendar = createView({
+            View: CalendarView,
+            model: 'event',
+            data: this.data,
+            arch:
+            '<calendar date_start="start">'+
+                    '<field name="name"/>'+
+            '</calendar>',
+            archs: archs,
+            viewOptions: {
+                initialDate: initialDate,
+            },
+            translateParameters: {
+                time_format: "%I:%M:%S",
+            },
+        });
+
+        assert.strictEqual(calendar.$('.fc-axis:contains(8am)').length, 1, "calendar should show according to timeformat");
+        assert.strictEqual(calendar.$('.fc-event:first:contains(12:00am)').length, 1,
+            "event time format should 12 hour");
+
+        calendar.destroy();
+    });
+
     QUnit.test('create all day event in week mode', function (assert) {
         assert.expect(3);
 
@@ -1601,12 +1883,15 @@ QUnit.module('Views', {
             viewOptions: {
                 initialDate: initialDate,
             },
+            translateParameters: { // Avoid issues due to localization formats
+                time_format: "%H:%M:%S",
+            },
         });
 
         // Click on Tuesday 12am
         var $view = $('#qunit-fixture').contents();
         $view.prependTo('body');
-        var top = calendar.$('.fc-axis:contains(12am)').offset().top + 5;
+        var top = calendar.$('.fc-axis:contains(0:00)').offset().top + 5;
         var left = calendar.$('.fc-day:eq(2)').offset().left + 5;
         try {
             testUtils.triggerPositionalMouseEvent(left, top, "mousedown");
-- 
GitLab