Initial commit: Hr packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:50 +02:00
commit 62531cd146
2820 changed files with 1432848 additions and 0 deletions

View file

@ -0,0 +1,6 @@
// = HR Holidays
// ============================================================================
// No CSS hacks, variables overrides only
.o_timeoff_card, .o_time_off_icon_types {
--timeOffCard-icon-filter: invert(1) brightness(2);
}

View file

@ -0,0 +1,21 @@
/* @odoo-module */
import Popover from "web.Popover";
const { Component } = owl;
export class TimeOffCardPopover extends Component {}
TimeOffCardPopover.components = { Popover };
TimeOffCardPopover.template = 'hr_holidays.TimeOffCardPopover';
TimeOffCardPopover.props = ['allocated', 'approved', 'planned', 'left', 'usable'];
export class TimeOffCard extends Component {}
TimeOffCard.components = { TimeOffCardPopover };
TimeOffCard.template = 'hr_holidays.TimeOffCard';
TimeOffCard.props = ['name', 'id', 'data', 'requires_allocation'];
export class TimeOffCardMobile extends TimeOffCard {}
TimeOffCardMobile.template = 'hr_holidays.TimeOffCardMobile';

View file

@ -0,0 +1,74 @@
.o_timeoff_card {
flex-direction: column;
flex-grow: 1;
display: flex;
text-align: center;
&:not(:last-child) {
border-right: 2px solid $border-color;
}
span {
line-height: 1;
}
.o_timeoff_name {
color: $body-color;
font-size: 15px;
margin-bottom: 3px;
}
.o_timeoff_duration {
font-size: 30px;
font-weight: bold;
img {
height: 30px;
filter: var(--timeOffCard-icon-filter);
}
}
.o_timeoff_validity {
font-size: 10px;
}
.o_timeoff_info {
display: inline-block;
margin-right: -10px;
span {
cursor: pointer;
padding-right: 10px;
}
}
}
.o_timeoff_card_mobile {
.o_timeoff_green {
color: $o-enterprise-primary-color;
}
}
.o_timeoff_popover {
background-color: $o-view-background-color;
font-size: 12px;
ul {
list-style-type: none;
margin-bottom: 0;
padding: 0;
}
li {
display: flex;
justify-content: space-between;
span {
padding-left: 3px;
}
}
}
.o_time_off_icon_types img {
filter: var(--timeOffCard-icon-filter);
}

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<div t-name="hr_holidays.TimeOffCard" owl="1" class="o_timeoff_card py-3 text-odoo">
<t t-set="data" t-value="props.data"/>
<t t-set="duration" t-value="props.requires_allocation ? data.virtual_remaining_leaves : data.virtual_leaves_taken"/>
<t t-set="show_popover" t-value="true"/>
<strong class="o_timeoff_name"><t t-esc="props.name"/></strong>
<span class="o_timeoff_duration">
<t t-if="data and data.icon">
<img t-att-src="data.icon" />
</t>
<t t-esc="duration"/>
</span>
<div class="text-uppercase">
<t t-if="data.request_unit == 'hour'" name="duration_unit">hours</t>
<t t-else="">days</t>
<t t-if="props.requires_allocation" name="duration_type">
available
</t>
<t t-else="">
taken
</t>
<t t-if="show_popover">
<TimeOffCardPopover
allocated="data.max_leaves"
approved="data.leaves_approved"
planned="data.leaves_requested"
left="data.virtual_remaining_leaves"
usable="data.usable_remaining_leaves" />
</t>
</div>
<span t-if="props.requires_allocation and data.closest_allocation_expire !== false" class="text-uppercase o_timeoff_validity">
<t t-if="data.closest_allocation_remaining != data.virtual_remaining_leaves">
(<t t-esc="data.closest_allocation_remaining"/> <t t-if="data.request_unit == 'hour'">hours</t><t t-else="">days</t>
valid until <span t-esc="data.closest_allocation_expire"/>)
</t>
<t t-else="">
(valid until <span t-esc="data.closest_allocation_expire"/>)
</t>
</span>
</div>
<t t-name="hr_holidays.TimeOffCardMobile" owl="1">
<t t-set="data" t-value="props.data"/>
<t t-set="duration" t-value="props.requires_allocation ? data.virtual_remaining_leaves : data.virtual_leaves_taken" />
<span class="float-end o_timeoff_card_mobile">
<t t-if="props.requires_allocation" name="duration_type">
<strong t-esc="duration" class="o_timeoff_green"/> / <span t-esc="data.max_leaves"/> <t t-if="data.request_unit == 'hour'">Hours</t><t t-else="">Days</t> <span class="o_timeoff_green">Available</span>
</t>
<t t-else="">
<strong t-esc="duration"/> <t t-if="data.request_unit == 'hour'">Hours</t><t t-else="">Days</t> <span class="text-odoo">Taken</span>
</t>
</span>
</t>
<t t-name="hr_holidays.TimeOffCardPopover" owl="1">
<div class="o_timeoff_info">
<Popover position="'right'" popoverClass="'o_timeoff_popover'">
<span class="fa fa-question-circle-o"/>
<t t-set-slot="opened">
<ul>
<li>Allocated: <span t-esc="props.allocated"/></li>
<li>Approved: <span t-esc="props.approved"/></li>
<li style="border-bottom: 1px solid gray;">Planned: <span t-esc="props.planned"/></li>
<li>Left: <span t-esc="props.left"/></li>
<li t-if="props.left != props.usable">Usable: <span t-esc="props.usable"/></li>
</ul>
</t>
</Popover>
</div>
</t>
</templates>

View file

@ -0,0 +1,42 @@
/* @odoo-module */
import { TimeOffCard } from './time_off_card';
import { useBus, useService } from "@web/core/utils/hooks";
const { Component, useState, onWillStart } = owl;
export class TimeOffDashboard extends Component {
setup() {
this.orm = useService("orm");
this.state = useState({
holidays: [],
});
useBus(this.env.timeOffBus, 'update_dashboard', async () => {
await this.loadDashboardData()
});
onWillStart(async () => {
await this.loadDashboardData();
});
}
async loadDashboardData() {
const context = {};
if (this.props.employeeId !== null) {
context['employee_id'] = this.props.employeeId;
}
this.state.holidays = await this.orm.call(
'hr.leave.type',
'get_days_all_request',
[],
{
context: context
}
);
}
}
TimeOffDashboard.components = { TimeOffCard };
TimeOffDashboard.template = 'hr_holidays.TimeOffDashboard';
TimeOffDashboard.props = ['employeeId'];

View file

@ -0,0 +1,11 @@
.o_timeoff_dashboard {
display: flex;
justify-content: space-between;
height: auto;
box-shadow: inset 0 -1px 0 $border-color;
position: sticky;
top: 0;
z-index: 100;
background-color: $o-webclient-background-color;
}

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<div t-name="hr_holidays.TimeOffDashboard" owl="1" class="o_timeoff_dashboard">
<t t-foreach="state.holidays" t-as="holiday" t-key="holiday[3]">
<TimeOffCard name="holiday[0]" data="holiday[1]" requires_allocation="holiday[2] == 'yes'" id="holiday[3]"/>
</t>
</div>
</templates>