[WIP] Kanban project cleanup

bzr revid: fme@openerp.com-20120522155815-y23ps3bs32i9vqlu
This commit is contained in:
Fabien Meghazi 2012-05-22 17:58:15 +02:00
parent 9c0aa9b3d2
commit 5ee2a8abde
8 changed files with 89 additions and 429 deletions

View File

@ -71,7 +71,7 @@ Dashboard for project members that includes:
'auto_install': False,
'application': True,
'css': ['static/src/css/project.css'],
'js': ['static/src/js/dropdown.js','static/src/js/project.js'],
'js': ['static/src/js/project.js'],
'certificate': '0075116868317',
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -188,13 +188,12 @@
<field name="task_count"/>
<templates>
<t t-name="kanban-box">
<div class="project_vignettes">
<li t-attf-class="#{kanban_color(record.color.raw_value)} oe_project_kanban_vignette" id="oe_project_kanban_vignette">
<a href="#" class="oe_project_kanban_action dropdown-toggle"><span class="oe_i">B</span></a>
<ul class="dropdown-menu">
<li ><a type="edit" >Edit...</a></li>
<li ><a type="delete">Delete</a></li>
<li>
<div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_card">
<a class="oe_kanban_menuaction oe_i">B</a>
<ul class="oe_kanban_menu">
<li><a type="edit" >Edit...</a></li>
<li><a type="delete">Delete</a></li>
<li>
<ul class="color-chooser">
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_0 square"></span></a></li>
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_1 square"></span></a></li>
@ -203,46 +202,37 @@
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_4 square"></span></a></li>
<li><a t-att-id="record.id.value" class="bgcolor"><span class="oe_kanban_color_5 square"></span></a></li>
</ul>
</li>
</ul>
<h4><t t-esc="record.name.value.substr(0,33)"/><t t-if="record.name.value.length > 33">...</t></h4>
<div id="list">
<a t-if="record.use_tasks.raw_value" class="oe_project_buttons"
id="1" name="%(act_project_project_2_project_task_all)d" type="action">
</li>
</ul>
<div class="oe_kanban_content">
<h3>
<!--<t t-esc="record.name.value.substr(0,33)"/><t t-if="record.name.value.length > 33">...</t>-->
<field name="name"/>
</h3>
<div class="oe_kanban_project_list">
<a t-if="record.use_tasks.raw_value"
name="%(act_project_project_2_project_task_all)d" type="action">
Tasks(<field name="task_count"/>)</a>
</div>
<br/>
<button class="click_button" name="dummy" type="object">
<table class="project_fields">
<tr id="deadline" t-if="record.date.raw_value">
<th align="left">Deadline</th>
<td align="left"><field name="date"/></td>
</tr>
<tr>
<th align="left" width="70px">Progress</th>
<td align="left">
<t t-esc="Math.round(record.effective_hours.raw_value)"/> / <t t-esc="Math.round(record.planned_hours.raw_value)"/> <field name="company_uom_id"/>
</td>
</tr>
</table>
<br/>
<div class="oe_kanban_project_fields oe_kanban_project_deadline" t-if="record.date.raw_value">
<div>Deadline</div>
<div><field name="date"/></div>
</div>
<div class="oe_kanban_project_fields oe_kanban_project_progress">
<div>Progress</div>
<div><t t-esc="Math.round(record.effective_hours.raw_value)"/> / <t t-esc="Math.round(record.planned_hours.raw_value)"/> <field name="company_uom_id"/></div>
</div>
<div class="oe_kanban_project_avatars">
<t t-foreach="record.members.raw_value" t-as="member">
<img t-att-src="kanban_image('res.users', 'avatar', member)" t-att-id="member" class="project_avatar"/>
<img t-att-src="kanban_image('res.users', 'avatar', member)" t-att-data-member_id="member"/>
</t>
</button>
</li>
</div>
</div>
</div>
<script type="text/javascript">
//open dropdwon when click on the icon.
$('.dropdown-toggle').dropdown();
//show and hide the dropdown icon when mouseover and mouseour.
$('.oe_project_kanban_vignette').mouseover(function() {
return $(this).find('.oe_project_kanban_action').show();
}).mouseout(function() {
return $(this).find('.oe_project_kanban_action').hide();
});
</script>
</t>
</templates>
</kanban>

View File

@ -1,246 +1,39 @@
.project_fields {
margin-top: 1px;
margin-bottom: 1px;
font-size: 11px;
padding-left: 0px;
.oe_kanban_project_list {
margin: 8px 0 8px 0;
}
.project_fields td {
border: none;
padding: 2px 0 2px 8px;
.oe_kanban_project_list a:not(:first-child):before {
content: ' - ';
}
.project_fields th {
padding: 0;
.oe_kanban_project_fields div {
display: inline-block;
width: 49%;
box-sizing: border-box;
padding: 2px 0 2px 0;
}
.oe_kanban_project_fields div:nth-child(odd) {
border-right: 1px solid #dddddd;
vertical-align: top;
margin-right: 8px;
padding-right: 8px;
}
.project_vignettes{
padding: 4px !important;
.oe_kanban_project_fields div:nth-child(even) {
padding-left: 8px;
color: #888888;
}
.project_vignettes li {
float: left;
.oe_kanban_project_avatars {
padding-top: 1em;
text-align: center;
margin-top: 8px;
}
.project_vignettes .project_avatar {
.oe_kanban_project_avatars img {
width: 30px;
height: 30px;
padding-left: 0px;
}
.project_vignettes .project_fields {
width: 100%;
}
.project_vignettes .project_fields th {
width: 120px;
font-weight: normal;
}
.project_vignettes .project_fields td {
color: #888888;
}
.project_vignettes h4 a {
color: #4c4c4c;
}
.project_vignettes > li h4 {
margin-bottom: 2px;
padding-left: 2px;
}
.oe_project_buttons {
padding: 2px 2px !important;
background: none !important;
background-color: transparent !important;
border: hidden !important;
color: #8A89BA !important;
}
.oe_project_buttons:hover {
cursor: pointer;
color: #0000FF;
}
.project_avatar {
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.click_button {
background: none !important;
background-color: transparent !important;
border: hidden !important;
min-height: 155px;
min-width: 265px;
border: hidden;
/*margin-left: 6px !important;*/
margin-top: 3px !important;
text-align: left;
vertical-align: super;
font-size: 11px;
-webkit-box-align: baseline;
}
.click_button:hover {
cursor: default !important;
}
.dropdown-menu {
display: none;
position: absolute;
}
.dropdown {
position: relative;
}
.dropdown-toggle:after {
width: 0;
height: 0;
display: inline-block;
content: "&darr";
text-indent: -99999px;
vertical-align: top;
margin-top: 8px;
margin-left: 4px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid white;
filter: alpha(opacity=50);
-khtml-opacity: 0.5;
-moz-opacity: 0.5;
opacity: 0.5;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser {
padding: 0 3px;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser li {
float: left;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser li a {
padding: 2px;
}
a.oe_project_kanban_action {
position: absolute;
right: 0;
display: none;
}
a.oe_project_kanban_action:hover {
text-decoration: none;
}
a.oe_project_kanban_action .oe_i {
color: #4c4c4c;
}
.square {
display: inline-block;
width: 18px;
height: 18px;
border:1px solid grey;
}
.oe_kanban_color_0 {
background: white;
}
.oe_kanban_color_1 {
background: #B1DCFE;
}
.oe_kanban_color_2 {
background: #FF8E8E;
}
.oe_kanban_color_3 {
background: khaki;
}
.oe_kanban_color_4 {
background: thistle;
}
.oe_kanban_color_5 {
background: orange;
}
.open {
display: block;
}
.open .dropdown-menu {
display: block;
}
a.oe_project_kanban_action {
position: absolute;
right: 0;
}
a.oe_project_kanban_action:hover {
text-decoration: none;
}
a.oe_project_kanban_action .eo_i {
color: #4c4c4c;
}
.oe_project_kanban_vignette {
position: relative;
min-height: 50px;
/*background: white;*/
border: 1px solid #d8d8d8;
border-bottom-color: #b9b9b9;
padding: 6px;
margin: 6px 0;
display: inline-block;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.oe_project_kanban_vignette:last-child {
margin-bottom: 0;
}
.oe_project_kanban_vignette:hover {
-moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.6);
-webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.6);
-box-shadow: 0 0 3px rgba(0, 0, 0, 0.6);
}
.oe_project_kanban_vignette h4 {
margin: 0 0 2px;
border-radius: 4px;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.oe_project_kanban_vignette .dropdown-menu {
top: 30px;
right: -140px;
padding: 4px;
border: 1px solid #afafb6;
width: 160px;
overflow-x: hidden;
z-index: 900;
background: white;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.oe_project_kanban_vignette .dropdown-menu p {
margin-left: 12px;
}
.oe_project_kanban_vignette .dropdown-menu li {
float: none;
display: block;
background-color: none;
}
.oe_project_kanban_vignette .dropdown-menu li a {
display: block;
padding: 3px 6px;
clear: both;
font-weight: normal;
line-height: 14px;
color: #4c4c4c;
text-decoration: none;
}
.oe_project_kanban_vignette .dropdown-menu li a:hover {
background: #f0f0fa;
background: -moz-linear-gradient(#f0f0fa, #eeeef6);
background: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
background: -webkit-linear-gradient(#f0f0fa, #eeeef6);
-moz-box-shadow: none;
-webkit-box-shadow: none;
-box-shadow: none;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser {
padding: 0 3px;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser li {
float: left;
}
.oe_project_kanban_vignette .dropdown-menu .color-chooser li a {
padding: 2px;
}

View File

@ -1,92 +0,0 @@
/* ============================================================
* bootstrap-dropdown.js v2.0.2
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function( $ ){
"use strict"
/* DROPDOWN CLASS DEFINITION
* ========================= */
var toggle = '[data-toggle="dropdown"]'
, Dropdown = function ( element ) {
var $el = $(element).on('click.dropdown.data-api', this.toggle)
$('html').on('click.dropdown.data-api', function () {
$el.parent().removeClass('open')
})
}
Dropdown.prototype = {
constructor: Dropdown
, toggle: function ( e ) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
, isActive
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
isActive = $parent.hasClass('open')
clearMenus()
!isActive && $parent.toggleClass('open')
return false
}
}
function clearMenus() {
$(toggle).parent().removeClass('open')
}
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
$.fn.dropdown = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('dropdown')
if (!data) $this.data('dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.dropdown.Constructor = Dropdown
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
$('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
})
}( window.jQuery );

View File

@ -1,46 +1,23 @@
openerp.project = function(openerp) {
openerp.web_kanban.ProjectKanban = openerp.web_kanban.KanbanRecord.include({
bind_events: function() {
openerp.web_kanban.KanbanView.include({
on_groups_started: function() {
var self = this;
self._super();
if (this.view.dataset.model == 'project.project') {
/*set avatar title for members.
In many2many fields, returns only list of ids.
we can implement return value of m2m fields like [(1,"Adminstration"),...].
self._super.apply(this, arguments);
if (this.dataset.model === 'project.project') {
/* Set avatar title for members.
In many2many fields, returns only list of ids.
we can implement return value of m2m fields like [(1,"Adminstration"),...].
*/
_.each($(this.$element).find('.project_avatar'), function(avatar) {
var dataset = new openerp.web.DataSetSearch(this, 'res.users', self.session.context, [['id','=',avatar.id]]);
dataset.read_slice(['name']).then(function(result) {
avatar.setAttribute("title",result[0].name);
var members_ids = [];
this.$element.find('.project_avatar').each(function() {
members_ids.push($(this).data('member_id'));
});
var dataset = new openerp.web.DataSetSearch(this, 'res.users', self.session.context, [['id', 'in', _.uniq(members_ids)]]);
dataset.read_slice(['id', 'name']).then(function(result) {
_.each(result, function(v, k) {
self.$element.find('.project_avatar[data-member_id=' + v.id + ']').attr('title', v.name).tipsy();
});
});
// set sequence like Tasks,Issues,Timesheets and Phases
var my_list = $("#list a");
my_list.sort(function (a, b) {
var aValue = parseInt(a.id, 10);
var bValue = parseInt(b.id, 10);
return aValue == bValue ? 0 : aValue < bValue ? -1 : 1;
});
$('#list').replaceWith(my_list);
// when vignette is clicked, it opens the first action in sequence
if (my_list.length !== 0) {
var click_button = $(this.$element).find('.click_button');
click_button.attr('data-name', my_list[0].getAttribute('data-name'));
click_button.attr('data-type', "action");
}
/* set background color.
we can do other way to implement new widget.
because we need to rpc call for that.
*/
this.$element.find('.bgcolor').click(function() {
var color = parseInt($(this).find('span').attr('class').split(' ')[0].substring(16), 10);
var color_class = $(this).find('span').attr('class').split(' ')[0];
$(this).closest('#oe_project_kanban_vignette').removeClass().addClass(color_class + ' oe_project_kanban_vignette');
self.view.dataset.write(parseInt(this.id, 10), {color:color});
});
}
}
});

View File

@ -373,9 +373,9 @@
<field name="use_issues"/>
<field name="issue_count" invisible="1"/>
</field>
<xpath expr="//div[@id='list']" position="inside">
<a t-if="record.use_issues.raw_value" class="oe_project_buttons"
id="2" name="%(act_project_project_2_project_issue_all)d" type="action">
<xpath expr="//div[contains(@class, 'oe_kanban_project_list')]" position="inside">
<a t-if="record.use_issues.raw_value"
name="%(act_project_project_2_project_issue_all)d" type="action">
Issues(<field name="issue_count"/>)</a>
</xpath>
</field>

View File

@ -129,9 +129,9 @@
<field name="use_phases"/>
<field name="phase_count"/>
</field>
<xpath expr="//div[@id='list']" position="inside">
<a t-if="record.use_phases.raw_value" class="oe_project_buttons"
id="4" name="%(act_project_phases)d" type="action">Phases(<field name="phase_count"/>)</a>
<xpath expr="//div[contains(@class, 'oe_kanban_project_list')]" position="inside">
<a t-if="record.use_phases.raw_value"
name="%(act_project_phases)d" type="action">Phases(<field name="phase_count"/>)</a>
</xpath>
</field>
</record>

View File

@ -36,25 +36,17 @@
<field name="currency_id"/>
<field name="partner_id"/>
</field>
<xpath expr="//div[@id='list']" position="inside">
<a t-if="record.use_timesheets.raw_value" class="oe_project_buttons"
id="3" name="open_timesheets" type="object">Timesheets(<field name="timesheet_count"/>)</a>
<xpath expr="//div[contains(@class, 'oe_kanban_project_list')]" position="inside">
<a t-if="record.use_timesheets.raw_value"
name="open_timesheets" type="object">Timesheets(<field name="timesheet_count"/>)</a>
</xpath>
<xpath expr="//tr[@id='deadline']" position="before">
<t t-if="record.partner_id.raw_value">
<tr>
<th align="left">Amount to invoice</th>
<td align="left">
<field name="amount_to_invoice"/> <t t-esc="record.currency_id.raw_value[1].split(' ')[1][1]"/>
</td>
</tr>
<tr>
<th align="left">Time to Invoice</th>
<td align="left">
<field name="time_to_invoice"/> <field name="company_uom_id"/>
</td>
</tr>
</t>
<xpath expr="//div[contains(@class, 'oe_kanban_project_deadline')]" position="before">
<div class="oe_kanban_project_fields oe_kanban_project_invoice" t-if="record.partner_id.raw_value">
<div>Amount to invoice</div>
<div><field name="amount_to_invoice"/> <t t-esc="record.currency_id.raw_value[1].split(' ')[1][1]"/></div>
<div>Time to Invoice</div>
<div><field name="time_to_invoice"/> <field name="company_uom_id"/></div>
</div>
</xpath>
</field>
</record>