Enhanced JavaScript with modern features, better UX, error handling and performance improvements

This commit is contained in:
Mohamed Eltayar 2025-08-28 16:04:58 +03:00
parent 291549ab11
commit c3bf990c8a
1 changed files with 176 additions and 40 deletions

View File

@ -7,6 +7,7 @@ odoo.define('web_hijri_datepicker.datepicker', function (require) {
var FieldDate = require('web.basic_fields').FieldDate;
var _t = core._t;
// Enhanced Hijri month names with better Arabic support
var hijriMonths = {
"Muharram": "مُحَرَّم",
"Safar": "صَفَر",
@ -22,10 +23,11 @@ odoo.define('web_hijri_datepicker.datepicker', function (require) {
"Dhu al-Hijjah": "ذُوالْحِجَّة"
}
// Enhanced number conversion for better Arabic display
String.prototype.fromDigits = function () {
var id = ['۰', '۱', '۲', '۳', '٤', '۵', '٦', '۷', '۸', '۹'];
return this.replace(/[0-9]/g, function (w) {
return id[+w]
var arabicDigits = ['۰', '۱', '۲', '۳', '٤', '۵', '٦', '۷', '۸', '۹'];
return this.replace(/[0-9]/g, function (digit) {
return arabicDigits[+digit];
});
}
@ -34,81 +36,180 @@ odoo.define('web_hijri_datepicker.datepicker', function (require) {
var self = this;
this.$input = this.$('input.o_datepicker_input');
this.$input_hijri = this.$('input.o_hijri');
// Enhanced click handler with better UX
this.$input_hijri.click(function (e) {
e.preventDefault();
e.stopPropagation();
self.$input_hijri.calendarsPicker('show');
});
// Modern configuration with enhanced features
this.$input_hijri.calendarsPicker({
calendar: $.calendars.instance('islamic', this.options.locale),
calendar: $.calendars.instance('islamic', this.options.locale || 'ar'),
dateFormat: 'M d, yyyy',
closeOnDateSelect: false,
showAnim: 'slideDown', // Modern animation
showSpeed: 'fast',
showOnFocus: false,
closeOnDateSelect: true, // Better UX - close after selection
yearRange: 'c-100:c+50', // Extended year range
changeMonth: true,
changeYear: true,
showOtherMonths: true,
selectOtherMonths: true,
localNumbers: true, // Enable Arabic numerals
renderer: this._getModernRenderer(),
onSelect: this._convertDateToHijri.bind(this),
onShow: function() {
// Add modern CSS class for styling
$('.calendars-popup').addClass('hijri-modern-popup');
}
});
this.__libInput++;
this.$el.datetimepicker(this.options);
this.__libInput--;
this._setReadonly(false);
},
/**
* Get modern renderer configuration for better styling
*/
_getModernRenderer: function() {
return $.extend({}, $.calendarsPicker.defaultRenderer, {
picker: '<div class="calendars">{months}</div>',
monthRow: '<div class="calendars-month-row">{months}</div>',
month: '<div class="calendars-month">' +
'<div class="calendars-month-header">' +
'<button type="button" class="calendars-nav calendars-prev" title="{prevText}">{prevText}</button>' +
'<div class="calendars-month-year">{monthHeader}</div>' +
'<button type="button" class="calendars-nav calendars-next" title="{nextText}">{nextText}</button>' +
'</div>' +
'<table><thead>{weekHeader}</thead><tbody>{weeks}</tbody></table>' +
'</div>',
weekHeader: '<tr>{days}</tr>',
dayHeader: '<th>{day}</th>',
week: '<tr>{days}</tr>',
day: '<td>{day}</td>',
monthSelector: '.calendars-month',
daySelector: 'td',
rtlClass: 'calendars-rtl',
multiClass: 'calendars-multi',
defaultClass: '',
selectedClass: 'calendars-selected',
highlightedClass: 'calendars-highlight',
todayClass: 'calendars-today',
otherMonthClass: 'calendars-other-month',
weekendClass: 'calendars-weekend',
commandClass: 'calendars-cmd calendars-cmd-',
commandButtonClass: '',
commandLinkClass: '',
disabledClass: 'calendars-disabled'
});
},
_convertGregorianToHijri: function (date) {
var year, month, day, jd, formatted_date;
var calendar = $.calendars.instance('islamic');
if (date && !_.isUndefined(date)) {
date = moment(date).locale('en');
month = parseInt(date.format('M'));
day = parseInt(date.format('D'));
year = parseInt(date.format('YYYY'));
jd = $.calendars.instance('gregorian').toJD(year, month, day);
formatted_date = calendar.fromJD(jd);
var month = calendar.formatDate('MM', formatted_date);
var date = calendar.formatDate('d, yyyy', formatted_date);
if (this.options.locale == 'ar') {
date = date.fromDigits();
month = _.find(hijriMonths, function (value, key) {
if (key === month) {
return value;
}
var hijriMonth = calendar.formatDate('MM', formatted_date);
var hijriDate = calendar.formatDate('d, yyyy', formatted_date);
// Enhanced Arabic localization
if (this.options.locale === 'ar' || !this.options.locale) {
hijriDate = hijriDate.fromDigits();
// Find Arabic month name
var arabicMonth = _.find(hijriMonths, function (value, key) {
return key === hijriMonth;
});
hijriMonth = arabicMonth || hijriMonth;
}
return _.str.sprintf("%s %s", month, date);
return _.str.sprintf("%s %s", hijriMonth, hijriDate);
}
},
_convertDateToHijri: function (date) {
if (!date || date.length === 0) {
return false;
}
$(document).on('click', '.calendars a', function (e) {
// Prevent event bubbling for better UX
$(document).off('click.calendars').on('click.calendars', '.calendars a', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
return false;
});
var jd = $.calendars.instance('islamic').toJD(parseInt(date[0].year()), parseInt(date[0].month()), parseInt(date[0].day()));
var formatted_date = $.calendars.instance('gregorian').fromJD(jd);
var date_value = moment(time.str_to_date(formatted_date)).add(1, 'days');
this.setValue(this._parseClient(date_value));
this.trigger("datetime_changed");
try {
var selectedDate = date[0];
var jd = $.calendars.instance('islamic').toJD(
parseInt(selectedDate.year()),
parseInt(selectedDate.month()),
parseInt(selectedDate.day())
);
var gregorianDate = $.calendars.instance('gregorian').fromJD(jd);
var dateValue = moment(time.str_to_date(gregorianDate)).add(1, 'days');
this.setValue(this._parseClient(dateValue));
this.trigger("datetime_changed");
// Hide the popup after selection for better UX
this.$input_hijri.calendarsPicker('hide');
} catch (error) {
console.warn('Hijri date conversion error:', error);
}
},
_parseDate: function (v) {
return v.clone().locale('en').format('YYYY-MM-DD');
},
setValue: function (value) {
this._super.apply(this, arguments);
var parsed_date = value ? this._parseDate(value) : null;
var hijri_value = parsed_date ? this._convertGregorianToHijri(parsed_date) : null;
this.$input_hijri.val(hijri_value);
// Enhanced placeholder handling
if (hijri_value) {
this.$input_hijri.val(hijri_value).removeClass('o_input_placeholder');
} else {
this.$input_hijri.val('').addClass('o_input_placeholder');
}
},
destroy: function () {
// Clean up event handlers
$(document).off('click.calendars');
if (this.$input_hijri && this.$input_hijri.hasClass('hasCalendarsPicker')) {
this.$input_hijri.calendarsPicker('destroy');
}
if (this.$el) {
this.__libInput++;
this.$el.datetimepicker('destroy');
this.__libInput--;
}
},
_onInputClicked: function (e) {
if (e && e.target && ! $(e.target).hasClass('o_hijri')){
if (e && e.target && !$(e.target).hasClass('o_hijri')) {
return this._super();
}
},
_formatClients: function (v) {
return field_utils.format[this.type_of_date](v, null, {timezone: true});
},
@ -118,31 +219,66 @@ odoo.define('web_hijri_datepicker.datepicker', function (require) {
_renderReadonly: function () {
var self = this;
this._super.apply(this, arguments);
if (this.value) {
window.dv = this.value;
this.datewidget = this._makeDatePicker();
var $div = $('<div/>');
var value = this.value ? this.datewidget._formatClients(this.value) : '';
var $container = $('<div/>');
// Gregorian date
var gregorianValue = this.value ? this.datewidget._formatClients(this.value) : '';
var $gregorianDiv = $('<div>', {
class: this.$el.attr('class') + ' o_gregorian_date',
text: gregorianValue,
});
// Hijri date with enhanced styling
var parsed_date = this.value ? this.datewidget._parseDate(this.value) : '';
var hijri_value = parsed_date ? this.datewidget._convertGregorianToHijri(parsed_date) : '';
// Gregorian Date
$('<div>', {
class: this.$el.attr('class'),
text: value,
}).appendTo($div);
// Hijri Date with simple display
$('<div>', {
class: this.$el.attr('class') + ' mt-1',
text: hijri_value ? 'التاريخ الهجري: ' + hijri_value : '',
style: 'font-size: 12px; color: #6c757d;'
}).appendTo($div);
if (hijri_value) {
var $hijriDiv = $('<div>', {
class: this.$el.attr('class') + ' o_hijri_date mt-1',
style: 'font-size: 12px; color: #28a745; font-weight: 500;'
});
$hijriDiv.html('<span style="color: #6c757d;">التاريخ الهجري:</span> ' + hijri_value);
$container.append($gregorianDiv).append($hijriDiv);
} else {
$container.append($gregorianDiv);
}
this.datewidget.appendTo('<div>').then(function () {
self._replaceElement($div);
self._replaceElement($container);
});
}
},
})
});
// Enhanced integration with Odoo's form validation
if ($.validator) {
$.validator.addMethod('hijriDate', function(value, element) {
if (!value) return true;
try {
var calendar = $.calendars.instance('islamic');
var parsedDate = calendar.parseDate('M d, yyyy', value);
return parsedDate && calendar.isValid(parsedDate.year(), parsedDate.month(), parsedDate.day());
} catch (e) {
return false;
}
}, 'الرجاء إدخال تاريخ هجري صحيح');
}
// Global configuration for better performance
if ($.calendarsPicker) {
$.calendarsPicker.setDefaults({
showSpeed: 'fast',
showAnim: 'slideDown',
changeMonth: true,
changeYear: true,
localNumbers: true,
yearRange: 'c-100:c+50'
});
}
});