Change dashboard

This commit is contained in:
Frederic Guillot 2015-07-01 17:36:21 -04:00
parent 3f084916e3
commit 109a2a2e25
25 changed files with 276 additions and 290 deletions

View File

@ -13,6 +13,76 @@ use Model\Task as TaskModel;
*/
class App extends Base
{
/**
* Common layout for dashboard views
*
* @access private
* @param string $template Template name
* @param array $params Template parameters
* @return string
*/
private function layout($template, array $params)
{
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
$params['content_for_sublayout'] = $this->template->render($template, $params);
return $this->template->layout('app/layout', $params);
}
/**
* Get project pagination
*
* @access private
* @param integer $user_id
* @param string $action
* @param integer $max
*/
private function getProjectPaginator($user_id, $action, $max)
{
return $this->paginator
->setUrl('app', $action, array('pagination' => 'projects', 'user_id' => $user_id))
->setMax($max)
->setOrder('name')
->setQuery($this->project->getQueryColumnStats($this->projectPermission->getActiveMemberProjectIds($user_id)))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
}
/**
* Get task pagination
*
* @access private
* @param integer $user_id
* @param string $action
* @param integer $max
*/
private function getTaskPaginator($user_id, $action, $max)
{
return $this->paginator
->setUrl('app', $action, array('pagination' => 'tasks', 'user_id' => $user_id))
->setMax($max)
->setOrder('tasks.id')
->setQuery($this->taskFinder->getUserQuery($user_id))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks');
}
/**
* Get subtask pagination
*
* @access private
* @param integer $user_id
* @param string $action
* @param integer $max
*/
private function getSubtaskPaginator($user_id, $action, $max)
{
return $this->paginator
->setUrl('app', $action, array('pagination' => 'subtasks', 'user_id' => $user_id))
->setMax($max)
->setOrder('tasks.id')
->setQuery($this->subtask->getUserQuery($user_id, array(SubTaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS)))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
}
/**
* Check if the user is connected
*
@ -24,56 +94,97 @@ class App extends Base
}
/**
* User dashboard view for admins
* Dashboard overview
*
* @access public
*/
public function dashboard()
public function index()
{
$this->index($this->request->getIntegerParam('user_id'), 'dashboard');
$user = $this->getUser();
$this->response->html($this->layout('app/overview', array(
'title' => t('Overview'),
'project_paginator' => $this->getProjectPaginator($user['id'], 'index', 10),
'task_paginator' => $this->getTaskPaginator($user['id'], 'index', 10),
'subtask_paginator' => $this->getSubtaskPaginator($user['id'], 'index', 10),
'user' => $user,
)));
}
/**
* Dashboard for the current user
* My tasks
*
* @access public
*/
public function index($user_id = 0, $action = 'index')
public function tasks()
{
$status = array(SubTaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS);
$user_id = $user_id ?: $this->userSession->getId();
$projects = $this->projectPermission->getActiveMemberProjects($user_id);
$project_ids = array_keys($projects);
$user = $this->getUser();
$task_paginator = $this->paginator
->setUrl('app', $action, array('pagination' => 'tasks', 'user_id' => $user_id))
->setMax(10)
->setOrder('tasks.id')
->setQuery($this->taskFinder->getUserQuery($user_id))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks');
$this->response->html($this->layout('app/tasks', array(
'title' => t('My tasks'),
'paginator' => $this->getTaskPaginator($user['id'], 'tasks', 50),
'user' => $user,
)));
}
$subtask_paginator = $this->paginator
->setUrl('app', $action, array('pagination' => 'subtasks', 'user_id' => $user_id))
->setMax(10)
->setOrder('tasks.id')
->setQuery($this->subtask->getUserQuery($user_id, $status))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
/**
* My subtasks
*
* @access public
*/
public function subtasks()
{
$user = $this->getUser();
$project_paginator = $this->paginator
->setUrl('app', $action, array('pagination' => 'projects', 'user_id' => $user_id))
->setMax(10)
->setOrder('name')
->setQuery($this->project->getQueryColumnStats($project_ids))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
$this->response->html($this->layout('app/subtasks', array(
'title' => t('My subtasks'),
'paginator' => $this->getSubtaskPaginator($user['id'], 'subtasks', 50),
'user' => $user,
)));
}
$this->response->html($this->template->layout('app/dashboard', array(
'title' => t('Dashboard'),
'board_selector' => $this->projectPermission->getAllowedProjects($user_id),
'events' => $this->projectActivity->getProjects($project_ids, 5),
'task_paginator' => $task_paginator,
'subtask_paginator' => $subtask_paginator,
'project_paginator' => $project_paginator,
'user_id' => $user_id,
/**
* My projects
*
* @access public
*/
public function projects()
{
$user = $this->getUser();
$this->response->html($this->layout('app/projects', array(
'title' => t('My projects'),
'paginator' => $this->getProjectPaginator($user['id'], 'projects', 25),
'user' => $user,
)));
}
/**
* My activity stream
*
* @access public
*/
public function activity()
{
$user = $this->getUser();
$this->response->html($this->layout('app/activity', array(
'title' => t('My activity stream'),
'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveMemberProjectIds($user['id']), 100),
'user' => $user,
)));
}
/**
* My calendar
*
* @access public
*/
public function calendar()
{
$this->response->html($this->layout('app/calendar', array(
'title' => t('My calendar'),
'user' => $this->getUser(),
)));
}

View File

@ -306,4 +306,25 @@ abstract class Base extends \Core\Base
return $project;
}
/**
* Common method to get the user
*
* @access protected
* @return array
*/
protected function getUser()
{
$user = $this->user->getById($this->request->getIntegerParam('user_id', $this->userSession->getId()));
if (empty($user)) {
$this->notfound();
}
if (! $this->userSession->isAdmin() && $this->userSession->getId() != $user['id']) {
$this->forbidden();
}
return $user;
}
}

View File

@ -31,27 +31,6 @@ class User extends Base
return $this->template->layout('user/layout', $params);
}
/**
* Common method to get the user
*
* @access protected
* @return array
*/
protected function getUser()
{
$user = $this->user->getById($this->request->getIntegerParam('user_id'));
if (empty($user)) {
$this->notfound();
}
if (! $this->userSession->isAdmin() && $this->userSession->getId() != $user['id']) {
$this->forbidden();
}
return $user;
}
/**
* List all users
*
@ -138,20 +117,6 @@ class User extends Base
)));
}
/**
* Display user calendar
*
* @access public
*/
public function calendar()
{
$user = $this->getUser();
$this->response->html($this->layout('user/calendar', array(
'user' => $user,
)));
}
/**
* Display timesheet
*

View File

@ -69,7 +69,6 @@ class Acl extends Base
* @var array
*/
private $admin_acl = array(
'app' => array('dashboard'),
'user' => array('index', 'create', 'save', 'remove'),
'config' => '*',
'link' => '*',

View File

@ -0,0 +1,4 @@
<div class="page-header">
<h2><?= t('My activity stream') ?></h2>
</div>
<?= $this->render('event/events', array('events' => $events)) ?>

View File

@ -3,4 +3,4 @@
data-user-id="<?= $user['id'] ?>"
data-save-url="<?= $this->url->href('calendar', 'save') ?>"
>
</div>
</div>

View File

@ -1,60 +0,0 @@
<section id="main">
<div class="page-header page-header-mobile">
<ul>
<?php if ($this->user->isAdmin()): ?>
<li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'project', 'create') ?></li>
<?php endif ?>
<li><i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'project', 'create', array('private' => 1)) ?></li>
<li><i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('Project management'), 'project', 'index') ?></li>
<?php if ($this->user->isAdmin()): ?>
<li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('User management'), 'user', 'index') ?></li>
<li><i class="fa fa-cog fa-fw"></i><?= $this->url->link(t('Settings'), 'config', 'index') ?></li>
<?php endif ?>
<li>
<span class="dropdown">
<span>
<i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Change dashboard view') ?></a>
<ul>
<li>
<a href="#" class="dashboard-toggle" data-toggle="projects"><?= t('Show/hide projects') ?></a>
</li>
<li>
<a href="#" class="dashboard-toggle" data-toggle="tasks"><?= t('Show/hide tasks') ?></a>
</li>
<li>
<a href="#" class="dashboard-toggle" data-toggle="subtasks"><?= t('Show/hide subtasks') ?></a>
</li>
<li>
<a href="#" class="dashboard-toggle" data-toggle="calendar"><?= t('Show/hide calendar') ?></a>
</li>
<li>
<a href="#" class="dashboard-toggle" data-toggle="activities"><?= t('Show/hide activities') ?></a>
</li>
</ul>
</span>
</span>
</li>
</ul>
</div>
<section id="dashboard">
<div class="dashboard-left-column">
<div id="dashboard-projects"><?= $this->render('app/projects', array('paginator' => $project_paginator)) ?></div>
<div id="dashboard-tasks"><?= $this->render('app/tasks', array('paginator' => $task_paginator)) ?></div>
<div id="dashboard-subtasks"><?= $this->render('app/subtasks', array('paginator' => $subtask_paginator)) ?></div>
</div>
<div class="dashboard-right-column">
<div id="dashboard-calendar">
<div id="user-calendar"
data-check-url="<?= $this->url->href('calendar', 'user') ?>"
data-user-id="<?= $user_id ?>"
data-save-url="<?= $this->url->href('calendar', 'save') ?>"
>
</div>
</div>
<div id="dashboard-activities">
<h2><?= t('Activity stream') ?></h2>
<?= $this->render('event/events', array('events' => $events)) ?>
</div>
</div>
</section>
</section>

View File

@ -0,0 +1,21 @@
<section id="main">
<div class="page-header page-header-mobile">
<ul>
<?php if ($this->user->isAdmin()): ?>
<li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'project', 'create') ?></li>
<?php endif ?>
<li><i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'project', 'create', array('private' => 1)) ?></li>
<li><i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('Project management'), 'project', 'index') ?></li>
<?php if ($this->user->isAdmin()): ?>
<li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('User management'), 'user', 'index') ?></li>
<li><i class="fa fa-cog fa-fw"></i><?= $this->url->link(t('Settings'), 'config', 'index') ?></li>
<?php endif ?>
</ul>
</div>
<section class="sidebar-container" id="dashboard">
<?= $this->render('app/sidebar', array('user' => $user)) ?>
<div class="sidebar-content">
<?= $content_for_sublayout ?>
</div>
</section>
</section>

View File

@ -0,0 +1,3 @@
<?= $this->render('app/projects', array('paginator' => $project_paginator)) ?>
<?= $this->render('app/tasks', array('paginator' => $task_paginator)) ?>
<?= $this->render('app/subtasks', array('paginator' => $subtask_paginator)) ?>

View File

@ -1,4 +1,6 @@
<h2><?= t('My projects') ?> (<?= $paginator->getTotal() ?>)</h2>
<div class="page-header">
<h2><?= t('My projects') ?> (<?= $paginator->getTotal() ?>)</h2>
</div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('Your are not member of any project.') ?></p>
<?php else: ?>

View File

@ -0,0 +1,23 @@
<div class="sidebar">
<h2><?= $this->e($user['name'] ?: $user['username']) ?></h2>
<ul>
<li>
<?= $this->url->link(t('Overview'), 'app', 'index', array('user_id' => $user['id'])) ?>
</li>
<li>
<?= $this->url->link(t('My projects'), 'app', 'projects', array('user_id' => $user['id'])) ?>
</li>
<li>
<?= $this->url->link(t('My tasks'), 'app', 'tasks', array('user_id' => $user['id'])) ?>
</li>
<li>
<?= $this->url->link(t('My subtasks'), 'app', 'subtasks', array('user_id' => $user['id'])) ?>
</li>
<li>
<?= $this->url->link(t('My calendar'), 'app', 'calendar', array('user_id' => $user['id'])) ?>
</li>
<li>
<?= $this->url->link(t('My activity stream'), 'app', 'activity', array('user_id' => $user['id'])) ?>
</li>
</ul>
</div>

View File

@ -1,4 +1,6 @@
<h2><?= t('My subtasks') ?> (<?= $paginator->getTotal() ?>)</h2>
<div class="page-header">
<h2><?= t('My subtasks') ?> (<?= $paginator->getTotal() ?>)</h2>
</div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
<?php else: ?>

View File

@ -1,4 +1,6 @@
<h2><?= t('My tasks') ?> (<?= $paginator->getTotal() ?>)</h2>
<div class="page-header">
<h2><?= t('My tasks') ?> (<?= $paginator->getTotal() ?>)</h2>
</div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
<?php else: ?>

View File

@ -1,5 +1,5 @@
<?php if (empty($events)): ?>
<p class="alert"><?= t('No activity.') ?></p>
<p class="alert"><?= t('There is no activity yet.') ?></p>
<?php else: ?>
<?php foreach ($events as $event): ?>

View File

@ -6,10 +6,7 @@
</li>
<?php if ($this->user->isAdmin()): ?>
<li>
<?= $this->url->link(t('User dashboard'), 'app', 'dashboard', array('user_id' => $user['id'])) ?>
</li>
<li>
<?= $this->url->link(t('User calendar'), 'user', 'calendar', array('user_id' => $user['id'])) ?>
<?= $this->url->link(t('User dashboard'), 'app', 'index', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
<?php if ($this->user->isAdmin() || $this->user->isCurrentUser($user['id'])): ?>

View File

@ -1384,31 +1384,6 @@ span.task-board-date-overdue {
margin-left: 25px;
}
/* dashboard */
@media only screen and (min-width: 1280px) {
#dashboard table {
font-size: 0.9em;
}
.dashboard-left-column {
width: 58%;
float: left;
}
.dashboard-right-column {
margin-left: 2%;
width: 40%;
float: left;
}
}
.dashboard-single-column {
width: 95%;
margin-left: 0;
float: none;
}
.dashboard-project-stats span {
font-size: 0.75em;
margin-right: 10px;
@ -1430,9 +1405,10 @@ span.task-board-date-overdue {
color: #999;
}
#dashboard-calendar {
font-size: 0.85em;
}/* datepicker */
#dashboard .sidebar-content {
font-size: 0.9em;
}
/* datepicker */
#ui-datepicker-div {
font-size: 0.8em;
}

View File

@ -1,29 +1,4 @@
/* dashboard */
@media only screen and (min-width: 1280px) {
#dashboard table {
font-size: 0.9em;
}
.dashboard-left-column {
width: 58%;
float: left;
}
.dashboard-right-column {
margin-left: 2%;
width: 40%;
float: left;
}
}
.dashboard-single-column {
width: 95%;
margin-left: 0;
float: none;
}
.dashboard-project-stats span {
font-size: 0.75em;
margin-right: 10px;
@ -45,6 +20,6 @@
color: #999;
}
#dashboard-calendar {
font-size: 0.85em;
}
#dashboard .sidebar-content {
font-size: 0.9em;
}

File diff suppressed because one or more lines are too long

View File

@ -29,8 +29,7 @@ Kanboard.Calendar = (function() {
lang: $("body").data("js-lang"),
editable: true,
eventLimit: true,
height: Kanboard.Exists("dashboard-calendar") ? 500 : "auto",
defaultView: "agendaWeek",
defaultView: "month",
header: {
left: 'prev,next today',
center: 'title',

View File

@ -1,52 +0,0 @@
Kanboard.Dashboard = (function() {
jQuery(document).ready(function() {
var state = Kanboard.GetStorageItem("dashboard_view");
if (state) {
var sections = JSON.parse(state);
for (var section in sections) {
$("#dashboard-" + section).toggle(sections[section]);
}
hideColumns();
}
});
jQuery(document).on('click', ".dashboard-toggle", function(e) {
e.preventDefault();
$("#dashboard-" + $(this).data("toggle")).toggle();
hideColumns();
var sections = ["projects", "tasks", "subtasks", "activities", "calendar"];
var state = {};
for (var i = 0; i < sections.length; i++) {
state[sections[i]] = $("#dashboard-" + sections[i]).is(":visible");
}
Kanboard.SetStorageItem("dashboard_view", JSON.stringify(state));
});
function hideColumns()
{
if ($(".dashboard-right-column > div:visible").size() > 0) {
$(".dashboard-left-column").removeClass("dashboard-single-column");
}
else {
$(".dashboard-left-column").addClass("dashboard-single-column");
}
if ($(".dashboard-left-column > div:visible").size() > 0) {
$(".dashboard-right-column").removeClass("dashboard-single-column");
}
else {
$(".dashboard-right-column").addClass("dashboard-single-column");
}
}
})();

View File

@ -7,7 +7,7 @@
"eluceo/ical": "*",
"erusev/parsedown" : "1.5.3",
"fabiang/xmpp" : "0.6.1",
"fguillot/json-rpc" : "0.0.3",
"fguillot/json-rpc" : "dev-master",
"fguillot/picodb" : "1.0.0",
"fguillot/simpleLogger" : "0.0.2",
"fguillot/simple-validator" : "0.0.3",

16
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "776660c2872a470ad420af02104aea45",
"hash": "1ef3f084b6c8651977b1bbc84d86cb69",
"packages": [
{
"name": "christian-riesen/base32",
@ -260,16 +260,16 @@
},
{
"name": "fguillot/json-rpc",
"version": "v0.0.3",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/fguillot/JsonRPC.git",
"reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8"
"reference": "ac3ddcf8f74777d72b8044d6d128f41aabe292f2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8",
"reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8",
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/ac3ddcf8f74777d72b8044d6d128f41aabe292f2",
"reference": "ac3ddcf8f74777d72b8044d6d128f41aabe292f2",
"shasum": ""
},
"require": {
@ -287,13 +287,12 @@
],
"authors": [
{
"name": "Frédéric Guillot",
"homepage": "http://fredericguillot.com"
"name": "Frédéric Guillot"
}
],
"description": "Simple Json-RPC client/server library that just works",
"homepage": "https://github.com/fguillot/JsonRPC",
"time": "2015-05-20 15:08:40"
"time": "2015-07-01 19:26:37"
},
{
"name": "fguillot/picodb",
@ -820,6 +819,7 @@
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"fguillot/json-rpc": 20,
"swiftmailer/swiftmailer": 0,
"symfony/console": 0
},

View File

@ -71,7 +71,7 @@ Using Kanboard
### More
- [Search syntax](search.markdown)
- [Advanced Search Syntax](search.markdown)
- [Command line interface](cli.markdown)
- [Syntax guide](syntax-guide.markdown)
- [Frequently asked questions](faq.markdown)

View File

@ -6,7 +6,7 @@ Kanboard use a simple query language for advanced search.
Example of query
----------------
This example will returns all tasks assigned to me with a due date for tomorrow and that have a title that contains "my title":
This example will returns all tasks assigned to me with a due date for tomorrow and a title that contains "my title":
```
assigne:me due:tomorrow my title
@ -125,5 +125,5 @@ Search by category
Attribute: **category**
- Find tasks with a specific category: `category:"Feature Request"`
- Find all tasks that have those category: `category:"Bug" category:"Improvements"`
- Find all tasks that have those categories: `category:"Bug" category:"Improvements"`
- Find tasks with no category assigned: `category:none`

View File

@ -4,7 +4,7 @@ print_css="print links table board task comment subtask markdown"
app_css="base links title table form button alert tooltip header board task comment subtask markdown listing activity dashboard pagination popover confirm sidebar responsive dropdown"
vendor_css="jquery-ui.min chosen.min fullcalendar.min font-awesome.min c3.min"
app_js="base board calendar analytic swimlane dashboard screenshot"
app_js="base board calendar analytic swimlane screenshot"
vendor_js="jquery-1.11.1.min jquery-ui.min jquery.ui.touch-punch.min chosen.jquery.min dropit.min moment.min fullcalendar.min mousetrap.min mousetrap-global-bind.min app.min"
lang_js="da de es fi fr hu it ja nl pl pt-br ru sv sr th tr zh-cn"