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')));?>