diff --git a/addons_extensions/hr_attendance_extended/models/hr_attendance_report.py b/addons_extensions/hr_attendance_extended/models/hr_attendance_report.py index 7b5ae2ab6..c7eaf2a68 100644 --- a/addons_extensions/hr_attendance_extended/models/hr_attendance_report.py +++ b/addons_extensions/hr_attendance_extended/models/hr_attendance_report.py @@ -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 diff --git a/addons_extensions/hr_attendance_extended/static/src/js/attendance_report.js b/addons_extensions/hr_attendance_extended/static/src/js/attendance_report.js index 92faba293..9c495baee 100644 --- a/addons_extensions/hr_attendance_extended/static/src/js/attendance_report.js +++ b/addons_extensions/hr_attendance_extended/static/src/js/attendance_report.js @@ -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); diff --git a/addons_extensions/hr_attendance_extended/static/src/xml/attendance_report.xml b/addons_extensions/hr_attendance_extended/static/src/xml/attendance_report.xml index f19d0884a..f22ace0f5 100644 --- a/addons_extensions/hr_attendance_extended/static/src/xml/attendance_report.xml +++ b/addons_extensions/hr_attendance_extended/static/src/xml/attendance_report.xml @@ -65,6 +65,7 @@ Check In Check Out Worked Hours + Break Hours Status @@ -78,7 +79,16 @@ - + + + + : + + + + + : + diff --git a/third_party_addons/ks_dashboard_ninja/__manifest__.py b/third_party_addons/ks_dashboard_ninja/__manifest__.py index f3a72f398..01deb3625 100644 --- a/third_party_addons/ks_dashboard_ninja/__manifest__.py +++ b/third_party_addons/ks_dashboard_ninja/__manifest__.py @@ -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', diff --git a/third_party_addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py b/third_party_addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py index 2612c4aec..73f172a38 100644 --- a/third_party_addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py +++ b/third_party_addons/ks_dashboard_ninja/models/ks_dashboard_ninja_items.py @@ -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") diff --git a/third_party_addons/ks_dashboard_ninja/static/images/profile.svg b/third_party_addons/ks_dashboard_ninja/static/images/profile.svg new file mode 100644 index 000000000..da51491cc --- /dev/null +++ b/third_party_addons/ks_dashboard_ninja/static/images/profile.svg @@ -0,0 +1,6 @@ + + + + diff --git a/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_graphs/ks_dashboard_graphs.js b/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_graphs/ks_dashboard_graphs.js index a2020f02a..c235f80fe 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_graphs/ks_dashboard_graphs.js +++ b/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_graphs/ks_dashboard_graphs.js @@ -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(); diff --git a/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_profile_view/ks_dashboard_profile.js b/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_profile_view/ks_dashboard_profile.js new file mode 100644 index 000000000..8a86ab172 --- /dev/null +++ b/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_profile_view/ks_dashboard_profile.js @@ -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"; \ No newline at end of file diff --git a/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_profile_view/ks_dashboard_profile.xml b/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_profile_view/ks_dashboard_profile.xml new file mode 100644 index 000000000..34e972601 --- /dev/null +++ b/third_party_addons/ks_dashboard_ninja/static/src/components/ks_dashboard_profile_view/ks_dashboard_profile.xml @@ -0,0 +1,330 @@ + + + + +
+
+
+
+ +
+
+ +
+
+
+
+ + + +
+
+ voice-img +
+
+ loading gif +
+
+
+
+
+ + + +
+
+
+
+
+ +
+ +
+ + +
+
+ + + +
+
+
+ + +
+
+ + + + + + + + + + +
+ + +
+
+
+ + +
+
+ Employee Image +
+
+
+

+ + + - + +

+

+ + + + + Add Job Title + +

+

+ + Joined ago + + + Joined Date: + +

+

+ Birthday : + + + + + --/--/---- + +

+
+
+

+ + + + + + --- + +

+

+ + + + + + --- + +

+

+ Address: + + + + + --- + +

+
+
+
+ +
+ +
+
+

Check In

+
+
+ +
+ +
+
+

Check Out

+
+
+
+
+
+
+ + +
\ No newline at end of file diff --git a/third_party_addons/ks_dashboard_ninja/static/src/css/ks_profile.css b/third_party_addons/ks_dashboard_ninja/static/src/css/ks_profile.css new file mode 100644 index 000000000..b9e85034a --- /dev/null +++ b/third_party_addons/ks_dashboard_ninja/static/src/css/ks_profile.css @@ -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; +} + diff --git a/third_party_addons/ks_dashboard_ninja/static/src/js/chatWizardIntegration.js b/third_party_addons/ks_dashboard_ninja/static/src/js/chatWizardIntegration.js index 0037e7197..ef542f5a1 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/js/chatWizardIntegration.js +++ b/third_party_addons/ks_dashboard_ninja/static/src/js/chatWizardIntegration.js @@ -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){ diff --git a/third_party_addons/ks_dashboard_ninja/static/src/js/ks_ai_dash_action.js b/third_party_addons/ks_dashboard_ninja/static/src/js/ks_ai_dash_action.js index 5aa665c83..c71540bd4 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/js/ks_ai_dash_action.js +++ b/third_party_addons/ks_dashboard_ninja/static/src/js/ks_ai_dash_action.js @@ -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); diff --git a/third_party_addons/ks_dashboard_ninja/static/src/js/ks_custom_dialog.js b/third_party_addons/ks_dashboard_ninja/static/src/js/ks_custom_dialog.js index fa87be887..3dccd9d63 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/js/ks_custom_dialog.js +++ b/third_party_addons/ks_dashboard_ninja/static/src/js/ks_custom_dialog.js @@ -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"; diff --git a/third_party_addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja_new.js b/third_party_addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja_new.js index f8e8a3814..348eefca1 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja_new.js +++ b/third_party_addons/ks_dashboard_ninja/static/src/js/ks_dashboard_ninja_new.js @@ -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); diff --git a/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_graph_view/ks_graph_view.js b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_graph_view/ks_graph_view.js index 788609e0d..19aae0908 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_graph_view/ks_graph_view.js +++ b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_graph_view/ks_graph_view.js @@ -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") { diff --git a/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_item_type_selection/ks_item_type_selection.js b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_item_type_selection/ks_item_type_selection.js index 52db29cc8..d3c2bcdcd 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_item_type_selection/ks_item_type_selection.js +++ b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_item_type_selection/ks_item_type_selection.js @@ -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'} ]}); } diff --git a/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_item_type_selection/ks_item_type_selection.xml b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_item_type_selection/ks_item_type_selection.xml index be4b44e41..d03efad85 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_item_type_selection/ks_item_type_selection.xml +++ b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_item_type_selection/ks_item_type_selection.xml @@ -195,6 +195,15 @@
+ + + + + + +
diff --git a/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_profile/ks_profile.js b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_profile/ks_profile.js new file mode 100644 index 000000000..feb393e24 --- /dev/null +++ b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_profile/ks_profile.js @@ -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); diff --git a/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_profile/ks_profile.xml b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_profile/ks_profile.xml new file mode 100644 index 000000000..56c4815fa --- /dev/null +++ b/third_party_addons/ks_dashboard_ninja/static/src/widgets/ks_profile/ks_profile.xml @@ -0,0 +1,96 @@ + + + +
+
+
+ Employee Image +
+
+
+

+ + + - + +

+

+ + + + + Add Job Title + +

+

+ + Joined ago + + + Joined Date: + +

+

+ Birthday : + + + + + --/--/---- + +

+
+
+

+ + + + + + --- + +

+

+ + + + + + --- + +

+

+ Address: + + + + + --- + +

+
+
+
+ +
+ +
+
+

Check In

+
+
+ +
+ +
+
+

Check Out

+
+
+
+
+
+
+
\ No newline at end of file diff --git a/third_party_addons/ks_dashboard_ninja/static/src/xml/custom_dialog_ai.xml b/third_party_addons/ks_dashboard_ninja/static/src/xml/custom_dialog_ai.xml index f528ef4b9..494fabcb6 100644 --- a/third_party_addons/ks_dashboard_ninja/static/src/xml/custom_dialog_ai.xml +++ b/third_party_addons/ks_dashboard_ninja/static/src/xml/custom_dialog_ai.xml @@ -6,17 +6,17 @@
- - - - + + + + + + + + + + + + + + diff --git a/third_party_addons/ks_dashboard_ninja/views/ks_dashboard_ninja_item_view.xml b/third_party_addons/ks_dashboard_ninja/views/ks_dashboard_ninja_item_view.xml index e23522ea2..ef370b860 100644 --- a/third_party_addons/ks_dashboard_ninja/views/ks_dashboard_ninja_item_view.xml +++ b/third_party_addons/ks_dashboard_ninja/views/ks_dashboard_ninja_item_view.xml @@ -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']"/> @@ -240,10 +240,19 @@ + +
-
+
+
+ +