Compare commits

...

3 Commits

Author SHA1 Message Date
administrator 89fe25e387 Merge branch 'develop' 2025-01-21 18:31:15 +05:30
administrator f47d38d3d5 Initial commit 2025-01-21 18:30:59 +05:30
raman c0bb40cafc employee dashboard 2025-01-21 18:19:35 +05:30
14 changed files with 996 additions and 3 deletions

View File

@ -15,6 +15,7 @@
# Check https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Flutter',
'license': 'LGPL-3',
'version': '0.1',
# any module necessary for this one to work correctly

View File

@ -16,6 +16,7 @@
# for the full list
'category': 'Human Resources/Attendances',
'version': '0.1',
'license': 'LGPL-3',
# any module necessary for this one to work correctly
'depends': ['base','hr','hr_attendance','hr_holidays','hr_employee_extended'],

View File

@ -0,0 +1,2 @@
from . import models

View File

@ -0,0 +1,22 @@
# my_employee_profile/__manifest__.py
{
'name': 'Employee Profile',
'version': '1.0',
'category': 'Human Resources',
'summary': 'Display employee profile using Owl.js',
'depends': ['base', 'hr', 'web','muk_web_theme'], # Depends on base, hr, and web for Owl.js and hr.employee model
'data': [
'views/employee_dashboard_views.xml', # Your template
],
'assets': {
'web.assets_backend': [
'hr_emp_dashboard/static/src/js/profile_component.js',
'hr_emp_dashboard/static/src/xml/employee_profile_template.xml',
'hr_emp_dashboard/static/src/css/employee_dashboard.css'
],
},
'installable': True,
'application': False,
}

View File

@ -0,0 +1 @@
from . import emp_dashboard

View File

@ -0,0 +1,170 @@
import pandas as pd
from collections import defaultdict
from datetime import timedelta, datetime, date
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.http import request
from odoo.tools import float_utils
from odoo.tools import format_duration
from pytz import utc
class HrEmployee(models.Model):
""" Inherit hr_employee to add birthday field and custom methods. """
_inherit = 'hr.employee'
def attendance_manual(self):
"""Create and update an attendance for the user employee"""
employee = request.env['hr.employee'].sudo().browse(
self.env.user.employee_id.id)
employee.sudo()._attendance_action_change({
'city': request.geoip.city.name or _('Unknown'),
'country_name': request.geoip.country.name or
request.geoip.continent.name or _('Unknown'),
'latitude': request.geoip.location.latitude or False,
'longitude': request.geoip.location.longitude or False,
'ip_address': request.geoip.ip,
'browser': request.httprequest.user_agent.browser,
'mode': 'kiosk'
})
return employee
@api.model
def get_user_employee_details(self):
"""To fetch the details of employee"""
uid = request.session.uid
employee = self.env['hr.employee'].sudo().search_read(
[('user_id', '=', uid)], limit=1)
attendance = self.env['hr.attendance'].sudo().search_read(
[('employee_id', '=', employee[0]['id'])],
fields=['id', 'check_in', 'check_out', 'worked_hours'])
attendance_line = []
for line in attendance:
if line['check_in'] and line['check_out']:
val = {
'id':line['id'],
'date': line['check_in'].date(),
'sign_in': line['check_in'].time().strftime('%H:%M'),
'sign_out': line['check_out'].time().strftime('%H:%M'),
'worked_hours': format_duration(line['worked_hours'])
}
attendance_line.append(val)
leaves = self.env['hr.leave'].sudo().search_read(
[('employee_id', '=', employee[0]['id'])],
fields=['request_date_from', 'request_date_to', 'state',
'holiday_status_id'])
for line in leaves:
line['type'] = line.pop('holiday_status_id')[1]
if line['state'] == 'confirm':
line['state'] = 'To Approve'
line['color'] = 'orange'
elif line['state'] == 'validate1':
line['state'] = 'Second Approval'
line['color'] = '#7CFC00'
elif line['state'] == 'validate':
line['state'] = 'Approved'
line['color'] = 'green'
elif line['state'] == 'cancel':
line['state'] = 'Cancelled'
line['color'] = 'red'
else:
line['state'] = 'Refused'
line['color'] = 'red'
expense =[]
# self.env['hr.expense'].sudo().search_read(
# [('employee_id', '=', employee[0]['id'])],
# fields=['name', 'date', 'state', 'total_amount'])
# for line in expense:
# if line['state'] == 'draft':
# line['state'] = 'To Report'
# line['color'] = '#17A2B8'
# elif line['state'] == 'reported':
# line['state'] = 'To Submit'
# line['color'] = '#17A2B8'
# elif line['state'] == 'submitted':
# line['state'] = 'Submitted'
# line['color'] = '#FFAC00'
# elif line['state'] == 'approved':
# line['state'] = 'Approved'
# line['color'] = '#28A745'
# elif line['state'] == 'done':
# line['state'] = 'Done'
# line['color'] = '#28A745'
# else:
# line['state'] = 'Refused'
# line['color'] = 'red'
leaves_to_approve = self.env['hr.leave'].sudo().search_count(
[('state', 'in', ['confirm', 'validate1'])])
today = datetime.strftime(datetime.today(), '%Y-%m-%d')
query = """
select count(id)
from hr_leave
WHERE (hr_leave.date_from::DATE,hr_leave.date_to::DATE)
OVERLAPS ('%s', '%s') and
state='validate'""" % (today, today)
cr = self._cr
cr.execute(query)
leaves_today = cr.fetchall()
first_day = date.today().replace(day=1)
last_day = (date.today() + relativedelta(months=1, day=1)) - timedelta(
1)
query = """
select count(id)
from hr_leave
WHERE (hr_leave.date_from::DATE,hr_leave.date_to::DATE)
OVERLAPS ('%s', '%s')
and state='validate'""" % (first_day, last_day)
cr = self._cr
cr.execute(query)
leaves_this_month = cr.fetchall()
leaves_alloc_req = self.env['hr.leave.allocation'].sudo().search_count(
[('state', 'in', ['confirm', 'validate1'])])
timesheet_count = self.env['account.analytic.line'].sudo().search_count(
[('project_id', '!=', False), ('user_id', '=', uid)])
timesheet_view_id = self.env.ref(
'hr_timesheet.hr_timesheet_line_search')
job_applications = self.env['hr.applicant'].sudo().search_count([])
if employee:
# sql = """select broad_factor from hr_employee_broad_factor
# where id =%s"""
# self.env.cr.execute(sql, (employee[0]['id'],))
# result = self.env.cr.dictfetchall()
# broad_factor = result[0]['broad_factor'] if result[0][
# 'broad_factor'] else False
broad_factor = False
if employee[0]['birthday']:
diff = relativedelta(datetime.today(), employee[0]['birthday'])
age = diff.years
else:
age = False
if employee[0]['joining_date']:
diff = relativedelta(datetime.today(),
employee[0]['joining_date'])
years = diff.years
months = diff.months
days = diff.days
experience = '{} years {} months {} days'.format(years, months,
days)
else:
experience = False
if employee:
data = {
'broad_factor': broad_factor if broad_factor else 0,
'leaves_to_approve': leaves_to_approve,
'leaves_today': leaves_today,
'leaves_this_month': leaves_this_month,
'leaves_alloc_req': leaves_alloc_req,
'emp_timesheets': timesheet_count,
'job_applications': job_applications,
'timesheet_view_id': timesheet_view_id,
'experience': experience,
'age': age,
'attendance_lines': attendance_line,
'leave_lines': leaves,
'expense_lines': expense
}
employee[0].update(data)
return employee
else:
return False

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,321 @@
/* General Container */
.netflix-profile-container {
font-family: 'Arial', sans-serif;
color: #333;
padding: 30px;
background-color: #fafafa;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
margin-top: 20px;
max-height: 900px; /* Adjust this value as needed */
overflow-y: auto; /* Make the container scrollable */
}
/* General Container */
.stats-container {
display: flex;
gap: 20px; /* Space between cards */
padding: 20px;
overflow-x: auto; /* Allow horizontal scrolling if needed */
background-color: #f8f9fa;
}
/* Card Design */
.stat-card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
padding: 20px;
text-align: center;
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
width: 300px; /* Fixed width for each card */
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.stat-icon {
font-size: 2.5em;
color: #6a11cb;
margin-bottom: 10px;
}
.stat-content p {
font-size: 1.1em;
color: #555;
margin: 0;
}
.stat-content h2 {
font-size: 2em;
margin: 5px 0;
color: #333;
}
/* Manager-specific card styles */
.stat-card.leave-manager,
.stat-card.leaves_request_today,
.stat-card.leaves_request_month,
.stat-card.hr_leave_allocations_approve,
.stat-card.hr_job_application_approve {
color: white;
}
.stat-card.leave-manager .stat-icon,
.stat-card.leaves_request_today .stat-icon,
.stat-card.leaves_request_month .stat-icon,
.stat-card.hr_leave_allocations_approve .stat-icon,
.stat-card.hr_job_application_approve .stat-icon {
color: #ffc107;
}
/* Tables Section */
.tables-section {
display: flex;
gap: 30px;
flex-wrap: wrap;
padding-bottom: 30px;
overflow-y: auto;
}
.table-card {
flex: 1;
min-width: 320px;
background-color: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.card-header {
background-color: #007bff;
color: #fff;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 12px 12px 0 0;
}
.card-header h2 {
margin: 0;
font-size: 1.6em;
}
.action-button {
background-color: #fff;
color: #007bff;
border: 1px solid #007bff;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
transition: background-color 0.3s, color 0.3s;
}
.action-button:hover {
background-color: #007bff;
color: #fff;
}
.card-content {
padding: 20px;
overflow-y: auto;
max-height: 350px;
font-size: 1em;
color: #555;
}
.table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
.table th,
.table td {
padding: 12px;
border-bottom: 1px solid #ddd;
}
.table th {
background-color: #f4f4f4;
color: #333;
}
.table tbody tr:hover {
background-color: #f9f9f9;
}
/* Status Badge */
.status-badge {
padding: 6px 12px;
border-radius: 20px;
color: #fff;
font-size: 14px;
display: inline-block;
text-transform: uppercase;
}
/* General Styling */
body {
font-family: 'Arial', sans-serif;
color: #333;
background-color: #f0f0f5;
margin: 0;
padding: 0;
overflow-y: auto;
height: 100%;
}
.profile-header {
display: flex;
flex-wrap: wrap;
align-items: center;
background: linear-gradient(135deg, #6a11cb, #2575fc);
color: #fff;
border-radius: 15px;
padding: 30px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
}
.profile-image {
flex: 0 0 200px;
height: 200px;
border-radius: 50%;
overflow: hidden;
margin-right: 25px;
border: 5px solid #fff;
}
.profile-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.profile-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 15px;
}
.profile-info h1 {
margin: 0;
font-size: 2.2em;
font-weight: bold;
}
.job-title {
font-style: italic;
font-size: 1.2em;
color: #ddd;
}
.joined {
font-size: 1em;
opacity: 0.9;
}
.profile-contact {
margin-top: 20px;
font-size: 1em;
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.profile-contact p {
margin: 0;
display: flex;
align-items: center;
gap: 10px;
}
.profile-contact i {
color: #ffd700;
font-size: 1.5em;
}
/* Main Section */
.employee_dashboard_main {
background: #fafafa;
padding: 30px;
height: 100%;
overflow-y: auto;
}
.main-section {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.card {
background: white;
border-radius: 12px;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
overflow: hidden;
}
.card-body {
padding: 20px;
}
.stat-title {
font-size: 16px;
color: #888;
margin-bottom: 10px;
}
.stat-count {
font-size: 28px;
font-weight: bold;
color: #333;
}
.card:hover {
transform: scale(1.05);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}
.col-md-6, .col-lg-6 {
padding: 15px;
}
.mb-4 {
margin-bottom: 30px;
}
/* Quick Action Cards */
./* Quick Action Cards */
.stat-att {
width: 50%; /* Set width to 400px */
background: #fff;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
text-align: center;
padding: 100px;
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
margin: 20px; /* Optional: Add margin for spacing between cards */
}
.stat-atticon {
font-size: 7em;
color: #fff;
margin-bottom: 10px;
}
.stat-attcontent p {
font-size: 1.1em;
color: #fff;
margin: 0;
}

View File

@ -0,0 +1,195 @@
/** @odoo-module **/
import { useEffect, useState, Component ,onMounted, useRef} from "@odoo/owl";
import { registry } from "@web/core/registry";
const actionRegistry = registry.category("actions");
import { useService } from "@web/core/utils/hooks";
import { ActivityMenu } from "@hr_attendance/components/attendance_menu/attendance_menu";
import { patch } from "@web/core/utils/patch";
export class NetflixProfileContainer extends Component {
static template = 'employee_profile_template'; // The template for the profile
static props = ["*"];
setup() {
super.setup(...arguments);
debugger;
this.orm = useService("orm");
this.effect = useService("effect");
this.log_in_out = useRef("log_in_out")
this.action = useService("action");
this.action = useService("action");
// Initialize state for storing employee data
this.state = useState({
login_employee: {
name: '',
image_1920: '',
job_id: null,
current_company_exp: null,
doj:'',
employee_id:null,
birthday: '',
attendance_state:null,
mobile_phone: '',
work_email: '',
private_street: ''
},
attendance_lines:[],
leaves:[]
});
onMounted(() => {
this.fetchEmployeeData();
});
}
attendance_sign_in_out() {
if (this.state.login_employee.attendance_state == 'checked_out') {
this.state.login_employee.attendance_state = 'checked_in'
}
else{
if (this.state.login_employee.attendance_state == 'checked_in') {
this.state.login_employee.attendance_state = 'checked_out'
}
}
this.update_attendance()
}
async update_attendance() {
var self = this;
var result = await this.orm.call('hr.employee', 'attendance_manual',[[this.props.action.context.user_id]])
if (result) {
var attendance_state = this.state.login_employee.attendance_state;
var message = ''
if (attendance_state == 'checked_in'){
message = 'Checked In'
this.env.bus.trigger('signin_signout', {
mode: "checked_in",
});
}
else if (attendance_state == 'checked_out'){
message = 'Checked Out'
this.env.bus.trigger('signin_signout', {
mode: false,
});
}
this.effect.add({
message: ("Successfully " + message),
type: 'rainbow_man',
fadeout: "fast",
})
}
}
add_attendance() {
this.action.doAction({
name: ("Attendances"),
type: 'ir.actions.act_window',
res_model: 'hr.attendance',
view_mode: 'form',
views: [[false, 'form']],
target: 'new'
});
}
add_leave() {
this.action.doAction({
name: ("Leave Request"),
type: 'ir.actions.act_window',
res_model: 'hr.leave',
view_mode: 'form',
views: [[false, 'form']],
target: 'new'
});
}
// Method to fetch employee data from hr.employee model
async fetchEmployeeData() {
console.log(this.props.action.context.user_id)
try {
const employeeData = await this.orm.searchRead("hr.employee", [["user_id", "=", this.props.action.context.user_id]], ['name', 'image_1920','job_id','employee_id','current_company_exp','doj','birthday','mobile_phone','work_email','private_street','attendance_state' ,'id'
] );
const attendanceLines = await this.orm.searchRead(
'hr.attendance',
[['employee_id', '=', employeeData[0].id]],
['create_date', 'check_in', 'check_out', 'worked_hours']
);
const Leaves = await this.orm.searchRead('hr.leave',[['employee_id', '=', employeeData[0].id]],
['request_date_from', 'request_date_to', 'state','holiday_status_id']);
Leaves.forEach(line => {
// Extract the 'type' from 'holiday_status_id' and assign it
line['type'] = line['holiday_status_id'][1];
// Change state and set color based on the state
if (line['state'] === 'confirm') {
line['state'] = 'To Approve';
line['color'] = 'orange';
} else if (line['state'] === 'validate1') {
line['state'] = 'Second Approval';
line['color'] = '#7CFC00';
} else if (line['state'] === 'validate') {
line['state'] = 'Approved';
line['color'] = 'green';
} else if (line['state'] === 'cancel') {
line['state'] = 'Cancelled';
line['color'] = 'red';
} else {
line['state'] = 'Refused';
line['color'] = 'red';
}
});
if (employeeData.length > 0) {
const employee = employeeData[0];
attendanceLines.forEach(line => {
let createDate = new Date(line.create_date);
line.create_date = createDate.toISOString().split('T')[0]; // Format as 'YYYY-MM-DD'
let checkIn = new Date(line.check_in);
line.check_in = checkIn.toTimeString().slice(0, 5); // Format as 'HH:MM'
let checkOut = new Date(line.check_out);
line.check_out = checkOut.toTimeString().slice(0, 5); // Format as 'HH:MM'
line.worked_hours = line.worked_hours.toFixed(2);
});
this.state.attendance_lines = attendanceLines,
this.state.leaves = Leaves
this.state.login_employee = {
name: employee.name,
image_1920: employee.image_1920,
doj:employee.doj,
job_id: employee.job_id,
employee_id:employee.employee_id,
current_company_exp: employee.current_company_exp,
attendance_state:employee.attendance_state,
birthday: employee.birthday,
mobile_phone: employee.mobile_phone,
work_email: employee.work_email,
private_street: employee.private_street,
};
}
} catch (error) {
console.error('Error fetching employee data:', error);
}
}
}
registry.category("actions").add("NetflixProfileContainer", NetflixProfileContainer)
patch(ActivityMenu.prototype, {
setup() {
super.setup();
var self = this
onMounted(() => {
this.env.bus.addEventListener('signin_signout', ({
detail
}) => {
if (detail.mode == 'checked_in') {
self.state.checkedIn = detail.mode
} else {
self.state.checkedIn = false
}
})
})
},
})

View File

@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="employee_profile_template">
<div class="netflix-profile-container">
<div class="profile-header">
<div class="profile-image">
<img t-att-src="'data:image/png;base64,' + (state.login_employee.image_1920 || '')"
alt="Employee Image"
t-att-class="state.login_employee.image_1920 ? '' : 'default-image'" />
</div>
<div class="profile-details">
<div class="profile-info">
<h1>
<t t-esc="state.login_employee.name"/>
<t t-if="state.login_employee.employee_id">
- <t t-esc="state.login_employee.employee_id"/>
</t>
</h1>
<p class="job-title">
<t t-if="state.login_employee.job_id">
<t t-esc="state.login_employee.job_id[1]"/>
</t>
<t t-if="!state.login_employee.job_id">
Add Job Title
</t>
</p>
<p class="joined">
<t t-if="state.login_employee.doj">
Joined <t t-esc="state.login_employee.current_company_exp"/> ago
</t>
<t t-if="!state.login_employee.doj">
Joined Date: <t t-esc="state.login_employee.doj"/>
</t>
</p>
<p>
Birthday :
<t t-if="state.login_employee.birthday">
<t t-esc="state.login_employee.birthday"/>
</t>
<t t-else="">
--/--/----
</t>
</p>
</div>
<div class="profile-contact">
<p>
<i class="fa fa-phone"></i>
<t t-if="state.login_employee.mobile_phone">
<t t-esc="state.login_employee.mobile_phone"/>
</t>
<t t-if="!state.login_employee.mobile_phone">
---
</t>
</p>
<p>
<i class="fa fa-envelope"></i>
<t t-if="state.login_employee.work_email">
<t t-esc="state.login_employee.work_email"/>
</t>
<t t-if="!state.login_employee.work_email">
---
</t>
</p>
<p>
<i class="fa fa-home"></i> Address:
<t t-if="state.login_employee.private_street">
<t t-esc="state.login_employee.private_street"/>
</t>
<t t-if="!state.login_employee.private_street">
---
</t>
</p>
</div>
</div>
<div class="stat-att" t-on-click="attendance_sign_in_out">
<t t-if="this.state.login_employee.attendance_state == 'checked_out'">
<div class="stat-atticon">
<i class="fa fa-sign-in">
</i>
</div>
<div class="stat-attcontent">
<p>
Check In
</p>
</div>
</t>
<t t-if="this.state.login_employee.attendance_state == 'checked_in'">
<div class="stat-atticon">
<i class="fa fa-sign-out">
</i>
</div>
<div class="stat-attcontent">
<p>
Check Out
</p>
</div>
</t>
</div>
</div>
<div class="stats-container">
<div class="stat-card" t-on-click="hr_timesheets">
<div class="stat-icon">
<i class="fa fa-clock-o">
</i>
</div>
<div class="stat-content">
<p>
Timesheets
</p>
<h2>
<t t-esc="this.state.login_employee['emp_timesheets']" />
</h2>
</div>
</div>
</div>
<div class="tables-section">
<!-- Attendance Table -->
<div class="table-card">
<div class="card-header">
<h2>
Attendance
</h2>
<button class="action-button" t-on-click="add_attendance">
<i class="fa fa-plus">
</i>
Add
</button>
</div>
<div class="card-content">
<table class="table">
<thead>
<tr>
<th>
Date
</th>
<th>
Sign In
</th>
<th>
Sign Out
</th>
<th>
Worked Hours
</th>
</tr>
</thead>
<tbody>
<tr t-foreach="this.state.attendance_lines" t-as="line" t-key="line['id']">
<td t-esc="line.create_date">
</td>
<td t-esc="line['check_in']">
</td>
<td t-esc="line['check_out']">
</td>
<td t-esc="line['worked_hours']">
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Leaves Table -->
<div class="table-card">
<div class="card-header">
<h2>
Leaves
</h2>
<button class="action-button" t-on-click="add_leave">
<i class="fa fa-plus">
</i>
Add
</button>
</div>
<div class="card-content">
<table class="table">
<thead>
<tr>
<th>
From Date
</th>
<th>
To Date
</th>
<th>
Type
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>
<tr t-foreach="this.state.leaves" t-as="line" t-key="line['id']">
<td t-esc="line['request_date_from']">
</td>
<td t-esc="line['request_date_to']">
</td>
<td t-esc="line['type']">
</td>
<td>
<span class="status-badge" t-esc="line['state']" t-attf-style="background: #{line['color']}">
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Expenses Table -->
<div class="table-card">
<div class="card-header">
<h2>
Expenses
</h2>
<button class="action-button" t-on-click="add_expense">
<i class="fa fa-plus">
</i>
Add
</button>
</div>
<div class="card-content">
<table class="table">
<thead>
<tr>
<th>
Date
</th>
<th>
Subject
</th>
<th>
Amount
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>
<!-- <tr t-foreach="this.state.login_employee['expense_lines']" t-as="line" t-key="line['id']">-->
<!-- <td t-esc="line['date']">-->
<!-- </td>-->
<!-- <td t-esc="line['name']">-->
<!-- </td>-->
<!-- <td t-esc="line['total_amount']">-->
<!-- </td>-->
<!-- <td>-->
<!-- <span class="status-badge" t-esc="line['state']" t-attf-style="background: #{line['color']}">-->
<!-- </span>-->
<!-- </td>-->
<!-- </tr>-->
</tbody>
</table>
</div>
</div>
</div>
</div>
</t>
</templates>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Client action record for Dashboard -->
<record id="hr_action_dashboard" model="ir.actions.client">
<field name="name">Dashboard</field>
<field name="tag">NetflixProfileContainer</field>
<field name="context">{'user_id':uid}</field>
</record>
<!-- Menu record for dashboard -->
<menuitem id="dashboard_menu_root"
name="Dashboard"
action="hr_action_dashboard"
web_icon="hr_emp_dashboard,static/description/icon.png"
sequence="-100" groups="base.group_user"/>
</odoo>

View File

@ -16,13 +16,13 @@
# for the full list
'category': 'Human Resources/Employees',
'version': '0.1',
'license': 'LGPL-3',
# any module necessary for this one to work correctly
'depends': ['base','hr','mail'],
'depends': ['base','hr'],
# always loaded
'data': [
'security/security.xml',
'security/ir.model.access.csv',
'views/hr_employee.xml',
'wizards/work_location_wizard.xml'

View File

@ -16,6 +16,7 @@
# for the full list
'category': 'Human Resources/Time Off',
'version': '0.1',
'license': 'LGPL-3',
# any module necessary for this one to work correctly
'depends': ['base','hr','hr_holidays','hr_employee_extended'],

View File

@ -8,7 +8,7 @@
'version': '18.0.1.2.3',
'category': 'Themes/Backend',
'license': 'LGPL-3',
'author': 'MuK IT',
'author': 'Raman Marikanti',
'website': 'http://www.mukit.at',
'live_test_url': 'https://my.mukit.at/r/f6m',
'contributors': [
@ -19,6 +19,7 @@
'muk_web_dialog',
'muk_web_appsbar',
'muk_web_colors',
'hr', 'web'
],
'excludes': [
'web_enterprise',
@ -43,6 +44,7 @@
'web.assets_backend': [
'muk_web_theme/static/src/webclient/**/*.xml',
'muk_web_theme/static/src/webclient/**/*.scss',
# 'muk_web_theme/static/src/employeeDashboard/*',
'muk_web_theme/static/src/webclient/**/*.js',
'muk_web_theme/static/src/views/**/*.scss',
],