<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Program Delivery Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
background-color: #f0f0f0;
}
.header {
border: 2px solid #080909;
padding: 5px 10px;
border-radius: 0;
background-color: #a8c4e3;
color: #050101;
font-family: Arial, Helvetica, sans-serif;
box-shadow: 5px 5px 5px grey;
text-align: center;
}
/* Styled Date Picker */
.date-picker-container {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.date-picker-label {
margin-right: 10px;
margin-left: 10px;
font-size: 16px;
color: #010101;
}
.date-picker-input {
padding: 10px;
margin: 5px;
border: 2px solid #010101;
border-radius: 5px;
font-size: 16px;
outline: none;
}
.button-row {
margin-top: 10px;
border-radius: 0;
padding: 3px;
background-color: #81b9f2;
box-shadow: 5px 5px 5px grey;
display: flex;
}
.button-container {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: center;
/* Align items to the center */
}
.button {
display: inline-block;
padding: 5px;
/* Reduced padding */
margin: 5px;
width: 100px;
border: 2px solid #010101;
border-radius: 5px;
background-color: #fafbfc;
color: #010000;
cursor: pointer;
transition: background-color 0.3s, color 0.3s;
font-size: 14px;
/* Reduced font size */
}
.button:hover {
background-color: #81b9f2;
color: white;
}
.selected {
background-color: #81b9f2;
color: white;
}
h2 span {
padding: 5px 10px;
border-radius: 0;
background-color: #f9fafc;
color: #050101;
font-family: Arial, Helvetica, sans-serif;
box-shadow: 5px 5px 5px grey;
}
.card {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
text-align: center;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.card-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 5px;
margin-top: 10px;
}
.costKpi {
background-color: #7bcf90;
}
.teamKpi {
background-color: #d0e6a8;
}
.label {
margin-bottom: 10px;
font-size: 15px;
font-family: Arial, Helvetica, sans-serif;
text-align: left;
}
.value {
font-weight: bold;
font-size: 20px;
}
.submit-button {
text-align: center;
margin-top: 10px;
}
.submit-button button {
padding: 10px 20px;
background-color: #81b9f2;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s;
font-weight: bold;
width: 200px
}
.submit-button button:hover {
background-color: #5a8cb2;
}
</style>
</head>
<!-- Body section -->
<body>
<div id="app">
<div class="header">
<h1>{{ pageTitle }}</h1>
</div>
<!-- Date Picker -->
<div class="button-row" style="justify-content: center;">
<div class="date-picker-container">
<span class="date-picker-label">From Date:</span>
<input type="date" v-model="selectedFromDate" class="date-picker-input">
</div>
<div class="date-picker-container">
<span class="date-picker-label">To Date:</span>
<input type="date" v-model="selectedToDate" class="date-picker-input">
</div>
</div>
<div class="button-row" style="justify-content: center;">
<div class="button-container">
<button v-for="(otherFilter, index) in otherFilter" :key="index"
:class="{ 'button': true, 'selected': isOtherFilterSelected(index) }"
@click="selectOtherFilters(index)">{{ otherFilter.key }}</button>
</div>
</div>
<!-- Cost Center Buttons -->
<div class="button-row">
<div class="button-container">
<div class="button-container">
<button class="button selectAllCostCenterButton" @click="selectAllCostCenter">Select All</button>
<button class="button selectAllCostCenterButton" @click="unselectAllCostCenter">Unselect
All</button>
</div>
<div class="button-container"><button v-for="(costCenter, index) in costCenter" :key="index"
:class="{ 'button': true, 'selected': isCostCenterSelected(costCenter.id) }"
@click="selectCostCenter(costCenter.id)">{{ costCenter.name }}</button></div>
</div>
</div>
<div class="submit-button">
<button class="submit-button" @click="submitButtonAction">Submit</button>
</div>
<div>
<div>
<h2><span>Cost KPIs</span></h2>
</div>
<div class="card-container">
<div v-for="(item, index) in costKPIs" :key="index" class="card costKpi">
<div class="label">{{ item.label }}</div>
<div class="value">{{ item.value }}</div>
</div>
</div>
</div>
<div>
<div>
<h2><span>Team KPIs</span></h2>
</div>
<div class="card-container">
<div v-for="(item, index) in teamKPIs" :key="index" class="card teamKpi">
<div class="label">{{ item.label }}</div>
<div class="value">{{ item.value }}</div>
</div>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
pageTitle: 'Program Delivery Dashboard',
selectedFromDate: null,
selectedToDate: null,
costCenter: [],
selectedCostCenters: [],
otherFilter: [{ key: "Billable", isSelected: false }, { key: "Buffer", isSelected: false }],
selectedOtherFilters: [0, 1],
costKPIs: [{ label: "Actual Revenue ($)", value: "" }, { label: "Acsia Cost ($)", value: "" }, { label: "Acsia Direct Cost ($)", value: "" }, { label: "Expected Revenue ($)", value: "" },
{ label: "Avg Actual RPE/Hr ($)", value: "" }, { label: "Avg CPE/Hr ($)", value: "" }, { label: "Avg Direct CPE/Hr ($)", value: "" }, { label: "Avg Expected RPE/Hr ($)", value: "" }, { label: "EBITA % [Project Margin]", value: "" },
{ label: "Margin ($)", value: "" }, { label: "Gross Margin ($)", value: "" }, { label: "Revenue Leakage ($)", value: "" }],
teamKPIs: [{ label: "Available Hours", value: "" }, { label: "Billed Hours", value: "" }, { label: "RESOURCE UTILIZATION (%)", value: "" }, { label: "Resource Count", value: "" },
{ label: "Billable", value: "" }, { label: "Buffer", value: "" }],
companyUrl: "https://9267067-rp.app.netsuite.com/",
scriptId: 'customscript_jj_sl_cpe_dashboard_atpl577',
deploymentId: 'customdeploy_jj_sl_cpe_dashboard_atpl577',
unwrapInEscapedBody: (data) => {
return JSON.parse(decodeURIComponent(data));
},
},
mounted() {
this.getCostCenters()
this.get
},
methods: {
selectAllCostCenter() {
this.selectedCostCenters = this.costCenter.map(costCenter => costCenter.id);
},
selectCostCenter(id) {
if (this.selectedCostCenters.includes(id)) {
// Deselect month
this.selectedCostCenters.splice(this.selectedCostCenters.indexOf(id), 1);
} else {
// Select month
this.selectedCostCenters.push(id);
}
},
isCostCenterSelected(id) {
return this.selectedCostCenters.includes(id);
},
unselectAllCostCenter() {
this.selectedCostCenters = []
},
selectOtherFilters(index) {
if (this.selectedOtherFilters.includes(index)) {
// Deselect month
this.selectedOtherFilters.splice(this.selectedOtherFilters.indexOf(index), 1);
} else {
// Select month
this.selectedOtherFilters.push(index);
}
},
isOtherFilterSelected(index) {
return this.selectedOtherFilters.includes(index);
},
generateEndPoint(apiType) {
return `${this.companyUrl}/app/site/hosting/scriptlet.nl?script=${this.scriptId}&deploy=${this.deploymentId}&apiType=${apiType}`
},
async getCostCenters() {
let endPoint = this.generateEndPoint('listCostCenters')
let response = await fetch(endPoint, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
})
let responseData = await response.json();
this.costCenter = responseData.data.fields;
},
async submitButtonAction() {
let filters = {};
filters.fromDate = this.selectedFromDate || '';
filters.toDate = this.selectedToDate || '';
filters.costCenters = this.selectedCostCenters.length > 0 ? this.selectedCostCenters.join(',') : '';
filters.otherFilters = this.selectedOtherFilters.length > 0 ? this.selectedOtherFilters.map(index => this.otherFilter[index].key).join(',') : '';
let endPoint = this.generateEndPoint('fetchData');
let response = await fetch(endPoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(filters)
});
let responseData = await response.json();
// Set values for cost KPIs
this.costKPIs.forEach(item => {
const data = responseData.data.fields.find(obj => obj.label === item.label);
if (data) {
item.value = data.value;
}
});
// Set values for team KPIs
this.teamKPIs.forEach(item => {
const data = responseData.data.fields.find(obj => obj.label === item.label);
if (data) {
item.value = data.value;
}
});
}
}
});
</script>
</body>
</html>