[REF] hr: create hr_presence_status widget
Before this commit, the hr presence status icon is hardcoded in each view in which the icon is needed. This commit creates a widget for that icon to be able to use that widget in the views in which the icon is needed to avoid duplicating the code. task-3376776
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { Component } from "@odoo/owl";
|
||||
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { standardFieldProps } from "@web/views/fields/standard_field_props";
|
||||
|
||||
export class HrPresenceStatus extends Component {
|
||||
static template = "hr.HrPresenceStatus";
|
||||
static props = {
|
||||
...standardFieldProps,
|
||||
tag: { type: String, optional: true },
|
||||
};
|
||||
static defaultProps = {
|
||||
tag: "small",
|
||||
};
|
||||
|
||||
get classNames() {
|
||||
const classNames = ["fa"];
|
||||
classNames.push(
|
||||
`fa-${this.props.tag === "small" ? "fw" : "2x"}`,
|
||||
this.icon,
|
||||
"o_button_icon",
|
||||
"hr_presence",
|
||||
"align-middle",
|
||||
this.color,
|
||||
)
|
||||
return classNames.join(" ");
|
||||
}
|
||||
|
||||
get color() {
|
||||
switch (this.value) {
|
||||
case "presence_present":
|
||||
case "presence_absent_active":
|
||||
return "text-success";
|
||||
case "presence_absent":
|
||||
return "text-muted";
|
||||
case "presence_to_define":
|
||||
return "text-warning";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return `fa-circle${this.value.startsWith("presence_absent") ? "-o" : ""}`;
|
||||
}
|
||||
|
||||
get label() {
|
||||
return this.value !== false
|
||||
? this.options.find(([value, label]) => value === this.value)[1]
|
||||
: "";
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this.props.record.fields[this.props.name].selection.filter(
|
||||
(option) => option[0] !== false && option[1] !== ""
|
||||
);
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.props.record.data[this.props.name];
|
||||
}
|
||||
}
|
||||
|
||||
export const hrPresenceStatus = {
|
||||
component: HrPresenceStatus,
|
||||
displayName: _t("HR Presence Status"),
|
||||
extractProps({ viewType }, dynamicInfo) {
|
||||
return {
|
||||
tag: viewType === "kanban" ? "span" : "small",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
registry.category("fields").add("hr_presence_status", hrPresenceStatus)
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="hr.HrPresenceStatus">
|
||||
<t t-tag="props.tag" role="img" t-att-class="classNames" t-att-aria-label="label" t-att-title="label"/>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -147,31 +147,8 @@
|
||||
</div>
|
||||
<ul>
|
||||
<li id="last_login"/>
|
||||
<li id="presence">
|
||||
<t t-if="record.show_hr_icon_display.raw_value">
|
||||
<div class="o_employee_availability align-items-center">
|
||||
<!-- Employee is present/connected and it is normal according to his work schedule -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_present'"
|
||||
class="fa fa-2x fa-circle text-success" name="presence_present" role="img"
|
||||
aria-label="Present" title="Present"
|
||||
/>
|
||||
<!-- Employee is not present and it is normal according to his work schedule -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_absent'"
|
||||
class="fa fa-2x fa-circle-o text-warning" name="presence_absent" role="img"
|
||||
aria-label="Absent" title="Absent"
|
||||
/>
|
||||
<!-- Employee is connected but according to his work schedule, he should not work for now -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_absent_active'"
|
||||
class="fa fa-2x fa-circle-o text-success" name="presence_absent_active" role="img"
|
||||
aria-label="Present but not active" title="Present but not active"
|
||||
/>
|
||||
<!-- Employee is not here but according to his work schedule, he should be connected -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_to_define'"
|
||||
class="fa fa-2x fa-circle text-muted" name="presence_to_define" role="img"
|
||||
aria-label="To define" title="To define"
|
||||
/>
|
||||
</div>
|
||||
</t>
|
||||
<li t-if="record.show_hr_icon_display.raw_value">
|
||||
<field name="hr_icon_display" class="o_employee_availability align-items-center" widget="hr_presence_status" />
|
||||
</li>
|
||||
<li t-if="record.work_email.raw_value">
|
||||
<i class="fa fa-fw me-2 fa-envelope text-primary" title="Email"/>
|
||||
|
||||
@@ -52,8 +52,6 @@
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
<field name="user_partner_id" invisible="1"/>
|
||||
<field name="hr_presence_state" invisible="1"/>
|
||||
<field name="hr_icon_display" invisible="1"/>
|
||||
<field name="image_128" invisible="1" />
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="last_activity_time" invisible="1"/>
|
||||
@@ -81,16 +79,7 @@
|
||||
</div>
|
||||
<div class="o_employee_avatar m-0 p-0">
|
||||
<field name="image_1920" widget='image' class="oe_avatar m-0" options='{"zoom": true, "preview_image":"avatar_128"}'/>
|
||||
<div id="hr_presence_status" class="d-flex align-items-end fs-6 o_employee_availability">
|
||||
<!-- Employee is present/connected and it is normal according to his work schedule -->
|
||||
<small role="img" class="fa fa-fw fa-circle text-success o_button_icon hr_presence align-middle" invisible="hr_icon_display != 'presence_present'" aria-label="Present" title="Present"/>
|
||||
<!-- Employee is not present and it is normal according to his work schedule -->
|
||||
<small role="img" class="fa fa-fw fa-circle-o text-muted o_button_icon hr_presence align-middle" invisible="hr_icon_display != 'presence_absent'" aria-label="Absent" title="Absent" name="presence_absent"/>
|
||||
<!-- Employee is connected but according to his work schedule, he should not work for now -->
|
||||
<small role="img" class="fa fa-fw fa-circle-o text-success o_button_icon hr_presence align-middle" invisible="hr_icon_display != 'presence_absent_active'" aria-label="Present but not active" title="Present but not active" name="presence_absent_active"/>
|
||||
<!-- Employee is not here but according to his work schedule, he should be connected -->
|
||||
<small role="img" class="fa fa-fw fa-circle text-warning o_button_icon hr_presence align-middle" invisible="hr_icon_display != 'presence_to_define'" aria-label="To define" title="To define" name="presence_to_define"/>
|
||||
</div>
|
||||
<field name="hr_icon_display" class="d-flex align-items-end fs-6 o_employee_availability" widget="hr_presence_status"/>
|
||||
</div>
|
||||
</div>
|
||||
<group>
|
||||
@@ -300,31 +289,8 @@
|
||||
</div>
|
||||
<ul>
|
||||
<li id="last_login"/>
|
||||
<li id="presence">
|
||||
<t t-if="record.show_hr_icon_display.raw_value">
|
||||
<div class="o_employee_availability d-flex flex-row align-items-start">
|
||||
<!-- Employee is present/connected and it is normal according to his work schedule -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_present'"
|
||||
class="fa fa-2x fa-circle text-success" name="presence_present" role="img"
|
||||
aria-label="Present" title="Present"
|
||||
/>
|
||||
<!-- Employee is not present/connected and it is normal according to his work schedule -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_absent'"
|
||||
class="fa fa-2x fa-circle-o text-warning" name="presence_absent" role="img"
|
||||
aria-label="Absent" title="Absent"
|
||||
/>
|
||||
<!-- Employee is connected but according to his work schedule, he should not work for now -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_absent_active'"
|
||||
class="fa fa-2x fa-circle-o text-success" name="presence_absent_active" role="img"
|
||||
aria-label="Present but not active" title="Present but not active"
|
||||
/>
|
||||
<!-- Employee is not here but according to his work schedule, he should be connected -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_to_define'"
|
||||
class="fa fa-2x fa-circle text-muted" name="presence_to_define" role="img"
|
||||
aria-label="To define" title="To define"
|
||||
/>
|
||||
</div>
|
||||
</t>
|
||||
<li t-if="record.show_hr_icon_display.raw_value">
|
||||
<field name="hr_icon_display" class="o_employee_availability" widget="hr_presence_status" />
|
||||
</li>
|
||||
<li t-if="record.work_email.raw_value" class="o_text_overflow">
|
||||
<i class="fa fa-fw me-2 fa-envelope text-primary" title="Email"/>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
import { HrPresenceStatus } from "@hr/components/hr_presence_status/hr_presence_status";
|
||||
|
||||
patch(HrPresenceStatus.prototype, {
|
||||
get classNames() {
|
||||
const classNames = super.classNames;
|
||||
if (this.value.startsWith("presence_holiday")) {
|
||||
return `${classNames} fa-plane`;
|
||||
}
|
||||
return classNames;
|
||||
},
|
||||
|
||||
get color() {
|
||||
if (this.value.startsWith("presence_holiday")) {
|
||||
return `text-${this.value === "presence_holiday_present" ? "success" : "warning"}`;
|
||||
}
|
||||
return super.color;
|
||||
},
|
||||
});
|
||||
@@ -94,21 +94,6 @@
|
||||
<xpath expr="//templates" position="before">
|
||||
<field name="is_absent"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[hasclass('o_employee_availability')]" position="inside">
|
||||
<t t-if="record.hr_icon_display.raw_value.startsWith('presence_holiday')">
|
||||
<!-- Employee is on time off but he is connected -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_holiday_present'"
|
||||
class="fa fa-2x fa-plane text-success me-1" name="presence_absent_active" role="img"
|
||||
aria-label="Present but on leave" title="Present but on leave"
|
||||
/>
|
||||
<!-- Employee is on time off and he is not connected -->
|
||||
<span t-if="record.hr_icon_display.raw_value == 'presence_holiday_absent'"
|
||||
class="fa fa-2x fa-plane text-warning me-1" name="presence_absent" role="img"
|
||||
aria-label="To define" title="On Time Off"
|
||||
/>
|
||||
<span>On Time Off</span>
|
||||
</t>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -124,23 +109,6 @@
|
||||
<field name="leave_date_to"/>
|
||||
<field name="is_absent"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[hasclass('o_employee_availability')]" position="inside">
|
||||
<span class="d-flex" style="font-size: 100%"
|
||||
t-att-class="record.current_leave_state.raw_value=='validate'?'oe_kanban_button oe_kanban_color_3':'oe_kanban_button oe_kanban_color_2'"
|
||||
t-att-title="luxon.DateTime.fromISO(record.leave_date_from.raw_value).toFormat('ccc d MMM') + ' - ' + luxon.DateTime.fromISO(record.leave_date_to.raw_value).toFormat('ccc d MMM')">
|
||||
<!-- Employee is absent, in holiday but he is connected -->
|
||||
<i t-if="record.hr_icon_display.raw_value == 'presence_holiday_present'"
|
||||
class="fa fa-2x fa-plane text-success me-1" name="presence_absent_active" role="img"
|
||||
aria-label="Present but on leave" title="Present but on leave"
|
||||
/>
|
||||
<!-- Employee is on holiday, not present and not connected -->
|
||||
<i t-if="record.hr_icon_display.raw_value == 'presence_holiday_absent'"
|
||||
class="fa fa-2x fa-plane text-warning me-1" name="presence_absent" role="img"
|
||||
aria-label="To define" title="On Time Off"
|
||||
/>
|
||||
<field t-if="record.current_leave_id.raw_value" name="current_leave_id"/>
|
||||
</span>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -151,14 +119,8 @@
|
||||
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||
<field name = "priority" eval="20" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='hr_presence_status']" position="attributes">
|
||||
<attribute name="invisible">not last_activity or not user_id or not id</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='hr_presence_status']" position="inside">
|
||||
<!-- Employee is absent, in holiday but he is connected -->
|
||||
<div role="img" class="fa fa-fw fa-plane text-success o_button_icon hr_presence" invisible="hr_icon_display != 'presence_holiday_present'" aria-label="Present but on leave" title="Present but on leave" name="presence_absent_active"/>
|
||||
<!-- Employee is on holiday, not present and not connected -->
|
||||
<div role="img" class="fa fa-fw fa-plane text-warning o_button_icon hr_presence" invisible="hr_icon_display != 'presence_holiday_absent'" aria-label="On Time Off" title="On Time Off" name="presence_absent"/>
|
||||
<xpath expr="//field[@name='hr_icon_display']" position="attributes">
|
||||
<attribute name="invisible">not last_activity or not id</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//group[@name='managers']" position="inside">
|
||||
<field name="leave_manager_id" widget="many2one_avatar_user"/>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
import { HrPresenceStatus, hrPresenceStatus } from "@hr/components/hr_presence_status/hr_presence_status";
|
||||
|
||||
patch(HrPresenceStatus.prototype, {
|
||||
get color() {
|
||||
if (this.location) {
|
||||
let color = "text-muted";
|
||||
if (this.props.record.data.hr_presence_state !== "to_define") {
|
||||
color = this.props.record.data.hr_presence_state === "present" ? "text-success" : "text-warning";
|
||||
}
|
||||
return color;
|
||||
}
|
||||
return super.color;
|
||||
},
|
||||
|
||||
get icon() {
|
||||
if (this.location) {
|
||||
switch (this.location) {
|
||||
case "home":
|
||||
return "fa-home";
|
||||
case "office":
|
||||
return "fa-building";
|
||||
case "other":
|
||||
return "fa-map-marker";
|
||||
}
|
||||
}
|
||||
return super.icon;
|
||||
},
|
||||
|
||||
get label() {
|
||||
if (this.location) {
|
||||
return _t("Working from %s", this.location);
|
||||
}
|
||||
return super.label;
|
||||
},
|
||||
|
||||
get location() {
|
||||
let location = this.value?.split("_")[1] || "";
|
||||
if (location && !['home', 'office', 'other'].includes(location)) {
|
||||
location = "";
|
||||
}
|
||||
return location;
|
||||
},
|
||||
});
|
||||
|
||||
const additionalFieldDependencies = [
|
||||
{ name: "hr_presence_state", type: "selection" },
|
||||
];
|
||||
if (typeof hrPresenceStatus.fieldDependencies === "function") {
|
||||
const oldFieldDependencies = hrPresenceStatus.fieldDependencies;
|
||||
hrPresenceStatus.fieldDependencies = (widgetInfo) => {
|
||||
const fieldDependencies = oldFieldDependencies(widgetInfo);
|
||||
fieldDependencies.push(...additionalFieldDependencies);
|
||||
return fieldDependencies;
|
||||
}
|
||||
} else {
|
||||
hrPresenceStatus.fieldDependencies = [
|
||||
...(hrPresenceStatus.fieldDependencies || []),
|
||||
...additionalFieldDependencies,
|
||||
];
|
||||
}
|
||||
@@ -53,45 +53,8 @@
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" ref="hr.hr_kanban_view_employees"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('o_employee_availability')]" position="inside">
|
||||
<t t-set="text_color" t-value="'text-muted'"/>
|
||||
<t t-if="record.hr_presence_state.raw_value !== 'to_define'">
|
||||
<t t-set="text_color" t-value="record.hr_presence_state.raw_value === 'present' ? 'text-success' : 'text-warning'"/>
|
||||
</t>
|
||||
<t t-set="location_map" t-value="{'home': 'fa-home', 'office': 'fa-building', 'other': 'fa-map-marker'}"/>
|
||||
<t t-set="location" t-value="record.hr_icon_display.raw_value.split('_')[1]"/>
|
||||
<t t-set="icon" t-value="location_map[location]"/>
|
||||
<t t-if="location !== 'holiday'">
|
||||
<span class="fa fa-2x" t-attf-class="{{icon}} {{text_color}} d-flex me-2" role="img"
|
||||
t-attf-aria-label="Working from {{location}}" t-attf-title="Working from {{location}}"
|
||||
t-attf-name="presence {{record.hr_presence_state.raw_value}} at {{location}}"
|
||||
/>
|
||||
<field name="name_work_location_display" string="Work Location"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="hr_employee_public_view_kanban" model="ir.ui.view">
|
||||
<field name="name">hr.employee.kanban.inherit.homeworking</field>
|
||||
<field name="model">hr.employee.public</field>
|
||||
<field name="inherit_id" ref="hr.hr_employee_public_view_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('o_employee_availability')]" position="inside">
|
||||
<t t-set="text_color" t-value="text-muted"/>
|
||||
<t t-if="record.hr_presence_state.raw_value !== 'to_define'">
|
||||
<t t-set="text_color" t-value="record.hr_presence_state.raw_value === 'present' ? 'text-success' : 'text-warning'"/>
|
||||
</t>
|
||||
<t t-set="location_map" t-value="{'home': 'fa-home', 'office': 'fa-building', 'other': 'fa-map-marker'}"/>
|
||||
<t t-set="location" t-value="record.hr_icon_display.raw_value.split('_')[1]"/>
|
||||
<t t-set="icon" t-value="location_map[location]"/>
|
||||
<t t-if="location !== 'holiday'">
|
||||
<span class="fa fa-2x" t-attf-class="{{icon}} {{text_color}}" role="img"
|
||||
t-att-aria-label="`Working from ${location}`" t-att-title="`Working from ${location}`"
|
||||
t-att-name="`presence ${record.hr_presence_state.raw_value} at ${location}`"
|
||||
/>
|
||||
</t>
|
||||
<xpath expr="//field[@name='hr_icon_display'][hasclass('o_employee_availability')]" position="after">
|
||||
<field name="name_work_location_display" class="ps-2" string="Work Location"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
Reference in New Issue
Block a user