@ -0,0 +1,46 @@ |
|||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
Project Dashboard |
|||
================= |
|||
In this dashboard you can get Detailed Dashboard View for Project |
|||
|
|||
Configuration |
|||
============= |
|||
* No need of any configuration. |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (AGPL v3). |
|||
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
|||
|
|||
Credits |
|||
------- |
|||
* Developer: (V18) Nihala KP, Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Nihala KP @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import controllers |
|||
from . import models |
@ -0,0 +1,48 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Nihala KP @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
{ |
|||
'name': 'Project Dashboard', |
|||
'version': '18.0.1.0.0', |
|||
'category': 'Extra Tools', |
|||
'summary': """Get a Detailed View for Project.""", |
|||
'description': """In this dashboard user can get the Detailed Information |
|||
about Project, Task, Employee, Hours recorded, Total Margin and Total |
|||
Sale Orders.""", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'depends': ['sale_management', 'project', 'sale_timesheet'], |
|||
'data': ['views/dashboard_views.xml'], |
|||
'assets': { |
|||
'web.assets_backend': [ |
|||
'project_dashboard_odoo/static/src/js/dashboard.js', |
|||
'project_dashboard_odoo/static/src/css/dashboard.css', |
|||
'project_dashboard_odoo/static/src/xml/dashboard_templates.xml', |
|||
'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js' |
|||
]}, |
|||
'images': ['static/description/banner.png'], |
|||
'license': 'AGPL-3', |
|||
'installable': True, |
|||
'application': False, |
|||
'auto_install': False, |
|||
} |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import project_dashboard_odoo |
@ -0,0 +1,379 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Mruthul Raj @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
import datetime |
|||
from odoo import http |
|||
from odoo.http import request |
|||
|
|||
|
|||
class ProjectFilter(http.Controller): |
|||
"""The ProjectFilter class provides the filter option to the js. |
|||
When applying the filter returns the corresponding data.""" |
|||
|
|||
@http.route('/project/task/count', auth='public', type='json') |
|||
def get_project_task_count(self): |
|||
"""Summary: |
|||
when the page is loaded, get the data from different models and |
|||
transfer to the js file. |
|||
Return a dictionary variable. |
|||
Return: |
|||
type:It is a dictionary variable. This dictionary contains data for |
|||
the project task graph.""" |
|||
project_name = [] |
|||
total_task = [] |
|||
colors = [] |
|||
if request.env.user.has_group('project.group_project_manager'): |
|||
project_ids = request.env['project.project'].search([]) |
|||
else: |
|||
project_ids = request.env['project.project'].search( |
|||
[('user_id', '=', request.env.uid)]) |
|||
for project_id in project_ids: |
|||
project_name.append(project_id.name) |
|||
task = request.env['project.task'].search_count( |
|||
[('project_id', '=', project_id.id)]) |
|||
total_task.append(task) |
|||
color_code = request.env['project.project'].get_color_code() |
|||
colors.append(color_code) |
|||
return { |
|||
'project': project_name, |
|||
'task': total_task, |
|||
'color': colors |
|||
} |
|||
|
|||
@http.route('/employee/timesheet', auth='public', type='json') |
|||
def get_top_timesheet_employees(self): |
|||
"""Summary: |
|||
when the page is loaded, get the data for the timesheet graph. |
|||
Return: |
|||
type:It is a list. This list contains data that affects the graph |
|||
of employees.""" |
|||
query = '''select hr_employee.name as employee,sum(unit_amount) as unit |
|||
from account_analytic_line |
|||
inner join hr_employee on hr_employee.id = |
|||
account_analytic_line.employee_id |
|||
group by hr_employee.id ORDER |
|||
BY unit DESC Limit 10 ''' |
|||
request._cr.execute(query) |
|||
top_product = request._cr.dictfetchall() |
|||
unit = [record.get('unit') for record in top_product] |
|||
employee = [record.get('employee') for record in top_product] |
|||
return [unit, employee] |
|||
|
|||
@http.route('/project/filter', auth='public', type='json') |
|||
def project_filter(self): |
|||
"""Summary: |
|||
transferring data to the selection field that works as a filter |
|||
Returns: |
|||
type:list of lists, it contains the data for the corresponding |
|||
filter.""" |
|||
project_list = [] |
|||
employee_list = [] |
|||
project_ids = request.env['project.project'].search([]) |
|||
employee_ids = request.env['hr.employee'].search([]) |
|||
# getting partner data |
|||
for employee_id in employee_ids: |
|||
dic = {'name': employee_id.name, |
|||
'id': employee_id.id} |
|||
employee_list.append(dic) |
|||
for project_id in project_ids: |
|||
dic = {'name': project_id.name, |
|||
'id': project_id.id} |
|||
project_list.append(dic) |
|||
return [project_list, employee_list] |
|||
|
|||
@http.route('/project/filter-apply', auth='public', type='json') |
|||
def project_filter_apply(self, **kw): |
|||
"""Summary: |
|||
transferring data after filter 9th applied |
|||
Args: |
|||
kw(dict):This parameter contains the value of selection field |
|||
Returns: |
|||
type:dict, it contains the data for the corresponding |
|||
filtrated transferring data to ui after filtration.""" |
|||
data = kw['data'] |
|||
# checking the employee selected or not |
|||
if data['employee'] == 'null': |
|||
emp_selected = [employee.id for employee in |
|||
request.env['hr.employee'].search([])] |
|||
else: |
|||
emp_selected = [int(data['employee'])] |
|||
start_date = data['start_date'] |
|||
end_date = data['end_date'] |
|||
# checking the dates are selected or not |
|||
if start_date != 'null' and end_date != 'null': |
|||
start_date = datetime.datetime.strptime(start_date, |
|||
"%Y-%m-%d").date() |
|||
end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d").date() |
|||
if data['project'] == 'null': |
|||
pro_selected = [project.id for project in |
|||
request.env['project.project'].search( |
|||
[('date_start', '>', start_date), |
|||
('date_start', '<', end_date)])] |
|||
else: |
|||
pro_selected = [int(data['project'])] |
|||
elif start_date == 'null' and end_date != 'null': |
|||
end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d").date() |
|||
if data['project'] == 'null': |
|||
pro_selected = [project.id for project in |
|||
request.env['project.project'].search( |
|||
[('date_start', '<', end_date)])] |
|||
else: |
|||
pro_selected = [int(data['project'])] |
|||
elif start_date != 'null' and end_date == 'null': |
|||
start_date = datetime.datetime.strptime(start_date, |
|||
"%Y-%m-%d").date() |
|||
if data['project'] == 'null': |
|||
pro_selected = [project.id for project in |
|||
request.env['project.project'].search( |
|||
[('date_start', '>', start_date)])] |
|||
else: |
|||
pro_selected = [int(data['project'])] |
|||
else: |
|||
if data['project'] == 'null': |
|||
pro_selected = [project.id for project in |
|||
request.env['project.project'].search([])] |
|||
else: |
|||
pro_selected = [int(data['project'])] |
|||
report_project = request.env['timesheets.analysis.report'].search( |
|||
[('project_id', 'in', pro_selected), |
|||
('employee_id', 'in', emp_selected)]) |
|||
analytic_project = request.env['account.analytic.line'].search( |
|||
[('project_id', 'in', pro_selected), |
|||
('employee_id', 'in', emp_selected)]) |
|||
margin = round(sum(report_project.mapped('margin')), 2) |
|||
sale_orders = [] |
|||
for rec in analytic_project: |
|||
if rec.order_id.id and rec.order_id.id not in sale_orders: |
|||
sale_orders.append(rec.order_id.id) |
|||
total_time = sum(analytic_project.mapped('unit_amount')) |
|||
return { |
|||
'total_project': pro_selected, |
|||
'total_emp': emp_selected, |
|||
'total_task': [rec.id for rec in request.env['project.task'].search( |
|||
[('project_id', 'in', pro_selected)])], |
|||
'hours_recorded': total_time, |
|||
'list_hours_recorded': [rec.id for rec in analytic_project], |
|||
'total_margin': margin, |
|||
'total_so': sale_orders |
|||
} |
|||
|
|||
@http.route('/get/tiles/data', auth='public', type='json') |
|||
def get_tiles_data(self): |
|||
"""Summary: |
|||
when the page is loaded, get the data from different models and |
|||
transfer to the js file. |
|||
Return a dictionary variable. |
|||
Return: |
|||
type:It is a dictionary variable. This dictionary contains data that |
|||
affects the dashboard view.""" |
|||
if request.env.user.has_group('project.group_project_manager'): |
|||
all_project = request.env['project.project'].search([]) |
|||
all_task = request.env['project.task'].search([]) |
|||
analytic_project = request.env['account.analytic.line'].search([]) |
|||
report_project = request.env['timesheets.analysis.report'].search( |
|||
[]) |
|||
margin = round(sum(report_project.mapped('margin')), 2) |
|||
total_time = sum(analytic_project.mapped('unit_amount')) |
|||
employees = request.env['hr.employee'].search([]) |
|||
task = request.env['project.task'].sudo().search_read([ |
|||
('sale_order_id', '!=', False) |
|||
], ['sale_order_id']) |
|||
task_so_ids = [o['sale_order_id'][0] for o in task] |
|||
task_so_ids = list(set(task_so_ids)) |
|||
sale_orders = request.env['sale.order'].browse(task_so_ids) |
|||
project_stage_ids = request.env['project.project.stage'].search([]) |
|||
project_stage_list = [] |
|||
for project_stage_id in project_stage_ids: |
|||
total_projects = request.env[ |
|||
'project.project'].sudo().search_count( |
|||
[('stage_id', '=', project_stage_id.id)]) |
|||
project_stage_list.append({'name': project_stage_id.name, |
|||
'projects': total_projects}) |
|||
return { |
|||
'total_projects': len(all_project), |
|||
'total_projects_ids': all_project.ids, |
|||
'total_tasks': len(all_task), |
|||
'total_tasks_ids': all_task.ids, |
|||
'total_hours': total_time, |
|||
'total_profitability': margin, |
|||
'total_employees': len(employees), |
|||
'total_sale_orders': len(sale_orders), |
|||
'sale_orders_ids': sale_orders.mapped('id'), |
|||
'project_stage_list': project_stage_list, |
|||
'flag': 1} |
|||
else: |
|||
all_project = request.env['project.project'].search( |
|||
[('user_id', '=', request.env.uid)]) |
|||
all_task = [] |
|||
for task in request.env['project.task'].search([]): |
|||
for assignee in task.user_ids: |
|||
if assignee.id == request.env.uid: |
|||
all_task.append(task.id) |
|||
analytic_project = request.env['account.analytic.line'].search( |
|||
[('project_id', 'in', all_project.ids)]) |
|||
total_time = sum(analytic_project.mapped('unit_amount')) |
|||
task = request.env['project.task'].sudo().search_read([ |
|||
('sale_order_id', '!=', False), |
|||
('project_id', 'in', all_project.ids) |
|||
], ['sale_order_id']) |
|||
task_so_ids = [o['sale_order_id'][0] for o in task] |
|||
sale_orders = request.mapped('sale_line_id.order_id') | request.env[ |
|||
'sale.order'].browse(task_so_ids) |
|||
project_stage_ids = request.env['project.project.stage'].search([]) |
|||
project_stage_list = [] |
|||
for project_stage_id in project_stage_ids: |
|||
total_projects = request.env['project.project'].search_count( |
|||
[('stage_id', '=', project_stage_id.id), |
|||
('id', 'in', all_project.ids)]) |
|||
project_stage_list.append({ |
|||
'name': project_stage_id.name, |
|||
'projects': total_projects |
|||
}) |
|||
return { |
|||
'total_projects': len(all_project), |
|||
'total_projects_ids': all_project.ids, |
|||
'total_tasks': len(all_task), |
|||
'total_tasks_ids': all_task, |
|||
'total_hours': total_time, |
|||
'total_sale_orders': len(sale_orders), |
|||
'sale_orders_ids': sale_orders.mapped('id'), |
|||
'project_stage_list': project_stage_list, |
|||
'flag': 2} |
|||
|
|||
@http.route('/get/hours', auth='public', type='json') |
|||
def get_hours_data(self): |
|||
"""Summary: |
|||
when the page is loaded get the data for the hour table. |
|||
Return: |
|||
type:It is a dictionary variable. This dictionary contains data that |
|||
hours table.""" |
|||
if request.env.user.has_group('project.group_project_manager'): |
|||
query = '''SELECT sum(unit_amount) as hour_recorded FROM |
|||
account_analytic_line WHERE |
|||
timesheet_invoice_type='non_billable_project' ''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
hour_recorded = [] |
|||
for record in data: |
|||
hour_recorded.append(record.get('hour_recorded')) |
|||
query = '''SELECT sum(unit_amount) as hour_recorde FROM |
|||
account_analytic_line WHERE |
|||
timesheet_invoice_type='billable_time' ''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
hour_recorde = [] |
|||
for record in data: |
|||
hour_recorde.append(record.get('hour_recorde')) |
|||
query = '''SELECT sum(unit_amount) as billable_fix FROM |
|||
account_analytic_line WHERE |
|||
timesheet_invoice_type='billable_fixed' ''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
billable_fix = [] |
|||
for record in data: |
|||
billable_fix.append(record.get('billable_fix')) |
|||
query = '''SELECT sum(unit_amount) as non_billable FROM |
|||
account_analytic_line WHERE timesheet_invoice_type='non_billable' |
|||
''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
non_billable = [] |
|||
for record in data: |
|||
non_billable.append(record.get('non_billable')) |
|||
query = '''SELECT sum(unit_amount) as total_hr FROM |
|||
account_analytic_line WHERE |
|||
timesheet_invoice_type='non_billable_project' or |
|||
timesheet_invoice_type='billable_time' or |
|||
timesheet_invoice_type='billable_fixed' or |
|||
timesheet_invoice_type='non_billable' ''' |
|||
request._cr.execute(query) |
|||
data = request._cr.dictfetchall() |
|||
total_hr = [] |
|||
for record in data: |
|||
total_hr.append(record.get('total_hr')) |
|||
return { |
|||
'hour_recorded': hour_recorded, |
|||
'hour_recorde': hour_recorde, |
|||
'billable_fix': billable_fix, |
|||
'non_billable': non_billable, |
|||
'total_hr': total_hr, |
|||
} |
|||
else: |
|||
all_project = request.env['project.project'].search( |
|||
[('user_id', '=', request.env.uid)]).ids |
|||
analytic_project = request.env['account.analytic.line'].search( |
|||
[('project_id', 'in', all_project)]) |
|||
all_hour_recorded = analytic_project.filtered( |
|||
lambda x: x.timesheet_invoice_type == 'non_billable_project') |
|||
all_hour_recorde = analytic_project.filtered( |
|||
lambda x: x.timesheet_invoice_type == 'billable_time') |
|||
all_billable_fix = analytic_project.filtered( |
|||
lambda x: x.timesheet_invoice_type == 'billable_fixed') |
|||
all_non_billable = analytic_project.filtered( |
|||
lambda x: x.timesheet_invoice_type == 'non_billable') |
|||
hour_recorded = [sum(all_hour_recorded.mapped('unit_amount'))] |
|||
hour_recorde = [sum(all_hour_recorde.mapped('unit_amount'))] |
|||
billable_fix = [sum(all_billable_fix.mapped('unit_amount'))] |
|||
non_billable = [sum(all_non_billable.mapped('unit_amount'))] |
|||
total_hr = [ |
|||
sum(hour_recorded + hour_recorde + billable_fix + non_billable)] |
|||
return { |
|||
'hour_recorded': hour_recorded, |
|||
'hour_recorde': hour_recorde, |
|||
'billable_fix': billable_fix, |
|||
'non_billable': non_billable, |
|||
'total_hr': total_hr, |
|||
} |
|||
|
|||
@http.route('/get/task/data', auth='public', type='json') |
|||
def get_task_data(self): |
|||
""" |
|||
Summary: |
|||
when the page is loaded, get the data from different models and |
|||
transfer to the js file. |
|||
Return a dictionary variable. |
|||
Return: |
|||
type:It is a dictionary variable. This dictionary contains data |
|||
that affecting project task table.""" |
|||
if request.env.user.has_group('project.group_project_manager'): |
|||
request._cr.execute('''select project_task.name as task_name, |
|||
project_task.id, |
|||
pro.name as project_name from project_task |
|||
Inner join project_project as pro on project_task.project_id |
|||
= pro.id ORDER BY project_name ASC''') |
|||
data = request._cr.fetchall() |
|||
project_name = [] |
|||
for rec in data: |
|||
project_name.append(list(rec)) |
|||
return { |
|||
'project': project_name |
|||
} |
|||
else: |
|||
all_project = request.env['project.project'].search( |
|||
[('user_id', '=', request.env.uid)]).ids |
|||
all_tasks = request.env['project.task'].search( |
|||
[('project_id', 'in', all_project)]) |
|||
task_project = [[task.name, task.project_id.name, task.id] for task |
|||
in |
|||
all_tasks] |
|||
return { |
|||
'project': task_project |
|||
} |
@ -0,0 +1,7 @@ |
|||
## Module <project_dashboard_odoo> |
|||
|
|||
#### 4.10.2024 |
|||
#### Version 18.0.1.0.0 |
|||
##### ADD |
|||
|
|||
- Initial commit for Project Dashboard |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Nihala KP @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from . import project_project |
@ -0,0 +1,35 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Nihala KP @cybrosys(odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
import random |
|||
from odoo import models |
|||
|
|||
|
|||
class ProjectProject(models.Model): |
|||
"""This class inherits from 'project.project' and adds custom functionality |
|||
to it.It provides methods to work with project data.""" |
|||
_inherit = 'project.project' |
|||
|
|||
def get_color_code(self): |
|||
"""Generate a random color code in hexadecimal format. |
|||
:return: A random color code in the format '#RRGGBB.'""" |
|||
color = f"#{random.randint(0, 0xFFFFFF):06x}" |
|||
return color |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 628 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 272 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 912 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 207 KiB |
After Width: | Height: | Size: 207 KiB |
After Width: | Height: | Size: 233 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 197 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 880 KiB |
After Width: | Height: | Size: 91 KiB |
@ -0,0 +1,841 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8"/> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
|||
<title> Project Dashboard</title> |
|||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" |
|||
rel="stylesheet"/> |
|||
<link rel="preconnect" href="https://fonts.googleapis.com"> |
|||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
|||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" |
|||
rel="stylesheet"> |
|||
<link rel="stylesheet" |
|||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"/> |
|||
<link rel="stylesheet" |
|||
href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css"/> |
|||
<style> |
|||
:root { |
|||
--primary-color: #7f54b3; |
|||
--bg-white: #fff; |
|||
--text-color: #121212; |
|||
--text-color-light: #64728f; |
|||
} |
|||
body{ |
|||
font-family: "Montserrat", sans-serif; |
|||
} |
|||
.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { |
|||
color: #121212; |
|||
font-family: Montserrat; |
|||
font-size: 16px !important; |
|||
font-weight: 500 !important; |
|||
border-radius: 30px; |
|||
line-height: normal; |
|||
text-transform: capitalize; |
|||
background-color: #F5F5F5; |
|||
border: none; |
|||
margin-bottom: 0; |
|||
padding: 12px 24px; |
|||
} |
|||
|
|||
.nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { |
|||
border-color: transparent; |
|||
isolation: isolate; |
|||
} |
|||
|
|||
.nav-tabs .nav-link:focus-visible { |
|||
border-color: transparent; |
|||
box-shadow: none; |
|||
} |
|||
|
|||
/* owl-carosel */ |
|||
.owl-carousel .owl-nav { |
|||
position: absolute; |
|||
top: 42%; |
|||
width: 100%; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
transform: translateY(-42%); |
|||
} |
|||
|
|||
.owl-carousel .owl-nav button.owl-prev { |
|||
position: absolute; |
|||
right: -36px; |
|||
font-size: 28px; |
|||
background-color: #e4e4e4; |
|||
border-radius: 20px; |
|||
width: 40px; |
|||
height: 40px; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.owl-carousel .owl-nav button.owl-next { |
|||
position: absolute; |
|||
left: -36px; |
|||
font-size: 28px; |
|||
background-color: #e4e4e4; |
|||
border-radius: 20px; |
|||
width: 40px; |
|||
height: 40px; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
|
|||
} |
|||
|
|||
</style> |
|||
</head> |
|||
<body> |
|||
<!-- overview --> |
|||
<div class="container"> |
|||
<div class="my-5"> |
|||
<!-- button tab --> |
|||
<!-- --> |
|||
<!-- version support --> |
|||
<div class="my-3 d-flex align-items-center justify-content-end"> |
|||
<div class="text-center" |
|||
style="background-color:#017E84 !important; font-size:0.8rem !important; color:#fff !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width:120px !important"> |
|||
Community |
|||
</div> |
|||
<div class="text-center" |
|||
style="background-color:#875A7B !important; color:#fff !important; font-size:0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width:120px !important"> |
|||
Enterprise |
|||
</div> |
|||
<div class="text-center" |
|||
style="background-color:#7C7BAD !important; color:#fff !important; font-size:0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width:120px !important"> |
|||
Odoo.sh |
|||
</div> |
|||
</div> |
|||
<div class="tab-content" id="myTabContent"> |
|||
<!-- description --> |
|||
<div class="tab-pane fade show active" id="home" role="tabpanel" |
|||
aria-labelledby="home-tab"> |
|||
<div class="position-relative" |
|||
style="border-radius: 16px; background: #f8f8f8; padding: 20px 0;"> |
|||
<div class="row " style=" |
|||
padding: 2rem 0rem 0 !important; |
|||
"> |
|||
<div class="col-lg-8 mx-auto gap-4 d-flex flex-column align-items-center"> |
|||
<p class="my-1 text-center text-uppercase" |
|||
style=" |
|||
letter-spacing: 4px !important; |
|||
color: #7f54b3; |
|||
font-weight: bold; |
|||
text-align: center; |
|||
font-size: 14px; |
|||
font-weight: 600; |
|||
line-height: 15.96px; |
|||
text-transform: uppercase; |
|||
"> |
|||
In This Dashboard, You Can Get a Detailed View for Project. |
|||
</p> |
|||
<h1 class="text-center text-uppercase my-0" |
|||
style=" |
|||
color: #121212; |
|||
font-size: 46px; |
|||
font-weight: 700; |
|||
line-height: normal; |
|||
"> Project Dashboard</span> |
|||
</h1> |
|||
</div> |
|||
<div class="col-lg-12 d-flex justify-content-center align-items-center" |
|||
style="margin: 3rem 0;"> |
|||
<img src="./assets/icons/brand-pair.svg" |
|||
width="100%" |
|||
height="auto" |
|||
style="width: 50%" |
|||
class="img-responsive"/> |
|||
</div> |
|||
<div class="col-md-12 text-center"> |
|||
<a href="mailto:odoo@cybrosys.com" |
|||
target="_blank" |
|||
style="background-color: transparent;border-radius: 35px; |
|||
font-family: Montserrat; |
|||
display: inline-block; |
|||
padding: 7px 33px; |
|||
border: 1px solid #7f54b3; |
|||
color: #7f54b3; |
|||
text-decoration: none; |
|||
" |
|||
class="mx-1 mb-2 deep-1 deep_hover"> |
|||
<img class="img" |
|||
style="width: 24px" |
|||
src="./assets/icons/mail.svg"/> |
|||
<span class="pl-2" |
|||
style=" font-size: 16px; vertical-align: middle" |
|||
>Email Us</span |
|||
> |
|||
</a> |
|||
<a href="skype:cybroopenerp?chat" |
|||
target="_blank" |
|||
style=" |
|||
background-color: #7f289b; |
|||
font-family: Montserrat; |
|||
display: inline-block; |
|||
padding: 7px 33px; |
|||
border: 1px solid #7f289b; |
|||
border-radius: 35px; |
|||
text-decoration: none; |
|||
" |
|||
class="mx-1 mb-2 deep-1 deep_hover"> |
|||
<img |
|||
class="img" |
|||
style="width: 24px" |
|||
src="./assets/icons/skype-fill.svg" |
|||
/> |
|||
<span |
|||
class="pl-2" |
|||
style="color: #fff; font-size: 16px; vertical-align: middle" |
|||
>Skype Us</span |
|||
> |
|||
</a> |
|||
</div> |
|||
<div class="d-flex justify-content-center mt-2"> |
|||
<img src="./assets/icons/hero.gif" |
|||
class="w-100" |
|||
style="z-index: 3; height: auto;"> |
|||
</div> |
|||
</div> |
|||
<div class="position-absolute bottom-0" |
|||
style="z-index: 1; width: 100%;"> |
|||
<img src="./assets/icons/banner-bg.svg" |
|||
class="img-fluid w-100"> |
|||
</div> |
|||
<div class="position-absolute bottom-0 end-0" |
|||
style=" z-index: 2;"> |
|||
<img src="./assets/icons/patter.svg"> |
|||
</div> |
|||
</div> |
|||
<!-- key-highlight --> |
|||
<div class="" style="border-radius: 16px; |
|||
padding: 60px 40px; |
|||
border: 1px solid #EBEEF2; |
|||
background: #F5F5F7; |
|||
box-shadow: 0px 5px 20px -11px rgba(0, 0, 0, 0.25); "> |
|||
<div class="row"> |
|||
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center"> |
|||
<h2 style=" color: #121212; |
|||
text-align: center; |
|||
font-size: 40px; |
|||
font-weight: 700; |
|||
text-transform: uppercase; padding-bottom: 50px;">Key |
|||
Highlights</h2> |
|||
</div> |
|||
<div class="col-lg-4"> |
|||
<div class="mb-4 d-flex flex-column justify-content-center gap-3" |
|||
style="border-radius: 12px; border: 1px solid #B6BCCD; |
|||
background: #FFF;padding:32px "> |
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#7847D9 !important; border-radius:8px !important; height:42px; width:42px"> |
|||
<img src="./assets/icons/feature-icon.svg" |
|||
class="img-responsive" height="26px" |
|||
width="26px"> |
|||
</div> |
|||
<h5 class="m-0" |
|||
style="color:#000 !important; font-weight:bold"> |
|||
Dashboard view for Project module |
|||
</h5> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-4"> |
|||
<div class="mb-4 d-flex flex-column justify-content-center gap-3" |
|||
style="border-radius: 12px; |
|||
border: 1px solid #B6BCCD; |
|||
background: #FFF;padding:32px "> |
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#7847D9 !important; border-radius:8px !important; height:42px; width:42px"> |
|||
<img src="./assets/icons/feature-icon.svg" |
|||
class="img-responsive" height="26px" |
|||
width="26px"> |
|||
</div> |
|||
<h5 class="m-0" |
|||
style="color:#000 !important; font-weight:bold"> |
|||
Graphs Are Included in the view |
|||
</h5> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!--code --> |
|||
<div class="my-5"> |
|||
<div class="position-relative" style=" padding: 5rem 4rem 5rem 4rem; background-color: #0A1425; border-radius: 12px;"> |
|||
<div class="d-flex flex-column gap-4"> |
|||
<span class="wrapper-subtitle" |
|||
style="font-size: 40px; font-weight: 700; color: #fff;line-height: 60px; text-transform: capitalize; width: 450px; font-family: Montserrat;">Project Dashboard</span> |
|||
<h3 class="wrapper-details" |
|||
style="font-size: 20px; font-weight: 400; color: #fff; line-height: 32px; "> |
|||
Are you ready to make your business more |
|||
organized? |
|||
<br> Improve now! |
|||
</h3> |
|||
<div class="d-flex gap-3"> |
|||
<a href="mailto:odoo@cybrosys.com" |
|||
class="shop-btn" style="cursor: pointer; border-radius: 16px; display: flex; justify-content: center; align-items: center; gap: 7px; |
|||
border: 1px solid #ffffff33; |
|||
background-color: #ffffff14; |
|||
backdrop-filter: blur(10px); color: #fff; padding: 12px 16px 12px 16px; text-decoration: none;"> |
|||
<span style="border-radius: 12px; |
|||
background-color: #ffffff1a; |
|||
backdrop-filter: blur(6px);padding: 12px; "> |
|||
<img src="./assets/icons/banner-mail.svg"> |
|||
</span> |
|||
<span style="font-weight: 500;font-family: Montserrat;">odoo@cybrosys.com</span> |
|||
</a> |
|||
<a href="tel:+91 9074270811" class="shop-btn" |
|||
style="cursor: pointer; border-radius: 16px; display: flex; justify-content: center; align-items: center; gap: 7px; |
|||
border: 1px solid #ffffff33; |
|||
background-color: #ffffff14; |
|||
backdrop-filter: blur(10px); color: #fff; padding: 12px 22px 12px 18px; text-decoration: none;"> |
|||
<span style="border-radius: 12px; |
|||
background-color: #ffffff1a; |
|||
backdrop-filter: blur(6px);padding: 12px;"> |
|||
<img src="./assets/icons/banner-call.svg"> |
|||
</span> |
|||
<span style="font-weight: 500;font-family: Montserrat;">+91 9074270811</span> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="position-absolute bottom-0 end-0"> |
|||
<img src="./assets/icons/banner-pattern.svg" |
|||
style="width: 540px;"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- end-code --> |
|||
<!-- --> |
|||
<!-- screenshot and other --> |
|||
|
|||
<div class="mb-4 bg-white" |
|||
style=" border: 1px solid #EBEEF2; border-radius: 6px; box-shadow: 0px 8px 20px -4px rgba(0, 0, 0, 0.10); border: 1px solid #EBEEF2;"> |
|||
<div> |
|||
<ul class="nav nav-tabs justify-content-center bg-white py-2" |
|||
id="myTab" role="tablist" |
|||
style="border-radius: 6px 6px 0 0;"> |
|||
<li class="nav-item"> |
|||
<a aria-controls="overview" |
|||
aria-bs-selected="true" |
|||
class="nav-link active" data-bs-toggle="tab" |
|||
href="#overview" id="overview-tab" role="tab" |
|||
style="color:#121212; font-weight:500; font-size:16px"> |
|||
Screenshots</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a aria-controls="feature" |
|||
aria-bs-selected="false" |
|||
class="nav-link py-2" data-bs-toggle="tab" |
|||
href="#feature" id="feature-tab" role="tab" |
|||
style="color:#121212; font-weight:500; font-size:16px">Features</a> |
|||
</li> |
|||
|
|||
<li class="nav-item"> |
|||
<a aria-controls="releases" |
|||
aria-bs-selected="false" class="nav-link" |
|||
data-bs-toggle="tab" href="#releases" |
|||
id="releases-tab" role="tab" |
|||
style="color:#121212; font-weight:500; font-size:16px">Releases</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<div class="tab-content p-md-5 p-2 py-3" id="myTabContent"> |
|||
<div aria-labelledby="overview-tab" |
|||
class="tab-pane fade show active" id="overview" |
|||
role="tabpanel"> |
|||
<div class="position-relative mb-4" |
|||
style="border-radius:10px"> |
|||
<img alt="acc_bg" |
|||
class="w-100 h-100 position-absolute img-fluid left_0" |
|||
loading="lazy" |
|||
src="//apps.odoocdn.com/apps/assets/17.0/ks_dashboard_ninja/ai-img/o3.png?007008f" |
|||
style=""> |
|||
</div> |
|||
<!-- screenshots section--> |
|||
<div class="position-relative mb-4" |
|||
style="border-radius:10px; background-color:#f4f4f4"> |
|||
<div class="p-md-5 p-3 position-relative"> |
|||
<div class="row"> |
|||
<div class="col-md-12"> |
|||
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px; |
|||
font-weight: 700;"> |
|||
<span style="color:#121212; font-size:calc(1.1rem + 1vw)">Different Types of Graphs. |
|||
</span> |
|||
<!-- <span style="color: var(--primary-color); font-size:calc(1.1rem + 1vw)"> Menu</span>--> |
|||
</h1> |
|||
</div> |
|||
<div class="col-md-12 mb-4"> |
|||
<p style="font-weight:400; font-size:16px; line-height:150%; text-align:center; color:var(--text-color-light)"> |
|||
Project Dashboard has different types of Graphs that will give you a complete analyzing of the Project module. |
|||
</p> |
|||
</div> |
|||
<div class="col-md-12 text-center"> |
|||
<div class="d-inline-block p-3 shadow-sm" |
|||
style="background-color:#fff; border-radius:10px"> |
|||
<img alt="" class="img-fluid" |
|||
loading="lazy" |
|||
src="./assets/screenshots/1.png" |
|||
style="min-height: 1px;"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="position-relative mb-4" |
|||
style="border-radius:10px; background-color:#f4f4f4"> |
|||
<div class="p-md-5 p-3 position-relative"> |
|||
<div class="row"> |
|||
<div class="col-md-12"> |
|||
</div> |
|||
|
|||
<div class="col-md-12 text-center"> |
|||
<div class="d-inline-block p-3 shadow-sm" |
|||
style="background-color:#fff; border-radius:10px"> |
|||
<img alt="" class="img-fluid" |
|||
loading="lazy" |
|||
src="./assets/screenshots/2.png" |
|||
style="min-height: 1px;"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="position-relative mb-4" |
|||
style="border-radius:10px; background-color:#f4f4f4"> |
|||
<div class="p-md-5 p-3 position-relative"> |
|||
<div class="row"> |
|||
<div class="col-md-12"> |
|||
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px; |
|||
font-weight: 700;"> |
|||
|
|||
</h1> |
|||
</div> |
|||
<div class="col-md-12 mb-4"> |
|||
</div> |
|||
<div class="col-md-12 text-center"> |
|||
<div class="d-inline-block p-3 shadow-sm" |
|||
style="background-color:#fff; border-radius:10px"> |
|||
<img alt="" class="img-fluid" |
|||
loading="lazy" |
|||
src="./assets/screenshots/3.png" |
|||
style="min-height: 1px;"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="position-relative mb-4" |
|||
style="border-radius:10px; background-color:#f4f4f4"> |
|||
<div class="p-md-5 p-3 position-relative"> |
|||
<div class="row"> |
|||
<div class="col-md-12"> |
|||
<h1 style="font-weight:bold; font-size:calc(1.1rem + 1vw); line-height:120%; text-align:center; text-transform:capitalize; font-size: 40px; |
|||
font-weight: 700;"> |
|||
<span style="color:#121212; font-size:calc(1.1rem + 1vw)">Project Table |
|||
</span> |
|||
</h1> |
|||
</div> |
|||
<div class="col-md-12 mb-4"> |
|||
<p style="font-weight:400; font-size:16px; line-height:150%; text-align:center; color:var(--text-color-light)"> |
|||
User can see all Project ad its current Status. |
|||
</p> |
|||
</div> |
|||
<div class="col-md-12 text-center"> |
|||
<div class="d-inline-block p-3 shadow-sm" |
|||
style="background-color:#fff; border-radius:10px"> |
|||
<img alt="" class="img-fluid" |
|||
loading="lazy" |
|||
src="./assets/screenshots/6.png" |
|||
style="min-height: 1px;"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div aria-labelledby="feature-tab" |
|||
class="tab-pane fade show py-1" id="feature" |
|||
role="tabpanel"> |
|||
<div class="row py-4"> |
|||
<!-- Features Section --> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex flex-column align-items-start h-100" |
|||
style="padding:30px; border-radius:12px; background-color:#faf8ff"> |
|||
<div class="d-flex align-items-center justify-content-center"> |
|||
<div class="d-flex align-items-center justify-content-center " |
|||
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px"> |
|||
<i class="fa fa-star " |
|||
style="color:#fff; font-size:14px"></i> |
|||
</div> |
|||
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px"> |
|||
User can see all details about Project and Task through Graphs. </p> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex flex-column align-items-start h-100" |
|||
style="padding:30px; border-radius:12px; background-color:#faf8ff"> |
|||
<div class="d-flex align-items-center justify-content-center"> |
|||
<div class="d-flex align-items-center justify-content-center " |
|||
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px"> |
|||
<i class="fa fa-star " |
|||
style="color:#fff; font-size:14px"></i> |
|||
</div> |
|||
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px"> |
|||
User can see all details about Timesheet through Graphs. </p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex flex-column align-items-start h-100" |
|||
style="padding:30px; border-radius:12px; background-color:#faf8ff"> |
|||
<div class="d-flex align-items-center justify-content-center"> |
|||
<div class="d-flex align-items-center justify-content-center " |
|||
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px"> |
|||
<i class="fa fa-star " |
|||
style="color:#fff; font-size:14px"></i> |
|||
</div> |
|||
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px"> |
|||
User can see all Projects with Stages. </p> |
|||
</div> |
|||
<div class="ms-5"> |
|||
<p class="m-0" |
|||
style="color:#718096"> |
|||
</p> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="col-md-6 col-sm-12 p-3"> |
|||
<div class="d-flex flex-column align-items-start h-100" |
|||
style="padding:30px; border-radius:12px; background-color:#faf8ff"> |
|||
<div class="d-flex align-items-center justify-content-center"> |
|||
<div class="d-flex align-items-center justify-content-center " |
|||
style="width:36px; height:36px; border-radius:50%; background-color:#7847D9 ; margin-right:10px"> |
|||
<i class="fa fa-star " |
|||
style="color:#fff; font-size:14px"></i> |
|||
</div> |
|||
<p style="color:#1A202C; font-weight:600; font-size:1.2rem; margin-bottom:2px"> |
|||
User can use filter based on the Employee, Project and Dates. </p> |
|||
</div> |
|||
<div class="ms-5"> |
|||
<p class="m-0" |
|||
style="color:#718096"> |
|||
</p> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
<div aria-labelledby="releases-tab" |
|||
class="tab-pane fade show" id="releases" |
|||
role="tabpanel"> |
|||
<!-- Release Notes --> |
|||
<div class="row pt-5 m-0"> |
|||
<div class="col-md-3"> |
|||
<h4 style="font-size:16px; font-weight:600; color:#514F4F; margin:0; line-height:26px;"> |
|||
Latest Release 18.0.1.0.0 |
|||
</h4> |
|||
<span style="font-size:14px; color:#7A7979; display:block; margin-bottom:20px;"> |
|||
4th October, 2024 |
|||
</span> |
|||
</div> |
|||
<div class="col-md-8"> |
|||
<div style="padding:0 0 40px"> |
|||
<div style="margin:0 0 10px"> |
|||
<div style="display:inline-block; padding:0px 8px; color:#514F4F; background-color:#FFD8D8; border-radius:20px"> |
|||
Add |
|||
</div> |
|||
</div> |
|||
<div class="d-flex m-0" |
|||
style="color:#7A7979;"> |
|||
<ul class="pl-3 mb-0"> |
|||
<li> |
|||
Initial commit for Project Dashboard |
|||
</li> |
|||
|
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<div style="padding:0 0 0; border-bottom:1px solid #E3E3E3"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- --> |
|||
<!-- related post --> |
|||
<!-- --> |
|||
<section class="oe_container mt32"> |
|||
<h2 style="color: #091E42;font-family: "Montserrat";text-align: center;margin: 25px auto;text-transform: uppercase;" class="oe_slogan"> |
|||
<b>Related Products</b> |
|||
</h2> |
|||
<div id="demo" class="row carousel slide mt64 mb32" data-bs-ride="carousel"> |
|||
<!-- The slideshow --> |
|||
<div class="carousel-inner"> |
|||
<div class="carousel-item active"> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/base_accounting_kit" target="_blank" style="color: #000; text-decoration: none;"> |
|||
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
|||
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/1.gif" /> |
|||
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
|||
Odoo 17 Full Accounting Kit</h4> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/ohrms_core" target="_blank" style="color: #000; text-decoration: none;"> |
|||
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
|||
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/2.gif" /> |
|||
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
|||
Open HRMS Core</h4> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/invoice_format_editor" target="_blank" style="color: #000; text-decoration: none;"> |
|||
<div style="border-radius: 6px;padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
|||
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/3.png"/> |
|||
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
|||
Odoo17 Invoice Format Editor </h4> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="carousel-item"> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/login_user_detail" target="_blank" style="color: #000; text-decoration: none;"> |
|||
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
|||
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/4.png" /> |
|||
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
|||
User Log Details</h4> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/product_barcode" target="_blank" style="color: #000; text-decoration: none;"> |
|||
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
|||
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/5.png" /> |
|||
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
|||
Odoo17 Product Barcode Generator </h4> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float: left; padding: 10px;"> |
|||
<a href="https://apps.odoo.com/apps/modules/17.0/whatsapp_redirect" target="_blank" style="color: #000; text-decoration: none;"> |
|||
<div style="border-radius: 6px; padding: 16px; border: 1px solid #cbcbcb;" class="shadow-sm"> |
|||
<img class="img img-responsive center-block" style=" max-width: 100%;" src="./assets/modules/6.jpg" /> |
|||
<h4 class="mt0 text-truncate" style="text-align:center;width:100% margin-bottom: 8px; font-weight: 600; padding-top: 16px; text-decoration:none;font-size: 18px; padding-bottom: 8px; margin-bottom: 0px"> |
|||
Send Whatsapp Message Odoo17</h4> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- Left and right controls --> |
|||
<a class="carousel-control-prev" href="#demo" data-bs-slide="prev" style="margin-left: -30px;width: 35px;color: #000;"> |
|||
<span class="carousel-control-prev-icon"> |
|||
<i class="fa fa-chevron-left" style="font-size:24px"></i> |
|||
</span> |
|||
</a> |
|||
<a class="carousel-control-next" href="#demo" data-bs-slide="next" style="margin-right: -30px;width: 35px;color: #000;"> |
|||
<span class="carousel-control-next-icon"> |
|||
<i class="fa fa-chevron-right" style="font-size:24px"></i> |
|||
</span> |
|||
</a> |
|||
</div> |
|||
</section> |
|||
<!-- service-section --> |
|||
|
|||
<section id="services" class="mt-5" style="border-radius: 16px; |
|||
border: 1px solid #EBEEF2; |
|||
background: var(--Neutral-N0, #FFF); |
|||
padding: 60px 40px; |
|||
box-shadow: 0px 5px 20px -11px rgba(0, 0, 0, 0.25);"> |
|||
<div class="text-center mt-4"><h3 class="mb-0" style="color: #000; |
|||
text-align: center; |
|||
font-family: Montserrat; |
|||
font-size: 40px; |
|||
font-style: normal; |
|||
font-weight: 700; |
|||
line-height: normal; |
|||
text-transform: uppercase; |
|||
padding-bottom: 50px;"> |
|||
Our Services</h3></div> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-3 col-sm-12 mb-3"> |
|||
<a href="#" style="text-decoration:none"> |
|||
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
|||
style="font-size:25px; font-weight:bold;background-color:#FFE2E5; margin:auto; gap: 16px; border-radius: 8px;"> |
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#FA5A7D; border-radius:50%; height:56px; width:56px"> |
|||
<img src="./assets/icons/gear.svg" |
|||
class="img-responsive" |
|||
height="28px" width="28px"> |
|||
</div> |
|||
<span style="font-size: 18px; |
|||
color: var(--text-color); |
|||
font-weight: 600;"> Odoo Customization</span> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-lg-3 col-sm-12 mb-3"> |
|||
<a href="#" style="text-decoration:none"> |
|||
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
|||
style="font-size:25px; font-weight:bold;background-color:#FFF4DE; margin:auto; gap: 16px; border-radius: 8px;"> |
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#FF947A; border-radius:50%; height:56px; width:56px"> |
|||
<img src="./assets/icons/wrench-icon.svg" |
|||
class="img-responsive" |
|||
height="28px" width="28px"> |
|||
</div> |
|||
<span style="font-size: 18px; |
|||
color: var(--text-color); |
|||
font-weight: 600;"> Odoo Implementation</span> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-lg-3 col-sm-12 mb-3"> |
|||
<a href="#" style="text-decoration:none"> |
|||
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
|||
style="font-size:25px; font-weight:bold;background-color:#DCFCE7; margin:auto; gap: 16px; border-radius: 8px;"> |
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#3CD856; border-radius:50%; height:56px; width:56px"> |
|||
<img src="./assets/icons/life-ring-icon.svg" |
|||
class="img-responsive" |
|||
height="28px" width="28px"> |
|||
</div> |
|||
<span style="font-size: 18px; |
|||
color: var(--text-color); |
|||
font-weight: 600;">Odoo Support</span> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-lg-3 col-sm-12 mb-3"> |
|||
<a href="#" style="text-decoration:none"> |
|||
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
|||
style="font-size:25px; font-weight:bold;background-color:#F3E8FF; margin:auto; gap: 16px; border-radius: 8px;"> |
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#BF83FF; border-radius:50%; height:56px; width:56px"> |
|||
<img src="./assets/icons/arrows-repeat.svg" |
|||
class="img-responsive" |
|||
height="28px" width="28px"> |
|||
</div> |
|||
<span style="font-size: 18px; |
|||
color: var(--text-color); |
|||
font-weight: 600;">Odoo Migration</span> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-lg-3 col-sm-12 mb-3"> |
|||
<a href="#" style="text-decoration:none"> |
|||
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
|||
style="font-size:25px; font-weight:bold;background-color:#F1F9FF; margin:auto; gap: 16px; border-radius: 8px;"> |
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#01649C; border-radius:50%; height:56px; width:56px"> |
|||
<img src="./assets/icons/puzzle-piece-icon.svg" |
|||
class="img-responsive" |
|||
height="28px" width="28px"> |
|||
</div> |
|||
<span style="font-size: 18px; |
|||
color: var(--text-color); |
|||
font-weight: 600;">Odoo integration</span> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-lg-3 col-sm-12 mb-3"> |
|||
<a href="#" style="text-decoration:none"> |
|||
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
|||
style="font-size:25px; font-weight:bold;background-color:#EDF8ED; margin:auto; gap: 16px; border-radius: 8px;"> |
|||
|
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#69CC70; border-radius:50%; height:56px; width:56px"> |
|||
<img src="./assets/icons/odoo-consultancy.svg" |
|||
class="img-responsive" |
|||
height="28px" width="28px"> |
|||
</div> |
|||
<span style="font-size: 18px; |
|||
color: var(--text-color); |
|||
font-weight: 600;">Odoo Consultancy</span> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-lg-3 col-sm-12 mb-3"> |
|||
<a href="#" style="text-decoration:none"> |
|||
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
|||
style="font-size:25px; font-weight:bold;background-color:#F1F6FF; margin:auto; gap: 16px; border-radius: 8px;"> |
|||
|
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#2E4556; border-radius:50%; height:56px; width:56px"> |
|||
<img src="./assets/icons/odoo-licencing.svg" |
|||
class="img-responsive" |
|||
height="28px" width="28px"> |
|||
</div> |
|||
<span style="font-size: 18px; |
|||
color: var(--text-color); |
|||
font-weight: 600;">Odoo Licensing</span> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-lg-3 col-sm-12 mb-3"> |
|||
<a href="#" style="text-decoration:none"> |
|||
<div class="btn-lg btn-block p-4 mb-2 d-flex flex-column justify-content-center align-items-center" |
|||
style="font-size:25px; font-weight:bold;background-color:#FAF6EA; margin:auto; gap: 16px; border-radius: 8px;"> |
|||
|
|||
<div class="d-flex justify-content-center align-items-center" |
|||
style="background-color:#FCD12C; border-radius:50%; height:56px; width:56px"> |
|||
<img src="./assets/icons/hire-odoo.svg" |
|||
class="img-responsive" |
|||
height="28px" width="28px"> |
|||
</div> |
|||
<span style="font-size: 18px; |
|||
color: var(--text-color); |
|||
font-weight: 600;">Hire Odoo Developer</span> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- licence --> |
|||
<div class="tab-pane fade" id="profile" role="tabpanel" |
|||
aria-labelledby="profile-tab"> |
|||
<div class="px-5"> |
|||
.... |
|||
</div> |
|||
</div> |
|||
<!-- --> |
|||
</div> |
|||
</section> |
|||
<!-- --> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script> |
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" |
|||
integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" |
|||
crossorigin="anonymous" referrerpolicy="no-referrer"></script> |
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js"></script> |
|||
<script> |
|||
$('.owl-carousel').owlCarousel({ |
|||
rtl: true, |
|||
loop: true, |
|||
margin: 10, |
|||
nav: true, |
|||
responsive: { |
|||
0: { |
|||
items: 1 |
|||
}, |
|||
600: { |
|||
items: 3 |
|||
}, |
|||
1000: { |
|||
items: 3 |
|||
} |
|||
} |
|||
}) |
|||
</script> |
|||
</html> |
@ -0,0 +1,426 @@ |
|||
.oh-card h4 { |
|||
font-size: 1.1rem; |
|||
} |
|||
|
|||
.stat-icon { |
|||
display: inline-block; |
|||
} |
|||
|
|||
.stat-widget-one .stat-icon { |
|||
vertical-align: top; |
|||
margin: auto; |
|||
width: 100%; |
|||
} |
|||
|
|||
.stat-widget-one .stat-icon i { |
|||
font-size: 30px; |
|||
font-weight: 900; |
|||
display: inline-block; |
|||
color: #01c490; |
|||
} |
|||
|
|||
.stat-widget-one .stat-text { |
|||
font-size: 14px; |
|||
color: #868e96; |
|||
font-weight: bold; |
|||
|
|||
} |
|||
|
|||
.stat-widget-one .stat-digit { |
|||
font-size: 24px; |
|||
color: #02448b; |
|||
} |
|||
|
|||
.stat_count { |
|||
font-size: 28px !important; |
|||
} |
|||
|
|||
body .text-color { |
|||
color: #00438b; |
|||
} |
|||
/* Leave graph */ |
|||
path { |
|||
stroke: #fff; |
|||
} |
|||
|
|||
path:hover { |
|||
opacity: 0.9; |
|||
} |
|||
|
|||
rect:hover { |
|||
fill: #934da5; |
|||
} |
|||
|
|||
.axis { |
|||
font: 10px sans-serif; |
|||
} |
|||
|
|||
.legend tr { |
|||
border-bottom: 1px solid grey; |
|||
} |
|||
|
|||
.legend tr:first-child { |
|||
border-top: 1px solid grey; |
|||
} |
|||
|
|||
.axis path, |
|||
.axis line { |
|||
fill: none; |
|||
stroke: #000; |
|||
shape-rendering: crispEdges; |
|||
} |
|||
|
|||
.x.axis path { |
|||
display: none; |
|||
} |
|||
|
|||
.legend { |
|||
border-collapse: collapse; |
|||
border-spacing: 0px; |
|||
display: inline-block; |
|||
} |
|||
|
|||
.legend td, |
|||
.legend .legend_col { |
|||
padding: 4px 5px; |
|||
vertical-align: bottom; |
|||
} |
|||
|
|||
.legendFreq, |
|||
.legendPerc { |
|||
align: right; |
|||
width: 50px; |
|||
} |
|||
|
|||
/* Leave broadfactor graph */ |
|||
|
|||
.broad_factor_graph .axis path, |
|||
.broad_factor_graph .axis line { |
|||
fill: none; |
|||
stroke: black; |
|||
shape-rendering: crispEdges; |
|||
} |
|||
|
|||
.broad_factor_graph .axis text { |
|||
font-family: sans-serif; |
|||
font-size: 11px; |
|||
} |
|||
|
|||
.broad_factor_graph rect { |
|||
-moz-transition: all 0.3s; |
|||
-webkit-transition: all 0.3s; |
|||
-o-transition: all 0.3s; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
.broad_factor_graph rect:hover { |
|||
fill: #ff618a; |
|||
} |
|||
|
|||
#broad_factor_pdf { |
|||
background-color: #ffffff; |
|||
border: 0; |
|||
color: #000000; |
|||
float: right; |
|||
} |
|||
|
|||
#broad_factor_pdf i { |
|||
color: red; |
|||
} |
|||
|
|||
|
|||
/*=====================New Dashboard===========================*/ |
|||
|
|||
.oh_dashboards { |
|||
background-color: #f8faff !important; |
|||
padding: 0px !important; |
|||
|
|||
} |
|||
|
|||
.container-fluid.o_hr_dashboard { |
|||
padding: 0px !important; |
|||
} |
|||
|
|||
.employee-prof { |
|||
|
|||
padding: 0px; |
|||
height: 100%; |
|||
background-color: #3e6282; |
|||
/*background-image: linear-gradient(180deg, #3e6282, #41666f);*/ |
|||
position: fixed; |
|||
/*z-index: 999;*/ |
|||
} |
|||
|
|||
.employee-prof .oh-card:hover { |
|||
|
|||
transform: none !important; |
|||
box-shadow: none !important; |
|||
|
|||
} |
|||
|
|||
.oh-card { |
|||
|
|||
padding-top: 0px; |
|||
padding: 0px; |
|||
margin-bottom: 1.5rem; |
|||
border-radius: 0px; |
|||
box-shadow: none; |
|||
background: none; |
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
|
|||
} |
|||
|
|||
.oh-card:hover { |
|||
|
|||
transform: translateY(-2px) translateZ(0) !important; |
|||
box-shadow: 0 10px 10px 0 rgba(62, 57, 107, 0.12), 0 0 0 transparent !important; |
|||
|
|||
} |
|||
|
|||
.media-body.employee-name { |
|||
|
|||
background: #466b8d; |
|||
float: left; |
|||
margin: 0; |
|||
width: 100% |
|||
} |
|||
|
|||
.oh-payslip { |
|||
|
|||
margin-top: 1.5%; |
|||
|
|||
} |
|||
|
|||
.oh-payslip .stat-icon { |
|||
|
|||
width: 30%; |
|||
height: 85px; |
|||
text-align: center; |
|||
padding-top: 2%; |
|||
color: #fff; |
|||
|
|||
} |
|||
|
|||
.oh-payslip .oh-card { |
|||
|
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.06); |
|||
|
|||
} |
|||
|
|||
.stat-widget-one .stat-text { |
|||
|
|||
font-size: 14px; |
|||
color: #ff8762; |
|||
margin-top: 2.3rem; |
|||
margin-left: 1rem; |
|||
|
|||
} |
|||
|
|||
.stat-widget-one .stat-digit { |
|||
|
|||
font-size: 17px; |
|||
color: #000; |
|||
margin-left: 1rem; |
|||
padding-left: 26px; |
|||
|
|||
} |
|||
|
|||
.stat-widget-one .stat-icon i { |
|||
font-size: 32px; |
|||
display: inline-block; |
|||
color: #000; |
|||
top: 16px; |
|||
position: relative; |
|||
} |
|||
|
|||
.stat-widget-one { |
|||
|
|||
background-color: white; |
|||
text-align: inherit !important; |
|||
|
|||
} |
|||
|
|||
.stat-widget-one { |
|||
width: 100%; |
|||
} |
|||
|
|||
.oh-payslip .stat-icon { |
|||
|
|||
width: 30%; |
|||
height: 85px; |
|||
text-align: center; |
|||
padding-top: 2%; |
|||
|
|||
} |
|||
|
|||
|
|||
h4 .stat-count { |
|||
font-size: 17px; |
|||
text-align: center; |
|||
color: #000 !important; |
|||
margin-top: 0px; |
|||
width: 100%; |
|||
float: left; |
|||
margin: 0; |
|||
} |
|||
|
|||
.hr-chart-1 { |
|||
margin: 15px 0px; |
|||
background: #fff; |
|||
padding: 0px !important; |
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.06); |
|||
} |
|||
|
|||
.hr-chart-1:hover { |
|||
transform: translateY(-2px) translateZ(0) !important; |
|||
box-shadow: 0 10px 10px 0 rgba(62, 57, 107, 0.12), 0 0 0 transparent !important; |
|||
} |
|||
|
|||
.stat-head { |
|||
text-align: center !important; |
|||
font-weight: 300; |
|||
font-size: 21px; |
|||
margin-bottom: 12px; |
|||
margin-left: 2px; |
|||
} |
|||
|
|||
|
|||
.hr_birthday { |
|||
font-size: 17px; |
|||
text-align: center; |
|||
padding: 20px 0; |
|||
color: #00438b; |
|||
font-weight: 300; |
|||
} |
|||
|
|||
.hr_notification img { |
|||
width: 40px; |
|||
height: 40px; |
|||
border-radius: 100%; |
|||
} |
|||
|
|||
.hr_notification { |
|||
background: #fff; |
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.06); |
|||
height: 757px; |
|||
margin-bottom: 15px; |
|||
} |
|||
|
|||
.hr_notification .media { |
|||
border-bottom: 1px solid #e6e6e6; |
|||
padding-bottom: 6px; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
.hr_notification .text-color.display-6 { |
|||
margin: 0px 0 3px; |
|||
color: #2d2d2d; |
|||
} |
|||
|
|||
.hr_notification p { |
|||
margin: 0 0 1px; |
|||
color: #666; |
|||
font-size: 10px; |
|||
} |
|||
|
|||
.hr_notification_head { |
|||
font-size: 17px; |
|||
text-align: center; |
|||
padding: 50px 0; |
|||
color: #fff; |
|||
font-weight: 300; |
|||
background: #5ebade; |
|||
margin-bottom: 9px; |
|||
} |
|||
|
|||
.hr-chart-1 { |
|||
margin: 15px 0px; |
|||
background: #fff; |
|||
padding: 0px !important; |
|||
padding-top: 0px; |
|||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|||
will-change: transform, box-shadow; |
|||
box-shadow: 0 10px 40px 0 rgba(62, 57, 107, 0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.06); |
|||
padding-top: 3px !important; |
|||
} |
|||
|
|||
/* width */ |
|||
.hr_notification::-webkit-scrollbar { |
|||
width: 4px; |
|||
} |
|||
|
|||
/* Track */ |
|||
.hr_notification::-webkit-scrollbar-track { |
|||
background: #f1f1f1; |
|||
} |
|||
|
|||
/* Handle */ |
|||
.hr_notification::-webkit-scrollbar-thumb { |
|||
background: #495057; |
|||
; |
|||
} |
|||
|
|||
/* Handle on hover */ |
|||
.hr_notification::-webkit-scrollbar-thumb:hover { |
|||
background: #598da1; |
|||
} |
|||
|
|||
.oh-card-body { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.text-align { |
|||
margin-left: 17px; |
|||
} |
|||
|
|||
.inner_select { |
|||
min-width: 150px |
|||
} |
|||
|
|||
#table_status { |
|||
width: 90%; |
|||
margin-left: 5%; |
|||
font-family: Arial, Helvetica, sans-serif; |
|||
} |
|||
|
|||
#table_status tr:nth-child(even) { |
|||
background-color: #f2f2f2; |
|||
} |
|||
|
|||
#table_status tr:hover { |
|||
background-color: #ddd; |
|||
} |
|||
|
|||
.fleet-pill { |
|||
align-items: center; |
|||
font-family: "Open Sans", Arial, Verdana, sans-serif; |
|||
font-weight: bold; |
|||
font-size: 11px; |
|||
display: inline-block; |
|||
height: 100%; |
|||
white-space: nowrap; |
|||
width: auto; |
|||
position: relative; |
|||
border-radius: 100px; |
|||
line-height: 1; |
|||
overflow: hidden; |
|||
padding: 0px 8px 0px 7px; |
|||
text-overflow: ellipsis; |
|||
line-height: 1.25rem; |
|||
color: #fff; |
|||
word-break: break-word; |
|||
background: #0253e8; |
|||
} |
|||
|
|||
.inner_select p { |
|||
margin-left: 20px |
|||
} |
@ -0,0 +1,460 @@ |
|||
/** @odoo-module */ |
|||
import { registry} from '@web/core/registry'; |
|||
import { useService } from "@web/core/utils/hooks"; |
|||
const { Component, onWillStart} = owl |
|||
import { rpc } from "@web/core/network/rpc"; |
|||
import { _t } from "@web/core/l10n/translation"; |
|||
import { onMounted, useRef, useState} from "@odoo/owl"; |
|||
|
|||
export class ProjectDashboard extends Component { |
|||
/** |
|||
* Setup method to initialize required services and register event handlers. |
|||
*/ |
|||
setup() { |
|||
this.action = useService("action"); |
|||
this.orm = useService("orm"); |
|||
this.project_doughnut = useRef("project_doughnut"); |
|||
this.project_selection = useRef("project_selection"); |
|||
this.start_date = useRef("start_date"); |
|||
this.end_date = useRef("end_date"); |
|||
this.tot_project = useRef("tot_project"); |
|||
this.tot_employee = useRef("tot_employee"); |
|||
this.tot_hrs = useRef("tot_hrs"); |
|||
this.tot_margin = useRef("tot_margin"); |
|||
this.total_task = useRef("tot_task"); |
|||
this.total_so = useRef("tot_so"); |
|||
this.employee_selection = useRef("employee_selection"); |
|||
this.top_selling_employees = useRef("top_selling_employees"); |
|||
this.state = useState({ |
|||
projects : '', |
|||
employees: "", |
|||
}); |
|||
this.rpc = this.env.services.rpc |
|||
onWillStart(async () => { |
|||
await this.willStart(); |
|||
}); |
|||
onMounted(async () => { |
|||
await this.mounted() |
|||
}); |
|||
} |
|||
/** |
|||
* Event handler for the 'onWillStart' event. |
|||
*/ |
|||
async willStart() { |
|||
await this.fetch_data(); |
|||
} |
|||
/** |
|||
* Event handler for the 'onMounted' event. |
|||
* Renders various components and charts after fetching data. |
|||
*/ |
|||
async mounted() { |
|||
// Render other components after fetching data
|
|||
this.render_project_task(); |
|||
this.render_top_employees_graph(); |
|||
this.render_filter(); |
|||
} |
|||
/** |
|||
* Render the project task chart. |
|||
*/ |
|||
async render_project_task() { |
|||
var datas = await rpc("/project/task/count") |
|||
var ctx = this.project_doughnut; |
|||
const chart = new Chart(this.project_doughnut.el, { |
|||
type: "doughnut", |
|||
data: { |
|||
labels: datas.project, |
|||
datasets: [{ |
|||
backgroundColor: datas.color, |
|||
data: datas.task |
|||
}] |
|||
}, |
|||
options: { |
|||
legend: { |
|||
position: 'left' |
|||
}, |
|||
cutoutPercentage: 40, |
|||
responsive: true, |
|||
} |
|||
}); |
|||
} |
|||
/** |
|||
function for getting values to employee graph |
|||
*/ |
|||
async render_top_employees_graph() { |
|||
var ctx = this.top_selling_employees |
|||
var arrays = await rpc('/employee/timesheet') |
|||
var data = { |
|||
labels: arrays[1], |
|||
datasets: [{ |
|||
label: "Hours Spent", |
|||
data: arrays[0], |
|||
backgroundColor: [ |
|||
"rgba(190, 27, 75,1)", |
|||
"rgba(31, 241, 91,1)", |
|||
"rgba(103, 23, 252,1)", |
|||
"rgba(158, 106, 198,1)", |
|||
"rgba(250, 217, 105,1)", |
|||
"rgba(255, 98, 31,1)", |
|||
"rgba(255, 31, 188,1)", |
|||
"rgba(75, 192, 192,1)", |
|||
"rgba(153, 102, 255,1)", |
|||
"rgba(10,20,30,1)" |
|||
], |
|||
borderColor: [ |
|||
"rgba(190, 27, 75, 0.2)", |
|||
"rgba(190, 223, 122, 0.2)", |
|||
"rgba(103, 23, 252, 0.2)", |
|||
"rgba(158, 106, 198, 0.2)", |
|||
"rgba(250, 217, 105, 0.2)", |
|||
"rgba(255, 98, 31, 0.2)", |
|||
"rgba(255, 31, 188, 0.2)", |
|||
"rgba(75, 192, 192, 0.2)", |
|||
"rgba(153, 102, 255, 0.2)", |
|||
"rgba(10,20,30,0.3)" |
|||
], |
|||
borderWidth: 1 |
|||
}, |
|||
] |
|||
}; |
|||
//options
|
|||
var options = { |
|||
responsive: true, |
|||
title: { |
|||
display: true, |
|||
position: "top", |
|||
text: " Time by Employees", |
|||
fontSize: 18, |
|||
fontColor: "#111" |
|||
}, |
|||
legend: { |
|||
display: false, |
|||
}, |
|||
scales: { |
|||
yAxes: [{ |
|||
ticks: { |
|||
min: 0 |
|||
} |
|||
}] |
|||
} |
|||
}; |
|||
//create Chart class object
|
|||
var chart = new Chart(ctx.el, { |
|||
type: 'bar', |
|||
data: data, |
|||
options: options |
|||
}); |
|||
|
|||
} |
|||
/** |
|||
* Function for getting employees for filter. |
|||
*/ |
|||
async render_filter() { |
|||
var data = await rpc('/project/filter') |
|||
this.state.projects = data[0] |
|||
this.state.employees = data[1] |
|||
} |
|||
/** |
|||
* Event handler to apply filters based on user selections and update the dashboard data accordingly. |
|||
*/ |
|||
async _onchangeFilter(ev) { |
|||
this.flag = 1 |
|||
var start_date = this.start_date.el.value; |
|||
var end_date = this.end_date.el.value; |
|||
var employee_selection = this.employee_selection.el.value; |
|||
var project_selection = this.project_selection.el.value; |
|||
if (!start_date) { |
|||
start_date = "null" |
|||
} |
|||
if (!end_date) { |
|||
end_date = "null" |
|||
} |
|||
if (!employee_selection) { |
|||
employee_selection = "null" |
|||
} |
|||
if (!project_selection) { |
|||
project_selection = "null" |
|||
} |
|||
var data = await rpc('/project/filter-apply', { |
|||
'data': { |
|||
'start_date': start_date, |
|||
'end_date': end_date, |
|||
'project': project_selection, |
|||
'employee': employee_selection |
|||
} |
|||
}) |
|||
self.tot_hrs = data['list_hours_recorded'] |
|||
self.tot_employee = data['total_emp'] |
|||
self.tot_project = data['total_project'] |
|||
self.tot_task = data['total_task'] |
|||
self.tot_so = data['total_so'] |
|||
this.tot_project.el.innerHTML = data['total_project'].length |
|||
this.tot_employee.el.innerHTML = data['total_emp'].length |
|||
this.total_task.el.innerHTML = data['total_task'].length |
|||
this.tot_hrs.el.innerHTML = data['hours_recorded'] |
|||
this.tot_margin.el.innerHTML = data['total_margin'] |
|||
this.total_so.el.innerHTML = data['total_so'].length |
|||
} |
|||
/** |
|||
* Event handler to open a list of employees and display them to the user. |
|||
*/ |
|||
tot_emp(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
if (this.flag == 0) { |
|||
this.action.doAction({ |
|||
name: _t("Employees"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'hr.employee', |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} else { |
|||
this.action.doAction({ |
|||
name: _t("Employees"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'hr.employee', |
|||
domain: [ |
|||
["id", "in", this.tot_employee] |
|||
], |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
} |
|||
/** |
|||
function for getting values when page is loaded |
|||
*/ |
|||
fetch_data() { |
|||
this.flag = 0 |
|||
var self = this; |
|||
var def1 = rpc('/get/tiles/data').then(function(result) { |
|||
if (result['flag'] == 1) { |
|||
self.total_projects = result['total_projects'] |
|||
self.total_tasks = result['total_tasks'] |
|||
self.tot_task = result['total_tasks_ids'] |
|||
self.total_hours = result['total_hours'] |
|||
self.total_profitability = result['total_profitability'] |
|||
self.total_employees = result['total_employees'] |
|||
self.total_sale_orders = result['total_sale_orders'] |
|||
self.project_stage_list = result['project_stage_list'] |
|||
self.tot_so = result['sale_orders_ids'] |
|||
self.flag_user = result['flag'] |
|||
self.total_projects_ids = result['total_projects_ids'] |
|||
} else { |
|||
self.tot_task = result['total_tasks_ids'] |
|||
self.total_projects = result['total_projects'] |
|||
self.total_tasks = result['total_tasks'] |
|||
self.total_hours = result['total_hours'] |
|||
self.total_sale_orders = result['total_sale_orders'] |
|||
self.project_stage_list = result['project_stage_list'] |
|||
self.flag_user = result['flag'] |
|||
self.tot_so = result['sale_orders_ids'] |
|||
self.total_projects_ids = result['total_projects_ids'] |
|||
} |
|||
}); |
|||
/** |
|||
function for getting values to hours table |
|||
*/ |
|||
var def3 = rpc('/get/hours') |
|||
.then(function(res) { |
|||
self.hour_recorded = res['hour_recorded']; |
|||
self.hour_recorde = res['hour_recorde']; |
|||
self.billable_fix = res['billable_fix']; |
|||
self.non_billable = res['non_billable']; |
|||
self.total_hr = res['total_hr']; |
|||
}); |
|||
var def4 = rpc('/get/task/data') |
|||
.then(function(res) { |
|||
self.task_data = res['project']; |
|||
}); |
|||
return Promise.all([def1, def3, def4]) |
|||
.then(() => { |
|||
console.log('All data has been fetched successfully.'); |
|||
}) |
|||
.catch((error) => { |
|||
console.error('An error occurred while fetching data:', error); |
|||
}); |
|||
} |
|||
/** |
|||
* Event handler to open a list of projects and display them to the user. |
|||
*/ |
|||
tot_projects(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
if (this.flag == 0) { |
|||
this.action.doAction({ |
|||
name: _t("Projects"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'project.project', |
|||
domain: [ |
|||
["id", "in", this.total_projects_ids] |
|||
], |
|||
view_mode: 'kanban,form', |
|||
views: [ |
|||
[false, 'kanban'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} else { |
|||
if (this.tot_project) { |
|||
this.action.doAction({ |
|||
name: _t("Projects"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'project.project', |
|||
domain: [ |
|||
["id", "in", this.tot_project] |
|||
], |
|||
view_mode: 'kanban,form', |
|||
views: [ |
|||
[false, 'kanban'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
} |
|||
} |
|||
/** |
|||
* Event handler to open a list of tasks and display them to the user. |
|||
*/ |
|||
tot_tasks(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
this.action.doAction({ |
|||
name: _t("Tasks"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'project.task', |
|||
domain: [ |
|||
["id", "in", this.tot_task] |
|||
], |
|||
view_mode: 'tree,kanban,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
/** |
|||
for opening account analytic line view |
|||
*/ |
|||
hr_recorded(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
if (this.flag == 0) { |
|||
this.action.doAction({ |
|||
name: _t("Timesheets"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'account.analytic.line', |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} else { |
|||
if (this.tot_hrs) { |
|||
this.action.doAction({ |
|||
name: _t("Timesheets"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'account.analytic.line', |
|||
domain: [ |
|||
["id", "in", this.tot_hrs] |
|||
], |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
} |
|||
} |
|||
/** |
|||
for opening sale order view |
|||
*/ |
|||
tot_sale(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
this.action.doAction({ |
|||
name: _t("Sale Order"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'sale.order', |
|||
domain: [ |
|||
["id", "in", this.tot_so] |
|||
], |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} |
|||
/** |
|||
* Event handler to view a list of employees. |
|||
* @param {Event} e - The click event. |
|||
*/ |
|||
tot_emp(e) { |
|||
e.stopPropagation(); |
|||
e.preventDefault(); |
|||
var options = { |
|||
on_reverse_breadcrumb: this.on_reverse_breadcrumb, |
|||
}; |
|||
if (this.flag == 0) { |
|||
this.action.doAction({ |
|||
name: _t("Employees"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'hr.employee', |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
} else { |
|||
this.action.doAction({ |
|||
name: _t("Employees"), |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'hr.employee', |
|||
domain: [ |
|||
["id", "in", this.tot_employee] |
|||
], |
|||
view_mode: 'tree,form', |
|||
views: [ |
|||
[false, 'list'], |
|||
[false, 'form'] |
|||
], |
|||
target: 'current' |
|||
}, options) |
|||
|
|||
} |
|||
} |
|||
} |
|||
ProjectDashboard.template = "ProjectDashboard" |
|||
registry.category("actions").add("project_dashboard", ProjectDashboard) |
@ -0,0 +1,419 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- Template for the project dashboard --> |
|||
<templates id="template" xml:space="preserve"> |
|||
<t t-name="ProjectDashboard"> |
|||
<div class="oh_dashboards" |
|||
style="margin-top: 20px; overflow-y: scroll;vertical-align: middle;overflow-x: clip;max-height: -webkit-fill-available;"> |
|||
<div class="container-fluid o_pj_dashboard" |
|||
style="margin-left:4%;"> |
|||
<t t-call="DashboardProject"/> |
|||
<t t-call="DashboardChart"/> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<t t-name="DashboardProject"> |
|||
<!-- Template for filter items and cards --> |
|||
<div class="row main-section"> |
|||
<div class="inner_select" style="display: flex;"> |
|||
<p style="margin-left: 20px;">Start Date :</p> |
|||
<p> |
|||
<input type="date" class="inner_select" id="start_date" |
|||
name="start_date" t-ref="start_date" |
|||
t-on-change="(ev) => this._onchangeFilter(ev)"/> |
|||
</p> |
|||
<p>End Date :</p> |
|||
<p> |
|||
<input type="date" class="inner_select" id="end_date" |
|||
name="end_date" t-ref="end_date" |
|||
t-on-change="(ev) => this._onchangeFilter(ev)"/> |
|||
</p> |
|||
<p>Project :</p> |
|||
<p> |
|||
<select class="inner_select" id="project_selection" |
|||
t-ref="project_selection" |
|||
t-on-change="(ev) => this._onchangeFilter(ev)"> |
|||
<option value="null">All Projects</option> |
|||
<option t-foreach="state.projects" t-as="project" |
|||
t-key="project.id" |
|||
t-att-value="project.id"> |
|||
<t t-esc="project.name"/> |
|||
</option> |
|||
</select> |
|||
</p> |
|||
<p>Employees :</p> |
|||
<p> |
|||
<select class="inner_select" id="employee_selection" |
|||
t-ref="employee_selection" |
|||
t-on-change="(ev) => this._onchangeFilter(ev)"> |
|||
<option value="null">All Employees</option> |
|||
<option t-foreach="state.employees" t-as="employee" |
|||
t-key="employee.id" |
|||
t-att-value="employee.id"> |
|||
<t t-esc="employee.name"/> |
|||
</option> |
|||
</select> |
|||
</p> |
|||
<button class="btn btn-danger g-col-6 p-2" |
|||
onclick="location.reload()" |
|||
style="margin-left:18px; height: 37px;"> |
|||
Reset |
|||
</button> |
|||
</div> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_projects" |
|||
t-on-click="(e) => this.tot_projects(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-puzzle-piece" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Total Project</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" |
|||
id="tot_project" t-ref="tot_project"> |
|||
<t t-esc="total_projects"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<t t-if="flag_user == 1"> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_emp" |
|||
t-on-click="(e) => this.tot_emp(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-user" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Total Employees</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" |
|||
id="tot_employee" t-ref="tot_employee"> |
|||
<t t-esc="total_employees"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_tasks" |
|||
t-on-click="(e) => this.tot_tasks(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-tasks" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Total tasks</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" id="tot_task" t-ref="tot_task"> |
|||
<t t-esc="total_tasks"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<t t-if="flag_user == 1"> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body hr_recorded" |
|||
t-on-click="(e) => this.hr_recorded(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"><i class="fa fa-clock-o" /></div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;">Hours Recorded</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" |
|||
id="tot_hrs" t-ref="tot_hrs"> |
|||
<t t-esc="total_hours"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<t t-if="flag_user == 1"> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_profitability" |
|||
t-on-click="(e) => this.tot_sale(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"> |
|||
<i class="fa fa-dollar" /> |
|||
</div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;"> |
|||
Total Margin |
|||
</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;display:inline-table;" |
|||
id="tot_margin" t-ref="tot_margin"> |
|||
<t t-esc="total_profitability"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<div class="col-md-4 col-sm-6 oh-payslip"> |
|||
<div class="oh-card" style="width: 410px;"> |
|||
<div class="oh-card-body tot_sale" |
|||
t-on-click="(e) => this.tot_sale(e)" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="stat-widget-one" style="display:flex;"> |
|||
<div class="stat-icon"> |
|||
<i class="fa fa-ticket" /> |
|||
</div> |
|||
<div class="stat-head" |
|||
style="padding: 5%;width: 60%;"> |
|||
Total Sale Orders |
|||
</div> |
|||
<div class="stat_count" |
|||
style="padding: 4%;width: 30%;" id="tot_so" t-ref="tot_so"> |
|||
<t t-esc="total_sale_orders"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
<t t-name="DashboardChart"> |
|||
<!-- Template for charts --> |
|||
<div class="col-xs-12 col-sm-12 col-lg-12 col-md-12"> |
|||
<div class="row main-section"> |
|||
<div class="col-sm-7 col-lg-7"> |
|||
<div class="graph_view" style="box-shadow:5px 11px 30px;"> |
|||
<div class="text-color hr-chart-1"> |
|||
<div class="oh-card-body pb-0" |
|||
style="text-align:center;"> |
|||
<h2 style="margin-left:45%;padding-top:2%;"> |
|||
Project Task Analysis |
|||
</h2> |
|||
</div> |
|||
<canvas id="project_doughnut" |
|||
t-ref="project_doughnut" |
|||
style="background:#fff;" width="200" |
|||
height="115"/> |
|||
</div> |
|||
</div> |
|||
<div class="selling_product_graph_view" |
|||
style="box-shadow:5px 11px 30px;"> |
|||
<div class="oh-card text-color"> |
|||
<canvas class="top_selling_employees" |
|||
t-ref="top_selling_employees" |
|||
style="background:#fff;" width="200" |
|||
height="108"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-lg-4" style="top: 82px;right: -100px;"> |
|||
<div class="hr_notification" style="background: #fff;transition: transform 0.2s ease, box-shadow 0.2s ease;will-change: transform, box-shadow;box-shadow: 0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62,57,107,0.06); |
|||
height: 763px;margin-bottom: 15px;margin-top: 15px;box-shadow:5px 11px 30px;"> |
|||
<div class="hr_notification_head" |
|||
style="font-size: 17px;text-align: center;padding: 12px 0;color: #fff;font-weight: 300;background: #000080;margin-bottom: 9px;"> |
|||
Project Task Details |
|||
</div> |
|||
<div class="col-sm-12 col-lg-12" |
|||
style="padding:0;"> |
|||
<div class="text-color"> |
|||
<div class="media" |
|||
style="overflow-y: auto;height: 704px;"> |
|||
<div class="media-body"> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Project Name</th> |
|||
<th rowspan="14">Task Name</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="task_data" |
|||
t-as="proj" |
|||
t-key="proj[1]"> |
|||
<tr> |
|||
<td> |
|||
<t t-if="flag_user == 1"> |
|||
<t t-esc="proj[2]['en_US']"/> |
|||
</t> |
|||
<t t-else=""> |
|||
<t t-esc="proj[2]"/> |
|||
</t> |
|||
</td> |
|||
<td> |
|||
<t t-esc="proj[0]"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="hr_notification_head" |
|||
style="font-size: 17px;text-align: center;padding: 12px 0;color: #fff;font-weight: 300;background: #000080;margin-bottom: 9px;"> |
|||
Hours Recorded |
|||
</div> |
|||
<!-- Updated code with right-aligned values for all tables --> |
|||
<div class="col-sm-12 col-lg-12" style="padding: 0;"> |
|||
<div class="text-color"> |
|||
<div class=""> |
|||
<div class="media"> |
|||
<div class="media-body"> |
|||
<!-- Table 1: Billed on Timesheet --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Billed on Timesheet</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="hour_recorde" |
|||
t-as="hour_recorde" |
|||
t-key="hour_recorde"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" |
|||
style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="hour_recorde"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
<!-- Table 2: Billed at a Fixed Price --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Billed at a Fixed Price</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="billable_fix" |
|||
t-as="billable_fix" |
|||
t-key="billable_fix"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" |
|||
style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="billable_fix"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
<!-- Table 3: No Task Found --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">No Task Found</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="hour_recorded" |
|||
t-as="hour_recorded" |
|||
t-key="hour_recorded"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" |
|||
style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="hour_recorded"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
<!-- Table 4: Non Billable Tasks --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Non Billable Tasks</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="non_billable" |
|||
t-as="non_billable" |
|||
t-key="non_billable"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" |
|||
style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="non_billable"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
<!-- Table 5: Total --> |
|||
<table class="table table-sm"> |
|||
<thead> |
|||
<tr> |
|||
<th rowspan="14">Total:</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<t t-foreach="total_hr" |
|||
t-as="total_hr" |
|||
t-key="total_hr"> |
|||
<tr> |
|||
<td> |
|||
<!-- Right-align the value --> |
|||
<h2 class="text-color display-6" |
|||
style="font-size: 15px; text-align: right; margin-left: 400px; margin-top: -30px;"> |
|||
<t t-esc="total_hr"/> |
|||
</h2> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</tbody> |
|||
</table> |
|||
<div class="stage_wise_total"> |
|||
<div class="hr_notification" style="background: #fff;transition: transform 0.2s ease, box-shadow 0.2s ease;will-change: transform, box-shadow;box-shadow: 0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62,57,107,0.06); |
|||
height: auto;padding-bottom: 15px;box-shadow:5px 11px 30px;"> |
|||
<div class="hr_notification_head" |
|||
style="font-size: 17px;text-align: center;padding: 12px 0;color: #fff;font-weight: 300;background: #000080;margin-bottom: 9px;"> |
|||
Stage Wise Total Projects |
|||
</div> |
|||
<table id="table_status" style="width"> |
|||
<tr> |
|||
<th/> |
|||
<th/> |
|||
</tr> |
|||
<t t-foreach="project_stage_list" |
|||
t-as="data" t-key="data['name']"> |
|||
<tr> |
|||
<td style="text-align:center;"> |
|||
<h4 t-esc="data['name']"/> |
|||
</td> |
|||
<td style="text-align:center;"> |
|||
<h4 class="fleet-pill" |
|||
t-esc="data['projects']"/> |
|||
</td> |
|||
</tr> |
|||
</t> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
</templates> |
@ -0,0 +1,14 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Action for Project Dashboard--> |
|||
<record id="project_dashboard_action" model="ir.actions.client"> |
|||
<field name="name">Dashboard</field> |
|||
<field name="tag">project_dashboard</field> |
|||
</record> |
|||
<!-- Dashboard menu item --> |
|||
<menuitem id="project_menu_open_Dashboard" |
|||
name="Dashboard" |
|||
action="project_dashboard_action" |
|||
parent="project.menu_main_pm" |
|||
sequence="1"/> |
|||
</odoo> |