diff --git a/ProcessMaker/Http/Controllers/CasesController.php b/ProcessMaker/Http/Controllers/CasesController.php
new file mode 100644
index 0000000000..8a5c5f7196
--- /dev/null
+++ b/ProcessMaker/Http/Controllers/CasesController.php
@@ -0,0 +1,44 @@
+
+
+
+
+
diff --git a/resources/jscomposition/base/buttons/index.js b/resources/jscomposition/base/buttons/index.js
new file mode 100644
index 0000000000..a86eca3ca3
--- /dev/null
+++ b/resources/jscomposition/base/buttons/index.js
@@ -0,0 +1,3 @@
+import OutlineButton from "./OutlineButton.vue";
+
+export { OutlineButton };
diff --git a/resources/jscomposition/base/cards/ThreeSectionCard.vue b/resources/jscomposition/base/cards/ThreeSectionCard.vue
new file mode 100644
index 0000000000..9ff7ef6221
--- /dev/null
+++ b/resources/jscomposition/base/cards/ThreeSectionCard.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+ {{ header }}
+
+
+
+
+ {{ body }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/jscomposition/base/cards/index.js b/resources/jscomposition/base/cards/index.js
new file mode 100644
index 0000000000..e14a1b5958
--- /dev/null
+++ b/resources/jscomposition/base/cards/index.js
@@ -0,0 +1,3 @@
+import ThreeSectionCard from "./ThreeSectionCard.vue";
+
+export { ThreeSectionCard };
diff --git a/resources/jscomposition/base/form/Dropdown.vue b/resources/jscomposition/base/form/Dropdown.vue
new file mode 100644
index 0000000000..d203f60b34
--- /dev/null
+++ b/resources/jscomposition/base/form/Dropdown.vue
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+
+
+ -
+
+ {{ option.label || option.value }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/jscomposition/base/form/InputLeading.vue b/resources/jscomposition/base/form/InputLeading.vue
new file mode 100644
index 0000000000..a2fb84868f
--- /dev/null
+++ b/resources/jscomposition/base/form/InputLeading.vue
@@ -0,0 +1,33 @@
+
+
+
+
\ No newline at end of file
diff --git a/resources/jscomposition/base/form/index.js b/resources/jscomposition/base/form/index.js
new file mode 100644
index 0000000000..8eec1e2f5b
--- /dev/null
+++ b/resources/jscomposition/base/form/index.js
@@ -0,0 +1,9 @@
+import Dropdown from "./Dropdown.vue";
+import InputLeading from "./InputLeading.vue";
+
+export default {}
+
+export {
+ Dropdown,
+ InputLeading
+}
\ No newline at end of file
diff --git a/resources/jscomposition/base/index.js b/resources/jscomposition/base/index.js
new file mode 100644
index 0000000000..ddbe2a67b6
--- /dev/null
+++ b/resources/jscomposition/base/index.js
@@ -0,0 +1,7 @@
+export * from "./buttons/index"
+export * from "./cards/index"
+export * from "./form/index"
+export * from "./table/index"
+export * from "./ui/index"
+
+export default {}
diff --git a/resources/jscomposition/base/table/BaseTable.vue b/resources/jscomposition/base/table/BaseTable.vue
new file mode 100644
index 0000000000..638ba50f69
--- /dev/null
+++ b/resources/jscomposition/base/table/BaseTable.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/jscomposition/base/table/Pagination.vue b/resources/jscomposition/base/table/Pagination.vue
new file mode 100644
index 0000000000..37fa4d9dff
--- /dev/null
+++ b/resources/jscomposition/base/table/Pagination.vue
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 153 items
+
+
+
+ 15 per page
+
+
+
+
diff --git a/resources/jscomposition/base/table/TCell.vue b/resources/jscomposition/base/table/TCell.vue
new file mode 100644
index 0000000000..e990f8d452
--- /dev/null
+++ b/resources/jscomposition/base/table/TCell.vue
@@ -0,0 +1,46 @@
+
+
+
+ {{ getValue() }}
+
+
+
+ |
+
+
+
diff --git a/resources/jscomposition/base/table/THeader.vue b/resources/jscomposition/base/table/THeader.vue
new file mode 100644
index 0000000000..e7dee4d39e
--- /dev/null
+++ b/resources/jscomposition/base/table/THeader.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+ {{ getValue() }}
+
+
+
+
+
+
+ |
+
+
+
diff --git a/resources/jscomposition/base/table/TRow.vue b/resources/jscomposition/base/table/TRow.vue
new file mode 100644
index 0000000000..ad929a275a
--- /dev/null
+++ b/resources/jscomposition/base/table/TRow.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/resources/jscomposition/base/table/composables/columnComposable.js b/resources/jscomposition/base/table/composables/columnComposable.js
new file mode 100644
index 0000000000..2ce357f833
--- /dev/null
+++ b/resources/jscomposition/base/table/composables/columnComposable.js
@@ -0,0 +1,52 @@
+import { ref, onUnmounted } from "vue";
+
+export default {};
+/**
+ * This composable only works with columns in AppTable
+ * @param {*} column is a ref variable, come from App Table
+ * @param {*} tableName
+ * @returns
+ */
+export const columnResizeComposable = (column) => {
+ const startX = ref(0);
+ const startWidth = ref(0);
+ const isResizing = ref(false);
+
+ //Resize the column value
+ const doResize = (event) => {
+ if (isResizing.value) {
+ const diff = event.pageX - startX.value;
+ const min = 63;
+ const currentWidth = Math.max(min, startWidth.value + diff);
+
+ column.width = currentWidth;
+ }
+ };
+
+ const stopResize = () => {
+ if (isResizing.value) {
+ document.removeEventListener("mousemove", doResize);
+ document.removeEventListener("mouseup", stopResize);
+ isResizing.value = false;
+ }
+ };
+
+ // Init the events in mousemove and finish in mouseup event
+ const startResize = (event, index) => {
+ isResizing.value = true;
+ startX.value = event.pageX;
+ startWidth.value = column.width || 200;
+
+ document.addEventListener("mousemove", doResize);
+ document.addEventListener("mouseup", stopResize);
+ };
+
+ onUnmounted(() => {
+ document.removeEventListener("mousemove", doResize);
+ document.removeEventListener("mouseup", stopResize);
+ });
+
+ return {
+ startResize,
+ };
+};
diff --git a/resources/jscomposition/base/table/index.js b/resources/jscomposition/base/table/index.js
new file mode 100644
index 0000000000..9a2e23b1cf
--- /dev/null
+++ b/resources/jscomposition/base/table/index.js
@@ -0,0 +1,9 @@
+import BaseTable from "./BaseTable.vue";
+import Pagination from "./Pagination.vue";
+import TCell from "./TCell.vue";
+import THeader from "./THeader.vue";
+import TRow from "./TRow.vue";
+
+export default {};
+
+export { BaseTable, Pagination, TCell, THeader, TRow };
diff --git a/resources/jscomposition/base/ui/Badge.vue b/resources/jscomposition/base/ui/Badge.vue
new file mode 100644
index 0000000000..f1f6c173b4
--- /dev/null
+++ b/resources/jscomposition/base/ui/Badge.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/resources/jscomposition/base/ui/index.js b/resources/jscomposition/base/ui/index.js
new file mode 100644
index 0000000000..0e62cb7097
--- /dev/null
+++ b/resources/jscomposition/base/ui/index.js
@@ -0,0 +1,5 @@
+import Badge from "./Badge.vue";
+
+export default {};
+
+export { Badge };
diff --git a/resources/jscomposition/cases/casesMain/App.vue b/resources/jscomposition/cases/casesMain/App.vue
new file mode 100644
index 0000000000..fc6d013cb4
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/App.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/resources/jscomposition/cases/casesMain/CasesDataSection.vue b/resources/jscomposition/cases/casesMain/CasesDataSection.vue
new file mode 100644
index 0000000000..ff7a733cc7
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/CasesDataSection.vue
@@ -0,0 +1,54 @@
+
+
+
+
diff --git a/resources/jscomposition/cases/casesMain/CasesMain.vue b/resources/jscomposition/cases/casesMain/CasesMain.vue
new file mode 100644
index 0000000000..71e136cc11
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/CasesMain.vue
@@ -0,0 +1,51 @@
+
+
+
+
diff --git a/resources/jscomposition/cases/casesMain/api/index.js b/resources/jscomposition/cases/casesMain/api/index.js
new file mode 100644
index 0000000000..b7cbc82bc6
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/api/index.js
@@ -0,0 +1,35 @@
+export default {};
+
+// Method to get counters - change with processmaker API
+export const getCounters = async () => {
+ const url = "http://localhost:3000/appcounters";
+ return {
+ myCases: 36,
+ inProgress: 20,
+ completed: 125,
+ allCases: 145,
+ allRequests: 777,
+ };
+};
+
+// Method to get data case list - change with processmaker API
+export const getData = async () => {
+ let objects_list = [];
+
+ for (let i = 0; i <= 31; i++) {
+ const obj = {
+ caseNumber: `${i}`,
+ caseTitle: `Case Title ${i}`,
+ process: `Process ${i}`,
+ task: `Task ${i}`,
+ participants: `Avatar ${i}`,
+ status: `badge ${i}`,
+ started: `21/21/${i}`,
+ completed: `22/22/${i}`,
+ };
+
+ objects_list.push(obj);
+ }
+
+ return objects_list;
+};
diff --git a/resources/jscomposition/cases/casesMain/components/AppCounters.vue b/resources/jscomposition/cases/casesMain/components/AppCounters.vue
new file mode 100644
index 0000000000..203bede000
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/components/AppCounters.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
diff --git a/resources/jscomposition/cases/casesMain/components/AvatarContainer.vue b/resources/jscomposition/cases/casesMain/components/AvatarContainer.vue
new file mode 100644
index 0000000000..03a1b14a30
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/components/AvatarContainer.vue
@@ -0,0 +1,30 @@
+
+
+
+
diff --git a/resources/jscomposition/cases/casesMain/components/BadgeContainer.vue b/resources/jscomposition/cases/casesMain/components/BadgeContainer.vue
new file mode 100644
index 0000000000..af50e69d44
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/components/BadgeContainer.vue
@@ -0,0 +1,20 @@
+
+
+ In progress
+
+
+
+
diff --git a/resources/jscomposition/cases/casesMain/components/BadgesSection.vue b/resources/jscomposition/cases/casesMain/components/BadgesSection.vue
new file mode 100644
index 0000000000..86065b6759
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/components/BadgesSection.vue
@@ -0,0 +1,36 @@
+
+
+
+ {{ item.label }}
+
+
+
+
diff --git a/resources/jscomposition/cases/casesMain/components/CaseFilter.vue b/resources/jscomposition/cases/casesMain/components/CaseFilter.vue
new file mode 100644
index 0000000000..3d9e721653
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/components/CaseFilter.vue
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/resources/jscomposition/cases/casesMain/config/badges.js b/resources/jscomposition/cases/casesMain/config/badges.js
new file mode 100644
index 0000000000..d2832c87ba
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/config/badges.js
@@ -0,0 +1,16 @@
+export default {};
+
+export const badges = [
+ {
+ label: "Status : In progress",
+ },
+ {
+ label: "Status : To do",
+ },
+ {
+ label: "Status : New Badge",
+ },
+ {
+ label: "Status : Badge Example",
+ },
+];
diff --git a/resources/jscomposition/cases/casesMain/config/columns.js b/resources/jscomposition/cases/casesMain/config/columns.js
new file mode 100644
index 0000000000..531f9b6a60
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/config/columns.js
@@ -0,0 +1,129 @@
+import BadgeContainer from "../components/BadgeContainer.vue";
+import AvatarContainer from "../components/AvatarContainer.vue";
+
+export default {};
+/**
+ * Example Column
+ * field: String
+ * headerName: String
+ * headerFormatter: callback
+ * resizable: Boolean
+ * visible: Callback
+ * formatter: Callback - Build the value in the cell
+ * width: Number
+ * cellRenderer: Object Vue to custom the cell
+ */
+
+// My cases: [Case#, Case Title, Process, Task, Participants, Status, Started, Completed]
+// In Progress : [ Case#, Case Title, Process, Task, Participants, Status, Started]
+// Completed : [Case#, Case Title, Process, Task, Participants, Status, Started, Completed]
+// AllCases : [Case#, Case Title, Process, Task, Participants, Status, Started, Completed]
+// AllRequest : [Case#, Case Title, Process, Task, Participants, Status, Started, Completed]
+
+export const caseNumberColumn = () => ({
+ field: "caseNumber",
+ headerName: "Case #",
+ resizable: true,
+ width: 200,
+});
+
+export const caseTitleColumn = () => ({
+ field: "caseTitle",
+ headerName: "Case Title",
+ resizable: true,
+ width: 200,
+});
+
+export const processColumn = () => ({
+ field: "process",
+ headerName: "Process",
+ resizable: true,
+ width: 200,
+});
+
+export const taskColumn = () => ({
+ field: "task",
+ headerName: "Task",
+ resizable: true,
+ width: 200,
+});
+
+export const participantsColumn = () => ({
+ field: "participants",
+ headerName: "Participants",
+ resizable: true,
+ width: 200,
+ cellRenderer: () => {
+ return AvatarContainer;
+ },
+});
+
+export const statusColumn = () => ({
+ field: "status",
+ headerName: "Status",
+ resizable: true,
+ width: 200,
+ cellRenderer: () => {
+ return BadgeContainer;
+ },
+});
+
+export const startedColumn = () => ({
+ field: "started",
+ headerName: "Started",
+ resizable: true,
+ width: 200,
+});
+
+export const completedColumn = () => ({
+ field: "completed",
+ headerName: "Completed",
+ resizable: true,
+ width: 200,
+});
+
+export const getColumns = (type) => {
+ const columnsDefinition = {
+ "my-cases": [
+ caseNumberColumn(),
+ caseTitleColumn(),
+ processColumn(),
+ taskColumn(),
+ participantsColumn(),
+ statusColumn(),
+ startedColumn(),
+ completedColumn(),
+ ],
+ "in-progress": [
+ caseNumberColumn(),
+ caseTitleColumn(),
+ processColumn(),
+ taskColumn(),
+ participantsColumn(),
+ statusColumn(),
+ startedColumn(),
+ ],
+ completed: [
+ caseNumberColumn(),
+ caseTitleColumn(),
+ processColumn(),
+ taskColumn(),
+ participantsColumn(),
+ statusColumn(),
+ startedColumn(),
+ completedColumn(),
+ ],
+ "all-cases": [
+ caseNumberColumn(),
+ caseTitleColumn(),
+ processColumn(),
+ taskColumn(),
+ participantsColumn(),
+ statusColumn(),
+ startedColumn(),
+ completedColumn(),
+ ],
+ };
+
+ return columnsDefinition[type] || columnsDefinition.myCases;
+};
diff --git a/resources/jscomposition/cases/casesMain/config/index.js b/resources/jscomposition/cases/casesMain/config/index.js
new file mode 100644
index 0000000000..963420fb99
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/config/index.js
@@ -0,0 +1,2 @@
+export * from "./columns";
+export * from "./badges";
diff --git a/resources/jscomposition/cases/casesMain/main.js b/resources/jscomposition/cases/casesMain/main.js
new file mode 100644
index 0000000000..97c89abc83
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/main.js
@@ -0,0 +1,19 @@
+import App from "./App.vue";
+import { routes } from "./routes";
+
+Vue.use(VueRouter);
+
+const router = new VueRouter({
+ mode: "history",
+ base: "/",
+ routes,
+});
+
+new Vue({
+ el: "#cases-main",
+ router,
+ components: {
+ App,
+ },
+ render: (h) => h(App),
+});
diff --git a/resources/jscomposition/cases/casesMain/routes.js b/resources/jscomposition/cases/casesMain/routes.js
new file mode 100644
index 0000000000..e21e05406e
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/routes.js
@@ -0,0 +1,27 @@
+import CasesMain from "./CasesMain.vue";
+import CasesDataSection from "./CasesDataSection.vue";
+
+export default {};
+
+export const routes = [
+ {
+ name: "cases",
+ path: "/cases-main",
+ component: CasesMain,
+ props(route) {
+ return {};
+ },
+ children: [
+ {
+ name: "cases-request",
+ path: ":id?",
+ component: CasesDataSection,
+ props(route) {
+ return {
+ listId: route.params?.id || "my-cases",
+ };
+ },
+ },
+ ],
+ },
+];
diff --git a/resources/jscomposition/cases/casesMain/utils/counters.js b/resources/jscomposition/cases/casesMain/utils/counters.js
new file mode 100644
index 0000000000..2ce87946f8
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/utils/counters.js
@@ -0,0 +1,46 @@
+export default {};
+import { t } from "i18next";
+
+export const formatCounters = (data) => {
+ const counters = [
+ {
+ header: t("My cases"),
+ body: data.myCases.toString(),
+ color: "amber",
+ icon: "fa-regular fa-user",
+ url: "/cases-main/my-cases",
+ },
+ {
+ header: t("In progress"),
+ body: data.inProgress.toString(),
+ color: "green",
+ icon: "fa-solid fa-list",
+ url: "/cases-main/in-progress",
+ },
+ {
+ header: t("Completed"),
+ body: data.completed.toString(),
+ color: "blue",
+ icon: "fa-regular fa-circle-check",
+ url: "/cases-main/completed",
+ },
+ {
+ header: t("All cases"),
+ body: data.allCases.toString(),
+ color: "purple",
+ icon: "fa-regular fa-clipboard",
+ url: "/cases-main/all-cases",
+ },
+ {
+ header: t("All requests"),
+ body: data.allRequests.toString(),
+ color: "gray",
+ icon: "fa-solid fa-play",
+ url: () => {
+ window.location.href = "/cases";
+ },
+ },
+ ];
+
+ return counters;
+};
diff --git a/resources/jscomposition/cases/casesMain/utils/index.js b/resources/jscomposition/cases/casesMain/utils/index.js
new file mode 100644
index 0000000000..d6a4a113d4
--- /dev/null
+++ b/resources/jscomposition/cases/casesMain/utils/index.js
@@ -0,0 +1 @@
+export * from "./counters";
diff --git a/resources/jscomposition/system/Breadcrums.vue b/resources/jscomposition/system/Breadcrums.vue
new file mode 100644
index 0000000000..a302a27c55
--- /dev/null
+++ b/resources/jscomposition/system/Breadcrums.vue
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/resources/jscomposition/system/index.js b/resources/jscomposition/system/index.js
new file mode 100644
index 0000000000..02e0d67feb
--- /dev/null
+++ b/resources/jscomposition/system/index.js
@@ -0,0 +1,5 @@
+import Breadcrums from "./Breadcrums.vue";
+
+export default {};
+
+export { Breadcrums };
diff --git a/resources/views/cases/casesMain.blade.php b/resources/views/cases/casesMain.blade.php
new file mode 100644
index 0000000000..96c49d4dcb
--- /dev/null
+++ b/resources/views/cases/casesMain.blade.php
@@ -0,0 +1,22 @@
+@extends('layouts.layout')
+
+@section('title')
+@endsection
+
+@section('sidebar')
+@include('layouts.sidebar', ['sidebar'=> Menu::get('sidebar_request')])
+@endsection
+
+@section('breadcrumbs')
+@endsection
+
+@section('content')
+
+@endsection
+
+@section('js')
+
+@endsection
+
+@section('css')
+@endsection
diff --git a/resources/views/layouts/layout.blade.php b/resources/views/layouts/layout.blade.php
index 2fd4356d4c..301ddc5189 100644
--- a/resources/views/layouts/layout.blade.php
+++ b/resources/views/layouts/layout.blade.php
@@ -39,6 +39,7 @@
+
@yield('css')