diff --git a/ProcessMaker/Http/Controllers/Api/UserConfigurationController.php b/ProcessMaker/Http/Controllers/Api/UserConfigurationController.php
index 64ea095fd1..d325c1636b 100644
--- a/ProcessMaker/Http/Controllers/Api/UserConfigurationController.php
+++ b/ProcessMaker/Http/Controllers/Api/UserConfigurationController.php
@@ -25,6 +25,9 @@ class UserConfigurationController extends Controller
'tasks' => [
'isMenuCollapse' => true,
],
+ 'tasks_inbox' => [
+ 'isMenuCollapse' => false,
+ ],
];
public function index()
@@ -54,6 +57,7 @@ public function store(Request $request)
'ui_configuration.cases' => 'required|array',
'ui_configuration.requests' => 'required|array',
'ui_configuration.tasks' => 'required|array',
+ 'ui_configuration.tasks_inbox' => 'required|array',
]);
$uiConfiguration = json_encode($request->input('ui_configuration'));
diff --git a/ProcessMaker/Http/Controllers/HomeController.php b/ProcessMaker/Http/Controllers/HomeController.php
index df81ab10f9..40ca2e9eed 100644
--- a/ProcessMaker/Http/Controllers/HomeController.php
+++ b/ProcessMaker/Http/Controllers/HomeController.php
@@ -14,14 +14,22 @@ public function index(Request $request)
if (Auth::check()) {
// Redirect to home dynamic only if the package was enable
if (hasPackage('package-dynamic-ui')) {
- $user = \Auth::user();
- $homePage = \ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::getHomePage($user);
+ $user = Auth::user();
- return redirect($homePage);
+ // Check if there is at least one custom dashboard per user
+ $customDashboardExists = \ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::where('type', 'DASHBOARD')
+ ->where('assignable_id', $user->id)
+ ->count() > 0;
+
+ if ($customDashboardExists) {
+ $homePage = \ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::getHomePage($user);
+
+ return redirect($homePage);
+ }
}
// Redirect to the default view
- return redirect('/requests');
+ return redirect('/inbox');
}
}
diff --git a/ProcessMaker/Http/Controllers/TaskController.php b/ProcessMaker/Http/Controllers/TaskController.php
index fa4c945454..415bece910 100755
--- a/ProcessMaker/Http/Controllers/TaskController.php
+++ b/ProcessMaker/Http/Controllers/TaskController.php
@@ -44,6 +44,8 @@ public function index()
{
$title = 'To Do Tasks';
+ $showOldTaskScreen = Request::path() !== 'inbox';
+
if (Request::input('status') == 'CLOSED') {
$title = 'Completed Tasks';
}
@@ -58,7 +60,9 @@ public function index()
$taskDraftsEnabled = TaskDraft::draftsEnabled();
- return view('tasks.index', compact('title', 'userFilter', 'defaultColumns', 'taskDraftsEnabled'));
+ $userConfiguration = (new UserConfigurationController())->index()['ui_configuration'] ?? [];
+
+ return view('tasks.index', compact('title', 'userFilter', 'defaultColumns', 'taskDraftsEnabled', 'userConfiguration', 'showOldTaskScreen'));
}
public function edit(ProcessRequestToken $task, string $preview = '')
diff --git a/ProcessMaker/Traits/TaskControllerIndexMethods.php b/ProcessMaker/Traits/TaskControllerIndexMethods.php
index 078843c950..d0a351478f 100644
--- a/ProcessMaker/Traits/TaskControllerIndexMethods.php
+++ b/ProcessMaker/Traits/TaskControllerIndexMethods.php
@@ -23,7 +23,20 @@ private function indexBaseQuery($request)
// Determine if the data should be included
$includeData = in_array('data', $includes);
- $query = ProcessRequestToken::exclude(['data'])->with([
+ $query = ProcessRequestToken::exclude(['data']);
+
+ // If all_inbox is true and user has process requests, filter to only show the latest process
+ if ($request->has('all_inbox') && $request->input('all_inbox') === 'false') {
+ $latestProcessRequest = ProcessRequestToken::where('user_id', auth()->id())
+ ->orderBy('created_at', 'desc')
+ ->first();
+
+ if ($latestProcessRequest) {
+ $query->where('process_id', $latestProcessRequest->process_id);
+ }
+ }
+
+ $query = $query->with([
'processRequest' => function ($q) use ($includeData) {
if (!$includeData) {
return $q->exclude(['data']);
diff --git a/devhub/pm-font/svg/tachometer-alt-average.svg b/devhub/pm-font/svg/tachometer-alt-average.svg
new file mode 100644
index 0000000000..768e8904a4
--- /dev/null
+++ b/devhub/pm-font/svg/tachometer-alt-average.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/resources/fonts/pm-font/index.html b/resources/fonts/pm-font/index.html
index 281a2aa4c4..c91af38eb9 100644
--- a/resources/fonts/pm-font/index.html
+++ b/resources/fonts/pm-font/index.html
@@ -103,7 +103,7 @@
- bpmn-action-by-email
bpmn-data-connector
bpmn-data-object
bpmn-data-store
bpmn-docusign
bpmn-end-event
bpmn-flowgenie
bpmn-gateway
bpmn-generic-gateway
bpmn-idp
bpmn-intermediate-event
bpmn-pool
bpmn-send-email
bpmn-start-event
bpmn-task
bpmn-text-annotation
brush-icon
close
cloud-download-outline
copy
desktop
eye
fields-icon
flowgenie-outline
folder-outline
fullscreen
github
layout-icon
map
mobile
pdf
play-outline
plus
screen-outline
script-outline
slack-notification
slack
slideshow
table
trash
unlink
+ bpmn-action-by-email
bpmn-data-connector
bpmn-data-object
bpmn-data-store
bpmn-docusign
bpmn-end-event
bpmn-flowgenie
bpmn-gateway
bpmn-generic-gateway
bpmn-idp
bpmn-intermediate-event
bpmn-pool
bpmn-send-email
bpmn-start-event
bpmn-task
bpmn-text-annotation
brush-icon
close
cloud-download-outline
copy
desktop
eye
fields-icon
flowgenie-outline
folder-outline
fullscreen
github
layout-icon
map
mobile
pdf
play-outline
plus
screen-outline
script-outline
slack-notification
slack
slideshow
table
tachometer-alt-average
trash
unlink
diff --git a/resources/fonts/pm-font/processmaker-font.css b/resources/fonts/pm-font/processmaker-font.css
index d7fdea0a5e..4e239df8d8 100644
--- a/resources/fonts/pm-font/processmaker-font.css
+++ b/resources/fonts/pm-font/processmaker-font.css
@@ -1,11 +1,11 @@
@font-face {
font-family: "processmaker-font";
- src: url('processmaker-font.eot?t=1730322668195'); /* IE9*/
- src: url('processmaker-font.eot?t=1730322668195#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url("processmaker-font.woff2?t=1730322668195") format("woff2"),
- url("processmaker-font.woff?t=1730322668195") format("woff"),
- url('processmaker-font.ttf?t=1730322668195') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
- url('processmaker-font.svg?t=1730322668195#processmaker-font') format('svg'); /* iOS 4.1- */
+ src: url('processmaker-font.eot?t=1736427523749'); /* IE9*/
+ src: url('processmaker-font.eot?t=1736427523749#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url("processmaker-font.woff2?t=1736427523749") format("woff2"),
+ url("processmaker-font.woff?t=1736427523749") format("woff"),
+ url('processmaker-font.ttf?t=1736427523749') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
+ url('processmaker-font.svg?t=1736427523749#processmaker-font') format('svg'); /* iOS 4.1- */
}
[class^="fp-"], [class*=" fp-"] {
@@ -55,6 +55,7 @@
.fp-slack:before { content: "\ea25"; }
.fp-slideshow:before { content: "\ea26"; }
.fp-table:before { content: "\ea27"; }
-.fp-trash:before { content: "\ea28"; }
-.fp-unlink:before { content: "\ea29"; }
+.fp-tachometer-alt-average:before { content: "\ea28"; }
+.fp-trash:before { content: "\ea29"; }
+.fp-unlink:before { content: "\ea2a"; }
diff --git a/resources/fonts/pm-font/processmaker-font.eot b/resources/fonts/pm-font/processmaker-font.eot
index 58983ffe0e..7e53b1e846 100644
Binary files a/resources/fonts/pm-font/processmaker-font.eot and b/resources/fonts/pm-font/processmaker-font.eot differ
diff --git a/resources/fonts/pm-font/processmaker-font.less b/resources/fonts/pm-font/processmaker-font.less
index 004b55fd66..52ee31b680 100644
--- a/resources/fonts/pm-font/processmaker-font.less
+++ b/resources/fonts/pm-font/processmaker-font.less
@@ -1,10 +1,10 @@
@font-face {font-family: "processmaker-font";
- src: url('processmaker-font.eot?t=1730322668195'); /* IE9*/
- src: url('processmaker-font.eot?t=1730322668195#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url("processmaker-font.woff2?t=1730322668195") format("woff2"),
- url("processmaker-font.woff?t=1730322668195") format("woff"),
- url('processmaker-font.ttf?t=1730322668195') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
- url('processmaker-font.svg?t=1730322668195#processmaker-font') format('svg'); /* iOS 4.1- */
+ src: url('processmaker-font.eot?t=1736427523749'); /* IE9*/
+ src: url('processmaker-font.eot?t=1736427523749#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url("processmaker-font.woff2?t=1736427523749") format("woff2"),
+ url("processmaker-font.woff?t=1736427523749") format("woff"),
+ url('processmaker-font.ttf?t=1736427523749') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
+ url('processmaker-font.svg?t=1736427523749#processmaker-font') format('svg'); /* iOS 4.1- */
}
[class^="fp-"], [class*=" fp-"] {
@@ -53,5 +53,6 @@
.fp-slack:before { content: "\ea25"; }
.fp-slideshow:before { content: "\ea26"; }
.fp-table:before { content: "\ea27"; }
-.fp-trash:before { content: "\ea28"; }
-.fp-unlink:before { content: "\ea29"; }
+.fp-tachometer-alt-average:before { content: "\ea28"; }
+.fp-trash:before { content: "\ea29"; }
+.fp-unlink:before { content: "\ea2a"; }
diff --git a/resources/fonts/pm-font/processmaker-font.module.less b/resources/fonts/pm-font/processmaker-font.module.less
index 026d3cf05f..10350c683b 100644
--- a/resources/fonts/pm-font/processmaker-font.module.less
+++ b/resources/fonts/pm-font/processmaker-font.module.less
@@ -1,10 +1,10 @@
@font-face {font-family: "processmaker-font";
- src: url('processmaker-font.eot?t=1730322668195'); /* IE9*/
- src: url('processmaker-font.eot?t=1730322668195#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url("processmaker-font.woff2?t=1730322668195") format("woff2"),
- url("processmaker-font.woff?t=1730322668195") format("woff"),
- url('processmaker-font.ttf?t=1730322668195') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
- url('processmaker-font.svg?t=1730322668195#processmaker-font') format('svg'); /* iOS 4.1- */
+ src: url('processmaker-font.eot?t=1736427523749'); /* IE9*/
+ src: url('processmaker-font.eot?t=1736427523749#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url("processmaker-font.woff2?t=1736427523749") format("woff2"),
+ url("processmaker-font.woff?t=1736427523749") format("woff"),
+ url('processmaker-font.ttf?t=1736427523749') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
+ url('processmaker-font.svg?t=1736427523749#processmaker-font') format('svg'); /* iOS 4.1- */
}
[class^="fp-"], [class*=" fp-"] {
@@ -54,7 +54,8 @@
.fp-slack:before { content: "\ea25"; }
.fp-slideshow:before { content: "\ea26"; }
.fp-table:before { content: "\ea27"; }
-.fp-trash:before { content: "\ea28"; }
-.fp-unlink:before { content: "\ea29"; }
+.fp-tachometer-alt-average:before { content: "\ea28"; }
+.fp-trash:before { content: "\ea29"; }
+.fp-unlink:before { content: "\ea2a"; }
}
\ No newline at end of file
diff --git a/resources/fonts/pm-font/processmaker-font.scss b/resources/fonts/pm-font/processmaker-font.scss
index 1b4de47a37..a27ec90675 100644
--- a/resources/fonts/pm-font/processmaker-font.scss
+++ b/resources/fonts/pm-font/processmaker-font.scss
@@ -1,10 +1,10 @@
@font-face {font-family: "processmaker-font";
- src: url('processmaker-font.eot?t=1730322668195'); /* IE9*/
- src: url('processmaker-font.eot?t=1730322668195#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url("processmaker-font.woff2?t=1730322668195") format("woff2"),
- url("processmaker-font.woff?t=1730322668195") format("woff"),
- url('processmaker-font.ttf?t=1730322668195') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
- url('processmaker-font.svg?t=1730322668195#processmaker-font') format('svg'); /* iOS 4.1- */
+ src: url('processmaker-font.eot?t=1736427523749'); /* IE9*/
+ src: url('processmaker-font.eot?t=1736427523749#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url("processmaker-font.woff2?t=1736427523749") format("woff2"),
+ url("processmaker-font.woff?t=1736427523749") format("woff"),
+ url('processmaker-font.ttf?t=1736427523749') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
+ url('processmaker-font.svg?t=1736427523749#processmaker-font') format('svg'); /* iOS 4.1- */
}
[class^="fp-"], [class*=" fp-"] {
@@ -53,8 +53,9 @@
.fp-slack:before { content: "\ea25"; }
.fp-slideshow:before { content: "\ea26"; }
.fp-table:before { content: "\ea27"; }
-.fp-trash:before { content: "\ea28"; }
-.fp-unlink:before { content: "\ea29"; }
+.fp-tachometer-alt-average:before { content: "\ea28"; }
+.fp-trash:before { content: "\ea29"; }
+.fp-unlink:before { content: "\ea2a"; }
$fp-bpmn-action-by-email: "\ea01";
$fp-bpmn-data-connector: "\ea02";
@@ -95,6 +96,7 @@ $fp-slack-notification: "\ea24";
$fp-slack: "\ea25";
$fp-slideshow: "\ea26";
$fp-table: "\ea27";
-$fp-trash: "\ea28";
-$fp-unlink: "\ea29";
+$fp-tachometer-alt-average: "\ea28";
+$fp-trash: "\ea29";
+$fp-unlink: "\ea2a";
diff --git a/resources/fonts/pm-font/processmaker-font.styl b/resources/fonts/pm-font/processmaker-font.styl
index 004b55fd66..52ee31b680 100644
--- a/resources/fonts/pm-font/processmaker-font.styl
+++ b/resources/fonts/pm-font/processmaker-font.styl
@@ -1,10 +1,10 @@
@font-face {font-family: "processmaker-font";
- src: url('processmaker-font.eot?t=1730322668195'); /* IE9*/
- src: url('processmaker-font.eot?t=1730322668195#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url("processmaker-font.woff2?t=1730322668195") format("woff2"),
- url("processmaker-font.woff?t=1730322668195") format("woff"),
- url('processmaker-font.ttf?t=1730322668195') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
- url('processmaker-font.svg?t=1730322668195#processmaker-font') format('svg'); /* iOS 4.1- */
+ src: url('processmaker-font.eot?t=1736427523749'); /* IE9*/
+ src: url('processmaker-font.eot?t=1736427523749#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url("processmaker-font.woff2?t=1736427523749") format("woff2"),
+ url("processmaker-font.woff?t=1736427523749") format("woff"),
+ url('processmaker-font.ttf?t=1736427523749') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
+ url('processmaker-font.svg?t=1736427523749#processmaker-font') format('svg'); /* iOS 4.1- */
}
[class^="fp-"], [class*=" fp-"] {
@@ -53,5 +53,6 @@
.fp-slack:before { content: "\ea25"; }
.fp-slideshow:before { content: "\ea26"; }
.fp-table:before { content: "\ea27"; }
-.fp-trash:before { content: "\ea28"; }
-.fp-unlink:before { content: "\ea29"; }
+.fp-tachometer-alt-average:before { content: "\ea28"; }
+.fp-trash:before { content: "\ea29"; }
+.fp-unlink:before { content: "\ea2a"; }
diff --git a/resources/fonts/pm-font/processmaker-font.svg b/resources/fonts/pm-font/processmaker-font.svg
index e18aaa0817..77a7665540 100644
--- a/resources/fonts/pm-font/processmaker-font.svg
+++ b/resources/fonts/pm-font/processmaker-font.svg
@@ -124,11 +124,14 @@
-
+
diff --git a/resources/fonts/pm-font/processmaker-font.symbol.svg b/resources/fonts/pm-font/processmaker-font.symbol.svg
index 08cc447028..9cb49a34cd 100644
--- a/resources/fonts/pm-font/processmaker-font.symbol.svg
+++ b/resources/fonts/pm-font/processmaker-font.symbol.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/fonts/pm-font/processmaker-font.ttf b/resources/fonts/pm-font/processmaker-font.ttf
index f185cad7db..7c7bebd1d6 100644
Binary files a/resources/fonts/pm-font/processmaker-font.ttf and b/resources/fonts/pm-font/processmaker-font.ttf differ
diff --git a/resources/fonts/pm-font/processmaker-font.woff b/resources/fonts/pm-font/processmaker-font.woff
index df77709fdb..d2411b21d0 100644
Binary files a/resources/fonts/pm-font/processmaker-font.woff and b/resources/fonts/pm-font/processmaker-font.woff differ
diff --git a/resources/fonts/pm-font/processmaker-font.woff2 b/resources/fonts/pm-font/processmaker-font.woff2
index 4baf9b66f0..37fcbb3336 100644
Binary files a/resources/fonts/pm-font/processmaker-font.woff2 and b/resources/fonts/pm-font/processmaker-font.woff2 differ
diff --git a/resources/fonts/pm-font/symbol.html b/resources/fonts/pm-font/symbol.html
index 40b2f080af..dcd6fdf254 100644
--- a/resources/fonts/pm-font/symbol.html
+++ b/resources/fonts/pm-font/symbol.html
@@ -101,7 +101,7 @@
@@ -44,7 +46,7 @@ import ProcessDescription from "./optionsMenu/ProcessDescription.vue";
import ProcessCounter from "./optionsMenu/ProcessCounter.vue";
export default {
- props: ["process", "processId"],
+ props: ["process", "processId", "ellipsisPermission"],
components: {
ProcessInfo, ProcessScreen, MiniPieChart, Bookmark, ProcessDescription, ProcessCounter
},
diff --git a/resources/js/processes-catalogue/components/ProcessCollapseInfo.vue b/resources/js/processes-catalogue/components/ProcessCollapseInfo.vue
index 6dd2ef0f41..5439cf254a 100644
--- a/resources/js/processes-catalogue/components/ProcessCollapseInfo.vue
+++ b/resources/js/processes-catalogue/components/ProcessCollapseInfo.vue
@@ -3,6 +3,7 @@
+ :permission="$root.permission || ellipsisPermission" />
@@ -62,6 +62,10 @@ export default {
enableCollapse: {
type: Boolean,
default: true
+ },
+ ellipsisPermission: {
+ type: Array,
+ default: () => []
}
},
data() {
diff --git a/resources/js/processes-catalogue/components/ProcessInfo.vue b/resources/js/processes-catalogue/components/ProcessInfo.vue
index b16fc7b969..77669f2ab6 100644
--- a/resources/js/processes-catalogue/components/ProcessInfo.vue
+++ b/resources/js/processes-catalogue/components/ProcessInfo.vue
@@ -4,6 +4,7 @@
v-show="hideLaunchpad"
:process="process"
:current-user-id="currentUserId"
+ :ellipsis-permission="ellipsisPermission"
@goBackCategory="$emit('goBackCategory')"
/>
{
this.firstImage = pos + 1;
});
-
},
computed: {
},
diff --git a/resources/js/processes-catalogue/components/ProcessScreen.vue b/resources/js/processes-catalogue/components/ProcessScreen.vue
index 1b6870cbfe..1431c6f40f 100644
--- a/resources/js/processes-catalogue/components/ProcessScreen.vue
+++ b/resources/js/processes-catalogue/components/ProcessScreen.vue
@@ -2,6 +2,7 @@
permissionsNeeded.includes(permission));
+ const permissions = (this.$root && this.$root.permission) ? this.$root.permission : this.ellipsisPermission || [];
+ this.showEllipsis = permissions.some((permission) => permissionsNeeded.includes(permission));
},
/**
* Return a process cards from process info
diff --git a/resources/js/tasks/components/DashboardViewer.vue b/resources/js/tasks/components/DashboardViewer.vue
new file mode 100644
index 0000000000..aa5fd80457
--- /dev/null
+++ b/resources/js/tasks/components/DashboardViewer.vue
@@ -0,0 +1,19 @@
+
+
+
Dashboard Viewer
+
Dashboard ID: {{ dashboardId }}
+
No selected dashboard
+
+
+
+
\ No newline at end of file
diff --git a/resources/js/tasks/components/ListMixin.js b/resources/js/tasks/components/ListMixin.js
index a0a2827bf9..9fc0a93ea0 100644
--- a/resources/js/tasks/components/ListMixin.js
+++ b/resources/js/tasks/components/ListMixin.js
@@ -94,6 +94,11 @@ const ListMixin = {
if (this.additionalIncludes) {
include.push(...this.additionalIncludes);
}
+
+ let getAllTasksInbox = "&all_inbox=false";
+ if (this.$parent.allInbox) {
+ getAllTasksInbox = "&all_inbox=true";
+ }
// Load from our api client
ProcessMaker.apiClient
.get(
@@ -108,6 +113,7 @@ const ListMixin = {
}${this.getSortParam()
}&non_system=true` +
`&processesIManage=${(this.processesIManage ? 'true' : 'false')}` +
+ getAllTasksInbox +
advancedFilter +
this.columnsQuery,
{
diff --git a/resources/js/tasks/components/ParticipantHomeScreen.vue b/resources/js/tasks/components/ParticipantHomeScreen.vue
new file mode 100644
index 0000000000..aa6176c7a0
--- /dev/null
+++ b/resources/js/tasks/components/ParticipantHomeScreen.vue
@@ -0,0 +1,596 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ inOverdueMessage }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("Inbox Rules") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/js/tasks/components/ProcessBrowser.vue b/resources/js/tasks/components/ProcessBrowser.vue
new file mode 100644
index 0000000000..1f5d293f4b
--- /dev/null
+++ b/resources/js/tasks/components/ProcessBrowser.vue
@@ -0,0 +1,19 @@
+
+
+
Process Browser
+
Process ID: {{ processId }}
+
No selected Process
+
+
+
+
\ No newline at end of file
diff --git a/resources/js/tasks/components/ProcessesDashboardsMenu.vue b/resources/js/tasks/components/ProcessesDashboardsMenu.vue
new file mode 100644
index 0000000000..6fd5dd39f7
--- /dev/null
+++ b/resources/js/tasks/components/ProcessesDashboardsMenu.vue
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
diff --git a/resources/js/tasks/index.js b/resources/js/tasks/index.js
index 41ad99016b..dea8677c70 100644
--- a/resources/js/tasks/index.js
+++ b/resources/js/tasks/index.js
@@ -2,12 +2,18 @@ import Vue from "vue";
import TasksList from "./components/TasksList";
import TasksListCounter from "./components/TasksListCounter.vue";
import setDefaultAdvancedFilterStatus from "../common/setDefaultAdvancedFilterStatus";
+import ParticipantHomeScreen from './components/ParticipantHomeScreen.vue';
Vue.component("TasksList", TasksList);
+Vue.component('participant-home-screen', ParticipantHomeScreen);
new Vue({
el: "#tasks",
data: {
+ showOldTaskScreen: window.ProcessMaker.showOldTaskScreen,
+ userConfiguration: window.ProcessMaker.userConfiguration,
+ urlConfiguration: "users/configuration",
+ showMenu: true,
columns: window.Processmaker.defaultColumns || null,
filter: "",
pmql: "",
@@ -62,7 +68,9 @@ new Vue({
if (!window.location.search.includes("filter_user_recommendation")) {
this.$nextTick(() => {
- this.$refs.taskList.fetch();
+ if (this.$refs.taskList) {
+ this.$refs.taskList.fetch();
+ }
});
}
},
diff --git a/resources/js/tasks/mixins/TasksMixin.js b/resources/js/tasks/mixins/TasksMixin.js
new file mode 100644
index 0000000000..ad96cc0a3e
--- /dev/null
+++ b/resources/js/tasks/mixins/TasksMixin.js
@@ -0,0 +1,191 @@
+export default {
+ data() {
+ return {
+ showOldTaskScreen: false,
+ urlConfiguration: "users/configuration",
+ showMenu: false,
+ columns: window.Processmaker.defaultColumns || null,
+ filter: "",
+ pmql: "",
+ urlPmql: "",
+ filtersPmql: "",
+ fullPmql: "",
+ status: [],
+ inOverdueMessage: "",
+ additions: [],
+ priorityField: "is_priority",
+ draftField: "draft",
+ isDataLoading: false,
+ inbox: true,
+ priority: false,
+ draft: false,
+ tab: "inbox",
+ inboxCount: null,
+ draftCount: null,
+ priorityCount: null,
+ priorityFilter: [
+ {
+ subject: {
+ type: "Field",
+ value: "is_priority",
+ },
+ operator: "=",
+ value: true,
+ _column_field: "is_priority",
+ _column_label: "Priority",
+ _hide_badge: true,
+ },
+ ],
+ draftFilter: [
+ {
+ subject: {
+ type: "Relationship",
+ value: "draft.id",
+ },
+ operator: ">",
+ value: 0,
+ _column_field: "draft",
+ _column_label: "Draft",
+ _hide_badge: true,
+ },
+ ],
+ };
+ },
+ computed: {
+ effectiveSavedsearchDefaultsEditRoute() {
+ return (
+ this.savedsearchDefaultsEditRoute ||
+ window.ProcessMaker.savedsearchDefaultsEditRoute
+ );
+ },
+ },
+ methods: {
+ defineUserConfiguration() {
+ this.localUserConfiguration = JSON.parse(
+ window.ProcessMaker.userConfiguration || "{}"
+ );
+ if (this.localUserConfiguration.tasks_inbox) {
+ this.showMenu = this.localUserConfiguration.tasks_inbox.isMenuCollapse;
+ } else {
+ this.showMenu = false;
+ this.localUserConfiguration.tasks_inbox = {
+ isMenuCollapse: false,
+ };
+ }
+ },
+ hideMenu() {
+ this.showMenu = !this.showMenu;
+ this.updateUserConfiguration();
+ },
+ updateUserConfiguration() {
+ this.localUserConfiguration.tasks_inbox.isMenuCollapse = this.showMenu;
+ ProcessMaker.apiClient
+ .put(this.urlConfiguration, {
+ ui_configuration: this.localUserConfiguration,
+ })
+ .catch((error) => {
+ console.error("Error", error);
+ });
+ },
+ switchTab(tab) {
+ this.tab = tab;
+ const taskListComponent = this.$refs.taskList;
+ taskListComponent.advancedFilter[this.priorityField] = [];
+ taskListComponent.advancedFilter[this.draftField] = [];
+ switch (tab) {
+ case "priority":
+ taskListComponent.advancedFilter["is_priority"] = this.priorityFilter;
+ break;
+ case "draft":
+ taskListComponent.advancedFilter["draft"] = this.draftFilter;
+ break;
+ }
+ taskListComponent.markStyleWhenColumnSetAFilter();
+ taskListComponent.storeFilterConfiguration();
+ taskListComponent.fetch(true);
+ },
+ dataLoading(value) {
+ this.isDataLoading = value;
+ },
+ onFetchTask() {
+ this.inbox = true;
+ this.priority = this.draft = false;
+ let filters = window.ProcessMaker.advanced_filter?.filters;
+ if (!Array.isArray(filters)) {
+ filters = [];
+ }
+ filters.forEach((item) => {
+ if (item._column_field === "is_priority") {
+ this.priority = true;
+ this.inbox = this.draft = false;
+ }
+ if (item._column_field === "draft") {
+ this.draft = true;
+ this.inbox = this.priority = false;
+ }
+ });
+ },
+ handleTabCount(value) {
+ if (this.tab === "inbox") {
+ this.inboxCount = value;
+ }
+ if (this.tab === "draft") {
+ this.draftCount = value;
+ }
+ if (this.tab === "priority") {
+ this.priorityCount = value;
+ }
+ },
+ onFiltersPmqlChange(value) {
+ this.filtersPmql = value[0];
+ this.fullPmql = this.getFullPmql();
+ },
+ onNLQConversion(query) {
+ this.onChange(query);
+ this.onSearch();
+ },
+ onChange(query) {
+ this.pmql = query;
+ this.fullPmql = this.getFullPmql();
+ },
+ onSearch() {
+ if (this.$refs.taskList) {
+ this.$refs.taskList.fetch(true);
+ }
+ },
+ onInboxRules() {
+ window.location.href = "/tasks/rules";
+ },
+ setInOverdueMessage(inOverdue) {
+ let inOverdueMessage = "";
+ if (inOverdue) {
+ const taskText =
+ inOverdue > 1
+ ? this.$t("Tasks").toLowerCase()
+ : this.$t("Task").toLowerCase();
+ inOverdueMessage = this.$t(
+ "You have {{ inOverDue }} overdue {{ taskText }} pending",
+ { inOverDue: inOverdue, taskText }
+ );
+ }
+ this.inOverdueMessage = inOverdueMessage;
+ },
+ getFullPmql() {
+ let fullPmqlString = "";
+
+ if (this.filtersPmql && this.filtersPmql !== "") {
+ fullPmqlString = this.filtersPmql;
+ }
+
+ if (fullPmqlString !== "" && this.pmql && this.pmql !== "") {
+ fullPmqlString = `${fullPmqlString} AND ${this.pmql}`;
+ }
+
+ if (fullPmqlString === "" && this.pmql && this.pmql !== "") {
+ fullPmqlString = this.pmql;
+ }
+
+ return fullPmqlString;
+ },
+ },
+};
diff --git a/resources/js/tasks/router.js b/resources/js/tasks/router.js
new file mode 100644
index 0000000000..ab96b9b45b
--- /dev/null
+++ b/resources/js/tasks/router.js
@@ -0,0 +1,32 @@
+import Vue from "vue";
+import VueRouter from "vue-router";
+import ProcessBrowser from "./components/ProcessBrowser.vue";
+import DashboardViewer from "./components/DashboardViewer.vue";
+
+Vue.use(VueRouter);
+
+const router = new VueRouter({
+ mode: "history",
+ base: "/tasks",
+ routes: [
+ {
+ path: "/process/:processId",
+ name: "process-browser",
+ component: ProcessBrowser,
+ props: (route) => ({
+ processId: parseInt(route.params.processId) || null,
+ process: null,
+ }),
+ },
+ {
+ path: "/dashboard/:dashboardId",
+ name: "dashboard",
+ component: DashboardViewer,
+ props: (route) => ({
+ dashboardId: route.params.dashboardId || null,
+ }),
+ },
+ ],
+});
+
+export default router;
diff --git a/resources/views/tasks/index.blade.php b/resources/views/tasks/index.blade.php
index cc3082c845..08b4050ead 100644
--- a/resources/views/tasks/index.blade.php
+++ b/resources/views/tasks/index.blade.php
@@ -1,7 +1,7 @@
@extends('layouts.layout')
@section('title')
- {{__($title)}}
+ {{ __($title) }}
@endsection
@section('sidebar')
@@ -9,175 +9,163 @@
@endsection
@section('breadcrumbs')
- @include('shared.breadcrumbs', ['routes' => [
- __('Tasks') => route('tasks.index'),
- __($title) => null,
- ]])
+ @include('shared.breadcrumbs', [
+ 'routes' => [
+ __('Tasks') => route('tasks.index'),
+ __($title) => null,
+ ],
+ ])
@endsection
+
@section('content')
-
-
-
-
- @{{ inOverdueMessage }}
-
-
-
+
+
+
+
+
+
+ @{{ inOverdueMessage }}
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ __('Inbox Rules') }}
-
-
-
-
-
-
-
- {!! __('Inbox Rules act as your personal task manager. You tell them what to look for, and they take care of things automatically. ') !!}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ __('Inbox Rules') }}
+
+
+
+
+
+
+
+ {!! __(
+ 'Inbox Rules act as your personal task manager. You tell them what to look for, and they take care of things automatically. ',
+ ) !!}
+
+
+
+ @if (
+ (Auth::user()->is_administrator || Auth::user()->hasPermission('edit-screens')) &&
+ Route::has('package.savedsearch.defaults.edit'))
+
+
+
+ @endif
+
+
+
+
+
+
+
-
- @if((
- Auth::user()->is_administrator ||
- Auth::user()->hasPermission('edit-screens')
- ) && Route::has('package.savedsearch.defaults.edit'))
-
-
-
- @endif
-
-
-
+
+
-
-
-
-
-
-
+
-
@endsection
@section('js')
-
+
+
@endsection
@section('css')
@@ -222,14 +210,17 @@ class="ml-md-2"
min-height: 25px;
border-radius: 50%;
}
+
.task-nav {
border-bottom: 0 !important;
}
+
.task-nav-link.active {
color: #1572C2 !important;
font-weight: 700;
font-size: 15px;
}
+
.task-nav-link {
color: #556271;
font-weight: 400;
@@ -237,25 +228,227 @@ class="ml-md-2"
border-top-left-radius: 5px !important;
border-top-right-radius: 5px !important;
}
+
.task-list-body {
border-radius: 5px;
}
+
.task-inbox-rules {
- width: max-content;
+ width: max-content;
}
+
.task-inbox-rules-content {
- display: flex;
- justify-content: space-between;
- padding: 15px;
+ display: flex;
+ justify-content: space-between;
+ padding: 15px;
}
+
.task-inbox-rules-content-text {
- width: 310px;
- padding-left: 10px;
+ width: 310px;
+ padding-left: 10px;
}
+
@endsection
diff --git a/routes/web.php b/routes/web.php
index e9240fc6b3..fc00a596cc 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -136,6 +136,7 @@
Route::get('modeler/{process}/inflight/{request?}', [ModelerController::class, 'inflight'])->name('modeler.inflight')->middleware('can:view,request');
Route::get('/', [HomeController::class, 'index'])->name('home');
+ Route::get('/inbox', [TaskController::class, 'index'])->name('inbox')->middleware('no-cache');
Route::get('/redirect-to-intended', [HomeController::class, 'redirectToIntended'])->name('redirect_to_intended');
Route::post('/keep-alive', [LoginController::class, 'keepAlive'])->name('keep-alive');
diff --git a/tests/Feature/Api/UserConfigurationTest.php b/tests/Feature/Api/UserConfigurationTest.php
index 0a2b4a0c8d..95fdce0acc 100644
--- a/tests/Feature/Api/UserConfigurationTest.php
+++ b/tests/Feature/Api/UserConfigurationTest.php
@@ -55,6 +55,9 @@ public function testStoreUserConfigurationAndGetNewValues()
'tasks' => [
'isMenuCollapse' => false,
],
+ 'tasks_inbox' => [
+ 'isMenuCollapse' => false,
+ ],
];
$response = $this->apiCall('PUT', self::API_TEST_URL, ['ui_configuration' => $values]);
@@ -74,6 +77,7 @@ public function testStoreUserConfigurationAndGetNewValues()
$this->assertEquals($uiConfig->cases->isMenuCollapse, $values['cases']['isMenuCollapse']);
$this->assertEquals($uiConfig->requests->isMenuCollapse, $values['requests']['isMenuCollapse']);
$this->assertEquals($uiConfig->tasks->isMenuCollapse, $values['tasks']['isMenuCollapse']);
+ $this->assertEquals($uiConfig->tasks_inbox->isMenuCollapse, $values['tasks_inbox']['isMenuCollapse']);
}
/**
@@ -86,7 +90,7 @@ public function testStoreUserConfigurationWithInvalidValues()
// Validate the header status code
$response->assertStatus(422);
- $this->assertEquals('The Ui configuration field is required. (and 4 more errors)', $response->json()['message']);
+ $this->assertEquals('The Ui configuration field is required. (and 5 more errors)', $response->json()['message']);
// An incomplete ui_configuration
$values = [
@@ -99,6 +103,9 @@ public function testStoreUserConfigurationWithInvalidValues()
'tasks' => [
'isMenuCollapse' => false,
],
+ 'tasks_inbox' => [
+ 'isMenuCollapse' => false,
+ ],
];
$response = $this->apiCall('PUT', self::API_TEST_URL, ['ui_configuration' => $values]);
// Validate the header status code
diff --git a/webpack.mix.js b/webpack.mix.js
index ef6c05502a..96d065d9c7 100644
--- a/webpack.mix.js
+++ b/webpack.mix.js
@@ -131,6 +131,7 @@ mix
.js("resources/js/tasks/index.js", "public/js/tasks/index.js")
.js("resources/js/tasks/mobile.js", "public/js/tasks/mobile.js")
.js("resources/js/tasks/show.js", "public/js/tasks/show.js")
+ .js("resources/js/tasks/router.js", "public/js/tasks/router.js")
.js("resources/js/notifications/index.js", "public/js/notifications/index.js")
.js('resources/js/inbox-rules/index.js', 'public/js/inbox-rules')