employee dashboard
This commit is contained in:
parent
6301bd6fac
commit
c0bb40cafc
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
from . import models
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import emp_dashboard
|
||||
|
|
@ -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 |
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
],
|
||||
|
|
|
|||
Loading…
Reference in New Issue