diff --git a/module/productplan/control.php b/module/productplan/control.php
index 60f0bd94cc..472483c718 100644
--- a/module/productplan/control.php
+++ b/module/productplan/control.php
@@ -383,6 +383,7 @@ class productplan extends control
$this->view->browseType = $browseType;
$this->view->viewType = $viewType;
$this->view->orderBy = $orderBy;
+ $this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$this->view->plans = $this->productplan->getList($productID, $branch, $browseType, $pager, $sort, "", $queryID);
$this->view->pager = $pager;
$this->view->projects = $this->product->getProjectPairsByProduct($productID, $branch, '', $status = 'closed', 'multiple');
diff --git a/module/productplan/css/browse.css b/module/productplan/css/browse.css
index ae54b036db..9b583dbeaa 100644
--- a/module/productplan/css/browse.css
+++ b/module/productplan/css/browse.css
@@ -36,8 +36,24 @@ td.c-branch {overflow: hidden; text-align: left !important; white-space: nowrap;
.switchButton {background: #fff !important;}
.panel-actions {position: relative; padding: 0 0; padding-top: 1px;}
+#kanbanContainer {background: #efefef; box-shadow: none;}
+#kanbanContainer {padding-bottom: 0; margin-bottom: 0; border: unset;}
+#kanbanContainer.fullscreen {overflow: auto;}
.icon-list {padding-left: 7px;}
+.titleBox .icon-delay {padding-right: 8px;}
+
+.kanban-card .productplanInfo .user {float: right;display: block;}
+.kanban-card a[disabled] {color: #313c52;}
+.kanban-card .header .actions {position: absolute;top: 4px;right: 4px;opacity: 0;}
+.kanban-card .productplanDesc {min-width: 100%;color: #838a9d;overflow: hidden;white-space: nowrap;text-overflow: clip;}
+.kanban-card {height: auto !important;padding: 8px 14px !important;min-height: 60px;}
.icon-kanban {padding-left: 7px; font-size: 16px;}
+.label-wait {background: #EFEFEF !important;color: #838A9D;}
+.label-future {background: #EFEFEF !important;color: #838A9D;border-radius: 9px;}
+.label-doing {background: #f8d2d2 !important;color: #e64b4c;}
+.label-done {background: #dfe9d8 !important;color: #429b16;}
+.label-closed, .label-terminate {background: #e5e5e5 !important;color: #b8b8b8;}
+
#productplanList thead th.c-title {width: 100%;}
#productplanList .c-actions {width: 265px;}
#productplanList .c-actions .btn+.btn {margin-left: -1px;}
diff --git a/module/productplan/js/browse.js b/module/productplan/js/browse.js
index 1530caf5ba..cd95f5132c 100644
--- a/module/productplan/js/browse.js
+++ b/module/productplan/js/browse.js
@@ -365,8 +365,39 @@ function renderKanbanItem(item, $item)
if(privs.includes('view')) $title = $('').appendTo($titleBox).attr('href', createLink(rawModule, 'view', 'cardID=' + item.id));
if(!privs.includes('view')) $title = $('').appendTo($titleBox);
}
+ if(!$title.children('i').length)
+ {
+ $(function() {$title.prepend('');})
+ }
$title.text(item.title).attr('title', item.title);
+ var $info = $item.children('.productplanInfo');
+ if(!$info.length) $info = $(
+ [
+ '
',
+ '
'
+ ].join('')).appendTo($item);
+
+ /* Output plan desc information. */
+ var $descBox = $item.children('.productplanDesc');
+ if(!$descBox.length)
+ {
+ $descBox = $('' + item.desc + '
').appendTo($info);
+ }
+
+ var $statusBox = $item.children('.productplanStatus');
+ if(!$statusBox.length)
+ {
+ if(item.deleted == '0')
+ {
+ $statusBox = $('' + productplanLang.statusList[item.status] + '').appendTo($info);
+ }
+ else
+ {
+ $statusBox = $('' + productplanLang.deleted + '').appendTo($info);
+ }
+ }
+
/* Determine whether to print an expired label. */
var today = new Date();
var begin = $.zui.createDate(item.begin);
@@ -392,54 +423,71 @@ function renderKanbanItem(item, $item)
].join('')).appendTo($header);
}
- /* Output plan date information. */
- var $dateBox = $item.children('.dateBox');
- if(!$dateBox.length) $dateBox = $(
- [
- '',
- '',
- '
'
- ].join('')).appendTo($item);
+ /* Display date of product plan. */
+ var $date = $info.children('.date');
+ var begin = $.zui.createDate(item.begin);
+ var end = $.zui.createDate(item.end);
+ var today = new Date();
+ var labelType = (item.begin <= $.zui.formatDate(today, 'yyyy-MM-dd') && item.end >= $.zui.formatDate(today, 'yyyy-MM-dd')) ? 'danger' : 'wait';
+ var labelTitle = $.zui.formatDate(begin, 'MM-dd') + ' ' + productplanLang.to + ' ' + $.zui.formatDate(end, 'MM-dd');
- var $time = $dateBox.children('.time');
- var beginTimeShort = $.zui.formatDate(begin, 'MM-dd');
- var beginTimeLong = $.zui.formatDate(begin, 'yyyy-MM-dd');
- var endTimeShort = $.zui.formatDate(end, 'MM-dd');
- var endTimeLong = $.zui.formatDate(end, 'yyyy-MM-dd')
- var to = productplanLang.to;
- var undetermined = productplanLang.future;
- var undetermindeDate = '2030-01-01';
- if(item.begin != undetermindeDate && item.end != undetermindeDate)
+ if((item.begin == '2030-01-01' || item.end == '2030-01-01'))
{
- $time.text(beginTimeShort + ' ' + to + ' ' + endTimeShort).attr('title', beginTimeLong + to + endTimeLong).show();
+ labelType = 'future';
+ labelTitle = productplanLang.future;
}
- else if(item.begin != undetermindeDate && item.end == undetermindeDate)
+ if(!$date.length) $date = $('').appendTo($info);
+ $date.text(labelTitle).attr('title', labelTitle).show();
+
+ /* Display avatars of creator. */
+ var $user = $info.children('.user');
+ var user = [item.createdBy];
+ if(users[item.createdBy])
{
- $time.text(beginTimeShort + ' ' + to + ' ' + undetermined).attr('title', beginTimeLong + to).show();
+ if(!$user.length) $user = $('').appendTo($info);
+ $user.html(renderUsersAvatar(user, item.id)).attr('title', users[item.createdBy]);
}
- else if(item.begin == undetermindeDate && item.end != undetermindeDate)
+}
+
+/**
+* Render avatars of user.
+* @param {String|{account: string, avatar: string}} user User account or user object
+* @returns {string}
+*/
+function renderUsersAvatar(users, itemID, size)
+{
+ var avatarSizeClass = 'avatar-' + (size || 'md');
+
+ if(users.length == 0 || (users.length == 1 && users[0] == ''))
{
- $time.text(undetermined + ' ' + to + ' ' + endTimeShort).attr('title', to + endTimeLong).show();
- }
- else if(item.begin != '2030-01-01' && item.end == '2030-01-01')
- {
- $time.text($.zui.formatDate(begin, 'MM-dd') + ' ' + productplanLang.to + ' ' + productplanLang.future).attr('title', $.zui.formatDate(begin, 'yyyy-MM-dd') + productplanLang.to).show();
- }
- else if(item.begin == '2030-01-01' && item.end != '2030-01-01')
- {
- $time.text(productplanLang.future + ' ' + productplanLang.to + ' ' + $.zui.formatDate(end, 'MM-dd')).attr('title', $.zui.formatDate(end, 'yyyy-MM-dd') + productplanLang.to).show();
- }
- else
- {
- $time.text(undetermined).attr('title', undetermined).show();
+ return $('
');
}
- /* Output plan desc information. */
- var $desc = $item.children('.desc');
- if(!$desc.length)
+ var assignees = [];
+ for(var user of users)
{
- $("" + item.desc + '
').appendTo($item);
+ var $noPrivAndNoAssigned = $('
');
+ if(!priv.canAssignCard && !user)
+ {
+ assignees.push($noPrivAndNoAssigned);
+ continue;
+ }
+ if(!user)
+ {
+ assignees.push($('
'));
+ continue;
+ }
+
+ if(typeof user === 'string') user = {account: user};
+ if(!user.avatar && window.userList && window.userList[user.account]) user = window.userList[user.account];
+ if(!user.name && window.users && window.users[user.account]) user.name = window.users[user.account];
+
+ assignees.push($('').avatar({user: user}));
}
+
+ if(assignees.length > 3) assignees.splice(3, assignees.length - 3, '...');
+
+ return assignees;
}
/**
diff --git a/module/productplan/lang/de.php b/module/productplan/lang/de.php
index 1e77f9e257..df5bade5a8 100644
--- a/module/productplan/lang/de.php
+++ b/module/productplan/lang/de.php
@@ -56,6 +56,7 @@ $lang->productplan->batchUnlinkBug = "Mehrere Verknpfungen aufheben";
$lang->productplan->linkedBugs = 'Verknüpfte Bugs';
$lang->productplan->unlinkedBugs = 'Unverknüpfte Bugs';
$lang->productplan->unexpired = 'Unexpired Plans';
+$lang->productplan->noAssigned = 'No Assigned';
$lang->productplan->all = 'All Plans';
$lang->productplan->setDate = "Set Start and end Date";
$lang->productplan->expired = "Expired";
diff --git a/module/productplan/lang/en.php b/module/productplan/lang/en.php
index 65233b5eb2..e48cd6694f 100644
--- a/module/productplan/lang/en.php
+++ b/module/productplan/lang/en.php
@@ -56,6 +56,7 @@ $lang->productplan->batchUnlinkBug = "Batch Unlink Bugs";
$lang->productplan->linkedBugs = 'Linked Bugs';
$lang->productplan->unlinkedBugs = 'Unlinked Bugs';
$lang->productplan->unexpired = 'Unexpired';
+$lang->productplan->noAssigned = 'No Assigned';
$lang->productplan->all = 'All Plans';
$lang->productplan->setDate = "Set Start and end Date";
$lang->productplan->expired = "Expired";
diff --git a/module/productplan/lang/fr.php b/module/productplan/lang/fr.php
index 263bb511a7..e88dd8c09b 100644
--- a/module/productplan/lang/fr.php
+++ b/module/productplan/lang/fr.php
@@ -56,6 +56,7 @@ $lang->productplan->batchUnlinkBug = "Retirer Bugs par Lot";
$lang->productplan->linkedBugs = 'Bugs Planifiés';
$lang->productplan->unlinkedBugs = 'Bugs non Planifiés';
$lang->productplan->unexpired = 'Plans non échus';
+$lang->productplan->noAssigned = 'No Assigned';
$lang->productplan->all = 'Tous les Plans';
$lang->productplan->setDate = "Set Start and end Date";
$lang->productplan->expired = "Expired";
diff --git a/module/productplan/lang/zh-cn.php b/module/productplan/lang/zh-cn.php
index b756ef52d2..082e3d7cec 100644
--- a/module/productplan/lang/zh-cn.php
+++ b/module/productplan/lang/zh-cn.php
@@ -56,6 +56,7 @@ $lang->productplan->batchUnlinkBug = "批量移除Bug";
$lang->productplan->linkedBugs = 'Bug';
$lang->productplan->unlinkedBugs = '未关联Bug';
$lang->productplan->unexpired = "未过期";
+$lang->productplan->noAssigned = '未指派';
$lang->productplan->all = "所有计划";
$lang->productplan->setDate = "设置计划起止时间";
$lang->productplan->expired = "已过期";
diff --git a/module/productplan/view/browsebykanban.html.php b/module/productplan/view/browsebykanban.html.php
index 91f91d4a4c..e3a6326d90 100644
--- a/module/productplan/view/browsebykanban.html.php
+++ b/module/productplan/view/browsebykanban.html.php
@@ -26,14 +26,16 @@
.kanban-card:hover > .header > .actions {opacity: 1;}
.kanban-card .title {white-space: nowrap; overflow: hidden;}
.kanban-card .expired {margin-left: 2px;}
-.kanban-card .dateBox {padding-top: 22px;}
.kanban-card .desc {overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #838a9d; padding-top: 5px;}
.kanban-card .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.dropdown-menu > li > .disabled {pointer-events: none; color: #838a9d;}
rawModule);?>
+
+productplan->noAssigned);?>
productplan);?>
+ common::hasPriv('kanban', 'assigncard')));?>