From d7b0cfbbe57fae9afbf9637afa7e54d3bf708747 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sat, 8 Apr 2017 13:58:25 -0400 Subject: Improve dashboard --- app/Controller/DashboardController.php | 58 +++++++++++++- app/Core/Base.php | 3 + app/Formatter/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 +++++++ app/Template/dashboard/overview.php | 93 ++++++++++++++++++++++ app/Template/dashboard/projects.php | 27 +++++++ app/Template/dashboard/show.php | 82 ------------------- app/Template/dashboard/sidebar.php | 17 ++++ app/Template/dashboard/subtasks.php | 48 +++++++++++ app/Template/dashboard/tasks.php | 38 +++++++++ 17 files changed, 477 insertions(+), 86 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 create mode 100644 app/Template/dashboard/overview.php create mode 100644 app/Template/dashboard/projects.php delete mode 100644 app/Template/dashboard/show.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 (limited to 'app') diff --git a/app/Controller/DashboardController.php b/app/Controller/DashboardController.php index ef7d8772..0ff6f20e 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 e3dc2f23..c11faee2 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 50b08cd8..73391766 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 52c83fec..91745f58 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 cabfee8a..7f489c75 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 40cb517d..1e652ae2 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 00000000..8f1fa87c --- /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 00000000..83593b5e --- /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 00000000..53e05c13 --- /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 f510b80b..f8bb3ae4 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 00000000..45b52451 --- /dev/null +++ b/app/Template/dashboard/layout.php @@ -0,0 +1,29 @@ +
+ + +
diff --git a/app/Template/dashboard/overview.php b/app/Template/dashboard/overview.php new file mode 100644 index 00000000..e732a387 --- /dev/null +++ b/app/Template/dashboard/overview.php @@ -0,0 +1,93 @@ +
+ +
+ +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()): ?> + + +
+ render('task_list/header', array( + 'paginator' => $result['paginator'], + )) ?> + + getCollection() as $task): ?> +
+ render('task_list/task_title', array( + 'task' => $task, + )) ?> + + render('task_list/task_details', array( + 'task' => $task, + )) ?> + + render('task_list/task_avatars', array( + 'task' => $task, + )) ?> + + render('task_list/task_icons', array( + 'task' => $task, + )) ?> + + render('task_list/task_subtasks', array( + 'task' => $task, + 'user_id' => $user['id'], + )) ?> +
+ +
+ + + + + + +hook->render('template:dashboard:show', array('user' => $user)) ?> diff --git a/app/Template/dashboard/projects.php b/app/Template/dashboard/projects.php new file mode 100644 index 00000000..e84a9415 --- /dev/null +++ b/app/Template/dashboard/projects.php @@ -0,0 +1,27 @@ + +isEmpty()): ?> +

+ +
+ render('project_list/header', array('paginator' => $paginator)) ?> + getCollection() as $project): ?> +
+ render('project_list/project_title', array( + 'project' => $project, + )) ?> + + render('project_list/project_details', array( + 'project' => $project, + )) ?> + + render('project_list/project_icons', array( + 'project' => $project, + )) ?> +
+ +
+ + + diff --git a/app/Template/dashboard/show.php b/app/Template/dashboard/show.php deleted file mode 100644 index 64b90516..00000000 --- a/app/Template/dashboard/show.php +++ /dev/null @@ -1,82 +0,0 @@ - - -
- -
- - -

- - - isEmpty()): ?> - - -
- render('task_list/header', array( - 'paginator' => $result['paginator'], - )) ?> - - getCollection() as $task): ?> -
- render('task_list/task_title', array( - 'task' => $task, - )) ?> - - render('task_list/task_details', array( - 'task' => $task, - )) ?> - - render('task_list/task_avatars', array( - 'task' => $task, - )) ?> - - render('task_list/task_icons', array( - 'task' => $task, - )) ?> - - render('task_list/task_subtasks', array( - 'task' => $task, - 'user_id' => $user['id'], - )) ?> -
- -
- - - - - - -hook->render('template:dashboard:show', array('user' => $user)) ?> diff --git a/app/Template/dashboard/sidebar.php b/app/Template/dashboard/sidebar.php new file mode 100644 index 00000000..7507b00d --- /dev/null +++ b/app/Template/dashboard/sidebar.php @@ -0,0 +1,17 @@ + diff --git a/app/Template/dashboard/subtasks.php b/app/Template/dashboard/subtasks.php new file mode 100644 index 00000000..ccc08c59 --- /dev/null +++ b/app/Template/dashboard/subtasks.php @@ -0,0 +1,48 @@ + + +

+ +
+
+
+ 1): ?> + + + + +
+
+ +
+
+ + getCollection() as $task): ?> +
+ render('task_list/task_title', array( + 'task' => $task, + )) ?> + + render('task_list/task_subtasks', array( + 'task' => $task, + )) ?> +
+ +
+ + + diff --git a/app/Template/dashboard/tasks.php b/app/Template/dashboard/tasks.php new file mode 100644 index 00000000..23aef990 --- /dev/null +++ b/app/Template/dashboard/tasks.php @@ -0,0 +1,38 @@ + +isEmpty()): ?> +

+ +
+ render('task_list/header', array( + 'paginator' => $paginator, + )) ?> + + getCollection() as $task): ?> +
+ render('task_list/task_title', array( + 'task' => $task, + )) ?> + + render('task_list/task_details', array( + 'task' => $task, + )) ?> + + render('task_list/task_avatars', array( + 'task' => $task, + )) ?> + + render('task_list/task_icons', array( + 'task' => $task, + )) ?> + + render('task_list/task_subtasks', array( + 'task' => $task, + )) ?> +
+ +
+ + + -- cgit v1.2.3