fixed bar chart

add-reporting-dashboard
Usama Khan 2025-03-27 12:56:54 -07:00
parent 9e25a4f2de
commit 7014d0bfcb
9 changed files with 4488 additions and 1 deletions

View File

@ -27,7 +27,7 @@ public class ReportingController {
} }
@GetMapping( "/summary") @GetMapping( "/summary")
public String showMasterBundles(@RequestParam(value = "item-id", required = false ) String itemId, @RequestParam(value = "sku" , required = false) String sku, @RequestParam(value = "start-date", required = false) String startDate, @RequestParam(value = "end-date", required = false) String endDate, Model model ){ public String summary(@RequestParam(value = "item-id", required = false ) String itemId, @RequestParam(value = "sku" , required = false) String sku, @RequestParam(value = "start-date", required = false) String startDate, @RequestParam(value = "end-date", required = false) String endDate, Model model ){
LocalDate startDate1 = StringUtils.isNullOrEmpty(startDate) ? LocalDate.now().minusDays(6) : LocalDate.parse(startDate); LocalDate startDate1 = StringUtils.isNullOrEmpty(startDate) ? LocalDate.now().minusDays(6) : LocalDate.parse(startDate);
LocalDate endDate1 = StringUtils.isNullOrEmpty(endDate) ? LocalDate.now().plusDays(1) : LocalDate.parse(endDate); LocalDate endDate1 = StringUtils.isNullOrEmpty(endDate) ? LocalDate.now().plusDays(1) : LocalDate.parse(endDate);
@ -40,6 +40,14 @@ public class ReportingController {
return "/reporting/inventory-summary"; return "/reporting/inventory-summary";
} }
@GetMapping( "/job-card-report")
public String jobCardReport(@RequestParam(value = "item-id", required = false ) String itemId, @RequestParam(value = "sku" , required = false) String sku, @RequestParam(value = "start-date", required = false) String startDate, @RequestParam(value = "end-date", required = false) String endDate, Model model ){
int progressValue = 75;
model.addAttribute("progressValue", progressValue);
return "/reporting/job-card-report";
}
private ArrayList<LocalDate> generateDateList(LocalDate start, LocalDate end) { private ArrayList<LocalDate> generateDateList(LocalDate start, LocalDate end) {
ArrayList<LocalDate> localDates = new ArrayList<>(); ArrayList<LocalDate> localDates = new ArrayList<>();
while (start.isBefore(end)) { while (start.isBefore(end)) {

View File

@ -0,0 +1,43 @@
( async function(){
Vue.prototype.$types = window.ctp.types;
Vue.prototype.$accounts = window.ctp.accounts;
let app = new Vue({
el : '#dashboard',
data :{
jobCard : {}
},
computed: {
getStatus: function() {
return `badge-${this.jobCard.status}`;
},
getInvStatus: function() {
return `badge-${this.jobCard.inventoryStatus}`;
}
},
methods : {
onCardSelect : function ( id, card ) {
// $.ajax({
// url: `/ctp/rest/job-cards/find/${id}`,
// method: 'GET',
// contentType: 'application/json',
// dataType: 'json',
// success: ( data ) => {
// this.jobCard = data;
// },
// error : function ( err ){
// alert( err );
// }
// })
console.log("hello")
}
},
mounted : function () {
}
})
})(jQuery);

View File

@ -0,0 +1,110 @@
document.addEventListener("DOMContentLoaded", function () {
if (typeof Highcharts === "undefined") {
console.error("Highcharts library not loaded!");
return;
}
function createGaugeChart(config) {
Highcharts.chart(config.containerId, {
chart: {
type: 'solidgauge',
width: config.width,
height: config.height,
backgroundColor: 'transparent',
plotBackgroundColor: null,
shadow: false
},
title: {
text: config.title,
y: 30
},
pane: {
startAngle: 0,
endAngle: 360,
y: config.CirclePosition,
background: [{
backgroundColor: 'transparent',
shape: "arc",
borderWidth: 0,
innerRadius: config.innerRadius,
outerRadius: config.outerRadius
}]
},
yAxis: {
min: config.minValue,
max: config.maxValue,
tickPositions: [],
lineWidth: 0,
minorTickLength: 0,
tickWidth: 0,
gridLineWidth: 0,
stops: [
[1, config.color]
]
},
series: [{
name: 'Percentage',
data: [config.speed],
dataLabels: {
format: '{y}% <br>' + config.totalPiece + " | " + config.completedPiece,
y: config.InnerTextYPosition,
borderWidth: 0,
backgroundColor: 'none',
style: {
fontSize: config.fontSize,
fontWeight: 'bold'
}
},
color: config.color,
showInLegend: false
}],
credits: {
enabled: false
}
});
}
function createBarChart(config) {
console.log(`Creating bar chart for '${config.containerId}'`);
if (!document.getElementById(config.containerId)) {
console.error(`Bar chart container '${config.containerId}' not found!`);
return;
}
Highcharts.chart(config.containerId, {
chart: {
type: 'column',
height: 400,
width: 1600, // Ensure the chart is wider than the container
marginTop: 60,
spacingTop: 30
},
xAxis: {
categories: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday",
"Next Monday", "Next Tuesday", "Next Wednesday", "Friday", "Saturday", "Sunday"],
labels: {
rotation: -45 // Optional: Rotate labels if needed
}
},
scrollbar: {
enabled: true // Ensure Highcharts scrollbar is enabled
},
series: [
{ name: 'Model A', data: [0.8, 0.75, 0.78, 0.79, 0.77, 0.81, 0.80, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80] },
{ name: 'Model B', data: [0.85, 0.78, 0.82, 0.80, 0.79, 0.84, 0.83, 0.78, 0.82, 0.83, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80] },
{ name: 'Model C', data: [0.88, 0.80, 0.85, 0.82, 0.81, 0.87, 0.85, 0.80, 0.85, 0.86, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80] },
{ name: 'Model D', data: [0.9, 0.83, 0.87, 0.84, 0.83, 0.89, 0.88, 0.83, 0.87, 0.88, 0.76, 0.79, 0.80, 0.76, 0.79, 0.80] }
]
});
console.log(`Bar chart '${config.containerId}' created successfully.`);
}
gaugeConfigs.forEach(createGaugeChart);
createBarChart(barChartConfigs);
});

View File

@ -0,0 +1,91 @@
(async function () {
Vue.component('bundle-table', {
props: ['bundles'],
methods: {
removeBundle: function (idx) {
this.$emit('bundle-remove', idx)
},
getFormattedDateTime: function (bundle) {
if (!!bundle.createdAt) {
return bundle.createdAt.split('T')[0] + ' ' + bundle.createdAt.split('T')[1];
}
return luxon.DateTime.now().toFormat('yyyy-MM-dd HH:mm:ss');
},
},
template: `
<table class="table table-bordered bg-white col-sm-8">
<thead>
<tr>
<th>ID</th>
<th>Item ID</th>
<th>Sku</th>
<th>Job Card ID</th>
<th>Wrap Quantity</th>
<th>Type</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr v-for="(bundle,index) in bundles">
<input hidden="hidden" v-bind:name="'bundles[' + index + '].id'" v-bind:value="bundle.id" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].itemId'" v-bind:value="bundle.itemId" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].sku'" v-bind:value="bundle.sku" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].masterBundleId'" v-bind:value="bundle.masterBundleId" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].jobCardId'" v-bind:value="bundle.jobCardId" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].wrapQuantity'" v-bind:value="bundle.wrapQuantity" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].type'" v-bind:value="bundle.type" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].barcode'" v-bind:value="bundle.barcode" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].createdAt'" v-bind:value="getFormattedDateTime(bundle.createdAt)" >
<input hidden="hidden" v-bind:name="'bundles[' + index + '].createdBy'" v-bind:value="bundle.createdBy" >
<td><span>{{bundle.id}}</span></td>
<td><span>{{bundle.itemId}}</span></td>
<td><span>{{bundle.sku}}</span></td>
<td><span>{{bundle.jobCardId}}</span></td>
<td><span>{{bundle.wrapQuantity}}</span></td>
<td><span>{{bundle.type}}</span></td>
<td>
<button type="button" title="Remove" class="btn btn-light text-left" v-on:click="removeBundle(index)">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
`
})
let app = new Vue({
el: "#jobcardsidebar",
data: {
bundles: [],
},
methods: {
removeBundle: function (idx) {
this.bundles.splice(idx, 1)
},
onBundleSelect: function (id, bundle) {
this.bundles.push(
bundle
)
},
hasMultipleItemSelect : function () {
const ids = this.bundles.map(item => item.itemId);
const uniqueIds = new Set(ids);
return uniqueIds.size > 1;
},
hasDuplicates: function () {
const ids = this.bundles.map(item => item.id);
const uniqueIds = new Set(ids);
return ids.length !== uniqueIds.size;
}
},
mounted: function () {
this.bundles = window.ctp.wrapper.bundles;
}
});
})(jQuery);

File diff suppressed because one or more lines are too long

View File

@ -1050,6 +1050,35 @@ if ( typeof Vue !== 'undefined' ) {
} }
}); });
/**
* location site search component
*/
Vue.component( 'search-job-card-dashBoard', {
mixins: [searchComponentMixin],
methods: {
getSearchUrl: function() {
return `/ctp/rest/locations/sites/search?term=${encodeURIComponent( this.list.term )}`;
},
getEmittedEventName: function() {
return 'location-site-select';
}
},
props: {
labelText: {
default: 'Search Site'
},
titleFieldName: {
default: 'siteTitle'
},
idFieldName: {
default: 'siteId'
},
codeFieldName: {
default: ''
}
}
});
/** /**
* location site search component * location site search component

View File

@ -16,6 +16,10 @@
<script th:src="@{/js/vendor/bignumber.min.js}"></script> <script th:src="@{/js/vendor/bignumber.min.js}"></script>
<script th:src="@{/js/vue-components.js}"></script> <script th:src="@{/js/vue-components.js}"></script>
<script th:src="@{/js/utils.js}"></script> <script th:src="@{/js/utils.js}"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-more.js"></script>
<script src="https://code.highcharts.com/modules/solid-gauge.js"></script>
<script th:src="@{/js/vue-components.js}"></script>
<title th:text="${#strings.concat(title, ' - CTP')}"></title> <title th:text="${#strings.concat(title, ' - CTP')}"></title>
</head> </head>
<body> <body>
@ -125,6 +129,20 @@
</li> </li>
</ul> </ul>
</nav> </nav>
<!--Second level of reporting-->
<nav class="navbar navbar-light bg-light navbar-expand-lg justify-content-between"
th:if="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/reporting')}">
<ul class="navbar-nav">
<li class="nav-item"
th:classappend="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/reporting/job-card-report') ? 'active' : ''}">
<a th:href="@{/reporting/job-card-report}" class="nav-link">Job Card Report</a>
</li>
<li class="nav-item"
th:classappend="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/reporting/summary') ? 'active' : ''}">
<a th:href="@{/reporting/summary}" class="nav-link">Summary</a>
</li>
</ul>
</nav>
<!-- second level stitching --> <!-- second level stitching -->
<nav class="navbar navbar-light bg-light navbar-expand-lg justify-content-between" <nav class="navbar navbar-light bg-light navbar-expand-lg justify-content-between"
th:if="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/stitching')}"> th:if="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/stitching')}">

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml">
<head th:replace="_fragments :: head('Home Page')"></head>
<body>
<!-- sidebar starts -->
<aside class="col-sm-2" th:fragment="sidebar">
<div class="page-filters-sidebar">
<form th:action="@{${#strings.replace(#httpServletRequest.requestURI, #request.getContextPath(), '')}}">
<h5 class="mb-4">Refine Your Search</h5>
<div class="form-group">
<label>ID</label>
<input type="text" class="form-control" name="id" maxlength="100" th:value="${param['id']}">
</div>
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" name="title" maxlength="100"
th:value="${param['title']}">
</div>
<div class="form-group">
<label>Active</label>
<select name="active" class="form-control" >
<option value="">Please select</option>
<option value="1" th:selected="${#strings.equals(param['active'], #strings.toString(1))}">Active</option>
<option value="0" th:selected="${#strings.equals(param['active'], #strings.toString(0))}">Inactive</option>
</select>
</div>
<div class="form-group">
<label>Created By</label>
<input type="text" class="form-control" name="created-by" maxlength="50" th:value="${param['created-by']}">
</div>
<div class="form-group">
<label>Created Start Date</label>
<input type="date" class="form-control" name="start-date" th:value="${param['start-date']}">
</div>
<div class="form-group">
<label>Created End Date</label>
<input type="date" class="form-control" name="end-date" th:value="${param['end-date']}">
</div>
<div class="form-group" data-vue-app th:with="id=${param['site-id']},title=${param['site-title']}">
<location-site-search th:attr="id=${id},title=${title}"
v-bind:id-field-name="'site-id'"
v-bind:title-field-name="'site-title'"
></location-site-search>
</div>
<div class="form-group">
<label>Count</label>
<input type="number" class="form-control" name="count" th:value="${param['count'] ?: 100}" min="0" step="1" />
</div>
<input type="submit" class="btn btn-secondary btn-block" value="Search">
<a th:href="@{${#strings.replace(#httpServletRequest.requestURI, #request.getContextPath(), '')}}"
class="btn btn-secondary btn-block">Reset</a>
</form>
</div>
</aside>
<!-- sidebar ends -->
<div th:fragment="page-footer-scripts">
<script th:src="@{/js/vendor/lazyload-db.js}"></script>
<script th:src="@{/js/main.js}"></script>
</div>
</body>
</html>

View File

@ -0,0 +1,247 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml"
xmlns:ctp="http://www.w3.org/1999/xhtml">
<head th:replace="_fragments :: head('Summary')"></head>
<body>
<div class="container-fluid">
<header class="row page-header" th:replace="_fragments :: page-header"></header>
<main class="row page-main">
<aside class="col-sm-2" th:replace="/reporting/job-card-report-sidebar :: sidebar"></aside>
<div class="col-sm">
<table class="table " >
<tbody>
<tr>
<td style="padding:0px;">
<div style="border: 2px solid #d5d8dc; padding: 10px; border-radius: 10px; height: 370px;">
<h1 style="text-align: center; margin-left: 300px">Job Card Report</h1>
<div style="display: flex; align-items: center;">
<div style="text-align: center;margin-top: -45px">
<div id="jobCard" style="width: 300px; height: 330px;"></div>
</div>
<div style="display: flex; ">
<div style="text-align: center; margin-top: 40px;">
<div id="cutting" style="width: 200px; height: 200px;"></div>
</div>
<div style="text-align: center; margin-top: 40px;">
<div id="stitching" style="width: 200px; height: 200px;"></div>
</div>
<div style="text-align: center; margin-top: 40px;">
<div id="quality" style="width: 200px; height: 200px;"></div>
</div>
<div style="text-align: center; margin-top: 40px;">
<div id="finishing" style="width: 200px; height: 200px;"></div>
</div>
<div style="text-align: center; margin-top: 40px;">
<div id="Packaging" style="width: 200px; height: 200px;"></div>
</div>
</div>
</div>
</div>
</td>
</tr>
<tr>
<td style="display: flex; flex-direction: column; align-items: center; border: none !important; outline: none;">
<div style="display: flex; gap: 10px;">
<div style="border: 2px solid #d5d8dc; border-radius: 10px; text-align: center; padding:20px;">
<H6>Cutting Total Time</H6>
<H6>10</H6>
</div>
<div style="border: 2px solid #d5d8dc; border-radius: 10px; text-align: center; padding:20px;"><H6>Stitching Total Time</H6></div>
<div style="border: 2px solid #d5d8dc; border-radius: 10px; text-align: center; padding:20px;"><H6>Quality Checking Total Time</H6></div>
<div style="border: 2px solid #d5d8dc; border-radius: 10px; text-align: center; padding:20px;"><H6>Finishing Total Time</H6></div>
<div style="border: 2px solid #d5d8dc; border-radius: 10px; text-align: center; padding:20px;"><H6>Packaging Total Time</H6></div>
</div>
</td>
</tr>
<tr>
<td style="padding: 0px; text-align: center;">
<div style="background-color: black; color: white; padding: 10px; font-size: 18px; font-weight: bold; text-align: center;">
Cutting Detail
</div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Cutting</th>
<th>Cutting Day</th>
<th>Cutting Table Descriptions</th>
</tr>
</thead>
<tbody>
<tr>
<td th:text="1"></td>
<td th:text="'Cutting Account 1'"></td>
<td th:text="Usama"></td>
<td th:text="Friday"></td>
<td th:text="'Cutting Table'"></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td style="padding: 0px; text-align: center; ">
<div style="background-color: black; color: white; padding: 10px; font-size: 18px; font-weight: bold; text-align: center;">
Stitching Detail
</div>
<table class="table" >
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Stitching</th>
<th>Stitching Day</th>
<th>Stitching Table Descriptions</th>
</tr>
</thead>
<tbody>
<tr>
<td th:text="1"></td>
<td th:text="'Stitching Account 1'"></td>
<td th:text="Usama"></td>
<td th:text="Friday"></td>
<td th:text="'Stitching Table'"></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td style="padding-left: 150px;">
<div style="border: 2px solid #d5d8dc; padding-top: 10px; border-radius: 10px; height: 460px; width: 80%; overflow-x: auto;">
<div id="barChart" style="height: 400px; width: 1600px;"></div> <!-- 100% width for responsiveness -->
</div>
</td>
</tr>
</tbody>
</table>
</div>
</main>
</div>
<script>
var gaugeConfigs = [
{
containerId: "jobCard",
width: 350,
height: 350,
minValue: 0,
maxValue: 100,
speed: 10,
innerRadius: "80%",
outerRadius: "20%",
color: "#566573",
title: "Job Card Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100,
CirclePosition:-20
},
{
containerId: "cutting",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#5dade2",
title: "Cutting Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
},
{
containerId: "stitching",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#76d7c4",
title: "Stitching Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
},
{
containerId: "quality",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#73c6b6",
title: "Quality Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
},{
containerId: "finishing",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#58d68d",
title: "Finishing Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
},
{
containerId: "Packaging",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#f5cba7",
title: "Finishing Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
}
];
const barChartConfigs = {
containerId: "barChart",
title: "Day-wise Performance Metrics",
max:16,
categories: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "Next Monday", "Next Tuesday", "Next Wednesday", "Friday", "Saturday", "Sunday", "Next Monday", "Next Tuesday", "Next Wednesday"],
series: [
{ name: "Model A", data: [0.8, 0.75, 0.78, 0.76, 0.74, 0.80, 0.77, 0.79, 0.81, 0.76, 0.80, 0.77, 0.79, 0.81, 0.76], color: "#007bff" },
{ name: "Model B", data: [0.85, 0.7, 0.77, 0.79, 0.72, 0.83, 0.78, 0.80, 0.82, 0.75,0.83, 0.78, 0.80, 0.82, 0.75], color: "#28a745" },
{ name: "Model C", data: [0.82, 0.72, 0.79, 0.80, 0.78, 0.85, 0.81, 0.84, 0.86, 0.79, 0.85, 0.81, 0.84, 0.86, 0.79], color: "#dc3545" },
{ name: "Model D", data: [0.88, 0.78, 0.83, 0.85, 0.82, 0.89, 0.85, 0.87, 0.90, 0.83, 0.89, 0.85, 0.87, 0.90, 0.83], color: "#fd7e14" }
]
};
</script>
<!-- Load JavaScript file -->
<script th:src="@{/js/charts.js}"></script>
</body>
</html>