Component

Module

Charts.js

Examples of 8 chart types and 2 mixed charts.
For more chart style design or other configs refer:
https://www.chartjs.org/docs/latest/

Directory

File Folder Link
Option.php (model) \\SYNAS\Allan\DOCUMENTATION\Component\Charts.js\rckuc\wolf\app\models
OptionController.php (controller) \\SYNAS\Allan\DOCUMENTATION\Component\Charts.js\rckuc\wolf\app\controllers
index.php (view) \\SYNAS\Allan\DOCUMENTATION\Component\Charts.js\rckuc\wolf\app\views\option
backend.php \\SYNAS\Allan\DOCUMENTATION\Component\Charts.js\rckuc\wolf\app\layouts
charts script .js \\SYNAS\Allan\DOCUMENTATION\Component\Charts.js\rckuc\wolf\admin\javascripts

 

Step 1

Update: backend.php

Include script link to <head> section

<!-- Charts.js --> <script type="text/javascript" charset="utf-8" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/chart-v4.5.1.js"></script> <script type="text/javascript" charset="utf-8" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/chartjs-plugin-datalabels@2-v2.2.0.js"></script>
    <!-- Charts.js -->
    <script type="text/javascript" charset="utf-8" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/chart-v4.5.1.js"></script>
    <script type="text/javascript" charset="utf-8" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/chartjs-plugin-datalabels@2-v2.2.0.js"></script>

Step 2

Update: Option.php (model)

Write a custom SQL query to get your data for chart

public static function getCountsByCategory() { $tablename = self::tableNameFromClassName('Option'); // Prepare SQL: get the total number of each unique category $sql = " SELECT category, COUNT(*) AS total FROM $tablename GROUP BY category "; $stmt = self::$__CONN__->prepare($sql); $stmt->execute(); $result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); return $result; }
public static function getCountsByCategory() {
    $tablename = self::tableNameFromClassName('Option');

    // Prepare SQL: get the total number of each unique category
    $sql = "
        SELECT 
        category,
        COUNT(*) AS total
        FROM $tablename
        GROUP BY category
        ";

    $stmt = self::$__CONN__->prepare($sql);
    $stmt->execute();

    $result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
    return $result;
}

Step 3

Update: OptionController.php (controller)

In your function browse() get data from model and display to frontend

public function browse(){ //→ checks permission, builds path safely, prevents directory traversal, sets file folder, fetches files and options, and renders the page. $this->_checkPermission(); $params = func_get_args(); // Collect Route Parameters $this->path = join('/', $params); // make sure there's a / at the end if (substr($this->path, -1, 1) != '/') $this->path .= '/'; //security // we dont allow back link if (strpos($this->path, '..') !== false) { if (Plugin::isEnabled('statistics_api')) { $user = null; if (AuthUser::isLoggedIn()) $user = AuthUser::getUserName(); $ip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR']:($_SERVER['REMOTE_ADDR']); $event = array('event_type' => 'hack_attempt', // simple event type identifier 'description' => __('A possible hack attempt was detected.'), // translatable description 'ipaddress' => $ip, 'username' => $user); Observer::notify('stats_file_manager_hack_attempt', $event); } } $this->fullpath = FILES_DIR.'/themes/option/images/'; // clean up nicely $this->fullpath = preg_replace('/\/\//', '/', $this->fullpath); $options = Record::query('select * from '.TABLE_PREFIX.'option'); // ORDER BY sequence asc, title desc $optionData = Option::getCountsByCategory(); $chartLabel = json_encode(array_keys($optionData)); $chartData = json_encode(array_values($optionData)); $this->display('option/index', array( 'dir' => $this->path, 'files' => $this->_getListFiles(), 'options' => $options, 'chartLabel' => $chartLabel, 'chartData' => $chartData, )); } // browse public function _getListFiles(){ $files = array(); if (is_dir($this->fullpath) && $handle = opendir($this->fullpath)) // Checks if $this->fullpath is a folder and open it. { $i = 0; // check each files ... while (false !== ($file = readdir($handle))) { // do not display . and the root .. if ($file == '.' || $file == '..') continue; $object = new stdClass; // Create an object to store properties. $file_stat = stat($this->fullpath.$file); // returns information about a file. // make the link depending on if it's a file or a dir $object->is_dir = false; $object->is_file = true; $object->link = '<a href="'.get_url('option/form'.$this->path.$file).'">'.$file.'</a>'; $object->name = $file; // humain size $object->size = convert_size($file_stat['size']); // permission list($object->perms, $object->chmod) = $this->_getPermissions($this->fullpath.$file); // date modification $object->mtime = date('D, j M, Y', $file_stat['mtime']); $files[$object->name] = $object; $i++; } // while closedir($handle); } uksort($files, 'strnatcmp'); return $files; } // _getListFiles
    public function browse(){ //→ checks permission, builds path safely, prevents directory traversal, sets file folder, fetches files and options, and renders the page.

        $this->_checkPermission();
        $params = func_get_args(); // Collect Route Parameters

        $this->path = join('/', $params);
        // make sure there's a / at the end
        if (substr($this->path, -1, 1) != '/') $this->path .= '/';

        //security

        // we dont allow back link
        if (strpos($this->path, '..') !== false)
        {
            if (Plugin::isEnabled('statistics_api'))
            {
                $user = null;
                if (AuthUser::isLoggedIn())
                    $user = AuthUser::getUserName();
                $ip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR']:($_SERVER['REMOTE_ADDR']);
                $event = array('event_type'  => 'hack_attempt',            // simple event type identifier
                               'description' => __('A possible hack attempt was detected.'), // translatable description
                               'ipaddress'   => $ip,
                               'username'    => $user);
                Observer::notify('stats_file_manager_hack_attempt', $event);
            }
        }

        $this->fullpath = FILES_DIR.'/themes/option/images/';
        // clean up nicely
        $this->fullpath = preg_replace('/\/\//', '/', $this->fullpath);

        $options = Record::query('select * from '.TABLE_PREFIX.'option'); // ORDER BY sequence asc, title desc
        $optionData = Option::getCountsByCategory();

        $chartLabel = json_encode(array_keys($optionData));
        $chartData = json_encode(array_values($optionData));

        $this->display('option/index', array(
            'dir'   => $this->path,
            'files' => $this->_getListFiles(),
            'options' => $options,
            'chartLabel' => $chartLabel,
            'chartData' => $chartData,
        ));
    } // browse

     public function _getListFiles(){
        $files = array();

        if (is_dir($this->fullpath) && $handle = opendir($this->fullpath)) // Checks if $this->fullpath is a folder and open it.
        {
            $i = 0;
            // check each files ...
            while (false !== ($file = readdir($handle)))
            {
                // do not display . and the root ..
                if ($file == '.' || $file == '..')
                    continue;

                $object = new stdClass; // Create an object to store properties.
                $file_stat = stat($this->fullpath.$file); // returns information about a file.

                // make the link depending on if it's a file or a dir

                $object->is_dir = false;
                $object->is_file = true;
                $object->link = '<a href="'.get_url('option/form'.$this->path.$file).'">'.$file.'</a>';

                $object->name = $file;
                // humain size
                $object->size = convert_size($file_stat['size']);
                // permission
                list($object->perms, $object->chmod) = $this->_getPermissions($this->fullpath.$file);
                // date modification
                $object->mtime = date('D, j M, Y', $file_stat['mtime']);

                $files[$object->name] = $object;

                $i++;
            } // while
            closedir($handle);
        }

        uksort($files, 'strnatcmp');
        return $files;
    } // _getListFiles

Step 4

Update: index.php (view)

In this index.php show examples of 8 chart types and 2 mixed charts, each provided as a <canvas> and <script> pair.
Copy any pair and customize the <script> config to render your chart.
<!-- Bar Chart --> <canvas id="bar_chart"></canvas> <script> $(document).ready(function () { const bar_chart = $('#bar_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller // Start render new Chart(bar_chart, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Category', // legend name data: dataValues, // Charts.js have default styling, but you can modify it: // backgroundColor: 'rgba(54, 162, 235, 0.6)', // borderColor: 'rgba(54, 162, 235, 1)', // borderWidth: 1, // borderRadius: 5, // hoverBackgroundColor: 'rgba(54, 162, 235, 0.8)' }] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { anchor: 'end', // Suggested for bar align: 'top', // Suggested for bar // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } }, scales: { y: { beginAtZero: true, // min: 10, // max: 100, // Charts.js will auto generate stepSize, you can modify custom stepSize // ticks: { // stepSize: 20 // Force increments of 20 // }, // grace: '10%', // You can add extra space above title: { display: true, text: 'Total Number', font: { weight: 'bold' } }, grid: { display: true, color: '#e5e5e5' } }, x: { title: { display: true, text: 'Category', font: { weight: 'bold' } }, grid: { display: false // Cleaner look on x-axis for bar chart } } } } }); }) </script> <!-- Line Chart --> <canvas id="line_chart"></canvas> <script> $(document).ready(function () { const line_chart = $('#line_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller // Start render new Chart(line_chart, { type: 'line', data: { labels: labels, datasets: [{ label: 'Category', // legend name data: dataValues, // borderColor: '#36A2EB', // backgroundColor: 'rgba(54, 162, 235, 0.15)', // borderWidth: 2, // tension: 0.35, // Smooth curve // fill: true, // pointRadius: 4, // pointHoverRadius: 6, // pointBackgroundColor: '#36A2EB' }] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { anchor: 'end', // Suggested for line chart align: 'top', // Suggested for line chart // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } }, scales: { y: { beginAtZero: true, // min: 10, // max: 100, // Charts.js will auto generate stepSize, you can modify custom stepSize // ticks: { // stepSize: 20 // Force increments of 20 // }, // grace: '10%', // You can add extra space above title: { display: true, text: 'Total Number', font: { weight: 'bold' } }, grid: { display: true, color: '#e5e5e5' } }, x: { title: { display: true, text: 'Category', font: { weight: 'bold' } }, grid: { display: false // Cleaner look on x-axis for line chart } } } } }); }) </script> <!-- Pie Chart --> <canvas id="pie_chart"></canvas> <script> $(document).ready(function () { const pie_chart = $('#pie_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller // Color palette const colorsPalette = [ '#36A2EB', '#FF6384', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40', '#2ecc71', '#C9CBCF' ]; // Start render new Chart(pie_chart, { type: 'pie', data: { labels: labels, datasets: [{ label: 'Category', // legend name data: dataValues, // Charts.js have default styling, but you can modify it: // backgroundColor: colorsPalette, // color array corresponding to your data // borderColor: '#ffffff', // borderWidth: 2, // hoverOffset: 10 }] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } } } }); }) </script> <!-- Doughnut Chart --> <canvas id="doughnut_chart"></canvas> <script> $(document).ready(function () { const doughnut_chart = $('#doughnut_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller // Color palette const colorsPalette = [ '#36A2EB', '#FF6384', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40', '#2ecc71', '#C9CBCF' ]; // Start render new Chart(doughnut_chart, { type: 'doughnut', data: { labels: labels, datasets: [{ label: 'Category', // legend name data: dataValues, // Charts.js have default styling, but you can modify it: // backgroundColor: colorsPalette, // color array corresponding to your data // borderColor: '#ffffff', // borderWidth: 2, // hoverOffset: 10 }] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, cutout: '50%', // Doughnut hole size plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } } } }); }) </script> <!-- PolarArea Chart --> <canvas id="polarArea_chart"></canvas> <script> $(document).ready(function () { const polarArea_chart = $('#polarArea_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller // Color palette const colorsPalette = [ '#36A2EB', '#FF6384', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40', '#2ecc71', '#C9CBCF' ]; // Start render new Chart(polarArea_chart, { type: 'polarArea', data: { labels: labels, datasets: [{ label: 'Category', // legend name data: dataValues, // Charts.js have default styling, but you can modify it: // backgroundColor: colorsPalette, // color array corresponding to your data // borderColor: '#ffffff', // borderWidth: 2, // hoverOffset: 10 }] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } }, scales: { r: { beginAtZero: true, // min: 1, // max: 20, ticks: { // Charts.js will auto generate stepSize, you can modify custom stepSize // stepSize: 3, backdropColor: 'transparent' }, grid: { color: '#e5e5e5' }, angleLines: { color: '#cccccc' } } } } }); }) </script> <!-- Radar Chart --> <canvas id="radar_chart"></canvas> <script> $(document).ready(function () { const radar_chart = $('#radar_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller // Start render new Chart(radar_chart, { type: 'radar', data: { labels: labels, datasets: [{ label: 'Category', // legend name data: dataValues, // Charts.js have default styling, but you can modify it: // backgroundColor: 'rgba(54, 162, 235, 0.2)', // borderColor: 'rgb(54, 162, 235)', // borderWidth: 2, // pointBackgroundColor: 'rgb(54, 162, 235)', // pointRadius: 4, // pointHoverRadius: 6 }] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } }, scales: { r: { beginAtZero: true, // min: 1, // max: 20, ticks: { // Charts.js will auto generate stepSize, you can modify custom stepSize // stepSize: 3, backdropColor: 'transparent' }, grid: { color: '#e5e5e5' }, angleLines: { color: '#cccccc' }, pointLabels: { font: { size: 13, weight: 'bold' } }, } } } }); }) </script> <!-- Scatter Chart --> <canvas id="scatter_chart"></canvas> <script> $(document).ready(function () { const scatter_chart = $('#scatter_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller // Map to {x, y} points const scatterPoints = labels.map((label, index) => ({ x: label, y: dataValues[index] })); // Start render new Chart(scatter_chart, { type: 'scatter', data: { datasets: [{ label: 'Category', // legend name data: scatterPoints, // Charts.js have default styling, but you can modify it: // backgroundColor: 'rgb(54, 162, 235)', // borderColor: 'rgb(54, 162, 235)', // borderWidth: 1, // pointRadius: 5, // pointHoverRadius: 7 }] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { anchor: 'end', align: 'top', // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } }, scales: { y: { beginAtZero: true, // min: 10, // max: 100, // Charts.js will auto generate stepSize, you can modify custom stepSize // ticks: { // stepSize: 20 // Force increments of 20 // }, // grace: '10%', // You can add extra space above title: { display: true, text: 'Total Number', font: { weight: 'bold' } }, grid: { display: true, color: '#e5e5e5' } }, x: { // For scatter x & y normally is numberic type: 'category', // 'category' for string, default type is 'linear' for numberic // beginAtZero: true, // min: 1, // max: 10, // Charts.js will auto generate stepSize, you can modify custom stepSize // ticks: { // stepSize: 10 // } title: { display: true, text: 'Category', font: { weight: 'bold' } }, grid: { display: true // Cleaner look on x-axis for scatter chart } } } } }); }) </script> <!-- Bubble Chart --> <canvas id="bubble_chart"></canvas> <script> $(document).ready(function () { const bubble_chart = $('#bubble_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller // Map to {x, y} points const bubblePoints = labels.map((label, index) => ({ x: label, y: dataValues[index], r: dataValues[index], })); // Start render new Chart(bubble_chart, { type: 'bubble', data: { datasets: [{ label: 'Category', // legend name data: bubblePoints, // Charts.js have default styling, but you can modify it: // backgroundColor: 'rgba(54, 162, 235, 0.6)', // borderColor: 'rgb(54, 162, 235)', // borderWidth: 1, // hoverBackgroundColor: 'rgba(54, 162, 235, 0.8)' }] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { anchor: 'end', align: 'top', // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } }, scales: { y: { beginAtZero: true, // min: 10, // max: 100, // Charts.js will auto generate stepSize, you can modify custom stepSize // ticks: { // stepSize: 20 // Force increments of 20 // }, // grace: '10%', // You can add extra space above title: { display: true, text: 'Total Number', font: { weight: 'bold' } }, grid: { display: true, color: '#e5e5e5' } }, x: { // For scatter x & y normally is numberic type: 'category', // 'category' for string, default type is 'linear' for numberic // beginAtZero: true, // min: 1, // max: 10, // Charts.js will auto generate stepSize, you can modify custom stepSize // ticks: { // stepSize: 10 // } title: { display: true, text: 'Category', font: { weight: 'bold' } }, grid: { display: true // Cleaner look on x-axis for scatter chart } } }, } }); }) </script> <!-- Mix Combination Bar + Bar Bar + Line Line + Line Scatter + Line Scatter + Bubble Doughnut + Doughnut Radar + Radar --> <!-- Bar + Line Chart --> <canvas id="mix_chart"></canvas> <script> $(document).ready(function () { const mix_chart = $('#mix_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller const dataValues_2 = <?php echo $chartData; ?>; // You can have another set of data // Start render new Chart(mix_chart, { data: { labels: labels, datasets: [{ type: 'bar', label: 'Category', // legend name data: dataValues, // Charts.js have default styling, but you can modify it: // refer bar styling }, { type: 'line', label: 'Category 2', // legend name data: dataValues_2, // Charts.js have default styling, but you can modify it: // refer line styling }, ] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { anchor: 'end', // Suggested for mix align: 'top', // Suggested for mix // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } }, scales: { y: { beginAtZero: true, // min: 10, // max: 100, // Charts.js will auto generate stepSize, you can modify custom stepSize // ticks: { // stepSize: 20 // Force increments of 20 // }, // grace: '10%', // You can add extra space above title: { display: true, text: 'Total Number', font: { weight: 'bold' } }, grid: { display: true, color: '#e5e5e5' } }, x: { title: { display: true, text: 'Category', font: { weight: 'bold' } }, grid: { display: false // Cleaner look on x-axis for bar chart } } }, // Show 2 or more intersecting points in ONE tooltip (For Mix Chart Only) interaction: { mode: 'index', intersect: false } } }); }) </script> <!-- Scatter + Bubble Chart --> <canvas id="scatter_bubble_chart"></canvas> <script> $(document).ready(function () { const scatter_bubble_chart = $('#scatter_bubble_chart')[0].getContext('2d'); // To Enable DataLabels Chart.register(ChartDataLabels); const labels = <?php echo $chartLabel; ?>; // From model & controller const dataValues = <?php echo $chartData; ?>; // From model & controller const dataValues_2 = <?php echo $chartData; ?>; // You can have another set of data // Map to {x, y} points const scatterPoints = labels.map((label, index) => ({ x: label, y: dataValues[index] })); // Map to {x, y} points const bubblePoints = labels.map((label, index) => ({ x: label, y: dataValues_2[index], r: dataValues_2[index], })); // Start render new Chart(scatter_bubble_chart, { data: { labels: labels, datasets: [{ type: 'scatter', label: 'Category', // legend name data: scatterPoints, // Charts.js have default styling, but you can modify it: // refer bar styling }, { type: 'bubble', label: 'Category 2', // legend name data: bubblePoints, // Charts.js have default styling, but you can modify it: // refer line styling }, ] }, options: { // indexAxis: 'y', // swap X & Y responsive: true, // Sometime datalabel or if something cutoff you can set padding layout: { padding: { top: 0, right: 60, bottom: 0, left: 0 } }, plugins: { // Chart Title title: { display: true, text: 'Documentation Chart', font: { size: 20 } }, // Legend legend: { display: true, position: 'bottom' }, // Tooltips tooltip: { enabled: true, // You can modify your tooltip text inside 'callbacks' callbacks: { // For Normal Tooltip // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}` // For Percentage Tooltip label: (ctx) => { const rawValue = ctx.raw; const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0; return ` ${ctx.dataset.label}: ${percentage}%`; } } }, // Data Labels datalabels: { anchor: 'end', // Suggested for mix align: 'top', // Suggested for mix // You can modify your datalabel text inside 'formatter' formatter: (value, ctx) => { // For Normal Datalabel // return value; // For Percentage Datalabel const v = value?.y ?? value; const label = ctx.chart.data.labels[ctx.dataIndex]; const total = ctx.dataset.data.reduce((a, b) => { const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b; return a + (Number(val) || 0); }, 0); const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00"; return `${label}: ${percentage}%`; }, // rotation: 270, // rotate datalabel font: { weight: 'bold' } } }, scales: { y: { beginAtZero: true, // min: 10, // max: 100, // Charts.js will auto generate stepSize, you can modify custom stepSize // ticks: { // stepSize: 20 // Force increments of 20 // }, // grace: '10%', // You can add extra space above title: { display: true, text: 'Total Number', font: { weight: 'bold' } }, grid: { display: true, color: '#e5e5e5' } }, x: { type: 'category', title: { display: true, text: 'Category', font: { weight: 'bold' } }, grid: { display: false // Cleaner look on x-axis for bar chart } } }, // Show 2 or more intersecting points in ONE tooltip (For Mix Chart Only) interaction: { mode: 'index', intersect: false } } }); }) </script>
<!-- Bar Chart -->
<canvas id="bar_chart"></canvas>
<script>
    $(document).ready(function () {            
        const bar_chart = $('#bar_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller

        // Start render
        new Chart(bar_chart, {
            type: 'bar',
            data: {
                labels: labels,
                datasets: [{
                    label: 'Category', // legend name
                    data: dataValues,
                    // Charts.js have default styling, but you can modify it:
                    // backgroundColor: 'rgba(54, 162, 235, 0.6)',
                    // borderColor: 'rgba(54, 162, 235, 1)',
                    // borderWidth: 1,
                    // borderRadius: 5,
                    // hoverBackgroundColor: 'rgba(54, 162, 235, 0.8)'
                }]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                        anchor: 'end', // Suggested for bar
                        align: 'top', // Suggested for bar
                        // You can modify your datalabel text inside 'formatter'
                        formatter: (value, ctx) => {
                          // For Normal Datalabel
                          // return value;
                          
                          // For Percentage Datalabel
                          const v = value?.y ?? value;
                          
                          const label = ctx.chart.data.labels[ctx.dataIndex];
                          
                          const total = ctx.dataset.data.reduce((a, b) => {
                              const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                              return a + (Number(val) || 0);
                          }, 0);
                          
                          const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                          return `${label}: ${percentage}%`;
                        },
                        // rotation: 270, // rotate datalabel
                        font: { weight: 'bold' }
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        // min: 10,
                        // max: 100,
                        // Charts.js will auto generate stepSize, you can modify custom stepSize 
                        // ticks: {
                        //     stepSize: 20 // Force increments of 20
                        // },
                        // grace: '10%', // You can add extra space above
                        title: {
                            display: true,
                            text: 'Total Number',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: true,
                            color: '#e5e5e5'
                        }
                    },
                    x: {
                        title: {
                            display: true,
                            text: 'Category',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: false // Cleaner look on x-axis for bar chart
                        }
                    }
                }
            }
        });
      })
</script>

<!-- Line Chart -->
<canvas id="line_chart"></canvas>
<script>
    $(document).ready(function () {
        const line_chart = $('#line_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller

        // Start render
        new Chart(line_chart, {
            type: 'line',
            data: {
                labels: labels,
                datasets: [{
                    label: 'Category', // legend name
                    data: dataValues,
                    // borderColor: '#36A2EB',
                    // backgroundColor: 'rgba(54, 162, 235, 0.15)',
                    // borderWidth: 2,
                    // tension: 0.35,       // Smooth curve
                    // fill: true,
                    // pointRadius: 4,
                    // pointHoverRadius: 6,
                    // pointBackgroundColor: '#36A2EB'
                }]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                        anchor: 'end', // Suggested for line chart
                        align: 'top', // Suggested for line chart
                        // You can modify your datalabel text inside 'formatter'
                        formatter: (value, ctx) => {
                          // For Normal Datalabel
                          // return value;
                          
                          // For Percentage Datalabel
                          const v = value?.y ?? value;
                          
                          const label = ctx.chart.data.labels[ctx.dataIndex];
                          
                          const total = ctx.dataset.data.reduce((a, b) => {
                              const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                              return a + (Number(val) || 0);
                          }, 0);
                          
                          const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                          return `${label}: ${percentage}%`;
                        },
                        // rotation: 270, // rotate datalabel
                        font: { weight: 'bold' }
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        // min: 10,
                        // max: 100,
                        // Charts.js will auto generate stepSize, you can modify custom stepSize 
                        // ticks: {
                        //     stepSize: 20 // Force increments of 20
                        // },
                        // grace: '10%', // You can add extra space above
                        title: {
                            display: true,
                            text: 'Total Number',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: true,
                            color: '#e5e5e5'
                        }
                    },
                    x: {
                        title: {
                            display: true,
                            text: 'Category',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: false // Cleaner look on x-axis for line chart
                        }
                    }
                }
            }
        });
      })
</script>

<!-- Pie Chart -->
<canvas id="pie_chart"></canvas>
<script>
    $(document).ready(function () {
        const pie_chart = $('#pie_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller

        // Color palette
        const colorsPalette = [
            '#36A2EB',
            '#FF6384',
            '#FFCE56',
            '#4BC0C0',
            '#9966FF',
            '#FF9F40',
            '#2ecc71',
            '#C9CBCF'
        ];

        // Start render
        new Chart(pie_chart, {
            type: 'pie',
            data: {
                labels: labels,
                datasets: [{
                    label: 'Category', // legend name
                    data: dataValues,
                    // Charts.js have default styling, but you can modify it:
                    // backgroundColor: colorsPalette, // color array corresponding to your data
                    // borderColor: '#ffffff',
                    // borderWidth: 2,
                    // hoverOffset: 10
                }]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                        // You can modify your datalabel text inside 'formatter'
                        formatter: (value, ctx) => {
                          // For Normal Datalabel
                          // return value;
                          
                          // For Percentage Datalabel
                          const v = value?.y ?? value;
                          
                          const label = ctx.chart.data.labels[ctx.dataIndex];
                          
                          const total = ctx.dataset.data.reduce((a, b) => {
                              const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                              return a + (Number(val) || 0);
                          }, 0);
                          
                          const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                          return `${label}: ${percentage}%`;
                        },
                        // rotation: 270, // rotate datalabel
                        font: { weight: 'bold' }
                    }
                }
            }
        });
      })
</script>

<!-- Doughnut Chart -->
<canvas id="doughnut_chart"></canvas>
<script>
    $(document).ready(function () {
        const doughnut_chart = $('#doughnut_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller

        // Color palette
        const colorsPalette = [
            '#36A2EB',
            '#FF6384',
            '#FFCE56',
            '#4BC0C0',
            '#9966FF',
            '#FF9F40',
            '#2ecc71',
            '#C9CBCF'
        ];

        // Start render
        new Chart(doughnut_chart, {
            type: 'doughnut',
            data: {
                labels: labels,
                datasets: [{
                    label: 'Category', // legend name
                    data: dataValues,
                    // Charts.js have default styling, but you can modify it:
                    // backgroundColor: colorsPalette, // color array corresponding to your data
                    // borderColor: '#ffffff',
                    // borderWidth: 2,
                    // hoverOffset: 10
                }]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                cutout: '50%', // Doughnut hole size
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                        // You can modify your datalabel text inside 'formatter'
                        formatter: (value, ctx) => {
                          // For Normal Datalabel
                          // return value;
                          
                          // For Percentage Datalabel
                          const v = value?.y ?? value;
                          
                          const label = ctx.chart.data.labels[ctx.dataIndex];
                          
                          const total = ctx.dataset.data.reduce((a, b) => {
                              const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                              return a + (Number(val) || 0);
                          }, 0);
                          
                          const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                          return `${label}: ${percentage}%`;
                        },
                        // rotation: 270, // rotate datalabel
                        font: { weight: 'bold' }
                      }
                }
            }
        });
      })
</script>

<!-- PolarArea Chart -->
<canvas id="polarArea_chart"></canvas>
<script>
    $(document).ready(function () {
        const polarArea_chart = $('#polarArea_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller

        // Color palette
        const colorsPalette = [
            '#36A2EB',
            '#FF6384',
            '#FFCE56',
            '#4BC0C0',
            '#9966FF',
            '#FF9F40',
            '#2ecc71',
            '#C9CBCF'
        ];

        // Start render
        new Chart(polarArea_chart, {
            type: 'polarArea',
            data: {
                labels: labels,
                datasets: [{
                    label: 'Category', // legend name
                    data: dataValues,
                    // Charts.js have default styling, but you can modify it:
                    // backgroundColor: colorsPalette, // color array corresponding to your data
                    // borderColor: '#ffffff',
                    // borderWidth: 2,
                    // hoverOffset: 10
                }]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                      // You can modify your datalabel text inside 'formatter'
                      formatter: (value, ctx) => {
                        // For Normal Datalabel
                        // return value;
                        
                        // For Percentage Datalabel
                        const v = value?.y ?? value;
                        
                        const label = ctx.chart.data.labels[ctx.dataIndex];
                        
                        const total = ctx.dataset.data.reduce((a, b) => {
                            const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                            return a + (Number(val) || 0);
                        }, 0);
                        
                        const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                        return `${label}: ${percentage}%`;
                      },
                    //   rotation: 270, // rotate datalabel
                      font: { weight: 'bold' }
                    }
                },
                scales: {
                    r: {
                        beginAtZero: true,
                        // min: 1,
                        // max: 20,
                        ticks: {
                            // Charts.js will auto generate stepSize, you can modify custom stepSize 
                            // stepSize: 3,
                            backdropColor: 'transparent'
                        },
                        grid: {
                            color: '#e5e5e5'
                        },
                        angleLines: {
                            color: '#cccccc'
                        }
                    }
                }
            }
        });
      })
</script>

<!-- Radar Chart -->
<canvas id="radar_chart"></canvas>
<script>
    $(document).ready(function () {
        const radar_chart = $('#radar_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller

        // Start render
        new Chart(radar_chart, {
            type: 'radar',
            data: {
                labels: labels,
                datasets: [{
                    label: 'Category', // legend name
                    data: dataValues,
                    // Charts.js have default styling, but you can modify it:
                    // backgroundColor: 'rgba(54, 162, 235, 0.2)',
                    // borderColor: 'rgb(54, 162, 235)',
                    // borderWidth: 2,
                    // pointBackgroundColor: 'rgb(54, 162, 235)',
                    // pointRadius: 4,
                    // pointHoverRadius: 6
                }]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                      // You can modify your datalabel text inside 'formatter'
                      formatter: (value, ctx) => {
                        // For Normal Datalabel
                        // return value;
                        
                        // For Percentage Datalabel
                        const v = value?.y ?? value;
                        
                        const label = ctx.chart.data.labels[ctx.dataIndex];
                        
                        const total = ctx.dataset.data.reduce((a, b) => {
                            const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                            return a + (Number(val) || 0);
                        }, 0);
                        
                        const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                        return `${label}: ${percentage}%`;
                      },
                    //   rotation: 270, // rotate datalabel
                      font: { weight: 'bold' }
                    }
                },
                scales: {
                    r: {
                        beginAtZero: true,
                        // min: 1,
                        // max: 20,
                        ticks: {
                            // Charts.js will auto generate stepSize, you can modify custom stepSize 
                            // stepSize: 3,
                            backdropColor: 'transparent'
                        },
                        grid: {
                            color: '#e5e5e5'
                        },
                        angleLines: {
                            color: '#cccccc'
                        },
                        pointLabels: {
                            font: {
                                size: 13,
                                weight: 'bold'
                            }
                        },
                    }
                }
            }
        });
      })
</script>

<!-- Scatter Chart -->
<canvas id="scatter_chart"></canvas>
<script>
    $(document).ready(function () {            
        const scatter_chart = $('#scatter_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller

        // Map to {x, y} points
        const scatterPoints = labels.map((label, index) => ({
            x: label,
            y: dataValues[index]
        }));

        // Start render
        new Chart(scatter_chart, {
            type: 'scatter',
            data: {
                datasets: [{
                    label: 'Category', // legend name
                    data: scatterPoints,
                    // Charts.js have default styling, but you can modify it:
                    // backgroundColor: 'rgb(54, 162, 235)',
                    // borderColor: 'rgb(54, 162, 235)',
                    // borderWidth: 1,
                    // pointRadius: 5,
                    // pointHoverRadius: 7
                }]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                        anchor: 'end',
                        align: 'top',
                        // You can modify your datalabel text inside 'formatter'
                        formatter: (value, ctx) => {
                          // For Normal Datalabel
                          // return value;
                          
                          // For Percentage Datalabel
                          const v = value?.y ?? value;
                          
                          const label = ctx.chart.data.labels[ctx.dataIndex];
                          
                          const total = ctx.dataset.data.reduce((a, b) => {
                              const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                              return a + (Number(val) || 0);
                          }, 0);
                          
                          const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                          return `${label}: ${percentage}%`;
                        },
                        // rotation: 270, // rotate datalabel
                        font: { weight: 'bold' }
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        // min: 10,
                        // max: 100,
                        // Charts.js will auto generate stepSize, you can modify custom stepSize 
                        // ticks: {
                        //     stepSize: 20 // Force increments of 20
                        // },
                        // grace: '10%', // You can add extra space above
                        title: {
                            display: true,
                            text: 'Total Number',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: true,
                            color: '#e5e5e5'
                        }
                    },
                    x: {
                        // For scatter x & y normally is numberic
                        type: 'category', // 'category' for string, default type is 'linear' for numberic 
                        // beginAtZero: true,
                        // min: 1,
                        // max: 10,
                        // Charts.js will auto generate stepSize, you can modify custom stepSize
                        // ticks: {
                        //     stepSize: 10
                        // }
                        title: {
                            display: true,
                            text: 'Category',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: true // Cleaner look on x-axis for scatter chart
                        }
                    }
                }
            }
        });
      })
</script>

<!-- Bubble Chart -->
<canvas id="bubble_chart"></canvas>
<script>
    $(document).ready(function () {            
        const bubble_chart = $('#bubble_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller

        // Map to {x, y} points
        const bubblePoints = labels.map((label, index) => ({
            x: label,
            y: dataValues[index],
            r: dataValues[index],
        }));

        // Start render
        new Chart(bubble_chart, {
            type: 'bubble',
            data: {
                datasets: [{
                    label: 'Category', // legend name
                    data: bubblePoints,
                    // Charts.js have default styling, but you can modify it:
                    // backgroundColor: 'rgba(54, 162, 235, 0.6)',
                    // borderColor: 'rgb(54, 162, 235)',
                    // borderWidth: 1,
                    // hoverBackgroundColor: 'rgba(54, 162, 235, 0.8)'
                }]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                        anchor: 'end',
                        align: 'top',
                        // You can modify your datalabel text inside 'formatter'
                        formatter: (value, ctx) => {
                          // For Normal Datalabel
                          // return value;
                          
                          // For Percentage Datalabel
                          const v = value?.y ?? value;
                          
                          const label = ctx.chart.data.labels[ctx.dataIndex];
                          
                          const total = ctx.dataset.data.reduce((a, b) => {
                              const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                              return a + (Number(val) || 0);
                          }, 0);
                          
                          const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                          return `${label}: ${percentage}%`;
                        },
                        // rotation: 270, // rotate datalabel
                        font: { weight: 'bold' }
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        // min: 10,
                        // max: 100,
                        // Charts.js will auto generate stepSize, you can modify custom stepSize 
                        // ticks: {
                        //     stepSize: 20 // Force increments of 20
                        // },
                        // grace: '10%', // You can add extra space above
                        title: {
                            display: true,
                            text: 'Total Number',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: true,
                            color: '#e5e5e5'
                        }
                    },
                    x: {
                        // For scatter x & y normally is numberic
                        type: 'category', // 'category' for string, default type is 'linear' for numberic 
                        // beginAtZero: true,
                        // min: 1,
                        // max: 10,
                        // Charts.js will auto generate stepSize, you can modify custom stepSize
                        // ticks: {
                        //     stepSize: 10
                        // }
                        title: {
                            display: true,
                            text: 'Category',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: true // Cleaner look on x-axis for scatter chart
                        }
                    }
                },
            }
        });
      })
</script>

<!-- 
Mix Combination
Bar + Bar
Bar + Line
Line + Line
Scatter + Line
Scatter + Bubble
Doughnut + Doughnut
Radar + Radar 
-->

<!-- Bar + Line Chart -->
<canvas id="mix_chart"></canvas>
<script>
    $(document).ready(function () {            
        const mix_chart = $('#mix_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller
        const dataValues_2 = <?php echo $chartData; ?>;  // You can have another set of data

        // Start render
        new Chart(mix_chart, {
            data: {
                labels: labels,
                datasets: [{
                    type: 'bar',
                    label: 'Category', // legend name
                    data: dataValues,
                    // Charts.js have default styling, but you can modify it:
                    // refer bar styling
                },
                {
                    type: 'line',
                    label: 'Category 2', // legend name
                    data: dataValues_2,
                    // Charts.js have default styling, but you can modify it:
                    // refer line styling
                },
              ]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                        anchor: 'end', // Suggested for mix
                        align: 'top', // Suggested for mix
                        // You can modify your datalabel text inside 'formatter'
                        formatter: (value, ctx) => {
                          // For Normal Datalabel
                          // return value;
                          
                          // For Percentage Datalabel
                          const v = value?.y ?? value;
                          
                          const label = ctx.chart.data.labels[ctx.dataIndex];
                          
                          const total = ctx.dataset.data.reduce((a, b) => {
                              const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                              return a + (Number(val) || 0);
                          }, 0);
                          
                          const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                          return `${label}: ${percentage}%`;
                        },
                        // rotation: 270, // rotate datalabel
                        font: { weight: 'bold' }
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        // min: 10,
                        // max: 100,
                        // Charts.js will auto generate stepSize, you can modify custom stepSize 
                        // ticks: {
                        //     stepSize: 20 // Force increments of 20
                        // },
                        // grace: '10%', // You can add extra space above
                        title: {
                            display: true,
                            text: 'Total Number',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: true,
                            color: '#e5e5e5'
                        }
                    },
                    x: {
                        title: {
                            display: true,
                            text: 'Category',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: false // Cleaner look on x-axis for bar chart
                        }
                    }
                },
                // Show 2 or more intersecting points in ONE tooltip (For Mix Chart Only)
                interaction: {
                    mode: 'index',
                    intersect: false
                }
            }
        });
      })
</script>

<!-- Scatter + Bubble Chart -->
<canvas id="scatter_bubble_chart"></canvas>
<script>
    $(document).ready(function () {            
        const scatter_bubble_chart = $('#scatter_bubble_chart')[0].getContext('2d');

        // To Enable DataLabels
        Chart.register(ChartDataLabels);

        const labels = <?php echo $chartLabel; ?>;  // From model & controller
        const dataValues = <?php echo $chartData; ?>;  // From model & controller
        const dataValues_2 = <?php echo $chartData; ?>;  // You can have another set of data

        // Map to {x, y} points
        const scatterPoints = labels.map((label, index) => ({
            x: label,
            y: dataValues[index]
        }));

        // Map to {x, y} points
        const bubblePoints = labels.map((label, index) => ({
            x: label,
            y: dataValues_2[index],
            r: dataValues_2[index],
        }));

        // Start render
        new Chart(scatter_bubble_chart, {
            data: {
                labels: labels,
                datasets: [{
                    type: 'scatter',
                    label: 'Category', // legend name
                    data: scatterPoints,
                    // Charts.js have default styling, but you can modify it:
                    // refer bar styling
                },
                {
                    type: 'bubble',
                    label: 'Category 2', // legend name
                    data: bubblePoints,
                    // Charts.js have default styling, but you can modify it:
                    // refer line styling
                },
              ]
            },
            options: {
                // indexAxis: 'y',   // swap X & Y
                responsive: true,
                // Sometime datalabel or if something cutoff you can set padding
                layout: {
                    padding: {
                        top: 0,
                        right: 60,
                        bottom: 0,
                        left: 0
                    }
                },
                plugins: {
                    // Chart Title
                    title: {
                        display: true,
                        text: 'Documentation Chart',
                        font: { size: 20 }
                    },
                    // Legend
                    legend: {
                        display: true,
                        position: 'bottom'
                    },
                    // Tooltips
                    tooltip: {
                        enabled: true,
                        // You can modify your tooltip text inside 'callbacks'
                        callbacks: {
                          // For Normal Tooltip
                          // label: (ctx) => `${ctx.dataset.label}: ${ctx.raw}`
                          
                          // For Percentage Tooltip
                          label: (ctx) => {
                              const rawValue = ctx.raw;
                              const v = (typeof rawValue === 'object' && rawValue !== null) ? (rawValue.y ?? rawValue.x ?? 0) : rawValue;
                              
                              const total = ctx.dataset.data.reduce((a, b) => {
                                  const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                                  return a + (Number(val) || 0);
                              }, 0);

                              const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : 0;
                              
                              return ` ${ctx.dataset.label}: ${percentage}%`;
                          }
                        }
                    },
                    // Data Labels
                    datalabels: {
                        anchor: 'end', // Suggested for mix
                        align: 'top', // Suggested for mix
                        // You can modify your datalabel text inside 'formatter'
                        formatter: (value, ctx) => {
                          // For Normal Datalabel
                          // return value;
                          
                          // For Percentage Datalabel
                          const v = value?.y ?? value;
                          
                          const label = ctx.chart.data.labels[ctx.dataIndex];
                          
                          const total = ctx.dataset.data.reduce((a, b) => {
                              const val = (typeof b === 'object' && b !== null) ? (b.y ?? b.x ?? 0) : b;
                              return a + (Number(val) || 0);
                          }, 0);
                          
                          const percentage = total > 0 ? ((v / total) * 100).toFixed(2) : "0.00";
                          return `${label}: ${percentage}%`;
                        },
                        // rotation: 270, // rotate datalabel
                        font: { weight: 'bold' }
                    }
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        // min: 10,
                        // max: 100,
                        // Charts.js will auto generate stepSize, you can modify custom stepSize 
                        // ticks: {
                        //     stepSize: 20 // Force increments of 20
                        // },
                        // grace: '10%', // You can add extra space above
                        title: {
                            display: true,
                            text: 'Total Number',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: true,
                            color: '#e5e5e5'
                        }
                    },
                    x: {
                        type: 'category',
                        title: {
                            display: true,
                            text: 'Category',
                            font: { weight: 'bold' }
                        },
                        grid: {
                            display: false // Cleaner look on x-axis for bar chart
                        }
                    }
                },
                // Show 2 or more intersecting points in ONE tooltip (For Mix Chart Only)
                interaction: {
                    mode: 'index',
                    intersect: false
                }
            }
        });
      })
</script>

Step 5

Copy all related files to your project

Code Copied To Clipboard!