diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/hr_skills/static/src | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/hr_skills/static/src')
| -rw-r--r-- | addons/hr_skills/static/src/css/hr_skills.scss | 152 | ||||
| -rw-r--r-- | addons/hr_skills/static/src/js/resume_widget.js | 240 | ||||
| -rw-r--r-- | addons/hr_skills/static/src/xml/resume_templates.xml | 55 | ||||
| -rw-r--r-- | addons/hr_skills/static/src/xml/skills_templates.xml | 24 |
4 files changed, 471 insertions, 0 deletions
diff --git a/addons/hr_skills/static/src/css/hr_skills.scss b/addons/hr_skills/static/src/css/hr_skills.scss new file mode 100644 index 00000000..9ff24615 --- /dev/null +++ b/addons/hr_skills/static/src/css/hr_skills.scss @@ -0,0 +1,152 @@ +.o_form_sheet { + $o-hrs-timeline-entry-padding: .5rem; + $o-hrs-timeline-dot-size: .6rem; + + // Overall design + // ========================================= + .o_hr_skills_group { + // Overwrite '.o_list_table' default design + .o_list_view { + .o_list_table { + cursor: auto; + } + + tbody:first-of-type > .o_resume_group_header:first-child { + box-shadow: none; + } + } + + // Use 'cursor:pointer' where is needed only + .o_data_row { + cursor: pointer; + } + + // Deny user interaction to headers but keep access to buttons + .o_group_header, .o_resume_group_header { + &, &:hover { + background: none !important; + cursor: initial; + pointer-events: none; + } + .o_field_x2many_list_row_add, .o_field_x2many_list_row_add:hover { + cursor: pointer; + pointer-events: initial; + } + } + } + + // Resumé design + // ========================================= + .o_group_resume { + .o_data_row td { + padding: $o-hrs-timeline-entry-padding; + + &.o_resume_timeline_cell { + div { + @include size($o-hrs-timeline-dot-size); + } + + &:before { + @include o-position-absolute(0, $left: ($o-hrs-timeline-dot-size * .5 + $o-hrs-timeline-entry-padding)); + @include size(1px, 100%); + margin-left: -.01rem; + background-color: $border-color; + content: ""; + } + } + } + + .o_resume_line_title, .o_resume_line_desc { + white-space: normal; + } + + .o_resume_line_title, .o_resume_line_dates { + line-height: 1; + } + + .o_resume_group_header + .o_data_row .o_resume_timeline_cell:before { + top: $o-hrs-timeline-entry-padding; + } + + .o_data_row.o_data_row_last { + .o_resume_line_desc { + margin-bottom: $headings-margin-bottom; + } + + .o_resume_timeline_cell:before { + height: $o-hrs-timeline-entry-padding; + } + } + } + + // Skills design + // ========================================= + .o_group_skills { + .o_resume_empty_helper { + display: none; + } + + .o_group_header { + > .o_group_name { + padding: .8rem .5rem 0 0; + } + + &:first-child > .o_group_name { + padding-top: 0; + } + } + + .o_skill_cell { + padding-left: 0; + white-space: normal !important; + + .o_progressbar { + display: inline-flex; + align-items: center; + } + + .o_progress { + border: 0; + background: $gray-300; + height: 5px; + } + + .o_progressbar_value { + width: auto; + font-size: $font-size-sm; + font-weight: bold; + } + } + } + + // Editing mode + // ========================================= + .o_form_view.o_form_editable & { + .o_group_name { + background-color: gray('200'); + } + + .o_resume_group_header .btn { + margin-top: .25rem; + margin-right: .4rem; + } + + .o_group_skills .o_group_name { + padding-top: .4em; + padding-bottom: .4rem; + } + + .o_group_name, .o_skill_cell { + padding-left: .5rem; + } + + .o_group_skills .o_group_name > b, .o_hr_skills_group .o_horizontal_separator { + color: color-yiq(gray('200')); + font-style: italic; + } + + .o_list_record_remove > button { + @include o-hover-text-color($text-muted, theme-color('danger')); + } + } +} diff --git a/addons/hr_skills/static/src/js/resume_widget.js b/addons/hr_skills/static/src/js/resume_widget.js new file mode 100644 index 00000000..413942ba --- /dev/null +++ b/addons/hr_skills/static/src/js/resume_widget.js @@ -0,0 +1,240 @@ +odoo.define('web.FieldResume', function (require) { +"use strict"; + +var time = require('web.time'); +var FieldOne2Many = require('web.relational_fields').FieldOne2Many; +var FieldProgressBar = require('web.basic_fields').FieldProgressBar; +var ListRenderer = require('web.ListRenderer'); +var field_registry = require('web.field_registry'); + +var core = require('web.core'); +var qweb = core.qweb; +var _t = core._t; + +var AbstractGroupedOne2ManyRenderer = ListRenderer.extend({ + /** + * This abstract renderer is use to render a one2many field in a form view. + * The records in the one2many field are displayed grouped by a specific field. + * + * A concrete renderer can/should set: + * - groupBy: field to group records + * - dataRowTemplate: template to render a record's data + * - groupTitleTemplate (optional): template to render the header row of a group + * - addLineButtonTemplate (optional): template to render the 'Add a line' button at the end of each group (edit mode only) + **/ + + groupBy: '', // Field: records are grouped based on this field + groupTitleTemplate: 'hr_default_group_row', // Template used to render the title row of a group + dataRowTemplate: '', // Template used to render a record + addLineButtonTemplate: 'group_add_item', + + /** + * Don't freeze the columns because as the header is empty, the algorithm + * won't work. + * + * @override + * @private + */ + _freezeColumnWidths: function () {}, + + /** + * Renders a empty header + * + * @override + * @private + */ + _renderHeader: function () { + return $('<thead/>'); + }, + + /** + * Renders a empty footer + * + * @override + * @private + */ + _renderFooter: function () { + return $('<tfoot/>'); + }, + + /** + * @override + * @private + */ + _renderGroupRow: function (display_name) { + return qweb.render(this.groupTitleTemplate, {display_name: display_name}); + }, + + /** + * This method is meant to be overriten by concrete renderers and + * is called each time a row is rendered. + * It is a hook to format record's data before it's given to the qweb template. + * + * @private + */ + _formatData: function (data) { + return data; + }, + + _renderRow: function (record, isLast) { + return $(qweb.render(this.dataRowTemplate, { + id: record.id, + data: this._formatData(record.data), + is_last: isLast, + })); + }, + + /** + * This method is meant to be overridden by concrete renderers. + * Returns a context used for the 'Add a line' button. + * It's useful to set default values. + * An 'Add a line' button is added after each group of records. + * The group passed as parameters allow to set a different context based on the group. + * If no records exist, group is undefined. + * + * @private + */ + _getCreateLineContext: function (group) { + return {}; + }, + + _renderTrashIcon: function() { + return qweb.render('hr_trash_button'); + }, + + _renderAddItemButton: function (group) { + return qweb.render(this.addLineButtonTemplate, { + context: JSON.stringify(this._getCreateLineContext(group)), + }); + }, + + _renderBody: function () { + var self = this; + + var grouped_by = _.groupBy(this.state.data, function (record) { + return record.data[self.groupBy].res_id; + }); + + var groupTitle; + var $body = $('<tbody>'); + for (var key in grouped_by) { + var group = grouped_by[key]; + if (key === 'undefined') { + groupTitle = _t("Other"); + } else { + groupTitle = group[0].data[self.groupBy].data.display_name; + } + var $title_row = $(self._renderGroupRow(groupTitle)); + $body.append($title_row); + + // Render each rows + group.forEach(function (record, index) { + var isLast = (index + 1 === group.length); + var $row = self._renderRow(record, isLast); + if (self.addTrashIcon) $row.append(self._renderTrashIcon()); + $body.append($row); + }); + + if (self.addCreateLine) { + $title_row.find('.o_group_name').append(self._renderAddItemButton(group)); + } + } + + if ($body.is(':empty') && self.addCreateLine) { + $body.append(this._renderAddItemButton()); + } + return $body; + }, + +}); + +var ResumeLineRenderer = AbstractGroupedOne2ManyRenderer.extend({ + + groupBy: 'line_type_id', + groupTitleTemplate: 'hr_resume_group_row', + dataRowTemplate: 'hr_resume_data_row', + + _formatData: function (data) { + var dateFormat = time.getLangDateFormat(); + var date_start = data.date_start && data.date_start.format(dateFormat) || ""; + var date_end = data.date_end && data.date_end.format(dateFormat) || _t("Current"); + return _.extend(data, { + date_start: date_start, + date_end: date_end, + }); + }, + + _getCreateLineContext: function (group) { + var ctx = this._super(group); + return group ? _.extend({default_line_type_id: group[0].data[this.groupBy] && group[0].data[this.groupBy].data.id || ""}, ctx) : ctx; + }, + + _render: function () { + var self = this; + return this._super().then(function () { + self.$el.find('table').removeClass('table-striped o_list_table_ungrouped'); + self.$el.find('table').addClass('o_resume_table table-borderless'); + }); + }, +}); + + +var SkillsRenderer = AbstractGroupedOne2ManyRenderer.extend({ + + groupBy: 'skill_type_id', + dataRowTemplate: 'hr_skill_data_row', + + _renderRow: function (record) { + var $row = this._super(record); + // Add progress bar widget at the end of rows + var $td = $('<td/>', {class: 'o_data_cell o_skill_cell'}); + var progress = new FieldProgressBar(this, 'level_progress', record, { + current_value: record.data.level_progress, + attrs: this.arch.attrs, + }); + progress.appendTo($td); + return $row.append($td); + }, + + _getCreateLineContext: function (group) { + var ctx = this._super(group); + return group ? _.extend({ default_skill_type_id: group[0].data[this.groupBy].data.id }, ctx) : ctx; + }, + + _render: function () { + var self = this; + return this._super().then(function () { + self.$el.find('table').toggleClass('table-striped'); + }); + }, +}); + + +var FieldResume = FieldOne2Many.extend({ + + /** + * @override + * @private + */ + _getRenderer: function () { + return ResumeLineRenderer; + }, +}); + +var FieldSkills = FieldOne2Many.extend({ + + /** + * @override + * @private + */ + _getRenderer: function () { + return SkillsRenderer; + }, +}); + +field_registry.add('hr_resume', FieldResume); +field_registry.add('hr_skills', FieldSkills); + +return FieldResume; + +}); diff --git a/addons/hr_skills/static/src/xml/resume_templates.xml b/addons/hr_skills/static/src/xml/resume_templates.xml new file mode 100644 index 00000000..5db3b006 --- /dev/null +++ b/addons/hr_skills/static/src/xml/resume_templates.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + +<t t-name="hr_resume_data_row"> + <tr class="o_data_row" t-attf-class="o_data_row #{is_last? 'o_data_row_last' : ''}" t-att-data-id="id"> + <t t-if="data.display_type === 'classic'"> + <td class="o_resume_timeline_cell position-relative pr-lg-2"> + <div class="rounded-circle bg-info position-relative"/> + </td> + <td class="o_data_cell pt-0 w-100"> + <div class="o_resume_line" t-att-data-id="id"> + <small class="o_resume_line_dates"> + <b t-esc="data.date_start"/> - <b t-esc="data.date_end"/> + </small> + <h4 class="o_resume_line_title mt-2" t-esc="data.name"/> + <p t-if="data.description" class="o_resume_line_desc" t-esc="data.description"/> + </div> + </td> + </t> + </tr> +</t> + +<t t-name="hr_trash_button"> + <td class="o_list_record_remove pr-3"> + <button name="delete" arial-label="Delete row" class="btn btn-secondary"> + <i class="fa fa-trash"/> + </button> + </td> +</t> + +<t t-name="hr_resume_group_row"> + <tr class="o_resume_group_header"> + <td class="o_group_name" colspan="100%"><span class="o_horizontal_separator my-0" t-esc="display_name"/></td> + </tr> +</t> + +<t t-name="group_add_item"> + <t t-set="empty" t-value="Object.keys(context).length == 2"/> + + <div t-attf-class="o_field_x2many_list_row_add #{empty? 'd-block w-100' : 'd-inline pull-right'}"> + <div t-if="empty" class="o_resume_empty_helper o_horizontal_separator text-muted my-0"> + <em>Resumé empty</em> + </div> + <a href="#" + role="button" + t-attf-class="btn o-kanban-button-new #{empty? 'btn-primary mt-3' : 'btn-secondary btn-sm'}" + t-attf-data-context="{{ context }}"> + <t t-if="empty">CREATE A NEW ENTRY</t> + <t t-else="">ADD</t> + </a> + </div> +</t> + + +</templates> diff --git a/addons/hr_skills/static/src/xml/skills_templates.xml b/addons/hr_skills/static/src/xml/skills_templates.xml new file mode 100644 index 00000000..e078ca3e --- /dev/null +++ b/addons/hr_skills/static/src/xml/skills_templates.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> + +<t t-name="hr_skill_data_row"> + <tr class="o_data_row" t-att-data-id="id"> + <td class="o_data_cell o_skill_cell w-100"> + <t t-esc="data.skill_id.data.display_name"/> + </td> + <td class="o_data_cell o_skill_cell pr-3"> + <t t-esc="data.skill_level_id.data.display_name"/> + </td> + </tr> +</t> + +<t t-name="hr_default_group_row"> + <tr class="o_group_header o_group_has_content"> + <td class="o_group_name border-0 pr-2" colspan="99"> + <b t-esc="display_name"/> + </td> + </tr> +</t> + + +</templates> |
