Skip to content
3 changes: 2 additions & 1 deletion src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import FormListTable from "./renderer/form-list-table.vue";
import FormAnalyticsChart from "./renderer/form-analytics-chart.vue";
import accordions from "@/components/accordions";
import VariableNameGenerator from "@/components/VariableNameGenerator";
import { LinkButton } from "./renderer";
import { LinkButton, CaseProgressBar } from "./renderer";
import "../assets/css/tabs.css";
import FormCollectionRecordControl from "./renderer/form-collection-record-control.vue";
import FormCollectionViewControl from "./renderer/form-collection-view-control.vue";
Expand Down Expand Up @@ -166,6 +166,7 @@ export default {
Vue.use(Vuex);
Vue.component("FormListTable", FormListTable);
Vue.component("LinkButton", LinkButton);
Vue.component("CaseProgressBar", CaseProgressBar);
Vue.component("FormCollectionRecordControl", FormCollectionRecordControl);
Vue.component("FormCollectionViewControl", FormCollectionViewControl);
const store = new Vuex.Store({
Expand Down
205 changes: 205 additions & 0 deletions src/components/renderer/case-progress-bar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
<template>
<div v-if="stagesPerCase.length > 0">
<div class="pipeline-wrapper">
<div class="pipeline">
<div
v-for="(stage, index) in stagesPerCase"
:key="index"
class="stage-container"
>
<div
:class="['stage-block', getStageClass(stage.status)]"
:style="getClipPathStyle(index)"
>
<div class="stage-name">{{ stage.name }}</div>
<template v-if="validateDate(stage.completed_at)">
<div class="stage-date">{{ formatDate(stage.completed_at) }}</div>
</template>
<template v-else>
<div class="stage-status">{{ getStatusLabel(stage.status) }}</div>
</template>
</div>
<!-- Connector Arrow, not shown after last block -->
<div
v-if="index < stagesPerCase.length - 1"
:class="['arrow-connector', getStageClass(stage.status)]"
></div>
</div>
</div>
</div>
</div>
<div v-else>
<div class="pipeline-wrapper">
<div class="pipeline">
<div class="stage-container">
<div class="stage-block pending">
<div class="stage-name">No stages available</div>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import moment from "moment";
import { getUserDateFormat } from "@processmaker/vue-form-elements";

export default {
props: ["label", "event", "eventData"],
data() {
return {
caseNumber: null,
stagesPerCase: [],
stagesStatus: {
Done: "closed",
"In Progress": "active",
Pending: "pending"
}
};
},
mounted() {
this.caseNumber = window.ProcessMaker?.caseNumber;
this.getStageStatus(this.caseNumber);
},
methods: {
getStageStatus(caseNumber) {
let url_api = 'cases/stages_bar';
if (caseNumber) {
url_api += `/${caseNumber}`;
}
ProcessMaker.apiClient
.get(url_api)
.then((response) => {
this.stagesPerCase = response.data.stages_per_case;
})
.catch((error) => {
console.error("Error fetching data:", error);
});
},
validateDate(date) {
return date && date !== "null";
},
getStageClass(status) {
return this.stagesStatus[status] || "pending";
},
getStatusLabel(status) {
if (status === "Done") return "Completed";
return status || "Pending";
},
formatDate(date) {
if (!date || date === "null") return "";
return moment
.utc(date, [getUserDateFormat(), moment.ISO_8601], true)
.toISOString()
.split(RegExp("T[0-9]"))[0];
},
getClipPathStyle(index) {
if (index === 0) {
return {
clipPath: "none"
};
}
if (this.stagesPerCase.length > 1) {
return {
clipPath: "polygon(0 0, 100% 0, 100% 100%, 0px 100%, 20px 50%)"
};
}
return {
clipPath: "none"
};
}
}
};
</script>
<style scoped>
.pipeline-wrapper {
width: 100%;
overflow-x: hidden;
padding: 10px;
}
.pipeline {
display: flex;
align-items: stretch;
width: 100%;
}
.stage-container {
display: flex;
align-items: center;
flex: 1;
margin-left: -10px;
align-self: stretch;
}

.stage-block {
flex: 1;
height: 100px;
padding: 22px 8px 40px 44px;;
color: #333;
text-align: center;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
border-radius: 4px;
transition: background-color 0.3s ease;
box-sizing: border-box;
}
.stage-block.active {
background-color: #ffcc99;
}
.stage-block.closed {
background-color: #ffd9b3;
}
.stage-block.pending {
background-color: #f0f0f0;
color: #999;
}
.stage-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: break-spaces;
font-size: 16px;
font-style: normal;
font-weight: 600;
line-height: 20px;
letter-spacing: -0.16px;
}
.stage-status,
.stage-date {
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: normal;
letter-spacing: -0.12px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.75);
position: absolute;
right: 4.999px;
bottom: 12px;
display: flex;
padding: 2px 6px;
align-items: center;
gap: 10px;
}

.arrow-connector {
width: 0;
height: 0;
border-top: 50px solid transparent;
border-bottom: 50px solid transparent;
border-left: 20px solid;
flex-shrink: 0;
}
.arrow-connector.active {
color: #ffcc99;
}
.arrow-connector.closed {
color: #ffd9b3;
}
.arrow-connector.pending {
color: #f0f0f0;
}
</style>
1 change: 1 addition & 0 deletions src/components/renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ export { default as FormTasks } from "./form-tasks.vue";
export { default as LinkButton } from "./link-button.vue";
export { default as FormCollectionRecordControl } from "./form-collection-record-control.vue";
export { default as FormCollectionViewControl } from "./form-collection-view-control.vue";
export { default as CaseProgressBar } from "./case-progress-bar.vue";
26 changes: 26 additions & 0 deletions src/form-builder-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from '@processmaker/vue-form-elements';
import { dataSourceValues } from '@/components/inspector/data-source-types';
import LinkButton from "./components/renderer/link-button.vue";
import CaseProgressBar from "./components/renderer/case-progress-bar.vue";

import {
bgcolorProperty,
Expand Down Expand Up @@ -1210,5 +1211,30 @@ export default [
}
],
},
},
{
editorComponent: CaseProgressBar,
editorBinding: 'CaseProgressBar',
rendererComponent: CaseProgressBar,
rendererBinding: 'CaseProgressBar',
control: {
popoverContent: "Add a progress bar to show the status of a case",
order: 7.0,
group: 'Dashboards',
label: 'Case Progress Bar',
component: 'CaseProgressBar',
'editor-component': 'CaseProgressBar',
'editor-control': 'CaseProgressBar',
config: {
label: 'New Case Progress Bar',
icon: 'fas fa-chart-bar',
variant: 'primary',
event: 'submit',
name: null,
fieldValue: null,
tooltip: {},
},
inspector: [],
}
}
];
2 changes: 1 addition & 1 deletion tests/e2e/specs/FOUR6788_ScreenPerformanceTests.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe("FOUR-6788 screen performance", () => {
// @todo Improve the boot-time of the stand alone app (general and within cypress)
const avgBootTime = 22000;
const avgBootTime = 36000;
const minimumPerformanceScore = 12;
const accessibility = 50;

Expand Down