Refactoring to implement new layout with filters: board/calendar/list views (work in progress)

This commit is contained in:
Frederic Guillot
2015-07-04 11:14:21 -04:00
parent a327f790ee
commit 554500aa49
52 changed files with 463 additions and 758 deletions

View File

@@ -0,0 +1,29 @@
<?php
namespace Controller;
/**
* Activity stream
*
* @package controller
* @author Frederic Guillot
*/
class Activity extends Base
{
/**
* Activity page for a project
*
* @access public
*/
public function project()
{
$project = $this->getProject();
$this->response->html($this->template->layout('activity/project', array(
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
'events' => $this->projectActivity->getProject($project['id']),
'project' => $project,
'title' => t('%s\'s activity', $project['name'])
)));
}
}

View File

@@ -327,4 +327,33 @@ abstract class Base extends \Core\Base
return $user;
}
/**
* Common method to get project filters
*
* @access protected
*/
protected function getProjectFilters($controller, $action)
{
$project = $this->getProject();
$search = $this->request->getStringParam('search', $this->userSession->getFilters($project['id']));
$board_selector = $this->projectPermission->getAllowedProjects($this->userSession->getId());
unset($board_selector[$project['id']]);
$filters = array(
'controller' => $controller,
'action' => $action,
'project_id' => $project['id'],
'search' => $search,
);
$this->userSession->setFilters($project['id'], $search);
return array(
'project' => $project,
'board_selector' => $board_selector,
'filters' => $filters,
'title' => $project['name'],
);
}
}

View File

@@ -27,7 +27,7 @@ class Board extends Base
}
// Display the board with a specific layout
$this->response->html($this->template->layout('board/public', array(
$this->response->html($this->template->layout('board/public_view', array(
'project' => $project,
'swimlanes' => $this->board->getBoard($project['id']),
'title' => $project['name'],
@@ -44,28 +44,17 @@ class Board extends Base
* Show a board for a given project
*
* @access public
* @param integer $project_id Default project id
*/
public function show($project_id = 0)
public function show()
{
$project = $this->getProject($project_id);
$projects = $this->projectPermission->getAllowedProjects($this->userSession->getId());
$params = $this->getProjectFilters('board', 'show');
$board_selector = $projects;
unset($board_selector[$project['id']]);
$this->response->html($this->template->layout('board/index', array(
'users' => $this->projectPermission->getMemberList($project['id'], true, true),
'projects' => $projects,
'project' => $project,
'swimlanes' => $this->board->getBoard($project['id']),
'categories_listing' => $this->category->getList($project['id'], true, true),
'title' => $project['name'],
'description' => $project['description'],
'board_selector' => $board_selector,
$this->response->html($this->template->layout('board/private_view', array(
'swimlanes' => $this->taskFilter->search($params['filters']['search'])->getBoard($params['project']['id']),
'description' => $params['project']['description'],
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
'board_highlight_period' => $this->config->get('board_highlight_period'),
)));
) + $params));
}
/**
@@ -100,9 +89,9 @@ class Board extends Base
}
$this->response->html(
$this->template->render('board/show', array(
$this->template->render('board/table_container', array(
'project' => $this->project->getById($project_id),
'swimlanes' => $this->board->getBoard($project_id),
'swimlanes' => $this->taskFilter->search($this->userSession->getFilters($project_id))->getBoard($project_id),
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
'board_highlight_period' => $this->config->get('board_highlight_period'),
)),
@@ -133,9 +122,9 @@ class Board extends Base
}
$this->response->html(
$this->template->render('board/show', array(
$this->template->render('board/table_container', array(
'project' => $this->project->getById($project_id),
'swimlanes' => $this->board->getBoard($project_id),
'swimlanes' => $this->taskFilter->search($this->userSession->getFilters($project_id))->getBoard($project_id),
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
'board_highlight_period' => $this->config->get('board_highlight_period'),
))
@@ -150,7 +139,7 @@ class Board extends Base
public function tasklinks()
{
$task = $this->getTask();
$this->response->html($this->template->render('board/tasklinks', array(
$this->response->html($this->template->render('board/tooltip_tasklinks', array(
'links' => $this->taskLink->getAll($task['id']),
'task' => $task,
)));
@@ -164,7 +153,7 @@ class Board extends Base
public function subtasks()
{
$task = $this->getTask();
$this->response->html($this->template->render('board/subtasks', array(
$this->response->html($this->template->render('board/tooltip_subtasks', array(
'subtasks' => $this->subtask->getAll($task['id']),
'task' => $task,
)));
@@ -179,7 +168,7 @@ class Board extends Base
{
$task = $this->getTask();
$this->response->html($this->template->render('board/files', array(
$this->response->html($this->template->render('board/tooltip_files', array(
'files' => $this->file->getAllDocuments($task['id']),
'images' => $this->file->getAllImages($task['id']),
'task' => $task,
@@ -195,7 +184,7 @@ class Board extends Base
{
$task = $this->getTask();
$this->response->html($this->template->render('board/comments', array(
$this->response->html($this->template->render('board/tooltip_comments', array(
'comments' => $this->comment->getAll($task['id'])
)));
}
@@ -209,7 +198,7 @@ class Board extends Base
{
$task = $this->getTask();
$this->response->html($this->template->render('board/description', array(
$this->response->html($this->template->render('board/tooltip_description', array(
'task' => $task
)));
}
@@ -224,7 +213,7 @@ class Board extends Base
$task = $this->getTask();
$project = $this->project->getById($task['project_id']);
$this->response->html($this->template->render('board/assignee', array(
$this->response->html($this->template->render('board/popover_assignee', array(
'values' => $task,
'users_list' => $this->projectPermission->getMemberList($project['id']),
'project' => $project,
@@ -262,7 +251,7 @@ class Board extends Base
$task = $this->getTask();
$project = $this->project->getById($task['project_id']);
$this->response->html($this->template->render('board/category', array(
$this->response->html($this->template->render('board/popover_category', array(
'values' => $task,
'categories_list' => $this->category->getList($project['id']),
'project' => $project,

View File

@@ -20,20 +20,9 @@ class Calendar extends Base
*/
public function show()
{
$project = $this->getProject();
$this->response->html($this->template->layout('calendar/show', array(
'check_interval' => $this->config->get('board_private_refresh_interval'),
'users_list' => $this->projectPermission->getMemberList($project['id'], true, true),
'categories_list' => $this->category->getList($project['id'], true, true),
'columns_list' => $this->board->getColumnsList($project['id'], true),
'swimlanes_list' => $this->swimlane->getList($project['id'], true),
'colors_list' => $this->color->getList(true),
'status_list' => $this->taskStatus->getList(true),
'project' => $project,
'title' => t('Calendar for "%s"', $project['name']),
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
)));
) + $this->getProjectFilters('calendar', 'show')));
}
/**
@@ -49,14 +38,8 @@ class Calendar extends Base
// Common filter
$filter = $this->taskFilter
->create()
->filterByProject($project_id)
->filterByCategory($this->request->getIntegerParam('category_id', -1))
->filterByOwner($this->request->getIntegerParam('owner_id', -1))
->filterByColumn($this->request->getIntegerParam('column_id', -1))
->filterBySwimlane($this->request->getIntegerParam('swimlane_id', -1))
->filterByColor($this->request->getStringParam('color_id'))
->filterByStatus($this->request->getIntegerParam('is_active', -1));
->search($this->userSession->getFilters($project_id))
->filterByProject($project_id);
// Tasks
if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') {

View File

@@ -0,0 +1,37 @@
<?php
namespace Controller;
use Model\Task as TaskModel;
/**
* List view controller
*
* @package controller
* @author Frederic Guillot
*/
class Listing extends Base
{
/**
* Show list view for projects
*
* @access public
*/
public function show()
{
$params = $this->getProjectFilters('listing', 'show');
$query = $this->taskFilter->search($params['filters']['search'])->filterByProject($params['project']['id'])->getQuery();
$paginator = $this->paginator
->setUrl('listing', 'show', array('project_id' => $params['project']['id']))
->setMax(30)
->setOrder(TaskModel::TABLE.'.id')
->setDirection('DESC')
->setQuery($query)
->calculate();
$this->response->html($this->template->layout('listing/show', $params + array(
'paginator' => $paginator,
)));
}
}

View File

@@ -1,95 +0,0 @@
<?php
namespace Controller;
/**
* Project Info controller (ActivityStream + completed tasks)
*
* @package controller
* @author Frederic Guillot
*/
class Projectinfo extends Base
{
/**
* Activity page for a project
*
* @access public
*/
public function activity()
{
$project = $this->getProject();
$this->response->html($this->template->layout('projectinfo/activity', array(
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
'events' => $this->projectActivity->getProject($project['id']),
'project' => $project,
'title' => t('%s\'s activity', $project['name'])
)));
}
/**
* Task search for a given project
*
* @access public
*/
public function search()
{
$project = $this->getProject();
$search = $this->request->getStringParam('search');
$nb_tasks = 0;
$paginator = $this->paginator
->setUrl('projectinfo', 'search', array('search' => $search, 'project_id' => $project['id']))
->setMax(30)
->setOrder('tasks.id')
->setDirection('DESC');
if ($search !== '') {
$paginator->setQuery($this->taskFilter->search($search)->filterByProject($project['id'])->getQuery())
->calculate();
$nb_tasks = $paginator->getTotal();
}
$this->response->html($this->template->layout('projectinfo/search', array(
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
'values' => array(
'search' => $search,
'controller' => 'projectinfo',
'action' => 'search',
'project_id' => $project['id'],
),
'paginator' => $paginator,
'project' => $project,
'columns' => $this->board->getColumnsList($project['id']),
'categories' => $this->category->getList($project['id'], false),
'title' => t('Search in the project "%s"', $project['name']).($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
)));
}
/**
* List of completed tasks for a given project
*
* @access public
*/
public function tasks()
{
$project = $this->getProject();
$paginator = $this->paginator
->setUrl('projectinfo', 'tasks', array('project_id' => $project['id']))
->setMax(30)
->setOrder('tasks.id')
->setDirection('DESC')
->setQuery($this->taskFinder->getClosedTaskQuery($project['id']))
->calculate();
$this->response->html($this->template->layout('projectinfo/tasks', array(
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
'project' => $project,
'columns' => $this->board->getColumnsList($project['id']),
'categories' => $this->category->getList($project['id'], false),
'paginator' => $paginator,
'title' => t('Completed tasks for "%s"', $project['name']).' ('.$paginator->getTotal().')'
)));
}
}

View File

@@ -37,7 +37,8 @@ class Acl extends Base
'comment' => '*',
'file' => '*',
'project' => array('show'),
'projectinfo' => array('tasks', 'search', 'activity'),
'listing' => '*',
'activity' => '*',
'subtask' => '*',
'task' => '*',
'tasklink' => '*',

View File

@@ -237,10 +237,11 @@ class Board extends Base
* Get all tasks sorted by columns and swimlanes
*
* @access public
* @param integer $project_id Project id
* @param integer $project_id
* @param callable $callback
* @return array
*/
public function getBoard($project_id)
public function getBoard($project_id, $callback = null)
{
$swimlanes = $this->swimlane->getSwimlanes($project_id);
$columns = $this->getColumns($project_id);
@@ -253,7 +254,11 @@ class Board extends Base
$swimlanes[$i]['nb_tasks'] = 0;
for ($j = 0; $j < $nb_columns; $j++) {
$swimlanes[$i]['columns'][$j]['tasks'] = $this->taskFinder->getTasksByColumnAndSwimlane($project_id, $columns[$j]['id'], $swimlanes[$i]['id']);
$column_id = $columns[$j]['id'];
$swimlane_id = $swimlanes[$i]['id'];
$swimlanes[$i]['columns'][$j]['tasks'] = $callback === null ? $this->taskFinder->getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id) : $callback($project_id, $column_id, $swimlane_id);
$swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']);
$swimlanes[$i]['columns'][$j]['score'] = $this->getColumnSum($swimlanes[$i]['columns'][$j]['tasks'], 'score');
$swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks'];

View File

@@ -512,6 +512,23 @@ class TaskFilter extends Base
return $this->query;
}
/**
* Get swimlanes and tasks to display the board
*
* @access public
* @return array
*/
public function getBoard($project_id)
{
$tasks = $this->filterByProject($project_id)->query->asc(Task::TABLE.'.position')->findAll();
return $this->board->getBoard($project_id, function ($project_id, $column_id, $swimlane_id) use ($tasks) {
return array_filter($tasks, function(array $task) use ($column_id, $swimlane_id) {
return $task['column_id'] == $column_id && $task['swimlane_id'] == $swimlane_id;
});
});
}
/**
* Format the results to the ajax autocompletion
*

View File

@@ -12,20 +12,6 @@ use PDO;
*/
class TaskFinder extends Base
{
/**
* Get query for closed tasks
*
* @access public
* @param integer $project_id Project id
* @return \PicoDb\Table
*/
public function getClosedTaskQuery($project_id)
{
return $this->getExtendedQuery()
->eq(Task::TABLE.'.project_id', $project_id)
->eq(Task::TABLE.'.is_active', Task::STATUS_CLOSED);
}
/**
* Get query for assigned user tasks
*
@@ -142,8 +128,8 @@ class TaskFinder extends Base
{
return $this->db
->table(Task::TABLE)
->eq('project_id', $project_id)
->eq('is_active', $status_id)
->eq(Task::TABLE.'.project_id', $project_id)
->eq(Task::TABLE.'.is_active', $status_id)
->findAll();
}

View File

@@ -28,6 +28,11 @@ class TaskPosition extends Base
{
$original_task = $this->taskFinder->getById($task_id);
// Ignore closed tasks
if ($original_task['is_active'] == Task::STATUS_CLOSED) {
return true;
}
$result = $this->calculateAndSave($project_id, $task_id, $column_id, $position, $swimlane_id);
if ($result) {

View File

@@ -94,4 +94,28 @@ class UserSession extends Base
{
return ! empty($this->session['user']);
}
/**
* Get project filters from the session
*
* @access public
* @param integer $project_id
* @return string
*/
public function getFilters($project_id)
{
return ! empty($_SESSION['filters'][$project_id]) ? $_SESSION['filters'][$project_id] : 'status:open';
}
/**
* Save project filters in the session
*
* @access public
* @param integer $project_id
* @param string $filters
*/
public function setFilters($project_id, $filters)
{
$_SESSION['filters'][$project_id] = $filters;
}
}

View File

@@ -12,9 +12,19 @@
</span>
</li>
<li>
<i class="fa fa-table fa-fw"></i>
<i class="fa fa-th fa-fw"></i>
<?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-calendar fa-fw"></i>
<?= $this->url->link(t('Back to the calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php if ($this->user->isManager($project['id'])): ?>
<li>
<i class="fa fa-cog fa-fw"></i>
<?= $this->url->link(t('Project settings'), 'project', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<li>
<i class="fa fa-folder fa-fw"></i>
<?= $this->url->link(t('All projects'), 'project', 'index') ?>

View File

@@ -12,9 +12,19 @@
</span>
</li>
<li>
<i class="fa fa-table fa-fw"></i>
<i class="fa fa-th fa-fw"></i>
<?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-calendar fa-fw"></i>
<?= $this->url->link(t('Back to the calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php if ($this->user->isManager($project['id'])): ?>
<li>
<i class="fa fa-cog fa-fw"></i>
<?= $this->url->link(t('Project settings'), 'project', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<li>
<i class="fa fa-folder fa-fw"></i>
<?= $this->url->link(t('All projects'), 'project', 'index') ?>

View File

@@ -1,6 +1,5 @@
<div id="user-calendar"
data-check-url="<?= $this->url->href('calendar', 'user') ?>"
data-user-id="<?= $user['id'] ?>"
<div id="calendar"
data-check-url="<?= $this->url->href('calendar', 'user', array('user_id' => $user['id'])) ?>"
data-save-url="<?= $this->url->href('calendar', 'save') ?>"
>
</div>

View File

@@ -1,7 +1,7 @@
<form method="get" action="?" autocomplete="off">
<?= $this->form->hidden('controller', array('controller' => 'search')) ?>
<?= $this->form->hidden('action', array('controller' => 'index')) ?>
<?= $this->form->text('search', array(), array(), array('required', 'placeholder="'.t('Search').'"'), 'form-input-large') ?>
<?= $this->form->text('search', array(), array(), array('placeholder="'.t('Search').'"'), 'form-input-large') ?>
<input type="submit" value="<?= t('Search') ?>" class="btn btn-blue"/>
</form>

View File

@@ -1,43 +0,0 @@
<div class="page-header">
<ul class="board-filters">
<li>
<span class="dropdown">
<span>
<i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a>
<ul>
<li>
<span class="filter-collapse">
<i class="fa fa-compress fa-fw"></i> <a href="#" class="filter-collapse-link"><?= t('Collapse tasks') ?></a>
</span>
<span class="filter-expand" style="display: none">
<i class="fa fa-expand fa-fw"></i> <a href="#" class="filter-expand-link"><?= t('Expand tasks') ?></a>
</span>
</li>
<li>
<span class="filter-compact">
<i class="fa fa-th fa-fw"></i> <a href="#" class="filter-toggle-scrolling"><?= t('Compact view') ?></a>
</span>
<span class="filter-wide" style="display: none">
<i class="fa fa-arrows-h fa-fw"></i> <a href="#" class="filter-toggle-scrolling"><?= t('Horizontal scrolling') ?></a>
</span>
</li>
<?= $this->render('project/dropdown', array('project' => $project)) ?>
</ul>
</span>
</span>
</li>
<li>
<?= $this->form->select('user_id', $users, array(), array(), array('data-placeholder="'.t('Filter by user').'"', 'data-notfound="'.t('No results match:').'"', 'tabindex=="-1"'), 'apply-filters chosen-select') ?>
</li>
<li>
<?= $this->form->select('category_id', $categories, array(), array(), array('data-placeholder="'.t('Filter by category').'"', 'data-notfound="'.t('No results match:').'"', 'tabindex=="-1"'), 'apply-filters chosen-select') ?>
</li>
<li>
<select id="more-filters" multiple data-placeholder="<?= t('More filters') ?>" data-notfound="<?= t('No results match:') ?>" class="apply-filters hide-mobile" tabindex="-1">
<option value=""></option>
<option value="filter-due-date"><?= t('Filter by due date') ?></option>
<option value="filter-recent"><?= t('Filter recently updated') ?></option>
</select>
</li>
</ul>
</div>

View File

@@ -1,12 +1,12 @@
<section id="main">
<?= $this->render('board/filters', array(
'categories' => $categories_listing,
'users' => $users,
<?= $this->render('project/filters', array(
'project' => $project,
'filters' => $filters,
'is_board' => true,
)) ?>
<?= $this->render('board/show', array(
<?= $this->render('board/table_container', array(
'project' => $project,
'swimlanes' => $swimlanes,
'board_private_refresh_interval' => $board_private_refresh_interval,

View File

@@ -1,6 +1,6 @@
<section id="main" class="public-board">
<?= $this->render('board/show', array(
<?= $this->render('board/table_container', array(
'project' => $project,
'swimlanes' => $swimlanes,
'board_private_refresh_interval' => $board_private_refresh_interval,

View File

@@ -17,7 +17,7 @@
<p class="alert alert-error"><?= t('There is no column in your project!') ?></p>
<?php break ?>
<?php else: ?>
<?= $this->render('board/swimlane', array(
<?= $this->render('board/table_swimlane', array(
'project' => $project,
'swimlane' => $swimlane,
'board_highlight_period' => $board_highlight_period,

View File

@@ -1,46 +1,13 @@
<section id="main">
<div class="page-header">
<ul>
<li>
<span class="dropdown">
<span>
<i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a>
<ul>
<?= $this->render('project/dropdown', array('project' => $project)) ?>
</ul>
</span>
</span>
</li>
<li>
<i class="fa fa-table fa-fw"></i>
<?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-folder fa-fw"></i>
<?= $this->url->link(t('All projects'), 'project', 'index') ?>
</li>
</ul>
<?= $this->render('project/filters', array(
'project' => $project,
'filters' => $filters,
)) ?>
<div id="calendar"
data-save-url="<?= $this->url->href('calendar', 'save') ?>"
data-check-url="<?= $this->url->href('calendar', 'project', array('project_id' => $project['id'])) ?>"
data-check-interval="<?= $check_interval ?>"
>
</div>
<section class="sidebar-container">
<?= $this->render('calendar/sidebar', array(
'project' => $project,
'users_list' => $users_list,
'categories_list' => $categories_list,
'columns_list' => $columns_list,
'swimlanes_list' => $swimlanes_list,
'colors_list' => $colors_list,
'status_list' => $status_list
)) ?>
<div class="sidebar-content">
<div id="calendar"
data-project-id="<?= $project['id'] ?>"
data-save-url="<?= $this->url->href('calendar', 'save') ?>"
data-check-url="<?= $this->url->href('calendar', 'project', array('project_id' => $project['id'])) ?>"
data-check-interval="<?= $check_interval ?>"
>
</div>
</div>
</section>
</section>

View File

@@ -1,40 +0,0 @@
<div class="sidebar">
<ul class="no-bullet">
<li>
<?= t('Filter by user') ?>
</li>
<li>
<?= $this->form->select('owner_id', $users_list, array(), array(), array(), 'calendar-filter') ?>
</li>
<li>
<?= t('Filter by category') ?>
</li>
<li>
<?= $this->form->select('category_id', $categories_list, array(), array(), array(), 'calendar-filter') ?>
</li>
<li>
<?= t('Filter by column') ?>
</li>
<li>
<?= $this->form->select('column_id', $columns_list, array(), array(), array(), 'calendar-filter') ?>
</li>
<li>
<?= t('Filter by swimlane') ?>
</li>
<li>
<?= $this->form->select('swimlane_id', $swimlanes_list, array(), array(), array(), 'calendar-filter') ?>
</li>
<li>
<?= t('Filter by color') ?>
</li>
<li>
<?= $this->form->select('color_id', $colors_list, array(), array(), array(), 'calendar-filter') ?>
</li>
<li>
<?= t('Filter by status') ?>
</li>
<li>
<?= $this->form->select('is_active', $status_list, array(), array(), array(), 'calendar-filter') ?>
</li>
</ul>
</div>

View File

@@ -0,0 +1,67 @@
<section id="main">
<?= $this->render('project/filters', array(
'project' => $project,
'filters' => $filters,
)) ?>
<?php if (! empty($values['search']) && $paginator->isEmpty()): ?>
<p class="alert"><?= t('No tasks found.') ?></p>
<?php elseif (! $paginator->isEmpty()): ?>
<table class="table-fixed table-small">
<tr>
<th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
<th class="column-8"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th>
<th class="column-8"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th>
<th><?= $paginator->order(t('Title'), 'tasks.title') ?></th>
<th class="column-10"><?= $paginator->order(t('Assignee'), 'users.username') ?></th>
<th class="column-10"><?= $paginator->order(t('Due date'), 'tasks.date_due') ?></th>
<th class="column-10"><?= $paginator->order(t('Date created'), 'tasks.date_creation') ?></th>
<th class="column-10"><?= $paginator->order(t('Date completed'), 'tasks.date_completed') ?></th>
<th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $task): ?>
<tr>
<td class="task-table color-<?= $task['color_id'] ?>">
<?= $this->url->link('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
</td>
<td>
<?= $this->e($task['column_name']) ?>
</td>
<td>
<?= $this->e($task['category_name']) ?>
</td>
<td>
<?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
</td>
<td>
<?php if ($task['assignee_username']): ?>
<?= $this->e($task['assignee_name'] ?: $task['assignee_username']) ?>
<?php else: ?>
<?= t('Unassigned') ?>
<?php endif ?>
</td>
<td>
<?= dt('%B %e, %Y', $task['date_due']) ?>
</td>
<td>
<?= dt('%B %e, %Y', $task['date_creation']) ?>
</td>
<td>
<?php if ($task['date_completed']): ?>
<?= dt('%B %e, %Y', $task['date_completed']) ?>
<?php endif ?>
</td>
<td>
<?php if ($task['is_active'] == \Model\Task::STATUS_OPEN): ?>
<?= t('Open') ?>
<?php else: ?>
<?= t('Closed') ?>
<?php endif ?>
</td>
</tr>
<?php endforeach ?>
</table>
<?= $paginator ?>
<?php endif ?>
</section>

View File

@@ -1,18 +1,6 @@
<li>
<i class="fa fa-search fa-fw"></i>
<?= $this->url->link(t('Search'), 'projectinfo', 'search', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-check-square-o fa-fw"></i>
<?= $this->url->link(t('Completed tasks'), 'projectinfo', 'tasks', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-dashboard fa-fw"></i>
<?= $this->url->link(t('Activity'), 'projectinfo', 'activity', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-calendar fa-fw"></i>
<?= $this->url->link(t('Calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?>
<?= $this->url->link(t('Activity'), 'activity', 'project', array('project_id' => $project['id'])) ?>
</li>
<?php if ($project['is_public']): ?>

View File

@@ -0,0 +1,49 @@
<div class="page-header">
<div class="dropdown">
<span>
<i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a>
<ul>
<?php if (isset($is_board)): ?>
<li>
<span class="filter-collapse">
<i class="fa fa-compress fa-fw"></i> <a href="#" class="filter-collapse-link"><?= t('Collapse tasks') ?></a>
</span>
<span class="filter-expand" style="display: none">
<i class="fa fa-expand fa-fw"></i> <a href="#" class="filter-expand-link"><?= t('Expand tasks') ?></a>
</span>
</li>
<li>
<span class="filter-compact">
<i class="fa fa-th fa-fw"></i> <a href="#" class="filter-toggle-scrolling"><?= t('Compact view') ?></a>
</span>
<span class="filter-wide" style="display: none">
<i class="fa fa-arrows-h fa-fw"></i> <a href="#" class="filter-toggle-scrolling"><?= t('Horizontal scrolling') ?></a>
</span>
</li>
<?php endif ?>
<?= $this->render('project/dropdown', array('project' => $project)) ?>
</ul>
</span>
</div>
<ul class="views">
<li <?= $filters['controller'] === 'board' ? 'class="active"' : '' ?>>
<i class="fa fa-th fa-fw"></i>
<?= $this->url->link(t('Board'), 'board', 'show', array('project_id' => $project['id'], 'search' => $filters['search'])) ?>
</li>
<li <?= $filters['controller'] === 'calendar' ? 'class="active"' : '' ?>>
<i class="fa fa-calendar fa-fw"></i>
<?= $this->url->link(t('Calendar'), 'calendar', 'show', array('project_id' => $project['id'], 'search' => $filters['search'])) ?>
</li>
<li <?= $filters['controller'] === 'listing' ? 'class="active"' : '' ?>>
<i class="fa fa-list fa-fw"></i>
<?= $this->url->link(t('List'), 'listing', 'show', array('project_id' => $project['id'], 'search' => $filters['search'])) ?>
</li>
</ul>
<form method="get" action="?" class="search">
<?= $this->form->hidden('project_id', $filters) ?>
<?= $this->form->hidden('controller', $filters) ?>
<?= $this->form->hidden('action', $filters) ?>
<?= $this->form->text('search', $filters, array(), array('placeholder="'.t('Filter').'"'), 'form-input-large') ?>
</form>
</div>

View File

@@ -35,7 +35,7 @@
<?= $this->e($project['identifier']) ?>
</td>
<td>
<?= $this->url->link('<i class="fa fa-table"></i>', 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Board')) ?>&nbsp;
<?= $this->url->link('<i class="fa fa-th"></i>', 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Board')) ?>&nbsp;
<?php if ($project['is_public']): ?>
<i class="fa fa-share-alt fa-fw"></i>

View File

@@ -12,9 +12,13 @@
</span>
</li>
<li>
<i class="fa fa-table fa-fw"></i>
<i class="fa fa-th fa-fw"></i>
<?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-calendar fa-fw"></i>
<?= $this->url->link(t('Back to the calendar'), 'calendar', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-folder fa-fw"></i>
<?= $this->url->link(t('All projects'), 'project', 'index') ?>

View File

@@ -1,43 +0,0 @@
<section id="main">
<div class="page-header">
<ul>
<li>
<span class="dropdown">
<span>
<i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a>
<ul>
<?= $this->render('project/dropdown', array('project' => $project)) ?>
</ul>
</span>
</span>
</li>
<li>
<i class="fa fa-table fa-fw"></i>
<?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-folder fa-fw"></i>
<?= $this->url->link(t('All projects'), 'project', 'index') ?>
</li>
</ul>
</div>
<form method="get" action="?" autocomplete="off">
<?= $this->form->hidden('controller', $values) ?>
<?= $this->form->hidden('action', $values) ?>
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->text('search', $values, array(), array('autofocus', 'required', 'placeholder="'.t('Search').'"'), 'form-input-large') ?>
<input type="submit" value="<?= t('Search') ?>" class="btn btn-blue"/>
</form>
<?php if (! empty($values['search']) && $paginator->isEmpty()): ?>
<p class="alert"><?= t('Nothing found.') ?></p>
<?php elseif (! $paginator->isEmpty()): ?>
<?= $this->render('task/table', array(
'paginator' => $paginator,
'categories' => $categories,
'columns' => $columns,
)) ?>
<?php endif ?>
</section>

View File

@@ -1,33 +0,0 @@
<section id="main">
<div class="page-header">
<ul>
<li>
<span class="dropdown">
<span>
<i class="fa fa-caret-down"></i> <a href="#" class="dropdown-menu"><?= t('Actions') ?></a>
<ul>
<?= $this->render('project/dropdown', array('project' => $project)) ?>
</ul>
</span>
</span>
</li>
<li>
<i class="fa fa-table fa-fw"></i>
<?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
<i class="fa fa-folder fa-fw"></i>
<?= $this->url->link(t('All projects'), 'project', 'index') ?>
</li>
</ul>
</div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is no completed tasks at the moment.') ?></p>
<?php else: ?>
<?= $this->render('task/table', array(
'paginator' => $paginator,
'categories' => $categories,
'columns' => $columns,
)) ?>
<?php endif ?>
</section>

View File

@@ -2,19 +2,19 @@
<div class="page-header">
<ul>
<li>
<i class="fa fa-table fa-fw"></i>
<i class="fa fa-th fa-fw"></i>
<?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $task['project_id']), false, '', '', false, 'swimlane-'.$task['swimlane_id']) ?>
</li>
<li>
<i class="fa fa-calendar fa-fw"></i>
<?= $this->url->link(t('Back to the calendar'), 'calendar', 'show', array('project_id' => $task['project_id'])) ?>
</li>
<?php if ($this->user->isManager($task['project_id'])): ?>
<li>
<i class="fa fa-cog fa-fw"></i>
<?= $this->url->link(t('Project settings'), 'project', 'show', array('project_id' => $task['project_id'])) ?>
</li>
<?php endif ?>
<li>
<i class="fa fa-calendar fa-fw"></i>
<?= $this->url->link(t('Project calendar'), 'calendar', 'show', array('project_id' => $task['project_id'])) ?>
</li>
</ul>
</div>
<section class="sidebar-container" id="task-section">

View File

@@ -1,7 +1,7 @@
<?php if (! $ajax): ?>
<div class="page-header">
<ul>
<li><i class="fa fa-table fa-fw"></i><?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $values['project_id'])) ?></li>
<li><i class="fa fa-th fa-fw"></i><?= $this->url->link(t('Back to the board'), 'board', 'show', array('project_id' => $values['project_id'])) ?></li>
</ul>
</div>
<?php else: ?>

View File

@@ -1,56 +0,0 @@
<table class="table-fixed table-small">
<tr>
<th class="column-8"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
<th class="column-8"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th>
<th class="column-8"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th>
<th><?= $paginator->order(t('Title'), 'tasks.title') ?></th>
<th class="column-10"><?= $paginator->order(t('Assignee'), 'users.username') ?></th>
<th class="column-10"><?= $paginator->order(t('Due date'), 'tasks.date_due') ?></th>
<th class="column-10"><?= $paginator->order(t('Date created'), 'tasks.date_creation') ?></th>
<th class="column-10"><?= $paginator->order(t('Date completed'), 'tasks.date_completed') ?></th>
<th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $task): ?>
<tr>
<td class="task-table color-<?= $task['color_id'] ?>">
<?= $this->url->link('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
</td>
<td>
<?= $this->text->in($task['column_id'], $columns) ?>
</td>
<td>
<?= $this->text->in($task['category_id'], $categories, '') ?>
</td>
<td>
<?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
</td>
<td>
<?php if ($task['assignee_username']): ?>
<?= $this->e($task['assignee_name'] ?: $task['assignee_username']) ?>
<?php else: ?>
<?= t('Unassigned') ?>
<?php endif ?>
</td>
<td>
<?= dt('%B %e, %Y', $task['date_due']) ?>
</td>
<td>
<?= dt('%B %e, %Y', $task['date_creation']) ?>
</td>
<td>
<?php if ($task['date_completed']): ?>
<?= dt('%B %e, %Y', $task['date_completed']) ?>
<?php endif ?>
</td>
<td>
<?php if ($task['is_active'] == \Model\Task::STATUS_OPEN): ?>
<?= t('Open') ?>
<?php else: ?>
<?= t('Closed') ?>
<?php endif ?>
</td>
</tr>
<?php endforeach ?>
</table>
<?= $paginator ?>

View File

@@ -37,10 +37,11 @@ body {
padding-bottom: 20px;
color: #333;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
font-smoothing: antialiased;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
}
.page {
clear: both;
}
ul.no-bullet li {
@@ -67,7 +68,18 @@ hr {
.avatar {
float: left;
margin-right: 10px;
}/* links */
}
.views {
display: inline-block;
}
.views li.active a {
font-weight: bold;
color: #000;
text-decoration: none;
}
/* links */
a {
color: #3366CC;
border: none;
@@ -717,7 +729,7 @@ nav .active a {
/* page header */
.page-header {
margin-bottom: 25px;
margin-bottom: 20px;
}
.page-header h2 {
@@ -748,6 +760,10 @@ nav .active a {
font-size: 0.9em;
}
.page-header form {
display: inline;
}
@media only screen and (max-width: 640px) {
.page-header-mobile li {
@@ -755,15 +771,6 @@ nav .active a {
margin-bottom: 5px;
}
}
/* board filters */
.board-filters {
font-size: 0.95em;
}
#more-filters {
display: none; /* Hide this filter initially, to avoid re-layout */
}
/* public board */
.public-board {
margin-top: 5px;
@@ -1603,6 +1610,12 @@ span.task-board-date-overdue {
padding-right: 10px;
}
.page-header div.dropdown {
font-size: 0.9em;
padding-right: 10px;
}
.page-header div.dropdown,
.page-header ul.dropdown {
display: inline;
}

View File

@@ -142,16 +142,7 @@ th a:hover {
.column-70 {
width: 70%;
}/* board filters */
.board-filters {
font-size: 0.95em;
}
#more-filters {
display: none; /* Hide this filter initially, to avoid re-layout */
}
/* public board */
}/* public board */
.public-board {
margin-top: 5px;
}

View File

@@ -20,10 +20,11 @@ body {
padding-bottom: 20px;
color: #333;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
font-smoothing: antialiased;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
}
.page {
clear: both;
}
ul.no-bullet li {
@@ -50,4 +51,14 @@ hr {
.avatar {
float: left;
margin-right: 10px;
}
}
.views {
display: inline-block;
}
.views li.active a {
font-weight: bold;
color: #000;
text-decoration: none;
}

View File

@@ -1,12 +1,3 @@
/* board filters */
.board-filters {
font-size: 0.95em;
}
#more-filters {
display: none; /* Hide this filter initially, to avoid re-layout */
}
/* public board */
.public-board {
margin-top: 5px;

View File

@@ -36,6 +36,12 @@
padding-right: 10px;
}
.page-header div.dropdown {
font-size: 0.9em;
padding-right: 10px;
}
.page-header div.dropdown,
.page-header ul.dropdown {
display: inline;
}

View File

@@ -66,7 +66,7 @@ nav .active a {
/* page header */
.page-header {
margin-bottom: 25px;
margin-bottom: 20px;
}
.page-header h2 {
@@ -97,6 +97,10 @@ nav .active a {
font-size: 0.9em;
}
.page-header form {
display: inline;
}
@media only screen and (max-width: 640px) {
.page-header-mobile li {

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,4 @@
Kanboard.Analytic = (function() {
(function() {
// CFD diagram
function drawCfd()
@@ -185,6 +184,4 @@ Kanboard.Analytic = (function() {
}
});
return {};
})();

View File

@@ -1,4 +1,3 @@
// Common functions
var Kanboard = (function() {
jQuery(document).ready(function() {

View File

@@ -1,4 +1,4 @@
Kanboard.Board = (function() {
(function() {
var checkInterval = null;
@@ -243,7 +243,6 @@ Kanboard.Board = (function() {
$("#main").append(data);
Kanboard.InitAfterAjax();
board_load_events();
filter_apply();
stack_show();
compactview_reload();
}
@@ -264,7 +263,6 @@ Kanboard.Board = (function() {
Kanboard.InitAfterAjax();
board_unload_events();
board_load_events();
filter_apply();
stack_show();
compactview_reload();
}
@@ -273,93 +271,6 @@ Kanboard.Board = (function() {
}
}
// Apply user or date filter (change tasks opacity)
function filter_apply()
{
var selectedUserId = $("#form-user_id").val();
var selectedCategoryId = $("#form-category_id").val();
var filterDueDate = $("#more-filters option[value=filter-due-date]").is(":selected")
var filterRecent = $("#more-filters option[value=filter-recent]").is(":selected")
var projectId = $('#board').data('project-id');
$("[data-task-id]").each(function(index, item) {
var ownerId = item.getAttribute("data-owner-id");
var dueDate = item.getAttribute("data-due-date");
var categoryId = item.getAttribute("data-category-id");
var recent = $(item).hasClass("task-board-recent");
if (ownerId != selectedUserId && selectedUserId != -1) {
item.style.display = "none";
}
else {
item.style.display = "block";
}
if (filterDueDate && (dueDate == "" || dueDate == "0")) {
item.style.display = "none";
}
if (categoryId != selectedCategoryId && selectedCategoryId != -1) {
item.style.display = "none";
}
if (filterRecent && ! recent) {
item.style.display = "none";
}
});
// Save filter settings
Kanboard.SetStorageItem("board_filter_" + projectId + "_form-user_id", selectedUserId);
Kanboard.SetStorageItem("board_filter_" + projectId + "_form-category_id", selectedCategoryId);
Kanboard.SetStorageItem("board_filter_" + projectId + "_filter-due-date", ~~(filterDueDate));
Kanboard.SetStorageItem("board_filter_" + projectId + "_filter-recent", ~~(filterRecent));
}
// Load filter events
function filter_load_events()
{
var projectId = $('#board').data('project-id');
$("#form-user_id").chosen({
width: "180px",
no_results_text: $("#form-user_id").data("notfound")
});
$("#form-category_id").chosen({
width: "200px",
no_results_text: $("#form-category_id").data("notfound")
});
$("#more-filters").chosen({
width: "30%",
no_results_text: $("#more-filters").data("notfound")
});
$(".apply-filters").change(function(e) {
filter_apply();
});
// Get and set filters from localStorage
$("#form-user_id").val(Kanboard.GetStorageItem("board_filter_" + projectId + "_form-user_id") || -1);
$("#form-user_id").trigger("chosen:updated");
$("#form-category_id").val(Kanboard.GetStorageItem("board_filter_" + projectId + "_form-category_id") || -1);
$("#form-category_id").trigger("chosen:updated");
if (+Kanboard.GetStorageItem("board_filter_" + projectId + "_filter-due-date")) {
$("#more-filters option[value=filter-due-date]").attr("selected", true);
}
if (+Kanboard.GetStorageItem("board_filter_" + projectId + "_filter-recent")) {
$("#more-filters option[value=filter-recent]").attr("selected", true);
}
$("#more-filters").trigger("chosen:updated");
filter_apply();
}
function compactview_load_events()
{
jQuery(document).on('click', ".filter-toggle-scrolling", function(e) {
@@ -401,7 +312,6 @@ Kanboard.Board = (function() {
if (Kanboard.Exists("board")) {
board_load_events();
filter_load_events();
stack_load_events();
compactview_load_events();
keyboard_shortcuts();

View File

@@ -1,150 +1,50 @@
Kanboard.Calendar = (function() {
var filter_storage_key = "";
// Save the new due date for a moved task
function move_calendar_event(calendar_event)
{
var url = $("#calendar").data("save-url") || $("#user-calendar").data("save-url");
$.ajax({
cache: false,
url: url,
contentType: "application/json",
type: "POST",
processData: false,
data: JSON.stringify({
"task_id": calendar_event.id,
"date_due": calendar_event.start.format()
})
});
}
// Show the user calendar
function show_user_calendar()
{
var calendar = $("#user-calendar");
calendar.fullCalendar({
lang: $("body").data("js-lang"),
editable: true,
eventLimit: true,
defaultView: "month",
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
viewRender: refresh_user_calendar,
eventDrop: move_calendar_event
});
}
// Refresh the calendar events
function refresh_user_calendar()
{
var calendar = $("#user-calendar");
var url = calendar.data("check-url");
var params = {
"start": calendar.fullCalendar('getView').start.format(),
"end": calendar.fullCalendar('getView').end.format(),
"user_id": calendar.data("user-id")
};
for (var key in params) {
url += "&" + key + "=" + params[key];
}
$.getJSON(url, function(events) {
calendar.fullCalendar('removeEvents');
calendar.fullCalendar('addEventSource', events);
calendar.fullCalendar('rerenderEvents');
});
}
// Show the project calendar
function show_project_calendar()
{
var calendar = $("#calendar");
calendar.fullCalendar({
lang: $("body").data("js-lang"),
editable: true,
eventLimit: true,
defaultView: "month",
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
viewRender: load_project_filters,
eventDrop: move_calendar_event
});
}
// Refresh the calendar events
function refresh_project_calendar(filters)
{
var calendar = $("#calendar");
var url = calendar.data("check-url");
var params = {
"start": calendar.fullCalendar('getView').start.format(),
"end": calendar.fullCalendar('getView').end.format()
};
jQuery.extend(params, filters);
for (var key in params) {
url += "&" + key + "=" + params[key];
}
$.getJSON(url, function(events) {
calendar.fullCalendar('removeEvents');
calendar.fullCalendar('addEventSource', events);
calendar.fullCalendar('rerenderEvents');
});
}
// Restore saved filters
function load_project_filters()
{
var filters = Kanboard.GetStorageItem(filter_storage_key);
if (filters !== "") {
filters = JSON.parse(filters);
for (var filter in filters) {
$("select[name=" + filter + "]").val(filters[filter]);
}
}
refresh_project_calendar(filters || {});
$('.calendar-filter').change(apply_project_filters);
}
// Apply filters on change
function apply_project_filters()
{
var filters = {};
$('.calendar-filter').each(function() {
filters[$(this).attr("name")] = $(this).val();
});
Kanboard.SetStorageItem(filter_storage_key, JSON.stringify(filters));
refresh_project_calendar(filters);
}
(function() {
jQuery(document).ready(function() {
if (Kanboard.Exists("calendar")) {
filter_storage_key = "calendar_filters_" + $("#calendar").data("project-id");
show_project_calendar();
load_project_filters();
}
else if (Kanboard.Exists("user-calendar")) {
show_user_calendar();
var calendar = $('#calendar');
calendar.fullCalendar({
lang: $("body").data("js-lang"),
editable: true,
eventLimit: true,
defaultView: "month",
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
eventDrop: function(event) {
$.ajax({
cache: false,
url: calendar.data("save-url"),
contentType: "application/json",
type: "POST",
processData: false,
data: JSON.stringify({
"task_id": event.id,
"date_due": event.start.format()
})
});
},
viewRender: function() {
var url = calendar.data("check-url");
var params = {
"start": calendar.fullCalendar('getView').start.format(),
"end": calendar.fullCalendar('getView').end.format()
};
for (var key in params) {
url += "&" + key + "=" + params[key];
}
$.getJSON(url, function(events) {
calendar.fullCalendar('removeEvents');
calendar.fullCalendar('addEventSource', events);
calendar.fullCalendar('rerenderEvents');
});
}
});
}
});

View File

@@ -1,4 +1,4 @@
Kanboard.Swimlane = (function() {
(function() {
// Expand a Swimlane via display attributes
function expand(swimlaneId)