diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py index 3c1563c84e2..573a5a5e3aa 100644 --- a/addons/website_event/controllers/main.py +++ b/addons/website_event/controllers/main.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 -*- import babel.dates +import pytz import re import werkzeug from ast import literal_eval from collections import defaultdict from datetime import datetime, timedelta +from dateutil.parser import parse from dateutil.relativedelta import relativedelta from werkzeug.datastructures import OrderedMultiDict from werkzeug.exceptions import NotFound @@ -380,22 +382,34 @@ class WebsiteEventController(http.Controller): # ------------------------------------------------------------ @http.route('/event/add_event', type='json', auth="user", methods=['POST'], website=True) - def add_event(self, event_name="New Event", **kwargs): - event = self._add_event(event_name, request.context) + def add_event(self, name, event_start, event_end, address_values, **kwargs): + values = self._prepare_event_values(name, event_start, event_end, address_values) + event = request.env['event.event'].create(values) return "/event/%s/register?enable_editor=1" % slug(event) - def _add_event(self, event_name=None, context=None, **kwargs): - if not event_name: - event_name = _("New Event") - date_begin = datetime.today() + timedelta(days=(14)) - vals = { - 'name': event_name, - 'date_begin': fields.Date.to_string(date_begin), - 'date_end': fields.Date.to_string((date_begin + timedelta(days=(1)))), + def _prepare_event_values(self, name, event_start, event_end, address_values=None): + """ + Return the values to create a new event. + event_start,event_date are datetimes in the user tz. + address_values is used to either choose an existing location or create one as we allow it in the frontend. + """ + date_begin = parse(event_start).astimezone(pytz.utc).replace(tzinfo=None) + date_end = parse(event_end).astimezone(pytz.utc).replace(tzinfo=None) + address_id = request.env['res.partner'] + if address_values: + (address_pid, address_vals) = int(address_values[0]), address_values[1] + address_id = address_pid + if address_pid == 0: + address_id = request.env['res.partner'].create(address_vals).id + return { + 'name': name, + 'date_begin': date_begin, + 'date_end': date_end, + 'address_id': address_id, 'seats_available': 1000, 'website_id': request.website.id, + 'event_ticket_ids': request.env['event.event.ticket'], } - return request.env['event.event'].with_context(context or {}).create(vals) # ------------------------------------------------------------ # TOOLS (JSON) diff --git a/addons/website_event/static/src/js/website_event.editor.js b/addons/website_event/static/src/js/website_event.editor.js index 48f3a995d08..4516e6ba43b 100644 --- a/addons/website_event/static/src/js/website_event.editor.js +++ b/addons/website_event/static/src/js/website_event.editor.js @@ -2,11 +2,257 @@ odoo.define('website_event.editor', function (require) { "use strict"; var core = require('web.core'); -var wUtils = require('website.utils'); +var Dialog = require('web.Dialog'); +var time = require('web.time'); var WebsiteNewMenu = require('website.newMenu'); - var _t = core._t; +var EventCreateDialog = Dialog.extend({ + xmlDependencies: Dialog.prototype.xmlDependencies.concat(['/website_event/static/src/xml/event_create.xml']), + template: 'website_event.event_create_dialog', + events: _.extend({}, Dialog.prototype.events, { + 'change select[name="event_location"]': '_onLocationChanged', + 'click .input-group-append': '_onDatetimeClicked', + }), + cssLibs: [ + '/web/static/lib/daterangepicker/daterangepicker.css', + ], + jsLibs: [ + '/web/static/lib/daterangepicker/daterangepicker.js', + '/web/static/src/legacy/js/libs/daterangepicker.js', + ], + + + /** + * @override + * @param {Object} parent + * @param {Object} options + */ + init: function (parent, options) { + options = _.defaults(options || {}, { + title: _t("New Event"), + size: 'medium', + buttons: [ + { + text: _t("Create"), + classes: 'btn-primary', + click: this._onClickCreate.bind(this), + }, + { + text: _t("Discard"), + close: true + }, + ] + }); + this.eventStart = moment(); + this.eventEnd = moment().add(1, "d"); + this._super(parent, options); + }, + + /** + * @override + */ + start: function () { + var self = this; + return this._super.apply(this, arguments).then(() => { + // stat / end datetimes widgets + self.$('input.daterange-input').each(function () { + self._initDateRangePicker($(this)); + }); + // address select2 configuration + self.$('input.o_wevent_js_address_id').select2(self._getSelect2AddressConfig()); + }); + }, + + /** + * @param {*} errors + */ + _display_errors: function (errors) { + this.$("p.text-danger").toggleClass('d-none', true); + for (let i = 0; i < errors.length; i++) { + this.$("#" + errors[i] + " p.text-danger").toggleClass('d-none', false); + } + }, + + _getSelect2AddressConfig: function () { + var self = this; + return { + allowClear: true, + formatNoMatches: false, + multiple: false, + selection_data: false, + width: '100%', + + createSearchChoice: function (name, data) { + var addedPartner = $(this.opts.element).select2('data'); + if (_.filter(_.union(addedPartner, data), function (partner) { + return partner.text.toLowerCase().localeCompare(name.toLowerCase()) === 0; + }).length === 0) { + return { + create: true, + id: _.uniqueId('address_'), + name: name, + text: _.str.sprintf(_t('Create "%s"'), name), + }; + } + }, + fill_data: function (query, data) { + var that = this; + var partners = {results: []}; + _.each(data, function (partner) { + var contact_address = partner.contact_address.replace(/[\n]+/g, '').trim(); + if (contact_address && that.matcher(query.term, contact_address)) { + partners.results.push({ + id: partner.id, + text: contact_address, + }); + } + }); + query.callback(partners); + }, + formatSelection: function (data) { + if (data.name) { + data.text = data.name; + } + return data.text; + }, + query: function (query) { + var that = this; + // fetch data only once and store it + if (!this.selection_data) { + self._rpc({ + model: 'res.partner', + method: 'search_read', + fields: ['contact_address'], + domain: [], + }).then(function (data) { + that.fill_data(query, data); + that.selection_data = data; + }); + } else { + this.fill_data(query, this.selection_data); + } + } + } + }, + + _getSelect2AddressValues: function () { + var select2Value = this.$('input.o_wevent_js_address_id').select2('data'); + if (select2Value) { + if (select2Value.create) { + return [0, {'name': select2Value.name}]; + } else { + return [select2Value.id, {}]; + } + } + return []; + }, + + /** + * + * @param {*} $dateGroup + */ + _initDateRangePicker: function ($dateGroup) { + var minDate = moment().subtract(1, "d"); + var maxDate = moment().add(200, "y"); + var self = this; + var textDirection = _t.database.parameters.direction; + + $dateGroup.daterangepicker({ + // dates + endDate: this.eventEnd, + maxDate: maxDate, + minDate: minDate, + startDate: this.eventStart, + // display + locale: { + direction: textDirection, + format: time.getLangDatetimeFormat().replace(':ss', ''), + separator: ' 🠖 ', + applyLabel: _t('Apply'), + cancelLabel: _t('Cancel'), + weekLabel: 'W', + customRangeLabel: _t('Custom Range'), + daysOfWeek: moment.weekdaysMin(), + monthNames: moment.monthsShort(), + firstDay: moment.localeData().firstDayOfWeek() + }, + opens: 'left', + timePicker: true, + timePicker24Hour: true, + viewDate: moment(new Date()).hours(minDate.hours()).minutes(minDate.minutes()).seconds(minDate.seconds()).milliseconds(minDate.milliseconds()), + }, function (start, end, label) { + self.eventStart = start; + self.eventEnd = end; + }); + }, + /** + * @private + */ + _prepareFormValues: function () { + return { + address_values: this._getSelect2AddressValues(), + event_start: this.eventStart, + event_end: this.eventEnd, + location: this.$('select[name=event_location]').val(), + name: this.$('input[name=name]').val().trim(), + }; + }, + /** + * @private + * @param {*} values + */ + _validateForm: function (values) { + var errors = []; + if (!values.name){ + errors.push('name'); + } + if (values.location === 'on_site' && !values.address_values.length) { + errors.push('address'); + } + if (!values.event_start || !values.event_end) { + errors.push('event_dates'); + } + return errors; + }, + + /** + * @private + */ + _submitForm: function () { + var self = this; + var eventValues = this._prepareFormValues(); + var errors = this._validateForm(eventValues) + console.log(eventValues, errors); + if (errors.length) { + this._display_errors(errors); + return; + } + return this._rpc({ + route: '/event/add_event', + params: eventValues, + }).then((url) => { + window.location.href = url; + return new Promise(function () {}); + }); + }, + + /** + * @private + */ + _onClickCreate: function () { + return this._submitForm(); + }, + + /** + * @private + * @param {*} ev + */ + _onLocationChanged: function (ev) { + this.$('.show_visibility_address').toggleClass('d-none', ev.target.value === 'online'); + }, +}); + WebsiteNewMenu.include({ actions: _.extend({}, WebsiteNewMenu.prototype.actions || {}, { new_event: '_createNewEvent', @@ -25,25 +271,12 @@ WebsiteNewMenu.include({ */ _createNewEvent: function () { var self = this; - return wUtils.prompt({ - id: "editor_new_event", - window_title: _t("New Event"), - input: _t("Event Name"), - }).then(function (result) { - var eventName = result.val; - if (!eventName) { - return; - } - return self._rpc({ - route: '/event/add_event', - params: { - event_name: eventName, - }, - }).then(function (url) { - window.location.href = url; - return new Promise(function () {}); - }); + var def = new Promise(function (resolve) { + var dialog = new EventCreateDialog(self, {}); + dialog.open(); + dialog.on('closed', self, resolve); }); + return def; }, }); }); diff --git a/addons/website_event/static/src/xml/event_create.xml b/addons/website_event/static/src/xml/event_create.xml new file mode 100644 index 00000000000..52cc5dc3339 --- /dev/null +++ b/addons/website_event/static/src/xml/event_create.xml @@ -0,0 +1,37 @@ + + + +
+
+ +
+ +
+

Please fill in this field

+
+
+ +
+ +
+
+
+ +
+ +
+

Please fill in this field

+
+
+ +
+ +
+

Please fill in this field

+
+
+
+
diff --git a/addons/website_event/static/tests/tours/website_event.js b/addons/website_event/static/tests/tours/website_event.js index 0a3a038705d..56cc9937ee8 100644 --- a/addons/website_event/static/tests/tours/website_event.js +++ b/addons/website_event/static/tests/tours/website_event.js @@ -4,6 +4,7 @@ odoo.define("website_event.tour", function (require) { const {_t} = require("web.core"); const {Markup} = require('web.utils'); var tour = require("web_tour.tour"); + var time = require('web.time'); tour.register("website_event_tour", { test: true, @@ -17,11 +18,16 @@ odoo.define("website_event.tour", function (require) { content: _t("Click here to create a new event."), position: "bottom", }, { - trigger: '.modal-dialog #editor_new_event input[type=text]', + trigger: '.modal-dialog #editor_new_event input[name=name]', content: Markup(_t("Create a name for your new event and click \"Continue\". e.g: Technical Training")), + run: 'text Technical Training', position: "left", }, { - trigger: '.modal-footer button.btn-primary.btn-continue', + trigger: '.modal-dialog #editor_new_event input[name=event_start_end]', + content: _t("Pick a Start date for your event"), + run: 'text ' + moment().format(time.getLangDatetimeFormat()) + ' - ' + moment().add(1, "d").format(time.getLangDatetimeFormat()), + }, { + trigger: '.modal-footer button.btn-primary', extra_trigger: '#editor_new_event input[type=text][value!=""]', content: Markup(_t("Click Continue to create the event.")), position: "right", diff --git a/addons/website_event_sale/controllers/main.py b/addons/website_event_sale/controllers/main.py index 1c1ad8e5532..088873a74bb 100644 --- a/addons/website_event_sale/controllers/main.py +++ b/addons/website_event_sale/controllers/main.py @@ -55,14 +55,17 @@ class WebsiteEventSaleController(WebsiteEventController): return res - def _add_event(self, event_name="New Event", context=None, **kwargs): + def _prepare_event_values(self, name, event_start, event_end, address_values=None): + values = super(WebsiteEventSaleController, self)._prepare_event_values(name, event_start, event_end, address_values) product = request.env.ref('event_sale.product_product_event', raise_if_not_found=False) if product: - context = dict(context or {}, default_event_ticket_ids=[[0, 0, { - 'name': _('Registration'), - 'product_id': product.id, - 'end_sale_datetime': False, - 'seats_max': 1000, - 'price': 0, - }]]) - return super(WebsiteEventSaleController, self)._add_event(event_name, context, **kwargs) + values.update({ + 'event_ticket_ids': [[0, 0, { + 'name': _('Registration'), + 'product_id': product.id, + 'end_sale_datetime': False, + 'seats_max': 1000, + 'price': 0, + }]] + }) + return values