Compare commits

...

50 Commits

Author SHA1 Message Date
administrator d9b45297ab Initial commit 2025-08-29 10:53:09 +05:30
administrator 7d3dd1bc2b Initial commit 2025-08-29 10:53:09 +05:30
administrator bc0c4ea437 Initial commit 2025-08-29 10:53:09 +05:30
administrator e1742b9d0c Initial commit 2025-08-29 10:53:09 +05:30
administrator 9c9f6c1c95 Initial commit 2025-08-29 10:53:09 +05:30
administrator 5914ed1944 Initial commit 2025-08-29 10:53:09 +05:30
administrator a27047bc4a Initial commit 2025-08-29 10:53:09 +05:30
administrator 9ff8890796 Initial commit 2025-08-29 10:53:09 +05:30
administrator 8b65e39d5c Initial commit 2025-08-29 10:53:09 +05:30
administrator de866d0747 Initial commit 2025-08-29 10:53:08 +05:30
administrator ec9b566f07 Initial commit 2025-08-29 10:53:08 +05:30
administrator 6fe280dfa2 Initial commit 2025-08-29 10:53:08 +05:30
administrator 5ec1941e4a Initial commit 2025-08-29 10:53:08 +05:30
administrator fbe0a3fbbc Initial commit 2025-08-29 10:53:08 +05:30
administrator b9061bab3f Initial commit 2025-08-29 10:53:08 +05:30
administrator 9c6fafb741 Initial commit 2025-08-29 10:53:08 +05:30
administrator 5ca3c9dee2 Initial commit 2025-08-29 10:53:08 +05:30
administrator 37ed3b5369 Initial commit 2025-08-29 10:53:08 +05:30
administrator 1304586f9f Initial commit 2025-08-29 10:53:08 +05:30
administrator 370aa24b6f Initial commit 2025-08-29 10:53:08 +05:30
administrator b6885903d7 Initial commit 2025-08-29 10:53:08 +05:30
administrator ea9fb09ff1 pull commit 2025-08-29 10:53:08 +05:30
administrator e34564041e Initial commit 2025-08-29 10:53:08 +05:30
Pranay 6dc6c386d2 TimeOff Fix 2025-08-29 10:53:08 +05:30
Pranay d761cc8d40 time-off FIX 2025-08-29 10:53:08 +05:30
Pranay e6cbbd679a Recruitment Changes 2025-08-29 10:53:08 +05:30
Pranay 3f8aa89705 fix whatsapp 2025-08-29 10:53:08 +05:30
Pranay bd4cfee509 update whatsapp code 2025-08-29 10:53:08 +05:30
administrator 45c1f46bc6 Initial commit 2025-08-29 10:53:08 +05:30
administrator 2a4ea19178 Initial commit 2025-08-29 10:53:08 +05:30
administrator 12aa59f401 Initial commit 2025-08-29 10:53:08 +05:30
administrator 5c1d136bca Initial commit 2025-08-29 10:53:07 +05:30
administrator df8fdd923e Initial commit 2025-08-29 10:53:07 +05:30
administrator 7ed247d057 Initial commit 2025-08-29 10:53:07 +05:30
administrator 29b5a22a6d Initial commit 2025-08-29 10:53:07 +05:30
administrator 4fb6f3a657 Initial commit 2025-08-29 10:53:07 +05:30
administrator f0a4bfdf90 Initial commit 2025-08-29 10:53:07 +05:30
administrator fe5f1e52b3 Initial commit 2025-08-29 10:53:07 +05:30
administrator 3d3fbd518f Initial commit 2025-08-29 10:53:07 +05:30
administrator ff0a7ce5e4 Initial commit 2025-08-29 10:53:07 +05:30
administrator 177655da30 Initial commit 2025-08-29 10:53:07 +05:30
administrator 60fa39b4a7 Initial commit 2025-08-29 10:53:07 +05:30
administrator 9958cc481f Initial commit 2025-08-29 10:53:07 +05:30
administrator dd418d2ca0 Initial commit 2025-08-29 10:53:07 +05:30
administrator fbf9830c5d Initial commit 2025-08-29 10:53:07 +05:30
administrator 5677b61e83 Initial commit 2025-08-29 10:53:07 +05:30
administrator 28cc9ce57a Initial commit 2025-08-29 10:53:07 +05:30
administrator 8655376460 Initial commit 2025-08-29 10:53:07 +05:30
pranay ebbfb0c445 Added Break Hours data in attendance report 2025-08-29 10:39:59 +05:30
Pranay 457867f3b0 user profile dashboard 2025-08-18 10:28:27 +05:30
24 changed files with 1165 additions and 141 deletions

View File

@ -61,111 +61,116 @@ class AttendanceReport(models.Model):
# Define the query
query = """
WITH date_range AS (
SELECT generate_series(
%s::date,
%s::date,
interval '1 day'
)::date AS date
),
employee_dates AS (
SELECT
emp.id AS employee_id,
emp.name AS employee_name,
dr.date,
TO_CHAR(dr.date, 'Day') AS day_name,
EXTRACT(WEEK FROM dr.date) AS week_number,
TO_CHAR(date_trunc('week', dr.date), 'MON DD') || ' - ' ||
TO_CHAR(date_trunc('week', dr.date) + interval '6 days', 'MON DD') AS week_range,
dep.name->>'en_US' AS department
FROM
hr_employee emp
CROSS JOIN
date_range dr
LEFT JOIN
hr_department dep ON emp.department_id = dep.id
WHERE
emp.active = true
""" + (" AND " + " AND ".join(emp_date_conditions) if emp_date_conditions else "") + """
),
daily_checkins AS (
SELECT
emp.id,
emp.name,
DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') AS date,
at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_in,
at.check_out AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_out,
at.worked_hours,
ROW_NUMBER() OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in) AS first_checkin_row,
ROW_NUMBER() OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in DESC) AS last_checkout_row,
dep.name->>'en_US' AS department
FROM
hr_attendance at
LEFT JOIN
hr_employee emp ON at.employee_id = emp.id
LEFT JOIN
hr_department dep ON emp.department_id = dep.id
WHERE
""" + " AND ".join(checkin_conditions) + """
),
attendance_summary AS (
SELECT
id,
name,
date,
MAX(CASE WHEN first_checkin_row = 1 THEN check_in END) AS first_check_in,
MAX(CASE WHEN last_checkout_row = 1 THEN check_out END) AS last_check_out,
SUM(worked_hours) AS total_worked_hours,
department
FROM
daily_checkins
GROUP BY
id, name, date, department
),
leave_data AS (
SELECT
hl.employee_id,
hl.date_from AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS leave_start,
hl.date_to AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS leave_end,
hlt.name->>'en_US' AS leave_type,
hl.request_unit_half AS is_half_day,
hl.request_date_from,
hl.request_date_to
FROM
hr_leave hl
JOIN
hr_leave_type hlt ON hl.holiday_status_id = hlt.id
WHERE
hl.state IN ('validate', 'confirm', 'validate1')
AND (hl.date_from, hl.date_to) OVERLAPS (%s::timestamp, %s::timestamp)
)
SELECT
ed.employee_id AS id,
ed.employee_name AS name,
ed.date,
'Week ' || ed.week_number || ' (' || ed.week_range || ')' AS week_info,
TRIM(ed.day_name) AS day_name,
COALESCE(ats.first_check_in, NULL) AS first_check_in,
COALESCE(ats.last_check_out, NULL) AS last_check_out,
COALESCE(ats.total_worked_hours, 0) AS total_worked_hours,
ed.department,
CASE
WHEN ld.leave_type IS NOT NULL AND ld.is_half_day THEN 'on Half day ' || ld.leave_type
WHEN ld.leave_type IS NOT NULL THEN 'on ' || ld.leave_type
WHEN ats.first_check_in IS NOT NULL THEN 'Present'
ELSE 'NA'
END AS status
FROM
employee_dates ed
LEFT JOIN
attendance_summary ats ON ed.employee_id = ats.id AND ed.date = ats.date
LEFT JOIN
leave_data ld ON ed.employee_id = ld.employee_id
AND ed.date >= DATE(ld.leave_start)
AND ed.date <= DATE(ld.leave_end)
ORDER BY
ed.employee_id, ed.date;
SELECT generate_series(
%s::date,
%s::date,
interval '1 day'
)::date AS date
),
employee_dates AS (
SELECT
emp.id AS employee_id,
emp.name AS employee_name,
dr.date,
TO_CHAR(dr.date, 'Day') AS day_name,
EXTRACT(WEEK FROM dr.date) AS week_number,
TO_CHAR(date_trunc('week', dr.date), 'MON DD') || ' - ' ||
TO_CHAR(date_trunc('week', dr.date) + interval '6 days', 'MON DD') AS week_range,
dep.name->>'en_US' AS department
FROM
hr_employee emp
CROSS JOIN
date_range dr
LEFT JOIN
hr_department dep ON emp.department_id = dep.id
WHERE
emp.active = true
""" + (" AND " + " AND ".join(emp_date_conditions) if emp_date_conditions else "") + """
),
daily_checkins AS (
SELECT
emp.id,
emp.name,
DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') AS date,
at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_in,
at.check_out AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_out,
at.worked_hours,
ROW_NUMBER() OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in) AS first_checkin_row,
ROW_NUMBER() OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in DESC) AS last_checkout_row,
dep.name->>'en_US' AS department,
LEAD(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in) AS next_check_in
FROM
hr_attendance at
LEFT JOIN
hr_employee emp ON at.employee_id = emp.id
LEFT JOIN
hr_department dep ON emp.department_id = dep.id
WHERE
""" + " AND ".join(checkin_conditions) + """
),
attendance_summary AS (
SELECT
id,
name,
date,
MAX(CASE WHEN first_checkin_row = 1 THEN check_in END) AS first_check_in,
MAX(CASE WHEN last_checkout_row = 1 THEN check_out END) AS last_check_out,
SUM(worked_hours) AS total_worked_hours,
-- 👇 Calculate total break time (sum of gaps between check_out and next check_in)
SUM(
EXTRACT(EPOCH FROM (next_check_in - check_out)) / 3600
) FILTER (WHERE next_check_in IS NOT NULL AND check_out IS NOT NULL) AS break_hours,
department
FROM
daily_checkins
GROUP BY
id, name, date, department
),
leave_data AS (
SELECT
hl.employee_id,
hl.date_from AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS leave_start,
hl.date_to AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS leave_end,
hlt.name->>'en_US' AS leave_type,
hl.request_unit_half AS is_half_day,
hl.request_date_from,
hl.request_date_to
FROM
hr_leave hl
JOIN
hr_leave_type hlt ON hl.holiday_status_id = hlt.id
WHERE
hl.state IN ('validate', 'confirm', 'validate1')
AND (hl.date_from, hl.date_to) OVERLAPS (%s::timestamp, %s::timestamp)
)
SELECT
ed.employee_id AS id,
ed.employee_name AS name,
ed.date,
'Week ' || ed.week_number || ' (' || ed.week_range || ')' AS week_info,
TRIM(ed.day_name) AS day_name,
COALESCE(ats.first_check_in, NULL) AS first_check_in,
COALESCE(ats.last_check_out, NULL) AS last_check_out,
COALESCE(ats.total_worked_hours, 0) AS total_worked_hours,
COALESCE(ats.break_hours, 0) AS total_break_hours,
ed.department,
CASE
WHEN ld.leave_type IS NOT NULL AND ld.is_half_day THEN 'on Half day ' || ld.leave_type
WHEN ld.leave_type IS NOT NULL THEN 'on ' || ld.leave_type
WHEN ats.first_check_in IS NOT NULL THEN 'Present'
ELSE 'NA'
END AS status
FROM
employee_dates ed
LEFT JOIN
attendance_summary ats ON ed.employee_id = ats.id AND ed.date = ats.date
LEFT JOIN
leave_data ld ON ed.employee_id = ld.employee_id
AND ed.date >= DATE(ld.leave_start)
AND ed.date <= DATE(ld.leave_end)
ORDER BY
ed.employee_id, ed.date;
"""
# Combine all parameters in the correct order:
# 1. date_range params (start_date_str, end_date_str)
# 2. employee_dates params (emp_date_params)
@ -196,6 +201,7 @@ class AttendanceReport(models.Model):
'check_in': r['first_check_in'],
'check_out': r['last_check_out'],
'worked_hours': float(r['total_worked_hours']) if r['total_worked_hours'] is not None else 0.0,
'break_hours': float(r['total_break_hours']) if r['total_break_hours'] is not None else 0.0,
'status': r['status']
})
@ -213,7 +219,6 @@ class AttendanceReport(models.Model):
attendance_data = self.get_attendance_report(department_id, employee_id, start_date, end_date)
if not attendance_data:
raise UserError("No data to export!")
# Create workbook and sheet
workbook = xlwt.Workbook(encoding='utf-8')
sheet = workbook.add_sheet('Attendance Report')
@ -281,28 +286,26 @@ class AttendanceReport(models.Model):
)
# Set column widths (in units of 1/256 of a character width)
col_widths = [6000, 8000, 7000, 3000, 4000, 5000, 5000, 4000, 5000]
col_widths = [6000, 8000, 7000, 3000, 4000, 5000, 5000, 4000, 4000, 5000]
for i, width in enumerate(col_widths):
sheet.col(i).width = width
# Write title
sheet.write_merge(0, 0, 0, 8, 'ATTENDANCE REPORT', title_style)
sheet.write_merge(0, 0, 0, 9, 'ATTENDANCE REPORT', title_style)
# Write date range
date_range = f"From: {start_date} To: {end_date}"
sheet.write_merge(1, 1, 0, 8, date_range, xlwt.easyxf(
sheet.write_merge(1, 1, 0, 9, date_range, xlwt.easyxf(
'font: italic on; align: horiz center'
))
# Write headers
headers = [
'Department', 'Employee Name','Week', 'Date', 'Day',
'Check-in', 'Check-out', 'Worked Hours', 'Status'
'Check-in', 'Check-out', 'Worked Hours', 'Break Hours', 'Status'
]
for col_num, header in enumerate(headers):
sheet.write(2, col_num, header, header_style)
# Write data rows
current_employee = None
for row_num, record in enumerate(attendance_data, start=3):
@ -345,11 +348,16 @@ class AttendanceReport(models.Model):
else:
sheet.write(row_num, 7, str(record['worked_hours']), data_style)
sheet.write(row_num, 8, record['status'], status_style)
# Break hours formatting
if isinstance(record['break_hours'], (float, int)):
sheet.write(row_num, 8, float(record['break_hours']), hours_style)
else:
sheet.write(row_num, 8, str(record['break_hours']), data_style)
sheet.write(row_num, 9, record['status'], status_style)
# Add freeze panes (headers will stay visible when scrolling)
sheet.set_panes_frozen(True)
sheet.set_horz_split_pos(4) # After row 3 (headers)
sheet.set_horz_split_pos(3) # After row 3 (headers)
sheet.set_vert_split_pos(0) # No vertical split
# Save to buffer

View File

@ -206,7 +206,7 @@ export default class AttendanceReport extends Component {
}
async generateReport() {
debugger;
let { startDate, endDate, selectedEmployeeIds } = this.state;
startDate = $('#from_date').val()
endDate = $('#to_date').val()
@ -231,7 +231,7 @@ export default class AttendanceReport extends Component {
// Fetch the attendance data based on the date range and selected employees
// const attendanceData = await this.orm.searchRead('hr.attendance', domain, ['employee_id', 'check_in', 'check_out', 'worked_hours']);
const attendanceData = await this.orm.call('attendance.report','get_attendance_report',[$('#dept').val(),$('#emp').val(),startDate,endDate]);
debugger;
// Group data by employee_id
const rawGroups = this.groupDataByEmployee(attendanceData);

View File

@ -65,6 +65,7 @@
<th>Check In</th>
<th>Check Out</th>
<th>Worked Hours</th>
<th>Break Hours</th>
<th>Status</th>
</tr>
</thead>
@ -78,7 +79,16 @@
<td><t t-esc="data.day_name"/></td>
<td><t t-esc="data.check_in"/></td>
<td><t t-esc="data.check_out"/></td>
<td><t t-esc="data.worked_hours"/></td>
<td>
<t t-set="hours" t-value="Math.floor(data.worked_hours)"/>
<t t-set="minutes" t-value="Math.round((data.worked_hours - hours) * 60)"/>
<t t-esc="hours"/>:<t t-esc="minutes >= 10 ? minutes : '0' + minutes"/>
</td>
<td>
<t t-set="hours" t-value="Math.floor(data.break_hours)"/>
<t t-set="minutes" t-value="Math.round((data.break_hours - hours) * 60)"/>
<t t-esc="hours"/>:<t t-esc="minutes >= 10 ? minutes : '0' + minutes"/>
</td>
<td><t t-esc="data.status"/></td>
</tr>
</tbody>

View File

@ -18,8 +18,12 @@
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base','hr','account','mail','hr_skills', 'hr_contract'],
# always loaded
'data': [
'security/security.xml',

View File

@ -256,6 +256,8 @@ class HRJobRecruitment(models.Model):
rec.submission_status = 'zero'
experience = fields.Many2one('candidate.experience', string="Experience")
@api.depends('application_ids.submitted_to_client')
def _compute_no_of_submissions(self):
counts = dict(self.env['hr.applicant']._read_group(

View File

@ -116,6 +116,7 @@ Dashboard Ninja v16.0,
'ks_dashboard_ninja/static/src/css/ks_toggle_icon.css',
'ks_dashboard_ninja/static/src/css/ks_flower_view.css',
'ks_dashboard_ninja/static/src/css/ks_map_view.css',
'ks_dashboard_ninja/static/src/css/ks_profile.css',
'ks_dashboard_ninja/static/src/css/ks_funnel_view.css',
'ks_dashboard_ninja/static/src/css/ks_dashboard_options.css',
'/ks_dashboard_ninja/static/lib/js/gridstack-h5.js',

View File

@ -387,7 +387,8 @@ class KsDashboardNinjaItems(models.Model):
('ks_to_do', 'To Do'),
('ks_map_view', 'Map View'),
('ks_funnel_chart', 'Funnel Chart'),
('ks_bullet_chart', 'Bullet Chart')
('ks_bullet_chart', 'Bullet Chart'),
('ks_profile', 'Profile Chart'),
], default=lambda self: self._context.get('ks_dashboard_item_type',
'ks_tile'), required=True,
string="Dashboard Item Type",
@ -639,6 +640,7 @@ class KsDashboardNinjaItems(models.Model):
help="Display the total sum of each legends as it grows with times")
ks_radial_preview = fields.Char(string="Radial Preview", default="Radial Preview")
ks_map_preview = fields.Char(string="Map Preview", default="Map Preview")
ks_profile_preview = fields.Char(string="Profile Preview", default="Profile Preview")
ks_partners_map = fields.Char(compute="_compute_map_partners")
ks_country_id = fields.Many2one('res.country', string="Country")
ks_country_code = fields.Char(related="ks_country_id.code", store=True)
@ -694,6 +696,11 @@ class KsDashboardNinjaItems(models.Model):
ks_three_d = fields.Boolean(string='3D View', default=False)
@api.onchange('ks_dashboard_item_type')
def onchange_ks_dashboard_item_type(self):
for rec in self:
if rec.ks_dashboard_item_type == 'ks_profile':
rec.ks_model_id = self.env['ir.model'].sudo().search([('model','=','hr.employee')],limit=1)
@api.model
def create_ai_dash(self, data, ks_dash_id, model):
@ -4533,7 +4540,9 @@ class KsDashboardItemsActions(models.Model):
('ks_radar_view', 'Radar View'),
('ks_flower_view', 'Flower View'),
('ks_funnel_chart', 'Funnel Chart'),
('ks_bullet_chart', 'Bullet Chart')],
('ks_bullet_chart', 'Bullet Chart'),
('ks_profile', 'Profile Chart')
],
string="Item Type")
ks_dashboard_item_id = fields.Many2one('ks_dashboard_ninja.item', string="Dashboard Item")

View File

@ -0,0 +1,6 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24 24C29.5228 24 34 19.5228 34 14C34 8.47715 29.5228 4 24 4C18.4772 4 14 8.47715 14 14C14 19.5228 18.4772 24 24 24Z"
stroke="" stroke-width="1.49595" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/>
<path d="M10 40C10 33.3726 16.3726 28 24 28C31.6274 28 38 33.3726 38 40"
stroke="" stroke-width="1.49595" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/>
</svg>

After

Width:  |  Height:  |  Size: 521 B

View File

@ -401,7 +401,6 @@ export class Ksdashboardgraph extends Component{
// 3D Bar Chart Implementation
create3DBarChart(chart, data, ks_data, item) {
// Create axes
debugger;
var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "category";
categoryAxis.renderer.labels.template.rotation = -45;
@ -736,7 +735,6 @@ export class Ksdashboardgraph extends Component{
['ks_bar_chart','ks_horizontalBar_chart','ks_pie_chart','ks_doughnut_chart'].includes(item.ks_dashboard_item_type);
if (use3D) {
debugger;
switch (theme) {
case "dark":
am4core.unuseAllThemes();

View File

@ -0,0 +1,218 @@
/** @odoo-module **/
import { Component, useState, useEffect, onWillUpdateProps, useRef, onMounted, onWillUnmount } from "@odoo/owl";
import { globalfunction } from '@ks_dashboard_ninja/js/ks_global_functions';
import { loadBundle } from "@web/core/assets";
import { useService } from "@web/core/utils/hooks";
import { _t } from "@web/core/l10n/translation";
import { onAudioEnded } from '@ks_dashboard_ninja/js/ks_global_functions';
import { rpc } from "@web/core/network/rpc";
export class Ksdashboardprofile extends Component {
setup() {
debugger;
var self = this;
this.orm = useService("orm");
this.actionService = useService("action");
this.ks_profile = useRef('ks_profile');
this.ks_container_class = 'grid-stack-item';
this.aiAudioRef = useRef("aiAudioRef");
this.ks_inner_container_class = 'grid-stack-item-content';
this.state = useState({
employee: {
name: '',
image_1920: '',
job_id: null,
current_company_exp: null,
doj: '',
employee_id: null,
birthday: '',
attendance_state: null,
mobile_phone: '',
work_email: '',
private_street: '',
department_id: ''
}
});
this.storeService = useService("mail.store");
this.item = this.props.item;
this.ks_dashboard_data = this.props.dashboard_data;
this.ks_ai_analysis = this.ks_dashboard_data.ks_ai_explain_dash;
if (this.ks_ai_analysis) {
this.ks_container_class = 'grid-stack-item ks_ai_explain_tile';
this.ks_inner_container_class = 'grid-stack-item-content ks_ai_dashboard_item';
} else {
this.ks_container_class = 'grid-stack-item';
this.ks_inner_container_class = 'encapsulated-profile-tile grid-stack-item-content';
}
if (this.item.ks_ai_analysis && this.item.ks_ai_analysis) {
var ks_analysis = this.item.ks_ai_analysis.split('ks_gap');
this.ks_ai_analysis_1 = ks_analysis[0];
this.ks_ai_analysis_2 = ks_analysis[1];
}
this.prepare_profile();
var update_interval = this.props.dashboard_data.ks_set_interval;
onWillUpdateProps(async (nextprops) => {
if (nextprops.ksdatefilter != 'none') {
await this.ksFetchUpdateProfile(this.item.id, this.props.dashboard_data.context);
}
if (Object.keys(nextprops.pre_defined_filter).length) {
if (nextprops.pre_defined_filter?.item_ids?.includes(this.item.id)) {
await this.ksFetchUpdateProfile(this.item.id, this.props.dashboard_data.context);
}
}
if (Object.keys(nextprops.custom_filter).length) {
if (nextprops.custom_filter?.item_ids?.includes(this.item.id)) {
await this.ksFetchUpdateProfile(this.item.id, this.props.dashboard_data.context);
}
}
});
useEffect(() => {
if (update_interval && !this.env.inDialog) {
const interval = setInterval(() => {
this.ksFetchUpdateProfile(this.item.id, this.props.dashboard_data.context);
}, update_interval);
return () => clearInterval(interval);
}
});
onMounted(() => {
if (this.ks_ai_analysis) {
const dashboardItems = this.ks_profile.el.querySelectorAll('.ks_dashboarditem_id');
dashboardItems.forEach(item => {
item.classList.add('ks_ai_chart_body');
});
}
this.aiAudioRef.el?.addEventListener('ended', onAudioEnded);
});
onWillUnmount(() => {
this.aiAudioRef.el?.removeEventListener('ended', onAudioEnded);
});
}
ksFetchUpdateProfile(item_id, domain) {
var self = this;
if (!domain) {
if (this.__owl__.parent.component.hasOwnProperty('ksGetParamsForItemFetch') &&
this.__owl__.parent.component?.ksGetParamsForItemFetch(item_id) instanceof Function) {
domain = this.__owl__.parent.component?.ksGetParamsForItemFetch(item_id);
} else {
domain = {};
}
}
return rpc("/web/dataset/call_kw/", {
model: 'ks_dashboard_ninja.board',
method: 'ks_fetch_item',
args: [
[parseInt(item_id)], self.ks_dashboard_data.ks_dashboard_id, domain
],
kwargs: { context: this.props.dashboard_data.context },
}).then(function (new_item_data) {
this.ks_dashboard_data.ks_item_data[item_id] = new_item_data[item_id];
this.item = this.ks_dashboard_data.ks_item_data[item_id];
this.__owl__.parent.component.ks_dashboard_data.ks_item_data[this.item.id] = new_item_data[item_id];
this.prepare_profile();
}.bind(this));
}
ksStopClickPropagation(e) {
this.ksAllowItemClick = false;
}
prepare_profile() {
var self = this;
var ks_rgba_background_color, ks_rgba_font_color, ks_rgba_button_color;
this.item.ksIsDashboardManager = self.ks_dashboard_data.ks_dashboard_manager;
this.item.ksIsUser = true;
this.ks_rgba_background_color = self._ks_get_rgba_format(this.item.ks_background_color);
this.ks_rgba_font_color = self._ks_get_rgba_format(this.item.ks_font_color);
this.ks_rgba_button_color = self._ks_get_rgba_format(this.item.ks_button_color);
if (this.item.ks_info) {
var ks_description = this.item.ks_info.split('\n');
var ks_description = ks_description.filter(element => element !== '');
} else {
var ks_description = false;
}
this.ks_info = ks_description;
this.ks_dashboard_list = self.ks_dashboard_data.ks_dashboard_list;
this.style_main_body = this._ksMainBodyStyle(this.ks_rgba_background_color, this.ks_rgba_font_color, this.item).background_style;
// Fetch employee data
this.fetchEmployeeData();
}
async fetchEmployeeData() {
try {
debugger;
const employeeData = await this.orm.call("hr.employee", 'get_user_employee_details');
debugger;
if (employeeData && employeeData.length > 0) {
const employee = employeeData[0];
this.state.employee = {
name: employee.name,
image_1920: employee.image_1920,
doj: employee.doj,
job_id: employee.job_id,
employee_id: employee.employee_id,
current_company_exp: employee.current_company_exp,
attendance_state: employee.attendance_state,
birthday: employee.birthday,
mobile_phone: employee.mobile_phone,
work_email: employee.work_email,
private_street: employee.private_street,
department_id: employee.department_id ? employee.department_id[1] : ''
};
}
} catch (error) {
console.error("Error fetching employee data:", error);
}
}
async attendance_sign_in_out() {
try {
await this.orm.call('hr.employee', 'attendance_manual', [[this.state.employee.id]]);
this.state.employee.attendance_state =
this.state.employee.attendance_state === 'checked_in' ? 'checked_out' : 'checked_in';
} catch (error) {
console.error("Error updating attendance:", error);
}
}
_ks_get_rgba_format(val) {
var rgba = val.split(',')[0].match(/[A-Za-z0-9]{2}/g);
rgba = rgba.map(function (v) {
return parseInt(v, 16);
}).join(",");
return "rgba(" + rgba + "," + val.split(',')[1] + ")";
}
_ksMainBodyStyle(ks_rgba_background_color, ks_rgba_font_color, tile) {
var background_style = "background-color:" + ks_rgba_background_color + ";color : " + ks_rgba_font_color + ";";
return {
'background_style': background_style,
};
}
}
Ksdashboardprofile.props = {
item: { type: Object, Optional: true },
dashboard_data: { type: Object, Optional: true },
ksdatefilter: { type: String, Optional: true },
pre_defined_filter: { type: Object, Optional: true },
custom_filter: { type: Object, Optional: true },
ks_speak: { type: Function, Optional: true },
hideButtons: { type: Number, optional: true },
on_dialog: { type: Boolean, optional: true },
generate_dialog: { type: Boolean, optional: true },
};
Ksdashboardprofile.template = "Ksdashboardprofile";

View File

@ -0,0 +1,330 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="Ksdashboardprofile">
<t t-if="props.on_dialog">
<div class="explain-ai pt-3">
<div class="container">
<div t-att-id="item.id" t-att-class="'ks_item_click ' + ks_container_class" t-ref="ks_profile">
<div class="row ks_ai_explain_body">
<t t-if="ks_ai_analysis">
<div class="col-xl-6 col-12">
<div class="charts-sec ks_explain_ai">
<t t-call="ks_profile_layout"/>
</div>
</div>
<div class="col-xl-6 col-12">
<div class="charts-data h-100">
<!-- <div class="charts-content-box">-->
<!-- <t t-call="ks_item_explanation"/>-->
<!-- </div>-->
<div class="voice-button" t-on-click="props.ks_speak" title="Text to speech">
<div class="voice-cricle">
<img src="/ks_dashboard_ninja/static/images/voice-cricle.svg" height="24" width="24" alt="voice-img" loading="lazy"
class="img-fluid"/>
</div>
<div class="comp-gif d-none">
<img src="/ks_dashboard_ninja/static/images/Comp.gif" alt="loading gif" height="24" width="24"
class="img-fluid"/>
</div>
<audio t-ref="aiAudioRef"/>
</div>
</div>
</div>
</t>
<t t-else="">
<t t-call="ks_profile_layout"/>
</t>
</div>
</div>
</div>
</div>
</t>
<t t-else="">
<div t-att-class="'ks_item_click ' + ks_container_class" t-att-id="item.id" t-ref="ks_profile">
<t t-if="ks_ai_analysis">
<div class="ks_ai_explain_body">
<t t-call="ks_profile_layout"/>
<!-- <t t-call="ks_item_explanation"/>-->
</div>
</t>
<t t-else="">
<t t-call="ks_profile_layout"/>
</t>
</div>
</t>
</t>
<t t-name="ks_profile_layout">
<div t-att-class="'ks_dashboarditem_id ks_profile_carousel encapsulated-profile-container ks_explain_ai_view ks_dashboard_profile ks_dashboard_item_hover ' + ks_inner_container_class"
t-att-style="style_main_body"
t-att-title="item.ks_info" t-att-id="item.id">
<div class="ks_dashboard_item_button_container ks_dropdown_container ks_dashboard_item_header ks_dashboard_item_header_hover"
t-att-data-item_id="item.id">
<t t-if="item.ksIsDashboardManager and props.hideButtons and !props.on_dialog">
<button title="Move/Duplicate" data-bs-toggle="dropdown"
class="ks_dashboard_item_action dropdown-toggle img-bg hover-item me-lg-2 kpi-tile-img bg-white d-md-block d-none"
type="button"
t-att-style="'color:'+ ks_rgba_button_color + ';'"
aria-expanded="true">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.59351 3.92004H3.40682C2.26682 3.92004 1.3335 4.85337 1.3335 5.99337V13.5667C1.3335 14.5334 2.02683 14.9467 2.87349 14.4734L5.49349 13.0134C5.77349 12.86 6.22683 12.86 6.50016 13.0134L9.12016 14.4734C9.96682 14.9467 10.6602 14.5334 10.6602 13.5667V5.99337C10.6668 4.85337 9.73351 3.92004 8.59351 3.92004Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.6668 5.99337V13.5667C10.6668 14.5334 9.9735 14.94 9.12683 14.4734L6.50684 13.0134C6.22684 12.86 5.77349 12.86 5.49349 13.0134L2.87349 14.4734C2.02683 14.94 1.3335 14.5334 1.3335 13.5667V5.99337C1.3335 4.85337 2.26682 3.92004 3.40682 3.92004H8.59351C9.73351 3.92004 10.6668 4.85337 10.6668 5.99337Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.6668 3.4067V10.98C14.6668 11.9467 13.9735 12.3534 13.1268 11.8867L10.6668 10.5134V5.99337C10.6668 4.85337 9.73351 3.92004 8.59351 3.92004H5.3335V3.4067C5.3335 2.2667 6.26682 1.33337 7.40682 1.33337H12.5935C13.7335 1.33337 14.6668 2.2667 14.6668 3.4067Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<ul role="menu" class="ks_dashboard_menu_container form-input-box form-control encapsulated-form-arrow dropdown-menu dropdown-menu-right">
<li class="ks_md_heading m-2">
<span>Select Dashboard</span>
</li>
<li class="m-2">
<select class="o_input o_group_selector o_add_group ks_dashboard_select">
<t t-foreach="ks_dashboard_list" t-as="ks_dashboard" t-key="ks_dashboard.id">
<option t-att-value="ks_dashboard['id']">
<t t-esc="ks_dashboard['name']"/>
</option>
</t>
</select>
</li>
<li class="m-2">
<button class="dash-btn-red o_apply_group o_add_group ks_duplicate_item"
tabindex="-1" type="button">Duplicate
</button>
<button class="dash-btn-red o_apply_group o_add_group ks_move_item" tabindex="-1"
type="button">Move
</button>
</li>
</ul>
<button title="Quick Customize"
class="ks_dashboard_quick_edit_action_popup d-md-block d-none img-bg hover-item me-lg-2 kpi-tile-img bg-white"
t-att-style="'color:'+ ks_rgba_button_color + ';'"
type="button" t-att-data-item-id="item.id">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 20 20" fill="none">
<path d="M9.16675 1.66663H7.50008C3.33341 1.66663 1.66675 3.33329 1.66675 7.49996V12.5C1.66675 16.6666 3.33341 18.3333 7.50008 18.3333H12.5001C16.6667 18.3333 18.3334 16.6666 18.3334 12.5V10.8333" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M13.3666 2.51663L6.7999 9.0833C6.5499 9.3333 6.2999 9.82497 6.2499 10.1833L5.89157 12.6916C5.75823 13.6 6.3999 14.2333 7.30823 14.1083L9.81657 13.75C10.1666 13.7 10.6582 13.45 10.9166 13.2L17.4832 6.6333C18.6166 5.49997 19.1499 4.1833 17.4832 2.51663C15.8166 0.849966 14.4999 1.3833 13.3666 2.51663Z" stroke="currentColor" stroke-width="1.25" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M12.425 3.45837C12.9834 5.45004 14.5417 7.00837 16.5417 7.57504" stroke="currentColor" stroke-width="1.25" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
<div class="ks_chart_inner_buttons dropdown d-md-none dn-setting-panel">
<button data-bs-toggle="dropdown"
class="ks_dashboard_item_action bg-transparent img-bg hover-item me-lg-2 kpi-tile-img dropdown-toggle"
type="button"
t-att-style="'color:'+ ks_rgba_button_color + ';'"
aria-expanded="true">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z" fill="#4B5563" stroke="currentColor" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.33301 8.58667V7.41333C1.33301 6.72 1.89967 6.14667 2.59967 6.14667C3.80634 6.14667 4.29967 5.29333 3.69301 4.24667C3.34634 3.64667 3.55301 2.86667 4.15967 2.52L5.31301 1.86C5.83967 1.54666 6.51967 1.73333 6.83301 2.26L6.90634 2.38666C7.50634 3.43333 8.49301 3.43333 9.09967 2.38666L9.17301 2.26C9.48634 1.73333 10.1663 1.54666 10.693 1.86L11.8463 2.52C12.453 2.86667 12.6597 3.64667 12.313 4.24667C11.7063 5.29333 12.1997 6.14667 13.4063 6.14667C14.0997 6.14667 14.673 6.71333 14.673 7.41333V8.58667C14.673 9.28 14.1063 9.85333 13.4063 9.85333C12.1997 9.85333 11.7063 10.7067 12.313 11.7533C12.6597 12.36 12.453 13.1333 11.8463 13.48L10.693 14.14C10.1663 14.4533 9.48634 14.2667 9.17301 13.74L9.09967 13.6133C8.49967 12.5667 7.51301 12.5667 6.90634 13.6133L6.83301 13.74C6.51967 14.2667 5.83967 14.4533 5.31301 14.14L4.15967 13.48C3.55301 13.1333 3.34634 12.3533 3.69301 11.7533C4.29967 10.7067 3.80634 9.85333 2.59967 9.85333C1.89967 9.85333 1.33301 9.28 1.33301 8.58667Z" stroke="currentColor" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right ks_chart_inner_min_width">
<div class="ks_chart_export_menu">
<div class="ks_dashboard_item_customize ks_chart_export_menu_item"
t-att-data-item_id="item.id"
data-format="chart_xls">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.16675 1.66663H7.50008C3.33341 1.66663 1.66675 3.33329 1.66675 7.49996V12.5C1.66675 16.6666 3.33341 18.3333 7.50008 18.3333H12.5001C16.6667 18.3333 18.3334 16.6666 18.3334 12.5V10.8333" stroke="#241C1D" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.3666 2.51663L6.7999 9.0833C6.5499 9.3333 6.2999 9.82497 6.2499 10.1833L5.89157 12.6916C5.75823 13.6 6.3999 14.2333 7.30823 14.1083L9.81657 13.75C10.1666 13.7 10.6582 13.45 10.9166 13.2L17.4832 6.6333C18.6166 5.49997 19.1499 4.1833 17.4832 2.51663C15.8166 0.849966 14.4999 1.3833 13.3666 2.51663Z" stroke="#241C1D" stroke-width="1.25" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.425 3.45837C12.9834 5.45004 14.5417 7.00837 16.5417 7.57504" stroke="#241C1D" stroke-width="1.25" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>Customize Item</span>
</div>
<div class="ks_dashboard_item_delete ks_chart_export_menu_item"
t-att-data-item_id="item.id"
data-format="chart_xls">
<svg width="16" height="16" viewBox="0 0 16 16" fill="" class="me-1" xmlns="http://www.w3.org/2000/svg">
<path d="M14 3.98665C11.78 3.76665 9.54667 3.65332 7.32 3.65332C6 3.65332 4.68 3.71999 3.36 3.85332L2 3.98665" fill="#241C1D"/>
<path d="M14 3.98665C11.78 3.76665 9.54667 3.65332 7.32 3.65332C6 3.65332 4.68 3.71999 3.36 3.85332L2 3.98665" stroke="#241C1D" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.66699 3.31337L5.81366 2.44004C5.92033 1.80671 6.00033 1.33337 7.12699 1.33337H8.87366C10.0003 1.33337 10.087 1.83337 10.187 2.44671L10.3337 3.31337" stroke="#241C1D" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.5669 6.09338L12.1336 12.8067C12.0603 13.8534 12.0003 14.6667 10.1403 14.6667H5.86026C4.00026 14.6667 3.94026 13.8534 3.86693 12.8067L3.43359 6.09338" stroke="#241C1D" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.88672 11H9.10672" stroke="#241C1D" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.33301 8.33337H9.66634" stroke="#241C1D" stroke-linecap="round" stroke-linejoin="round"/>
</svg> <span>Remove Item</span>
</div>
</div>
</div>
</div>
</t>
<t t-if="item.ksIsUser and props.hideButtons and !props.on_dialog">
<div class="ks_chart_inner_buttons dropdown">
<button title="Info" data-bs-toggle="dropdown"
class="ks_item_description dropdown-toggle o-no-caret img-bg hover-item me-lg-2 kpi-tile-img bg-white d-md-block d-none"
type="button"
t-att-style="'color:'+ ks_rgba_button_color + ';'"
aria-expanded="true">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.00004 14.6667C11.6667 14.6667 14.6667 11.6667 14.6667 8.00004C14.6667 4.33337 11.6667 1.33337 8.00004 1.33337C4.33337 1.33337 1.33337 4.33337 1.33337 8.00004C1.33337 11.6667 4.33337 14.6667 8.00004 14.6667Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 5.33337V8.66671" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.99646 10.6666H8.00245" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right" style="width:20rem">
<div class="ks_chart_export_menu">
<div class="ks_chart_export_menu_header" style="margin-left:-10px">
<span>Info</span>
</div>
<div class="ks_info" style="margin-left:10px">
<span>Company: <t t-esc="item.ks_company"/></span>
</div>
<div class="ks_info" style="margin-left:10px">
<t t-if="ks_info">
<t t-foreach="ks_info" t-as="ks_description" t-key="ks_description_index">
<span><t t-esc="ks_description"/></span>
<br></br>
</t>
</t>
</div>
</div>
</div>
</div>
</t>
<t t-if="item.ksIsUser and props.hideButtons and !props.on_dialog">
<div class="ks_chart_inner_buttons d-md-block d-none">
<button title="More" data-bs-toggle="dropdown"
class="ks_dashboard_item_action img-bg hover-item me-lg-2 kpi-tile-img bg-white dropdown-toggle o-no-caret"
type="button"
t-att-style="'color:'+ ks_rgba_button_color + ';'"
aria-expanded="true">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.2916 15.8333C12.2916 17.1 11.2666 18.125 9.99992 18.125C8.73325 18.125 7.70825 17.1 7.70825 15.8333C7.70825 14.5667 8.73325 13.5417 9.99992 13.5417C11.2666 13.5417 12.2916 14.5667 12.2916 15.8333ZM8.95825 15.8333C8.95825 16.4083 9.42492 16.875 9.99992 16.875C10.5749 16.875 11.0416 16.4083 11.0416 15.8333C11.0416 15.2583 10.5749 14.7917 9.99992 14.7917C9.42492 14.7917 8.95825 15.2583 8.95825 15.8333Z" fill="currentColor"/>
<path d="M12.2916 4.16671C12.2916 5.43337 11.2666 6.45837 9.99992 6.45837C8.73325 6.45837 7.70825 5.43337 7.70825 4.16671C7.70825 2.90004 8.73325 1.87504 9.99992 1.87504C11.2666 1.87504 12.2916 2.90004 12.2916 4.16671ZM8.95825 4.16671C8.95825 4.74171 9.42492 5.20837 9.99992 5.20837C10.5749 5.20837 11.0416 4.74171 11.0416 4.16671C11.0416 3.59171 10.5749 3.12504 9.99992 3.12504C9.42492 3.12504 8.95825 3.59171 8.95825 4.16671Z" fill="currentColor"/>
<path d="M12.2916 9.99996C12.2916 11.2666 11.2666 12.2916 9.99992 12.2916C8.73325 12.2916 7.70825 11.2666 7.70825 9.99996C7.70825 8.73329 8.73325 7.70829 9.99992 7.70829C11.2666 7.70829 12.2916 8.73329 12.2916 9.99996ZM8.95825 9.99996C8.95825 10.575 9.42492 11.0416 9.99992 11.0416C10.5749 11.0416 11.0416 10.575 11.0416 9.99996C11.0416 9.42496 10.5749 8.95829 9.99992 8.95829C9.42492 8.95829 8.95825 9.42496 8.95825 9.99996Z" fill="currentColor"/>
</svg>
</button>
<div role="menu" class="dropdown-menu dropdown-menu-right">
<div class="ks_chart_export_menu">
<div class="ks_chart_json_export ks_chart_export_menu_item d-flex"
t-att-data-item_id="item_id"
data-format="chart_xls">
<img src="/ks_dashboard_ninja/static/images/dashboardOverview/document-upload.svg" alt="document-upload" class="img-fluid" loading="lazy"/>
<span>Export Item</span>
</div>
<t t-if="item.ksIsDashboardManager and props.hideButtons and !props.on_dialog">
<div class="d-flex align-items-center">
<button type="button" title="Remove Item"
class="ks_dashboard_item_delete d-md-flex d-none img-bg hover-item me-lg-2 kpi-tile-img bg-white"
t-att-style="'color:'+ ks_rgba_button_color + ';'">
<svg width="16" height="16" viewBox="0 0 16 16" class="me-1" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 3.98665C11.78 3.76665 9.54667 3.65332 7.32 3.65332C6 3.65332 4.68 3.71999 3.36 3.85332L2 3.98665" fill="#4B5563"/>
<path d="M14 3.98665C11.78 3.76665 9.54667 3.65332 7.32 3.65332C6 3.65332 4.68 3.71999 3.36 3.85332L2 3.98665" stroke="#4B5563" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.66699 3.31337L5.81366 2.44004C5.92033 1.80671 6.00033 1.33337 7.12699 1.33337H8.87366C10.0003 1.33337 10.087 1.83337 10.187 2.44671L10.3337 3.31337" stroke="#4B5563" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.5669 6.09338L12.1336 12.8067C12.0603 13.8534 12.0003 14.6667 10.1403 14.6667H5.86026C4.00026 14.6667 3.94026 13.8534 3.86693 12.8067L3.43359 6.09338" stroke="#4B5563" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.88672 11H9.10672" stroke="#4B5563" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.33301 8.33337H9.66634" stroke="#4B5563" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Remove
</button>
</div>
<div class="d-flex align-items-center">
<button id='ks_ai_item_exp_dash' class="img-bg hover-item me-lg-2 kpi-tile-img bg-white"
t-on-click="_onButtonClick4" title="AI provides the insights of the item">
<img src="/ks_dashboard_ninja/static/images/favorite-chart.svg" alt="create" class="img-fluid" loading="lazy"/>
Explain With AI
</button>
</div>
<div class="d-flex align-items-center">
<button class="ks_dashboard_item_chatter_wizard img-bg hover-item me-lg-2 kpi-tile-img bg-white" title="Internal Chat" type="button" t-on-click="_openChatWizard">
<img src="/ks_dashboard_ninja/static/images/dashboardOverview/messages.svg" alt="messages" class="img-fluid" loading="lazy"/>
Chat
</button>
</div>
</t>
</div>
</div>
</div>
</t>
</div>
<!-- Profile Card Content -->
<div class="ks-profile-header">
<div class="ks-profile-image">
<img t-att-src="'data:image/png;base64,' + state.employee.image_1920"
alt="Employee Image"
t-att-class="state.employee.image_1920 ? '' : 'ks-default-image'" />
</div>
<div class="ks-profile-details">
<div class="ks-profile-info">
<h1>
<t t-esc="state.employee.name"/>
<t t-if="state.employee.employee_id">
- <t t-esc="state.employee.employee_id"/>
</t>
</h1>
<p class="ks-job-title">
<t t-if="state.employee.job_id">
<t t-esc="state.employee.job_id[1]"/>
</t>
<t t-if="!state.employee.job_id">
Add Job Title
</t>
</p>
<p class="ks-joined">
<t t-if="state.employee.doj">
Joined <t t-esc="state.employee.current_company_exp"/> ago
</t>
<t t-if="!state.employee.doj">
Joined Date: <t t-esc="state.employee.doj"/>
</t>
</p>
<p>
Birthday :
<t t-if="state.employee.birthday">
<t t-esc="state.employee.birthday"/>
</t>
<t t-else="">
--/--/----
</t>
</p>
</div>
<div class="ks-profile-contact">
<p>
<i class="fa fa-phone"></i>
<t t-if="state.employee.mobile_phone">
<t t-esc="state.employee.mobile_phone"/>
</t>
<t t-if="!state.employee.mobile_phone">
---
</t>
</p>
<p>
<i class="fa fa-envelope"></i>
<t t-if="state.employee.work_email">
<t t-esc="state.employee.work_email"/>
</t>
<t t-if="!state.employee.work_email">
---
</t>
</p>
<p>
<i class="fa fa-home"></i> Address:
<t t-if="state.employee.private_street">
<t t-esc="state.employee.private_street"/>
</t>
<t t-if="!state.employee.private_street">
---
</t>
</p>
</div>
</div>
<div class="ks-stat-att" t-on-click="attendance_sign_in_out">
<t t-if="state.employee.attendance_state == 'checked_out'">
<div class="ks-stat-atticon">
<i class="fa fa-sign-in"></i>
</div>
<div class="ks-stat-attcontent">
<p>Check In</p>
</div>
</t>
<t t-if="state.employee.attendance_state == 'checked_in'">
<div class="ks-stat-atticon">
<i class="fa fa-sign-out"></i>
</div>
<div class="ks-stat-attcontent">
<p>Check Out</p>
</div>
</t>
</div>
</div>
</div>
</t>
</templates>

View File

@ -0,0 +1,239 @@
.ks-profile-container {
font-family: 'Arial', sans-serif;
color: #333;
background: linear-gradient(135deg, #6a11cb, #2575fc);
border-radius: 15px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.ks-profile-header {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 20px;
color: #fff;
}
.ks-profile-image {
flex: 0 0 150px;
height: 150px;
border-radius: 50%;
overflow: hidden;
margin-right: 20px;
border: 4px solid rgba(255, 255, 255, 0.3);
}
.ks-profile-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.ks-default-image {
background-color: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
}
.ks-profile-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.ks-profile-info h1 {
margin: 0;
font-size: 1.8em;
font-weight: bold;
color: #fff;
}
.ks-job-title {
font-style: italic;
font-size: 1.1em;
color: rgba(255, 255, 255, 0.9);
margin: 0;
}
.ks-joined {
font-size: 0.9em;
opacity: 0.9;
margin: 0;
}
.ks-profile-contact {
margin-top: 10px;
font-size: 0.9em;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.ks-profile-contact p {
margin: 0;
display: flex;
align-items: center;
gap: 8px;
}
.ks-profile-contact i {
color: rgba(255, 255, 255, 0.8);
font-size: 1.2em;
}
.ks-stat-att {
width: 100px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
text-align: center;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
margin-left: auto;
}
.ks-stat-att:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-3px);
}
.ks-stat-atticon {
font-size: 2em;
color: #fff;
margin-bottom: 5px;
}
.ks-stat-attcontent p {
font-size: 0.9em;
color: #fff;
margin: 0;
font-weight: bold;
}
.ks-profile-container {
font-family: 'Arial', sans-serif;
color: #333;
background: linear-gradient(135deg, #6a11cb, #2575fc);
border-radius: 15px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.ks-profile-header {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 20px;
color: #fff;
}
.ks-profile-image {
flex: 0 0 150px;
height: 150px;
border-radius: 50%;
overflow: hidden;
margin-right: 20px;
border: 4px solid rgba(255, 255, 255, 0.3);
}
.ks-profile-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.ks-default-image {
background-color: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
}
.ks-profile-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.ks-profile-info h1 {
margin: 0;
font-size: 1.8em;
font-weight: bold;
color: #fff;
}
.ks-job-title {
font-style: italic;
font-size: 1.1em;
color: rgba(255, 255, 255, 0.9);
margin: 0;
}
.ks-joined {
font-size: 0.9em;
opacity: 0.9;
margin: 0;
}
.ks-profile-contact {
margin-top: 10px;
font-size: 0.9em;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.ks-profile-contact p {
margin: 0;
display: flex;
align-items: center;
gap: 8px;
}
.ks-profile-contact i {
color: rgba(255, 255, 255, 0.8);
font-size: 1.2em;
}
.ks-stat-att {
width: 100px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
text-align: center;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
margin-left: auto;
}
.ks-stat-att:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-3px);
}
.ks-stat-atticon {
font-size: 2em;
color: #fff;
margin-bottom: 5px;
}
.ks-stat-attcontent p {
font-size: 0.9em;
color: #fff;
margin: 0;
font-weight: bold;
}
/* Additional styles for the dashboard container */
.encapsulated-profile-container {
background: linear-gradient(135deg, #6a11cb, #2575fc);
border-radius: 15px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
overflow: hidden;
}

View File

@ -7,6 +7,8 @@ import { Ksdashboardtile } from '@ks_dashboard_ninja/components/ks_dashboard_til
import { patch } from "@web/core/utils/patch";
import { rpc } from "@web/core/network/rpc";
import { Ksdashboardkpiview } from '@ks_dashboard_ninja/components/ks_dashboard_kpi_view/ks_dashboard_kpi';
import { Ksdashboardprofile } from '@ks_dashboard_ninja/components/ks_dashboard_profile_view/ks_dashboard_profile';
patch(Ksdashboardgraph.prototype,{
@ -35,6 +37,7 @@ patch(Ksdashboardgraph.prototype,{
}
});
patch(Ksdashboardkpiview.prototype,{
async _openChatWizard(ev){

View File

@ -23,6 +23,7 @@ import { Ksdashboardtile } from '@ks_dashboard_ninja/components/ks_dashboard_til
import { Ksdashboardtodo } from '@ks_dashboard_ninja/components/ks_dashboard_to_do_item/ks_dashboard_to_do';
import { Ksdashboardkpiview } from '@ks_dashboard_ninja/components/ks_dashboard_kpi_view/ks_dashboard_kpi';
import { Ksdashboardgraph } from '@ks_dashboard_ninja/components/ks_dashboard_graphs/ks_dashboard_graphs';
import { Ksdashboardprofile } from '@ks_dashboard_ninja/components/ks_dashboard_profile_view/ks_dashboard_profile';
import { Dialog } from "@web/core/dialog/dialog";
import { rpc } from "@web/core/network/rpc";
@ -208,7 +209,7 @@ export class KsAIDashboardNinja extends Component {
// Add widgets to the grid
this.state.ks_dashboard_items.forEach(function (item) {
var graphs = ['ks_scatter_chart', 'ks_bar_chart', 'ks_horizontalBar_chart', 'ks_line_chart', 'ks_area_chart', 'ks_doughnut_chart', 'ks_polarArea_chart', 'ks_pie_chart', 'ks_flower_view', 'ks_radar_view', 'ks_radialBar_chart', 'ks_map_view', 'ks_funnel_chart', 'ks_bullet_chart', 'ks_to_do', 'ks_list_view'];
var graphs = ['ks_scatter_chart', 'ks_bar_chart', 'ks_horizontalBar_chart', 'ks_line_chart', 'ks_area_chart', 'ks_doughnut_chart', 'ks_polarArea_chart', 'ks_pie_chart', 'ks_flower_view', 'ks_radar_view', 'ks_radialBar_chart', 'ks_map_view', 'ks_funnel_chart', 'ks_bullet_chart', 'ks_to_do', 'ks_list_view', 'ks_profile'];
var ksPreview = document.getElementById(item.id);
if (ksPreview) {
@ -533,6 +534,6 @@ export class KsAIDashboardNinja extends Component {
speak_once(ev,item){
}
}
KsAIDashboardNinja.components = { Ksdashboardtile,Ksdashboardgraph,Ksdashboardkpiview, Ksdashboardtodo, Dialog, FormViewDialog};
KsAIDashboardNinja.components = { Ksdashboardtile,Ksdashboardgraph,Ksdashboardkpiview, Ksdashboardprofile, Ksdashboardtodo, Dialog, FormViewDialog};
KsAIDashboardNinja.template = "ksaiDashboardNinjaHeader"
registry.category("actions").add("ks_ai_dashboard_ninja",KsAIDashboardNinja);

View File

@ -10,6 +10,9 @@ import { Ksdashboardgraph } from './../components/ks_dashboard_graphs/ks_dashboa
import { Ksdashboardtile } from './../components/ks_dashboard_tile_view/ks_dashboard_tile';
import { Ksdashboardtodo } from './../components/ks_dashboard_to_do_item/ks_dashboard_to_do';
import { Ksdashboardkpiview } from './../components/ks_dashboard_kpi_view/ks_dashboard_kpi';
import { Ksdashboardprofile } from './../components/ks_dashboard_profile_view/ks_dashboard_profile';
import { rpc } from "@web/core/network/rpc";
import { user } from "@web/core/user";
@ -129,7 +132,7 @@ CustomDialog.props = {
}
CustomDialog.components = { Dialog, Ksdashboardgraph, Ksdashboardtile, Ksdashboardkpiview, Ksdashboardtodo };
CustomDialog.components = { Dialog, Ksdashboardgraph, Ksdashboardtile, Ksdashboardkpiview, Ksdashboardtodo, Ksdashboardprofile };
CustomDialog.template = "ks_dashboard_ninja.CustomDialog";

View File

@ -22,6 +22,7 @@ import { Ksdashboardlistview } from '@ks_dashboard_ninja/components/ks_dashboard
import { Ksdashboardtodo } from '@ks_dashboard_ninja/components/ks_dashboard_to_do_item/ks_dashboard_to_do';
import { Ksdashboardkpiview } from '@ks_dashboard_ninja/components/ks_dashboard_kpi_view/ks_dashboard_kpi';
import { Ksdashboardgraph } from '@ks_dashboard_ninja/components/ks_dashboard_graphs/ks_dashboard_graphs';
import { Ksdashboardprofile } from '@ks_dashboard_ninja/components/ks_dashboard_profile_view/ks_dashboard_profile';
import { KschatwithAI } from '@ks_dashboard_ninja/components/chatwithAI/ks_chat';
import { CustomFilter } from '@ks_dashboard_ninja/js/custom_filter';
import { DateTimePicker } from "@web/core/datetime/datetime_picker";
@ -379,7 +380,7 @@ export class KsDashboardNinja extends Component {
this.gridstackConfig = JSON.parse(this.ks_dashboard_data.ks_gridstack_config);
}
for (var i = 0; i < item.length; i++) {
var graphs = ['ks_scatter_chart','ks_bar_chart', 'ks_horizontalBar_chart', 'ks_line_chart', 'ks_area_chart', 'ks_doughnut_chart','ks_polarArea_chart','ks_pie_chart','ks_flower_view', 'ks_radar_view','ks_radialBar_chart','ks_map_view','ks_funnel_chart','ks_bullet_chart', 'ks_to_do', 'ks_list_view']
var graphs = ['ks_scatter_chart','ks_bar_chart', 'ks_horizontalBar_chart', 'ks_line_chart', 'ks_area_chart', 'ks_doughnut_chart','ks_polarArea_chart','ks_pie_chart','ks_flower_view', 'ks_radar_view','ks_radialBar_chart','ks_map_view','ks_funnel_chart','ks_bullet_chart', 'ks_to_do', 'ks_list_view', 'ks_profile']
var ks_preview = document.getElementById(item[i].id)
if (ks_preview && !this.ks_dashboard_data.ks_ai_explain_dash) {
if (item[i].id in self.gridstackConfig) {
@ -2022,7 +2023,7 @@ export class KsDashboardNinja extends Component {
}
KsDashboardNinja.components = { Ksdashboardtile, Ksdashboardlistview, Ksdashboardgraph, Ksdashboardkpiview, Ksdashboardtodo, DateTimePicker, DateTimeInput,KschatwithAI, CustomFilter};
KsDashboardNinja.components = { Ksdashboardtile, Ksdashboardlistview, Ksdashboardgraph, Ksdashboardkpiview, Ksdashboardtodo, Ksdashboardprofile, DateTimePicker, DateTimeInput,KschatwithAI, CustomFilter};
KsDashboardNinja.template = "ks_dashboard_ninja.KsDashboardNinjaHeader"
registry.category("actions").add("ks_dashboard_ninja", KsDashboardNinja);

View File

@ -35,7 +35,7 @@ export class KsGraphPreview extends Component {
el.remove();
});
if (rec.ks_dashboard_item_type !== 'ks_tile' && rec.ks_dashboard_item_type !== 'ks_kpi' &&
if (rec.ks_dashboard_item_type !== 'ks_tile' && rec.ks_dashboard_item_type !== 'ks_kpi' && rec.ks_dashboard_item_type !== 'ks_profile' &&
rec.ks_dashboard_item_type !== 'ks_list_view' && rec.ks_dashboard_item_type !== 'ks_to_do') {
if (rec.ks_data_calculation_type !== "query") {

View File

@ -24,7 +24,8 @@ export class KsDashboarditemtype extends Component {
{'id':14,'name':'List','Tech_name':'ks_list_view'},
{'id':15,'name':'Map','Tech_name':'ks_map_view'},
{'id':16,'name':'To-do','Tech_name':'ks_to_do'},
{'id':17,'name':'KPI','Tech_name':'ks_kpi'}
{'id':17,'name':'KPI','Tech_name':'ks_kpi'},
{'id':18,'name':'Profile','Tech_name':'ks_profile'}
]});
}

View File

@ -195,6 +195,15 @@
<path d="M14.7006 21.5609L20.8749 16.4059C21.3649 15.9943 22.0117 16.1119 22.3449 16.6607L24.6971 20.5809C25.0303 21.1297 25.6771 21.2277 26.1671 20.8357L32.3414 15.6807" stroke="" stroke-width="1.49595" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</t>
<t t-elif="item_name === 'Profile'">
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24 24C29.5228 24 34 19.5228 34 14C34 8.47715 29.5228 4 24 4C18.4772 4 14 8.47715 14 14C14 19.5228 18.4772 24 24 24Z"
stroke="" stroke-width="1.49595" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/>
<path d="M10 40C10 33.3726 16.3726 28 24 28C31.6274 28 38 33.3726 38 40"
stroke="" stroke-width="1.49595" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"/>
</svg>
</t>
</t>

View File

@ -0,0 +1,73 @@
/** @odoo-module */
import { registry } from "@web/core/registry";
import { useState, onMounted } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
import { Component } from "@odoo/owl";
class KsProfile extends Component {
setup() {
this.orm = useService("orm");
this.state = useState({
employee: {
name: '',
image_1920: '',
job_id: null,
current_company_exp: null,
doj: '',
employee_id: null,
birthday: '',
attendance_state: null,
mobile_phone: '',
work_email: '',
private_street: '',
department_id: ''
}
});
onMounted(async () => {
await this.fetchEmployeeData();
});
}
async fetchEmployeeData() {
try {
debugger;
const employeeData = await this.orm.call("hr.employee", 'get_user_employee_details');
if (employeeData && employeeData.length > 0) {
const employee = employeeData[0];
this.state.employee = {
name: employee.name,
image_1920: employee.image_1920,
doj: employee.doj,
job_id: employee.job_id,
employee_id: employee.employee_id,
current_company_exp: employee.current_company_exp,
attendance_state: employee.attendance_state,
birthday: employee.birthday,
mobile_phone: employee.mobile_phone,
work_email: employee.work_email,
private_street: employee.private_street,
department_id: employee.department_id ? employee.department_id[1] : ''
};
}
} catch (error) {
console.error("Error fetching employee data:", error);
}
}
async attendance_sign_in_out() {
try {
await this.orm.call('hr.employee', 'attendance_manual', [[this.state.employee.id]]);
this.state.employee.attendance_state =
this.state.employee.attendance_state === 'checked_in' ? 'checked_out' : 'checked_in';
} catch (error) {
console.error("Error updating attendance:", error);
}
}
}
KsProfile.template = "ks_profile";
export const KsProfileField = {
component: KsProfile,
};
registry.category("fields").add('ks_profile_preview', KsProfileField);

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="ks_profile" owl="1">
<div class="ks-profile-container">
<div class="ks-profile-header">
<div class="ks-profile-image">
<img t-att-src="'data:image/png;base64,' + state.employee.image_1920"
alt="Employee Image"
t-att-class="state.employee.image_1920 ? '' : 'ks-default-image'" />
</div>
<div class="ks-profile-details">
<div class="ks-profile-info">
<h1>
<t t-esc="state.employee.name"/>
<t t-if="state.employee.employee_id">
- <t t-esc="state.employee.employee_id"/>
</t>
</h1>
<p class="ks-job-title">
<t t-if="state.employee.job_id">
<t t-esc="state.employee.job_id[1]"/>
</t>
<t t-if="!state.employee.job_id">
Add Job Title
</t>
</p>
<p class="ks-joined">
<t t-if="state.employee.doj">
Joined <t t-esc="state.employee.current_company_exp"/> ago
</t>
<t t-if="!state.employee.doj">
Joined Date: <t t-esc="state.employee.doj"/>
</t>
</p>
<p>
Birthday :
<t t-if="state.employee.birthday">
<t t-esc="state.employee.birthday"/>
</t>
<t t-else="">
--/--/----
</t>
</p>
</div>
<div class="ks-profile-contact">
<p>
<i class="fa fa-phone"></i>
<t t-if="state.employee.mobile_phone">
<t t-esc="state.employee.mobile_phone"/>
</t>
<t t-if="!state.employee.mobile_phone">
---
</t>
</p>
<p>
<i class="fa fa-envelope"></i>
<t t-if="state.employee.work_email">
<t t-esc="state.employee.work_email"/>
</t>
<t t-if="!state.employee.work_email">
---
</t>
</p>
<p>
<i class="fa fa-home"></i> Address:
<t t-if="state.employee.private_street">
<t t-esc="state.employee.private_street"/>
</t>
<t t-if="!state.employee.private_street">
---
</t>
</p>
</div>
</div>
<div class="ks-stat-att" t-on-click="attendance_sign_in_out">
<t t-if="state.employee.attendance_state == 'checked_out'">
<div class="ks-stat-atticon">
<i class="fa fa-sign-in"></i>
</div>
<div class="ks-stat-attcontent">
<p>Check In</p>
</div>
</t>
<t t-if="state.employee.attendance_state == 'checked_in'">
<div class="ks-stat-atticon">
<i class="fa fa-sign-out"></i>
</div>
<div class="ks-stat-attcontent">
<p>Check Out</p>
</div>
</t>
</div>
</div>
</div>
</t>
</templates>

View File

@ -6,17 +6,17 @@
<div class="row" style="min-height: 60vh;">
<div class="col-6 d-flex flex-column align-items-stretch chat_explain_ai">
<t t-if="props.ks_dashboard_item_type === 'ks_tile'">
<Ksdashboardtile item="props.item"
dashboard_data="props.dashboard_data"
ksdatefilter="props.ksdatefilter"
pre_defined_filter="props.pre_defined_filter"
custom_filter="props.custom_filter"
hideButtons="0"
on_dialog="true"
ks_speak="props.ks_speak"/>
</t>
<t t-elif="props.ks_dashboard_item_type === 'ks_kpi'">
<!-- <t t-if="props.ks_dashboard_item_type === 'ks_tile'">-->
<!-- <Ksdashboardtile item="props.item"-->
<!-- dashboard_data="props.dashboard_data"-->
<!-- ksdatefilter="props.ksdatefilter"-->
<!-- pre_defined_filter="props.pre_defined_filter"-->
<!-- custom_filter="props.custom_filter"-->
<!-- hideButtons="0"-->
<!-- on_dialog="true"-->
<!-- ks_speak="props.ks_speak"/>-->
<!-- </t>-->
<t t-if="props.ks_dashboard_item_type === 'ks_kpi'">
<Ksdashboardkpiview item="props.item"
dashboard_data="props.dashboard_data"
ksdatefilter="props.ksdatefilter"

View File

@ -938,6 +938,9 @@
<t t-elif="items.ks_dashboard_item_type === 'ks_to_do'">
<Ksdashboardtodo item="items" hideButtons="1" dashboard_data="ks_dashboard_data" explain_ai_whole="explain_ai_whole"/>
</t>
<t t-elif="items.ks_dashboard_item_type === 'ks_profile'">
<Ksdashboardprofile item="items" hideButtons="1" on_dialog="on_dialog" dashboard_data="ks_dashboard_data" />
</t>
<t t-else="">
<Ksdashboardgraph item="items" hideButtons="1" explain_ai_whole="explain_ai_whole" dashboard_data="ks_dashboard_data" ksdatefilter="state.ksDateFilterSelection" pre_defined_filter = "state.pre_defined_filter" custom_filter="state.custom_filter" ks_speak="(ev)=>this.speak_once(ev,items)"/>
</t>

View File

@ -219,7 +219,7 @@
class="w-100"
widget="ks_dashboard_graph_preview"
invisible="ks_dashboard_item_type in
['ks_to_do', 'ks_tile', 'ks_list_view', 'ks_kpi', 'ks_map_view', 'ks_funnel_chart', 'ks_bullet_chart', 'ks_kpi']"/>
['ks_to_do', 'ks_tile', 'ks_list_view', 'ks_kpi', 'ks_map_view', 'ks_funnel_chart', 'ks_bullet_chart', 'ks_kpi', 'ks_profile']"/>
<field name="ks_list_view_preview" class="w-100 encapsulated-list-preview"
widget="ks_dashboard_list_view_preview"
invisible="ks_dashboard_item_type != 'ks_list_view'"/>
@ -240,10 +240,19 @@
<field name="ks_map_preview" string="Map Preview"
widget="ks_dashboard_map_preview" class="flex-grow-1 w-100"
invisible="ks_dashboard_item_type != 'ks_map_view'"/>
<field name="ks_profile_preview" string="Profile Preview"
widget="ks_profile_preview" class="flex-grow-1 w-100"
invisible="ks_dashboard_item_type != 'ks_profile'"/>
</div>
</div>
</div>
<div class="encapsulated-input-group col-xl-7 col-lg-6">
<div class="col-6 gap-2 d-flex flex-column" invisible="ks_dashboard_item_type != 'ks_profile'">
<label for="name" string="Name" class="o_form_label"/>
<field name="name" required="1" placeholder="Enter Name" class="form-control form-input-box w-100"/>
</div>
<div class="encapsulated-input-group col-xl-7 col-lg-6" invisible="ks_dashboard_item_type == 'ks_profile'">
<div class="chart-form-detail row">
<div class="col-6 gap-2 d-flex flex-column">
<label for="name" string="Name" class="o_form_label"/>
@ -267,7 +276,7 @@
<field name="ks_model_id" placeholder="Enter Model Name"
class="form-control form-input-box encapsulated-form-arrow bg-white"
options="{'no_create': True, 'no_create_edit':True, 'no_open': True}" nolabel="1"
context="{'current_id': id}" required="ks_dashboard_item_type != 'ks_to_do' and data_source == 'odoo'"
context="{'current_id': id}" required="ks_dashboard_item_type not in ['ks_to_do','ks_profile'] and data_source == 'odoo'"
invisible="ks_data_calculation_type == 'query' or data_source != 'odoo' or ks_dashboard_item_type not in
['ks_kpi','ks_radar_view','ks_flower_view','ks_tile','ks_bar_chart','ks_horizontalBar_chart','ks_line_chart','ks_area_chart','ks_pie_chart',
'ks_doughnut_chart','ks_polarArea_chart','ks_list_view','ks_scatter_chart','ks_radialBar_chart','ks_funnel_chart','ks_bullet_chart','ks_map_view']"