From d7b0cfbbe57fae9afbf9637afa7e54d3bf708747 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sat, 8 Apr 2017 13:58:25 -0400 Subject: [PATCH] Improve dashboard --- app/Controller/DashboardController.php | 58 ++++++++++++++++-- app/Core/Base.php | 3 + .../TaskListSubtaskAssigneeFormatter.php | 13 ++++ app/Helper/LayoutHelper.php | 13 ++++ app/Model/ProjectModel.php | 21 +++++++ app/Model/SubtaskModel.php | 9 +++ app/Pagination/ProjectPagination.php | 35 +++++++++++ app/Pagination/SubtaskPagination.php | 35 +++++++++++ app/Pagination/TaskPagination.php | 39 ++++++++++++ app/ServiceProvider/ClassProvider.php | 3 + app/Template/dashboard/layout.php | 29 +++++++++ .../dashboard/{show.php => overview.php} | 59 +++++++++++-------- app/Template/dashboard/projects.php | 27 +++++++++ app/Template/dashboard/sidebar.php | 17 ++++++ app/Template/dashboard/subtasks.php | 48 +++++++++++++++ app/Template/dashboard/tasks.php | 38 ++++++++++++ assets/css/app.min.css | 2 +- assets/sass/_table_list.sass | 3 + tests/integration/MeProcedureTest.php | 6 +- .../Pagination/ProjectPaginationTest.php | 35 +++++++++++ tests/units/Pagination/TaskPaginationTest.php | 30 ++++++++++ 21 files changed, 493 insertions(+), 30 deletions(-) create mode 100644 app/Pagination/ProjectPagination.php create mode 100644 app/Pagination/SubtaskPagination.php create mode 100644 app/Pagination/TaskPagination.php create mode 100644 app/Template/dashboard/layout.php rename app/Template/dashboard/{show.php => overview.php} (57%) create mode 100644 app/Template/dashboard/projects.php create mode 100644 app/Template/dashboard/sidebar.php create mode 100644 app/Template/dashboard/subtasks.php create mode 100644 app/Template/dashboard/tasks.php create mode 100644 tests/units/Pagination/ProjectPaginationTest.php create mode 100644 tests/units/Pagination/TaskPaginationTest.php diff --git a/app/Controller/DashboardController.php b/app/Controller/DashboardController.php index ef7d87721..0ff6f20eb 100644 --- a/app/Controller/DashboardController.php +++ b/app/Controller/DashboardController.php @@ -19,10 +19,60 @@ class DashboardController extends BaseController { $user = $this->getUser(); - $this->response->html($this->helper->layout->app('dashboard/show', array( - 'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)), - 'user' => $user, - 'results' => $this->dashboardPagination->getOverview($user['id']), + $this->response->html($this->helper->layout->dashboard('dashboard/overview', array( + 'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)), + 'user' => $user, + 'overview_paginator' => $this->dashboardPagination->getOverview($user['id']), + 'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', 10), + ))); + } + + /** + * My tasks + * + * @access public + */ + public function tasks() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->dashboard('dashboard/tasks', array( + 'title' => t('Tasks overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'tasks', 50), + 'user' => $user, + ))); + } + + /** + * My subtasks + * + * @access public + */ + public function subtasks() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array( + 'title' => t('Subtasks overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id']), + 'user' => $user, + 'nb_subtasks' => $this->subtaskModel->countByAssigneeAndTaskStatus($this->userSession->getId()), + ))); + } + + /** + * My projects + * + * @access public + */ + public function projects() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->dashboard('dashboard/projects', array( + 'title' => t('Projects overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'projects', 25), + 'user' => $user, ))); } } diff --git a/app/Core/Base.php b/app/Core/Base.php index e3dc2f237..c11faee2c 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -150,6 +150,9 @@ use Pimple\Container; * @property \Kanboard\Model\UserUnreadNotificationModel $userUnreadNotificationModel * @property \Kanboard\Model\UserMetadataModel $userMetadataModel * @property \Kanboard\Pagination\DashboardPagination $dashboardPagination + * @property \Kanboard\Pagination\ProjectPagination $projectPagination + * @property \Kanboard\Pagination\TaskPagination $taskPagination + * @property \Kanboard\Pagination\SubtaskPagination $subtaskPagination * @property \Kanboard\Pagination\UserPagination $userPagination * @property \Kanboard\Validator\ActionValidator $actionValidator * @property \Kanboard\Validator\AuthValidator $authValidator diff --git a/app/Formatter/TaskListSubtaskAssigneeFormatter.php b/app/Formatter/TaskListSubtaskAssigneeFormatter.php index 50b08cd82..733917667 100644 --- a/app/Formatter/TaskListSubtaskAssigneeFormatter.php +++ b/app/Formatter/TaskListSubtaskAssigneeFormatter.php @@ -11,6 +11,7 @@ namespace Kanboard\Formatter; class TaskListSubtaskAssigneeFormatter extends TaskListFormatter { protected $userId = 0; + protected $withoutEmptyTasks = false; /** * Set assignee @@ -24,6 +25,12 @@ class TaskListSubtaskAssigneeFormatter extends TaskListFormatter return $this; } + public function withoutEmptyTasks() + { + $this->withoutEmptyTasks = true; + return $this; + } + /** * Apply formatter * @@ -38,6 +45,12 @@ class TaskListSubtaskAssigneeFormatter extends TaskListFormatter $subtasks = array_column_index($subtasks, 'task_id'); array_merge_relation($tasks, $subtasks, 'subtasks', 'id'); + if ($this->withoutEmptyTasks) { + $tasks = array_filter($tasks, function (array $task) { + return count($task['subtasks']) > 0; + }); + } + return $tasks; } } diff --git a/app/Helper/LayoutHelper.php b/app/Helper/LayoutHelper.php index 52c83fecb..91745f582 100644 --- a/app/Helper/LayoutHelper.php +++ b/app/Helper/LayoutHelper.php @@ -140,6 +140,19 @@ class LayoutHelper extends Base return $this->subLayout('plugin/layout', 'plugin/sidebar', $template, $params); } + /** + * Common layout for dashboard views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function dashboard($template, array $params) + { + return $this->subLayout('dashboard/layout', 'dashboard/sidebar', $template, $params); + } + /** * Common layout for analytic views * diff --git a/app/Model/ProjectModel.php b/app/Model/ProjectModel.php index cabfee8a8..7f489c759 100644 --- a/app/Model/ProjectModel.php +++ b/app/Model/ProjectModel.php @@ -303,6 +303,27 @@ class ProjectModel extends Base return $projects; } + /** + * Get project summary for a list of project + * + * @access public + * @param array $project_ids List of project id + * @return \PicoDb\Table + */ + public function getQueryColumnStats(array $project_ids) + { + if (empty($project_ids)) { + return $this->db->table(ProjectModel::TABLE)->eq(ProjectModel::TABLE.'.id', 0); + } + + return $this->db + ->table(ProjectModel::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.username AS owner_username', UserModel::TABLE.'.name AS owner_name') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->in(self::TABLE.'.id', $project_ids) + ->callback(array($this, 'applyColumnStats')); + } + /** * Get query for list of project without column statistics * diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php index 40cb517dc..1e652ae29 100644 --- a/app/Model/SubtaskModel.php +++ b/app/Model/SubtaskModel.php @@ -88,6 +88,15 @@ class SubtaskModel extends Base ->asc(self::TABLE.'.position'); } + public function countByAssigneeAndTaskStatus($userId) + { + return $this->db->table(self::TABLE) + ->eq('user_id', $userId) + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->join(Taskmodel::TABLE, 'id', 'task_id') + ->count(); + } + /** * Get all subtasks for a given task * diff --git a/app/Pagination/ProjectPagination.php b/app/Pagination/ProjectPagination.php new file mode 100644 index 000000000..8f1fa87c6 --- /dev/null +++ b/app/Pagination/ProjectPagination.php @@ -0,0 +1,35 @@ +paginator + ->setUrl('DashboardController', $method, array('pagination' => 'projects', 'user_id' => $user_id)) + ->setMax($max) + ->setOrder(ProjectModel::TABLE.'.name') + ->setQuery($this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id))) + ->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects'); + } +} diff --git a/app/Pagination/SubtaskPagination.php b/app/Pagination/SubtaskPagination.php new file mode 100644 index 000000000..83593b5e5 --- /dev/null +++ b/app/Pagination/SubtaskPagination.php @@ -0,0 +1,35 @@ +paginator + ->setUrl('DashboardController', 'subtasks', array('user_id' => $userId)) + ->setMax(50) + ->setOrder(TaskModel::TABLE.'.priority') + ->setDirection('DESC') + ->setFormatter($this->taskListSubtaskAssigneeFormatter->withUserId($userId)->withoutEmptyTasks()) + ->setQuery($this->taskFinderModel->getUserQuery($userId)) + ->calculate(); + } +} diff --git a/app/Pagination/TaskPagination.php b/app/Pagination/TaskPagination.php new file mode 100644 index 000000000..53e05c133 --- /dev/null +++ b/app/Pagination/TaskPagination.php @@ -0,0 +1,39 @@ +taskFinderModel->getUserQuery($userId); + $this->hook->reference('pagination:dashboard:task:query', $query); + + return $this->paginator + ->setUrl('DashboardController', $method, array('pagination' => 'tasks', 'user_id' => $userId)) + ->setMax($max) + ->setOrder(TaskModel::TABLE.'.id') + ->setQuery($query) + ->setFormatter($this->taskListFormatter) + ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks'); + } +} diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index f510b80b8..f8bb3ae4a 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -130,6 +130,9 @@ class ClassProvider implements ServiceProviderInterface ), 'Pagination' => array( 'DashboardPagination', + 'ProjectPagination', + 'SubtaskPagination', + 'TaskPagination', 'UserPagination', ), 'Core' => array( diff --git a/app/Template/dashboard/layout.php b/app/Template/dashboard/layout.php new file mode 100644 index 000000000..45b52451a --- /dev/null +++ b/app/Template/dashboard/layout.php @@ -0,0 +1,29 @@ +
+ + +
diff --git a/app/Template/dashboard/show.php b/app/Template/dashboard/overview.php similarity index 57% rename from app/Template/dashboard/show.php rename to app/Template/dashboard/overview.php index 64b90516b..e732a387f 100644 --- a/app/Template/dashboard/show.php +++ b/app/Template/dashboard/overview.php @@ -1,25 +1,3 @@ - -
- +isEmpty()): ?> +
+ render('project_list/header', array('paginator' => $project_paginator)) ?> + getCollection() as $project): ?> +
+
+ user->hasProjectAccess('ProjectViewController', 'show', $project['id'])): ?> + render('project/dropdown', array('project' => $project)) ?> + + + + + + url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?> + + + + + +
+
+ + + text->e($column['title']) ?> + +
+
+ +
+ + + + +

- + isEmpty()): ?>