summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Lawrence <dlawrence@mozilla.com>2013-01-03 23:24:56 +0100
committerDave Lawrence <dlawrence@mozilla.com>2013-01-03 23:24:56 +0100
commite3b051b686c16ead5f8b63206940b885532e1c95 (patch)
tree1bb8f6bb49c872528f1d5dc53334a66c426db088
parenta29bc8846fac5d7bcbe37ed306eb612bfc74acee (diff)
downloadbugzilla-e3b051b686c16ead5f8b63206940b885532e1c95.tar.gz
bugzilla-e3b051b686c16ead5f8b63206940b885532e1c95.tar.xz
More ProductDashboard work
-rw-r--r--extensions/ProductDashboard/Extension.pm12
-rw-r--r--extensions/ProductDashboard/lib/Queries.pm66
-rw-r--r--extensions/ProductDashboard/template/en/default/pages/productdashboard.html.tmpl25
-rw-r--r--extensions/ProductDashboard/template/en/default/pages/productdashboard/components.html.tmpl354
-rw-r--r--extensions/ProductDashboard/template/en/default/pages/productdashboard/duplicates.html.tmpl71
-rw-r--r--extensions/ProductDashboard/template/en/default/pages/productdashboard/popularity.html.tmpl67
-rw-r--r--extensions/ProductDashboard/template/en/default/pages/productdashboard/recents.html.tmpl112
-rw-r--r--extensions/ProductDashboard/template/en/default/pages/productdashboard/roadmap.html.tmpl52
-rw-r--r--extensions/ProductDashboard/template/en/default/pages/productdashboard/summary.html.tmpl253
-rw-r--r--extensions/ProductDashboard/web/js/components.js75
-rw-r--r--extensions/ProductDashboard/web/js/duplicates.js28
-rw-r--r--extensions/ProductDashboard/web/js/popularity.js28
-rw-r--r--extensions/ProductDashboard/web/js/productdashboard.js98
-rw-r--r--extensions/ProductDashboard/web/js/recents.js32
-rw-r--r--extensions/ProductDashboard/web/js/roadmap.js24
-rw-r--r--extensions/ProductDashboard/web/js/summary.js45
-rw-r--r--extensions/ProductDashboard/web/styles/productdashboard.css20
17 files changed, 589 insertions, 773 deletions
diff --git a/extensions/ProductDashboard/Extension.pm b/extensions/ProductDashboard/Extension.pm
index 8ccc897ed..57192f195 100644
--- a/extensions/ProductDashboard/Extension.pm
+++ b/extensions/ProductDashboard/Extension.pm
@@ -108,10 +108,13 @@ sub _page_dashboard {
$vars->{'total_closed_bugs'} = total_closed_bugs($product);
$vars->{'severities'} = get_legal_field_values('bug_severity');
+ $vars->{'open_bugs_percentage'} = int($vars->{'total_open_bugs'} / $vars->{'total_bugs'} * 100);
+ $vars->{'closed_bugs_percentage'} = int($vars->{'total_closed_bugs'} / $vars->{'total_bugs'} * 100);
+
if ($current_tab_name eq 'summary') {
$vars->{'by_priority'} = by_priority($product, $bug_status);
$vars->{'by_severity'} = by_severity($product, $bug_status);
- $vars->{'by_assignee'} = by_assignee($product, $bug_status);
+ $vars->{'by_assignee'} = by_assignee($product, $bug_status, 50);
$vars->{'by_status'} = by_status($product, $bug_status);
}
@@ -173,11 +176,14 @@ sub _page_dashboard {
$milestone_stats{'name'} = $milestone->name;
$milestone_stats{'total_bugs'} = total_bug_milestone($product, $milestone);
$milestone_stats{'open_bugs'} = bug_milestone_by_status($product, $milestone, 'open');
- $milestone_stats{'closed_bugs'} = bug_milestone_by_status($product, $milestone, 'closed');
+ $milestone_stats{'closed_bugs'} = bug_milestone_by_status($product, $milestone, 'closed');
$milestone_stats{'link_total'} = bug_milestone_link_total($product, $milestone);
$milestone_stats{'link_open'} = bug_milestone_link_open($product, $milestone);
$milestone_stats{'link_closed'} = bug_milestone_link_closed($product, $milestone);
- push (@{$vars->{by_roadmap}}, \%milestone_stats);
+ $milestone_stats{'percentage'} = $milestone_stats{'total_bugs'}
+ ? int(($milestone_stats{'closed_bugs'} / $milestone_stats{'total_bugs'}) * 100)
+ : 0;
+ push (@{$vars->{'by_roadmap'}}, \%milestone_stats);
}
}
}
diff --git a/extensions/ProductDashboard/lib/Queries.pm b/extensions/ProductDashboard/lib/Queries.pm
index 9c3d91539..fe5d04977 100644
--- a/extensions/ProductDashboard/lib/Queries.pm
+++ b/extensions/ProductDashboard/lib/Queries.pm
@@ -66,7 +66,7 @@ sub total_closed_bugs {
return $dbh->selectrow_array("SELECT COUNT(bug_id)
FROM bugs
- WHERE bug_status IN ('CLOSED')
+ WHERE bug_status IN (" . join(',', quoted_closed_states()) . ")
AND product_id = ?", undef, $product->id);
}
@@ -96,12 +96,14 @@ sub by_version {
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
$extra = "AND bugs.bug_status IN (" . join(',', quoted_closed_states()) . ")" if $bug_status eq 'closed';
- return $dbh->selectall_arrayref("SELECT version, COUNT(bug_id)
+ return $dbh->selectall_arrayref("SELECT version, COUNT(bug_id),
+ ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
FROM bugs
WHERE product_id = ?
$extra
GROUP BY version
- ORDER BY COUNT(bug_id) DESC", undef, $product->id);
+ ORDER BY COUNT(bug_id) DESC",
+ undef, $product->id, $product->id);
}
sub by_milestone {
@@ -112,12 +114,14 @@ sub by_milestone {
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
$extra = "AND bugs.bug_status IN (" . join(',', quoted_closed_states()) . ")" if $bug_status eq 'closed';
- return $dbh->selectall_arrayref("SELECT target_milestone, COUNT(bug_id)
+ return $dbh->selectall_arrayref("SELECT target_milestone, COUNT(bug_id),
+ ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
FROM bugs
WHERE product_id = ?
$extra
GROUP BY target_milestone
- ORDER BY COUNT(bug_id) DESC", undef, $product->id);
+ ORDER BY COUNT(bug_id) DESC",
+ undef, $product->id, $product->id);
}
sub by_priority {
@@ -128,12 +132,14 @@ sub by_priority {
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
$extra = "AND bugs.bug_status IN (" . join(',', quoted_closed_states()) . ")" if $bug_status eq 'closed';
- return $dbh->selectall_arrayref("SELECT priority, COUNT(bug_id)
+ return $dbh->selectall_arrayref("SELECT priority, COUNT(bug_id),
+ ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
FROM bugs
WHERE product_id = ?
$extra
GROUP BY priority
- ORDER BY COUNT(bug_id) DESC", undef, $product->id);
+ ORDER BY COUNT(bug_id) DESC",
+ undef, $product->id, $product->id);
}
sub by_severity {
@@ -144,12 +150,14 @@ sub by_severity {
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
$extra = "AND bugs.bug_status IN (" . join(',', quoted_closed_states()) . ")" if $bug_status eq 'closed';
- return $dbh->selectall_arrayref("SELECT bug_severity, COUNT(bug_id)
+ return $dbh->selectall_arrayref("SELECT bug_severity, COUNT(bug_id),
+ ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
FROM bugs
WHERE product_id = ?
$extra
GROUP BY bug_severity
- ORDER BY COUNT(bug_id) DESC", undef, $product->id);
+ ORDER BY COUNT(bug_id) DESC",
+ undef, $product->id, $product->id);
}
sub by_component {
@@ -160,12 +168,14 @@ sub by_component {
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
$extra = "AND bugs.bug_status IN (" . join(',', quoted_closed_states()) . ")" if $bug_status eq 'closed';
- return $dbh->selectall_arrayref("SELECT components.name, COUNT(bugs.bug_id)
+ return $dbh->selectall_arrayref("SELECT components.name, COUNT(bugs.bug_id),
+ ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
FROM bugs INNER JOIN components ON bugs.component_id = components.id
WHERE bugs.product_id = ?
$extra
GROUP BY components.name
- ORDER BY COUNT(bugs.bug_id) DESC", undef, $product->id);
+ ORDER BY COUNT(bugs.bug_id) DESC",
+ undef, $product->id, $product->id);
}
sub by_value_summary {
@@ -225,20 +235,21 @@ sub by_assignee {
my $dbh = Bugzilla->dbh;
my $extra;
- $limit = detaint_natural($limit) ? $dbh->sql_limit($limit) : "";
+ $limit = ($limit && detaint_natural($limit)) ? $dbh->sql_limit($limit) : "";
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
$extra = "AND bugs.bug_status IN (" . join(',', quoted_closed_states()) . ")" if $bug_status eq 'closed';
- my @result = map { [ Bugzilla::User->new($_->[0]), $_->[1] ] }
- @{$dbh->selectall_arrayref("SELECT bugs.assigned_to AS userid, COUNT(bugs.bug_id)
+ my @result = map { [ Bugzilla::User->new($_->[0]), $_->[1], $_->[2] ] }
+ @{$dbh->selectall_arrayref("SELECT bugs.assigned_to AS userid, COUNT(bugs.bug_id),
+ ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
FROM bugs, profiles
WHERE bugs.product_id = ?
AND bugs.assigned_to = profiles.userid
$extra
GROUP BY profiles.login_name
ORDER BY COUNT(bugs.bug_id) DESC $limit",
- undef, $product->id)};
+ undef, $product->id, $product->id)};
return \@result;
}
@@ -251,12 +262,14 @@ sub by_status {
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
$extra = "AND bugs.bug_status IN (" . join(',', quoted_closed_states()) . ")" if $bug_status eq 'closed';
- return $dbh->selectall_arrayref("SELECT bugs.bug_status, COUNT(bugs.bug_id)
+ return $dbh->selectall_arrayref("SELECT bugs.bug_status, COUNT(bugs.bug_id),
+ ROUND(((COUNT(bugs.bug_id) / ( SELECT COUNT(*) FROM bugs WHERE bugs.product_id = ? $extra)) * 100))
FROM bugs
WHERE bugs.product_id = ?
$extra
GROUP BY bugs.bug_status
- ORDER BY COUNT(bugs.bug_id) DESC", undef, $product->id);
+ ORDER BY COUNT(bugs.bug_id) DESC",
+ undef, $product->id, $product->id);
}
sub total_bug_milestone {
@@ -267,10 +280,7 @@ sub total_bug_milestone {
FROM bugs
WHERE target_milestone = ?
AND product_id = ?",
- undef,
- $milestone->name,
- $product->id);
-
+ undef, $milestone->name, $product->id);
}
sub bug_milestone_by_status {
@@ -294,7 +304,7 @@ sub bug_milestone_by_status {
sub by_duplicate {
my ($product, $bug_status, $limit) = @_;
my $dbh = Bugzilla->dbh;
- $limit = detaint_natural($limit) ? $dbh->sql_limit($limit) : "";
+ $limit = ($limit && detaint_natural($limit)) ? $dbh->sql_limit($limit) : "";
my $extra;
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
@@ -324,7 +334,7 @@ sub by_duplicate {
sub by_popularity {
my ($product, $bug_status, $limit) = @_;
my $dbh = Bugzilla->dbh;
- $limit = detaint_natural($limit) ? $dbh->sql_limit($limit) : "";
+ $limit = ($limit && detaint_natural($limit)) ? $dbh->sql_limit($limit) : "";
my $extra;
$extra = "AND bugs.bug_status IN (" . join(',', quoted_open_states()) . ")" if $bug_status eq 'open';
@@ -359,7 +369,7 @@ sub recently_opened {
my $date_to = $params->{'date_to'};
$days ||= 7;
- $limit = detaint_natural($limit) ? $dbh->sql_limit($limit) : "";
+ $limit = ($limit && detaint_natural($limit)) ? $dbh->sql_limit($limit) : "";
my @values = ($product->id);
@@ -375,7 +385,7 @@ sub recently_opened {
push(@values, trick_taint($date_from), trick_taint($date_to));
}
else {
- $date_part = "AND bugs.creation_ts >= NOW() - " . $dbh->sql_to_days('?');
+ $date_part = "AND bugs.creation_ts >= CURRENT_DATE() - INTERVAL ? DAY";
push(@values, $days);
}
@@ -407,7 +417,7 @@ sub recently_closed {
my $date_to = $params->{'date_to'};
$days ||= 7;
- $limit = detaint_natural($limit) ? $dbh->sql_limit($limit) : "";
+ $limit = ($limit && detaint_natural($limit)) ? $dbh->sql_limit($limit) : "";
my @values = ($product->id);
@@ -419,11 +429,11 @@ sub recently_closed {
validate_date($date_to)
|| ThrowUserError('illegal_date', { date => $date_to,
format => 'YYYY-MM-DD' });
- $date_part = "AND bugs.creation_ts >= ? AND bugs.creation_ts <= ?";
+ $date_part = "AND bugs_activity.bug_when >= ? AND bugs_activity.bug_when <= ?";
push(@values, trick_taint($date_from), trick_taint($date_to));
}
else {
- $date_part = "AND bugs.creation_ts >= NOW() - " . $dbh->sql_to_days('?');
+ $date_part = "AND bugs_activity.bug_when >= CURRENT_DATE() - INTERVAL ? DAY";
push(@values, $days);
}
diff --git a/extensions/ProductDashboard/template/en/default/pages/productdashboard.html.tmpl b/extensions/ProductDashboard/template/en/default/pages/productdashboard.html.tmpl
index daf0ea6df..6ae515f34 100644
--- a/extensions/ProductDashboard/template/en/default/pages/productdashboard.html.tmpl
+++ b/extensions/ProductDashboard/template/en/default/pages/productdashboard.html.tmpl
@@ -8,19 +8,36 @@
[% PROCESS global/variables.none.tmpl %]
+[% javascript_urls = [ "js/util.js", "js/field.js" ] %]
+
+[% IF current_tab_name == 'summary' %]
+ [% javascript_urls.push("extensions/ProductDashboard/web/js/summary.js") %]
+ [% ELSIF current_tab_name == 'recents' %]
+ [% yui = [ "calendar" ] %]
+ [% javascript_urls.push("js/field.js") %]
+ [% javascript_urls.push("js/util.js") %]
+ [% javascript_urls.push("extensions/ProductDashboard/web/js/recents.js") %]
+[% ELSIF current_tab_name == 'components' %]
+ [% javascript_urls.push("extensions/ProductDashboard/web/js/components.js") %]
+[% ELSIF current_tab_name == 'duplicates' %]
+ [% javascript_urls.push("extensions/ProductDashboard/web/js/duplicates.js") %]
+[% ELSIF current_tab_name == 'popularity' %]
+ [% javascript_urls.push("extensions/ProductDashboard/web/js/popularity.js") %]
+[% ELSIF current_tab_name == 'roadmap' && Param('usetargetmilestone') %]
+ [% javascript_urls.push("extensions/ProductDashboard/web/js/roadmap.js") %]
+[% END %]
+
[% filtered_product = product.name FILTER html %]
[% PROCESS global/header.html.tmpl
title = "Product Dashboard: $filtered_product"
style_urls = [ "skins/standard/buglist.css",
"js/yui/assets/skins/sam/paginator.css",
"extensions/ProductDashboard/web/styles/productdashboard.css" ]
- yui = [ "datatable", "paginator", "calendar" ]
- javascript_urls = [ "js/util.js", "js/field.js",
- "extensions/ProductDashboard/web/js/productdashboard.js" ]
%]
<script type="text/javascript">
<!--
+ PD = {};
[%# Set up severities list for proper sorting %]
PD.severities = new Array();
[% sort_count = 0 %]
@@ -96,7 +113,7 @@
[% bug_link = bug_link_closed %]
[% END %]
-<div class="yui-skin-sam">
+<div class="yui3-skin-sam">
<a name="top"></a>
<form action="page.cgi" method="get">
diff --git a/extensions/ProductDashboard/template/en/default/pages/productdashboard/components.html.tmpl b/extensions/ProductDashboard/template/en/default/pages/productdashboard/components.html.tmpl
index 0d2ac5e6f..8f3703770 100644
--- a/extensions/ProductDashboard/template/en/default/pages/productdashboard/components.html.tmpl
+++ b/extensions/ProductDashboard/template/en/default/pages/productdashboard/components.html.tmpl
@@ -8,259 +8,139 @@
[% IF summary.keys %]
- <h3>Summary for [% summary.type FILTER html %]: [% summary.value FILTER html %]</h3>
+<h3>Summary for [% summary.type FILTER html %]: [% summary.value FILTER html %]</h3>
- <style>
- .yui-skin-sam .yui-dt table {width:100%;}
- </style>
-
- <script type="text/javascript">
- <!--
- PD.options = {
- paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false })
- };
- PD.column_defs = [
- { key:"id", label:"ID", sortable:true, sortOptions:{ sortFunction: PD.sortBugIdLinks } },
- { key:"bug_status", label:"Status", sortable:true },
- { key:"version", label:"Version", sortable:true },
- { key:"component", label:"Component", sortable:true },
- { key:"bug_severity", label:"Severity", sortable:true, sortOptions:{ sortFunction: PD.sortBugSeverity } },
- { key:"Summary", label:"Summary", sortable:false },
- ];
- PD.fields = [
- { key:"id" },
- { key:"bug_status" },
- { key:"version" },
- { key:"component" },
- { key:"bug_severity" },
- { key:"Summary" }
- ];
+<script>
+<!--
+ // Past due bugs
[% IF user.is_timetracker %]
- PD.addStatListener("past_due", "past_due_table", PD.column_defs, PD.fields, PD.options);
+ PD.past_due = [
+ [% FOREACH bug = summary.past_due %]
+ {
+ id: '[% bug.id FILTER js %]',
+ bug_status: '[% bug.status FILTER js %]',
+ version: '[% bug.version FILTER js %]',
+ component: '[% bug.component FILTER js %]',
+ severity: '[% bug.severity FILTER js %]',
+ summary: '[% bug.summary FILTER js %]'
+ },
+ [% END %]
+ ];
[% END %]
- PD.addStatListener("updated_recently", "updated_recently_table", PD.column_defs, PD.fields, PD.options);
- -->
- </script>
- [% IF user.is_timetracker %]
- <p>
- <a href="#past_due">Past Due</a> |
- <a href="#updated_recently">Updated Recently</a>
- </p>
+ // Bugs updated recently
+ PD.updated_recently = [
+ [% FOREACH bug = summary.updated_recently %]
+ {
+ id: '[% bug.id FILTER js %]',
+ bug_status: '[% bug.status FILTER js %]',
+ version: '[% bug.version FILTER js %]',
+ component: '[% bug.component FILTER js %]',
+ severity: '[% bug.severity FILTER js %]',
+ summary: '[% bug.summary FILTER js %]'
+ },
[% END %]
+ ];
+-->
+</script>
+
+[% IF user.is_timetracker %]
+ <p>
+ <a href="#past_due">Past Due</a> |
+ <a href="#updated_recently">Updated Recently</a>
+ </p>
+[% END %]
- <div class="yui-skin-sam">
+<div class="yui3-skin-sam">
- [% IF user.is_timetracker %]
- <a name="past_due"></a>
- <b>[% summary.past_due.size FILTER html %] Past Due [% terms.Bugs %]</b> (deadline is before today's date)
- (<a href="[% bug_link FILTER html %]&amp;[% summary.type FILTER uri %]=[% summary.value FILTER uri %]&field0-0-0=deadline&type0-0-0=lessthan&value0-0-0=[% summary.timestamp FILTER uri %]&order=deadline">full list</a>)
- <div id="past_due">
- <table id="past_due_table" cellspacing="3" cellpadding="0" border="0" width="100%">
- <thead>
- <tr bgcolor="#CCCCCC">
- [% FOREACH column = [ "ID", "Status", "Version", "Component", "Severity" "Summary" ] %]
- <th>[% column FILTER html %]</th>
- [% END %]
- </tr>
- </thead>
- <tbody>
- [% FOREACH bug = summary.past_due %]
- [% count = loop.count() %]
- <tr class="[%+ count % 2 == 1 ? "bz_row_odd" : "bz_row_even" -%]">
- <td align="center"><a href="[% urlbase FILTER none %]show_bug.cgi?id=[% bug.id FILTER uri %]">
- [% bug.id FILTER html %]</a></td>
- <td align="center">[% bug.status FILTER html %]</td>
- <td align="center">[% bug.version FILTER html %]</td>
- <td align="center">[% bug.component FILTER html %]</td>
- <td align="center">[% bug.severity FILTER html %]</td>
- <td>[% bug.summary FILTER html %]</td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
- <br>
- [% END %]
+ [% IF user.is_timetracker %]
+ <a name="past_due"></a>
+ <b>[% summary.past_due.size FILTER html %] Past Due [% terms.Bugs %]</b> (deadline is before today's date)
+ (<a href="[% bug_link FILTER html %]&amp;[% summary.type FILTER uri %]=[% summary.value FILTER uri %]&field0-0-0=deadline&type0-0-0=lessthan&value0-0-0=[% summary.timestamp FILTER uri %]&order=deadline">full list</a>)
+ <div id="past_due"></div>
+ <br>
+ [% END %]
- <a name="updated_recently"></a>
- <b>[% summary.updated_recently.size FILTER html %] Most Recently Updated [% terms.Bugs %]</b>
- [% IF user.is_timetracker %](<a href="#top">back to top</a>)[% END %]
- (<a href="[% bug_link FILTER html %]&amp;[% summary.type FILTER uri %]=[% summary.value FILTER uri %]&order=changeddate DESC">full list</a>)
- <div id="updated_recently">
- <table id="updated_recently_table" cellspacing="3" cellpadding="0" border="0" width="100%">
- <thead>
- <tr bgcolor="#CCCCCC">
- [% FOREACH column = [ "ID", "Status", "Version", "Component", "Severity" "Summary" ] %]
- <th>[% column FILTER html %]</th>
- [% END %]
- </tr>
- </thead>
- <tbody>
- [% FOREACH bug = summary.updated_recently %]
- [% count = loop.count() %]
- <tr class="[%+ count % 2 == 1 ? "bz_row_odd" : "bz_row_even" -%]">
- <td align="center"><a href="[% urlbase FILTER none %]show_bug.cgi?id=[% bug.id FILTER uri %]">
- [% bug.id FILTER html %]</a></td>
- <td align="center">[% bug.status FILTER html %]</td>
- <td align="center">[% bug.version FILTER html %]</td>
- <td align="center">[% bug.component FILTER html %]</td>
- <td align="center">[% bug.severity FILTER html %]</td>
- <td>[% bug.summary FILTER html %]</td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
- </div>
+ <a name="updated_recently"></a>
+ <b>[% summary.updated_recently.size FILTER html %] Most Recently Updated [% terms.Bugs %]</b>
+ [% IF user.is_timetracker %](<a href="#top">back to top</a>)[% END %]
+ (<a href="[% bug_link FILTER html %]&amp;[% summary.type FILTER uri %]=[% summary.value FILTER uri %]&order=changeddate DESC">full list</a>)
+ <div id="updated_recently"></div>
+</div>
[% ELSE %]
- <script type="text/javascript">
- <!--
- PD.column_defs = [
- { key:"name", label:"Name", sortable:true },
- { key:"count", label:"Count", sortable:true },
- { key:"percentage", label:"Percentage", sortable:false },
- { key:"bug_list", label:"[% terms.Bug %] List", sortable:false }
- ];
- PD.fields = [
- { key:"name" },
- { key:"count", parser:"number" },
- { key:"percentage" },
- { key:"bug_list" }
+<script type="text/javascript">
+<!--
+ PD.product_name = '[% product.name FILTER js %]';
+ PD.bug_status = '[% bug_status FILTER js %]';
+
+ // Component counts
+ PD.component_counts = [
+ [% FOREACH col = by_component %]
+ {
+ name: "[% col.0 FILTER js %]",
+ count: [% col.1 || 0 FILTER js %],
+ percentage: [% col.2 || 0 FILTER js %],
+ link: '<a href="[% bug_link FILTER html %]&amp;component=[% col.0 FILTER uri %]">Link</a>'
+ },
+ [% END %]
];
- PD.addStatListener("component_counts", "component_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
- PD.addStatListener("version_counts", "version_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
- PD.addStatListener("milestone_counts", "milestone_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
- -->
- </script>
-
- [% summary_url = "page.cgi?id=productdashboard.html&amp;product=$url_filtered_product&bug_status=$url_filtered_status&tab=components" %]
-
- <h3>[% terms.Bug %] counts per component, version and milestone.</h3>
-
- <p>
- <a href="#component">Component</a> |
- <a href="#version">Version</a> |
- <a href="#milestone">Milestone</a>
- </p>
- <p>Click on a value to show a list of most recently updated [% terms.bugs %].</p>
+ // Version counts
+ PD.version_counts = [
+ [% FOREACH col = by_version %]
+ {
+ name: "[% col.0 FILTER js %]",
+ count: [% col.1 || 0 FILTER js %],
+ percentage: [% col.2 || 0 FILTER js %],
+ link: '<a href="[% bug_link FILTER html %]&amp;version=[% col.0 FILTER uri %]">Link</a>'
+ },
+ [% END %]
+ ];
- <div class="yui-skin-sam">
- <a name="component"></a>
- <b>Component</b>
- <div id="component_counts">
- <table id="component_counts_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Name</th>
- <th>Count</th>
- <th>Percentage</th>
- <th>[% terms.Bug %] List</th>
- </tr>
- </thead>
- <tbody>
- [% FOREACH col = by_component %]
- <tr>
- <td>
- <a href="[% summary_url FILTER none %]&component=[% col.0 FILTER uri %]">
- [% col.0 FILTER html %]</a>
- </td>
- <td align="right">
- [% col.1 FILTER html %]
- </td>
- <td width="70%">
- [% INCLUDE bar_graph count = col.1 %]
- </td>
- <td>
- <a href="[% bug_link FILTER html %]&amp;component=[% col.0 FILTER uri %]">View</a>
- </td>
- </tr>
+ [% IF Param('usetargetmilestone') %]
+ // Milestone counts
+ PD.milestone_counts = [
+ [% FOREACH col = by_milestone %]
+ {
+ name: "[% col.0 FILTER js %]",
+ count: [% col.1 || 0 FILTER js %],
+ percentage: [% col.2 || 0 FILTER js %],
+ link: '<a href="[% bug_link FILTER html %]&amp;target_milestone=[% col.0 FILTER uri %]">Link</a>'
+ },
[% END %]
- </tbody>
- </table>
- </div>
+ ];
+ [% END %]
+-->
+</script>
+
+<h3>[% terms.Bug %] counts per component, version and milestone.</h3>
+
+<p>
+ <a href="#component">Component</a> |
+ <a href="#version">Version</a> |
+ <a href="#milestone">Milestone</a>
+</p>
+
+<p>Click on a value to show a list of most recently updated [% terms.bugs %].</p>
+
+<div class="yui3-skin-sam">
+ <a name="component"></a>
+ <b>Component</b>
+ <div id="component_counts"></div>
+ <br>
+ <a name="version"></a>
+ <b>Version</b>
+ (<a href="#top">back to top</a>)
+ <div id="version_counts"></div>
+ [% IF Param('usetargetmilestone') %]
<br>
- <a name="version"></a>
- <b>Version</b>
+ <a name="milestone"></a>
+ <b>Milestone</b>
(<a href="#top">back to top</a>)
- <div id="version_counts">
- <table id="version_counts_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Name</th>
- <th>Count</th>
- <th>Percentage</th>
- <th>[% terms.Bug %] List</th>
- </tr>
- </thead>
- <tbody>
- [% FOREACH col = by_version %]
- <tr>
- <td>
- <a href="[% summary_url FILTER none %]&version=[% col.0 FILTER uri %]">
- [% col.0 FILTER html %]</a>
- </td>
- <td align="right">
- [% col.1 FILTER html %]
- </td>
- <td width="70%">
- [% INCLUDE bar_graph count = col.1 %]
- </td>
- <td>
- <a href="[% bug_link FILTER html %]&amp;version=[% col.0 FILTER uri %]">View</a>
- </td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
-
- [% IF Param('usetargetmilestone') %]
- <br>
- <a name="milestone"></a>
- <b>Milestone</b>
- (<a href="#top">back to top</a>)
- <div id="milestone_counts">
- <table id="milestone_counts_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Name</th>
- <th>Count</th>
- <th>Percentage</th>
- <th>[% terms.Bug %] List</th>
- </tr>
- </thead>
- <tbody>
- [% FOREACH col = by_milestone %]
- <tr>
- <td>
- <a href="[% summary_url FILTER none %]&target_milestone=[% col.0 FILTER uri %]">
- [% col.0 FILTER html %]</a>
- </td>
- <td align="right">
- [% col.1 FILTER html %]
- </td>
- <td width="70%">
- [% INCLUDE bar_graph count = col.1 %]
- </td>
- <td>
- <a href="[% bug_link FILTER html %]&amp;target_milestone=[% col.0 FILTER uri %]">View</a>
- </td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
- [% END %]
- </div>
+ <div id="milestone_counts"></div>
+ [% END %]
+</div>
[% END %]
diff --git a/extensions/ProductDashboard/template/en/default/pages/productdashboard/duplicates.html.tmpl b/extensions/ProductDashboard/template/en/default/pages/productdashboard/duplicates.html.tmpl
index bf1cdaeb1..585cdc829 100644
--- a/extensions/ProductDashboard/template/en/default/pages/productdashboard/duplicates.html.tmpl
+++ b/extensions/ProductDashboard/template/en/default/pages/productdashboard/duplicates.html.tmpl
@@ -6,69 +6,28 @@
# defined by the Mozilla Public License, v. 2.0.
#%]
-<style>
- .yui-skin-sam .yui-dt table {width:100%;}
-</style>
-
<script type="text/javascript">
-<!--
-PD.column_defs = [
- { key:"id", label:"ID", sortable:true, sortOptions:{ sortFunction: PD.sortBugIdLinks } },
- { key:"count", label:"Count", sortable:true },
- { key:"bug_status", label:"Status", sortable:true },
- { key:"version", label:"Version", sortable:true },
- { key:"component", label:"Component", sortable:true },
- { key:"bug_severity", label:"Severity", sortable:true, sortOptions:{ sortFunction: PD.sortBugSeverity } },
- { key:"Summary", label:"Summary", sortable:false },
-];
-PD.fields = [
- { key:"id" },
- { key:"count", parser:"number" },
- { key:"bug_status" },
- { key:"version" },
- { key:"component" },
- { key:"bug_severity" },
- { key:"Summary" }
-];
-PD.addStatListener("duplicate_counts", "duplicate_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
--->
+ PD.duplicates = [
+ [% FOREACH bug = by_duplicate %]
+ {
+ id: '[% bug.id FILTER js %]',
+ count: '[% bug.dupe_count FILTER js %]',
+ status: '[% bug.status FILTER js %]',
+ version: '[% bug.version FILTER js %]',
+ component: '[% bug.component FILTER js %]',
+ severity: '[% bug.severity FILTER js %]',
+ summary: '[% bug.summary FILTER js %]'
+ },
+ [% END %]
+ ];
</script>
<h3>Most duplicated [% terms.bugs %]</h3>
[% IF by_duplicate.size %]
<b>[% by_duplicate.size FILTER html %]&nbsp;[% terms.Bugs %] Found</b>
- <div class="yui-skin-sam">
- <div id="duplicate_counts">
- <table id="duplicate_counts_table" cellspacing="3" cellpadding="0" border="0" width="100%">
- <thead>
- <tr bgcolor="#CCCCCC">
- [% FOREACH column = [ "ID", "Dupe Count", "Status", "Version"
- "Component", "Severity" "Summary" ] %]
-
- <th>[% column FILTER html %]</th>
- [% END %]
- </tr>
- </thead>
- <tbody>
- [% FOREACH bug = by_duplicate %]
- [% count = loop.count() %]
- <tr class="[%+ count % 2 == 1 ? "bz_row_odd" : "bz_row_even" -%]">
- <td align="center"><a href="[% urlbase FILTER none %]show_bug.cgi?id=[% bug.id FILTER uri %]">
- [% bug.id FILTER html %]</a></td>
- <td align="center">[% bug.dupe_count FILTER html %]</td>
- <td align="center">[% bug.status FILTER html %]</td>
- <td align="center">[% bug.version FILTER html %]</td>
- <td align="center">[% bug.component FILTER html %]</td>
- <td align="center">[% bug.severity FILTER html %]</td>
- <td>[% bug.summary FILTER html %]</td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+ <div class="yui3-skin-sam">
+ <div id="duplicates"></div>
</div>
[% ELSE %]
<b>No duplicate [% terms.bugs %] found.</b>
diff --git a/extensions/ProductDashboard/template/en/default/pages/productdashboard/popularity.html.tmpl b/extensions/ProductDashboard/template/en/default/pages/productdashboard/popularity.html.tmpl
index 5ecad3426..933f26c81 100644
--- a/extensions/ProductDashboard/template/en/default/pages/productdashboard/popularity.html.tmpl
+++ b/extensions/ProductDashboard/template/en/default/pages/productdashboard/popularity.html.tmpl
@@ -11,64 +11,27 @@
</style>
<script type="text/javascript">
-<!--
-PD.column_defs = [
- { key:"id", label:"ID", sortable:true, sortOptions:{ sortFunction: PD.sortBugIdLinks } },
- { key:"count", label:"Count", sortable:true },
- { key:"bug_status", label:"Status", sortable:true },
- { key:"version", label:"Version", sortable:true },
- { key:"component", label:"Component", sortable:true },
- { key:"bug_severity", label:"Severity", sortable:true, sortOptions:{ sortFunction: PD.sortBugSeverity } },
- { key:"Summary", label:"Summary", sortable:false },
-];
-PD.fields = [
- { key:"id" },
- { key:"count", parser:"number" },
- { key:"bug_status" },
- { key:"version" },
- { key:"component" },
- { key:"bug_severity" },
- { key:"Summary" }
-];
-PD.addStatListener("popularity_counts", "popularity_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
--->
+ PD.popularity = [
+ [% FOREACH bug = by_popularity %]
+ {
+ id: '[% bug.id FILTER js %]',
+ count: '[% bug.votes FILTER js %]',
+ status: '[% bug.status FILTER js %]',
+ version: '[% bug.version FILTER js %]',
+ component: '[% bug.component FILTER js %]',
+ severity: '[% bug.severity FILTER js %]',
+ summary: '[% bug.summary FILTER js %]'
+ },
+ [% END %]
+ ];
</script>
<h3>Most voted on [% terms.bugs %]</h3>
[% IF by_popularity.size %]
<b>[% by_popularity.size FILTER html %]&nbsp;[% terms.Bugs %] Found</b>
- <div class="yui-skin-sam">
- <div id="popularity_counts">
- <table id="popularity_counts_table" cellspacing="3" cellpadding="0" border="0" width="100%">
- <thead>
- <tr bgcolor="#CCCCCC">
- [% FOREACH column = [ "ID", "Count", "Status", "Version"
- "Component", "Severity" "Summary" ] %]
-
- <th>[% column FILTER html %]</th>
- [% END %]
- </tr>
- </thead>
- <tbody>
- [% FOREACH bug = by_popularity %]
- [% count = loop.count() %]
- <tr class="[%+ count % 2 == 1 ? "bz_row_odd" : "bz_row_even" -%]">
- <td align="center"><a href="[% urlbase FILTER none %]show_bug.cgi?id=[% bug.id FILTER uri %]">
- [% bug.id FILTER html %]</a></td>
- <td align="center">[% bug.votes FILTER html %]</td>
- <td align="center">[% bug.status FILTER html %]</td>
- <td align="center">[% bug.version FILTER html %]</td>
- <td align="center">[% bug.component FILTER html %]</td>
- <td align="center">[% bug.severity FILTER html %]</td>
- <td>[% bug.summary FILTER html %]</td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+ <div class="yui3-skin-sam">
+ <div id="popularity"></div>
</div>
[% ELSE %]
<b>No [% terms.bugs %] found.</b>
diff --git a/extensions/ProductDashboard/template/en/default/pages/productdashboard/recents.html.tmpl b/extensions/ProductDashboard/template/en/default/pages/productdashboard/recents.html.tmpl
index 919a7da97..18227aec2 100644
--- a/extensions/ProductDashboard/template/en/default/pages/productdashboard/recents.html.tmpl
+++ b/extensions/ProductDashboard/template/en/default/pages/productdashboard/recents.html.tmpl
@@ -6,36 +6,36 @@
# defined by the Mozilla Public License, v. 2.0.
#%]
-<style>
- .yui-skin-sam .yui-dt table {width:100%;}
-</style>
-
<script type="text/javascript">
-<!--
-PD.column_defs = [
- { key:"id", label:"ID", sortable:true, sortOptions:{ sortFunction: PD.sortBugIdLinks } },
- { key:"bug_status", label:"Status", sortable:true },
- { key:"version", label:"Version", sortable:true },
- { key:"component", label:"Component", sortable:true },
- { key:"bug_severity", label:"Severity", sortable:true, sortOptions:{ sortFunction: PD.sortBugSeverity } },
- { key:"Summary", label:"Summary", sortable:false },
-];
-PD.fields = [
- { key:"id" },
- { key:"bug_status" },
- { key:"version" },
- { key:"component" },
- { key:"bug_severity" },
- { key:"Summary" }
-];
-PD.addStatListener("recently_opened", "recently_opened_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
+ PD.recents = {};
+
+ // Recently opened bugs
+ PD.recents.opened = [
+ [% FOREACH bug = recently_opened %]
+ {
+ id: '[% bug.id FILTER js %]',
+ status: '[% bug.status FILTER js %]',
+ version: '[% bug.version FILTER js %]',
+ component: '[% bug.component FILTER js %]',
+ severity: '[% bug.severity FILTER js %]',
+ summary: '[% bug.summary FILTER js %]'
+ },
+ [% END %]
+ ];
-PD.addStatListener("recently_closed", "recently_closed_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
--->
+ // Recently closed bugs
+ PD.recents.closed = [
+ [% FOREACH bug = recently_closed %]
+ {
+ id: '[% bug.id FILTER js %]',
+ status: '[% bug.status FILTER js %]',
+ version: '[% bug.version FILTER js %]',
+ component: '[% bug.component FILTER js %]',
+ severity: '[% bug.severity FILTER js %]',
+ summary: '[% bug.summary FILTER js %]'
+ },
+ [% END %]
+ ];
</script>
<h3>Most recently opened and closed [% terms.bugs %]</h3>
@@ -50,7 +50,7 @@ PD.addStatListener("recently_closed", "recently_closed_table",
<button type="button" class="calendar_button"
id="button_calendar_date_from"
onclick="showCalendar('date_from')">
- <span>Calendar</span>
+ <span>Calendar</span>
</button>
<span id="con_calendar_date_from"></span>
to
@@ -60,7 +60,7 @@ PD.addStatListener("recently_closed", "recently_closed_table",
<button type="button" class="calendar_button"
id="button_calendar_date_to"
onclick="showCalendar('date_to')">
- <span>Calendar</span>
+ <span>Calendar</span>
</button>
<span id="con_calendar_date_to"></span>
<script type="text/javascript">
@@ -78,58 +78,10 @@ PD.addStatListener("recently_closed", "recently_closed_table",
<div class="yui-skin-sam">
<a name="recently_opened"></a>
<b>[% recently_opened.size FILTER html %] Recently Opened [% terms.Bugs %]</b>
- <div id="recently_opened">
- <table id="recently_opened_table" cellspacing="3" cellpadding="0" border="0" width="100%">
- <thead>
- <tr bgcolor="#CCCCCC">
- [% FOREACH column = [ "ID", "Status", "Version", "Component", "Severity" "Summary" ] %]
- <th>[% column FILTER html %]</th>
- [% END %]
- </tr>
- </thead>
- <tbody>
- [% FOREACH bug = recently_opened %]
- [% count = loop.count() %]
- <tr class="[%+ count % 2 == 1 ? "bz_row_odd" : "bz_row_even" -%]">
- <td align="center"><a href="[% urlbase FILTER none %]show_bug.cgi?id=[% bug.id FILTER uri %]">
- [% bug.id FILTER html %]</a></td>
- <td align="center">[% bug.status FILTER html %]</td>
- <td align="center">[% bug.version FILTER html %]</td>
- <td align="center">[% bug.component FILTER html %]</td>
- <td align="center">[% bug.severity FILTER html %]</td>
- <td>[% bug.summary FILTER html %]</td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+ <div id="recently_opened"></div>
<br>
<a name="recently_closed"></a>
<b>[% recently_closed.size FILTER html %] Recently Closed [% terms.Bugs %]</b>
(<a href="#top">back to top</a>)
- <div id="recently_closed">
- <table id="recently_closed_table" cellspacing="3" cellpadding="0" border="0" width="100%">
- <thead>
- <tr bgcolor="#CCCCCC">
- [% FOREACH column = [ "ID", "Status", "Version", "Component", "Severity" "Summary" ] %]
- <th>[% column FILTER html %]</th>
- [% END %]
- </tr>
- </thead>
- <tbody>
- [% FOREACH bug = recently_closed %]
- [% count = loop.count() %]
- <tr class="[%+ count % 2 == 1 ? "bz_row_odd" : "bz_row_even" -%]">
- <td align="center"><a href="[% urlbase FILTER none %]show_bug.cgi?id=[% bug.id FILTER uri %]">
- [% bug.id FILTER html %]</a></td>
- <td align="center">[% bug.status FILTER html %]</td>
- <td align="center">[% bug.version FILTER html %]</td>
- <td align="center">[% bug.component FILTER html %]</td>
- <td align="center">[% bug.severity FILTER html %]</td>
- <td>[% bug.summary FILTER html %]</td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+ <div id="recently_closed"></div>
</div>
diff --git a/extensions/ProductDashboard/template/en/default/pages/productdashboard/roadmap.html.tmpl b/extensions/ProductDashboard/template/en/default/pages/productdashboard/roadmap.html.tmpl
index 1597b7a36..48f96499d 100644
--- a/extensions/ProductDashboard/template/en/default/pages/productdashboard/roadmap.html.tmpl
+++ b/extensions/ProductDashboard/template/en/default/pages/productdashboard/roadmap.html.tmpl
@@ -8,50 +8,20 @@
<script type="text/javascript">
<!--
-PD.column_defs = [
- { key:"milestone", label:"Milestone", sortable:true },
- { key:"percentage complete", label:"Percentage Complete", sortable:false },
- { key:"links", label:"Links", sortable:false },
-];
-PD.fields = [
- { key:"milestone" },
- { key:"percentage complete" },
- { key:"links" }
-];
-PD.addStatListener("bug_milestones", "bug_milestones_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
+ PD.roadmap = [
+ [% FOREACH milestone = by_roadmap %]
+ {
+ name: '[% milestone.name FILTER js %]',
+ percentage: '[% milestone.percentage FILTER js %]',
+ link: '<a href="[% milestone.link_closed FILTER html %]">[% milestone.closed_bugs FILTER html %]</a> of <a href="[% milestone.link_total FILTER html %]"> [% milestone.total_bugs FILTER html %]</a> bugs have been closed',
+ },
+ [% END %]
+ ];
-->
</script>
<h3>Percentage of [% terms.bug %] closure per milestone</h3>
-<div class="yui-skin-sam">
-<div id="bug_milestones">
- <table id="bug_milestones_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Milestone</th>
- <th>Percentage Complete</th>
- <th>Links</th>
- </tr>
- </thead>
- <tbody>
- [% FOREACH milestone = by_roadmap %]
- <tr>
- <td>[% milestone.name FILTER html %]</td>
- <td width="70%">
- [% INCLUDE bar_graph count = milestone.closed_bugs full_bug_count = milestone.total_bugs %]
- </td>
- <td>
- <a href="[% milestone.link_closed FILTER html %]">
- [% milestone.closed_bugs FILTER html %]</a>&nbsp;of&nbsp;
- <a href="[% milestone.link_total FILTER html %]">
- [% milestone.total_bugs FILTER html %]</a>&nbsp;bugs have been closed
- </td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+<div class="yui3-skin-sam">
+ <div id="bug_milestones"></div>
</div>
diff --git a/extensions/ProductDashboard/template/en/default/pages/productdashboard/summary.html.tmpl b/extensions/ProductDashboard/template/en/default/pages/productdashboard/summary.html.tmpl
index a7398c823..e454ee8e8 100644
--- a/extensions/ProductDashboard/template/en/default/pages/productdashboard/summary.html.tmpl
+++ b/extensions/ProductDashboard/template/en/default/pages/productdashboard/summary.html.tmpl
@@ -6,34 +6,79 @@
# defined by the Mozilla Public License, v. 2.0.
#%]
-<script type="text/javascript">
-<!--
-PD.column_defs = [
- { key:"name", label:"Name", sortable:true },
- { key:"count", label:"Count", sortable:true },
- { key:"percentage", label:"Percentage", sortable:false }
-];
-PD.fields = [
- { key:"name" },
- { key:"count", parser:"number" },
- { key:"percentage" }
-];
-PD.addStatListener("bug_counts", "bug_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
-PD.addStatListener("status_counts", "status_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
-PD.addStatListener("priority_counts", "priority_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
-PD.addStatListener("severity_counts", "severity_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
-PD.addStatListener("assignee_counts", "assignee_counts_table",
- PD.column_defs, PD.fields,
- { paginator: new YAHOO.widget.Paginator({ rowsPerPage: 25, alwaysVisible: false }) });
--->
+<script>
+ PD.summary = {};
+
+ // Bug counts
+ PD.summary.bug_counts = [
+ {
+ name: "Total [% terms.Bugs %]",
+ count: [% total_bugs || 0 FILTER js %],
+ percentage: 100,
+ link: '<a href="[% bug_link_all FILTER js %]">Link</a>',
+ },
+ {
+ name: "Open [% terms.Bugs %]",
+ count: [% total_open_bugs || 0 FILTER js %],
+ percentage: [% open_bugs_percentage FILTER js %],
+ link: '<a href="[% bug_link_open FILTER js %]">Link</a>',
+ },
+ {
+ name: "Closed [% terms.Bugs %]",
+ count: [% total_closed_bugs || 0 FILTER js %],
+ percentage: [% closed_bugs_percentage FILTER js %],
+ link: '<a href="[% bug_link_closed FILTER js %]">Link</a>',
+ }
+ ];
+
+ // Status counts
+ PD.summary.status_counts = [
+ [% FOREACH col = by_status %]
+ [% NEXT IF col.0 == 'CLOSED' %]
+ {
+ name: "[% col.0 FILTER js %]",
+ count: [% col.1 || 0 FILTER js %],
+ percentage: [% col.2 || 0 FILTER js %],
+ link: '<a href="[% bug_link_all FILTER js %]&amp;bug_status=[% col.0 FILTER uri FILTER js %]">Link</a>'
+ },
+ [% END %]
+ ];
+
+ // Priority counts
+ PD.summary.priority_counts = [
+ [% FOREACH col = by_priority %]
+ {
+ name: "[% col.0 FILTER js %]",
+ count: [% col.1 || 0 FILTER js %],
+ percentage: [% col.2 || 0 FILTER js %],
+ link: '<a href="[% bug_link_all FILTER js %]&amp;priority=[% col.0 FILTER uri FILTER js %]">Link</a>'
+ },
+ [% END %]
+ ];
+
+ // Severity counts
+ PD.summary.severity_counts = [
+ [% FOREACH col = by_severity %]
+ {
+ name: "[% col.0 FILTER js %]",
+ count: [% col.1 || 0 FILTER js %],
+ percentage: [% col.2 || 0 FILTER js %],
+ link: '<a href="[% bug_link_all FILTER js %]&amp;bug_severity=[% col.0 FILTER uri FILTER js %]">Link</a>'
+ },
+ [% END %]
+ ];
+
+ // Assignee counts
+ PD.summary.assignee_counts = [
+ [% FOREACH col = by_assignee %]
+ {
+ name: "[% IF user.id %][% col.0.email FILTER js %][% ELSE %][% col.0.realname || 'No Name' FILTER js %][% END %]",
+ count: [% col.1 || 0 FILTER js %],
+ percentage: [% col.2 || 0 FILTER js %],
+ link: '[% IF user.id %]<a href="[% bug_link FILTER js %]&amp;emailassigned_to1=1&amp;emailtype1=exact&amp;email1=[% col.0.email FILTER uri FILTER js %]">Link</a>[% END %]'
+ },
+ [% END %]
+ ];
</script>
<h3>Summary of [% terms.bug %] counts</h3>
@@ -50,168 +95,28 @@ PD.addStatListener("assignee_counts", "assignee_counts_table",
<a href="#assignee">Assignee</a>
</p>
-<div class="yui-skin-sam">
+<div class="yui3-skin-sam">
<a name="counts"></a>
<b>[% terms.Bug %] Counts</b>
- <div id="bug_counts">
- <table id="bug_counts_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Name</th>
- <th>Count</th>
- <th>Percentage</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td><a href="[% bug_link_all FILTER html %]">Total [% terms.Bugs %]</a></td>
- <td>[% total_bugs FILTER html %]</td>
- <td>&nbsp;</td>
- </tr>
- <tr>
- <td><a href="[% bug_link_open FILTER html %]">Open [% terms.Bugs %]</a></td>
- <td>[% total_open_bugs FILTER html %]</td>
- <td width="70%">
- [% INCLUDE bar_graph count = total_open_bugs full_bug_count = total_bugs %]
- </td>
- </tr>
- <tr>
- <td><a href="[% bug_link_closed FILTER html %]">Closed [% terms.Bugs %]</a></td>
- <td>[% total_closed_bugs FILTER html %]</td>
- <td width="70%">
- [% INCLUDE bar_graph count = total_closed_bugs full_bug_count = total_bugs %]
- </td>
- </tr>
- </tbody>
- </table>
- </div>
+ <div id="bug_counts"></div>
<br>
<a name="status"></a>
<b>Status</b>
(<a href="#top">back to top</a>)
- <div id="status_counts">
- <table id="status_counts_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Name</th>
- <th>Count</th>
- <th>Percentage</th>
- </tr>
- </thead>
- <tbody>
- [% FOREACH col = by_status %]
- [% NEXT IF col.0 == 'CLOSED' %]
- <tr>
- <td>
- <a href="[% bug_link_all FILTER html %]&amp;bug_status=[% col.0 FILTER uri %]">
- [% col.0 FILTER html %]</a>
- </td>
- <td>
- [% col.1 FILTER html %]
- </td>
- <td width="70%">
- [% INCLUDE bar_graph count = col.1 %]
- </td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+ <div id="status_counts"></div>
<br>
<a name="priority"></a>
<b>Priority</b>
(<a href="#top">back to top</a>)
- <div id="priority_counts">
- <table id="priority_counts_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Name</th>
- <th>Count</th>
- <th>Percentage</th>
- </tr>
- </thead>
- </tbody>
- [% FOREACH col = by_priority %]
- <tr>
- <td>
- <a href="[% bug_link FILTER html %]&amp;priority=[% col.0 FILTER uri %]">
- [% col.0 FILTER html %]</a>
- </td>
- <td>
- [% col.1 FILTER html %]
- </td>
- <td width="70%">
- [% INCLUDE bar_graph count = col.1 %]
- </td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+ <div id="priority_counts"></div>
<br>
<a name="severity"></a>
<b>Severity</b>
(<a href="#top">back to top</a>)
- <div id="severity_counts">
- <table id="severity_counts_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Name</th>
- <th>Count</th>
- <th>Percentage</th>
- </tr>
- </thead>
- <tbody>
- [% FOREACH col = by_severity %]
- <tr>
- <td>
- <a href="[% bug_link FILTER html %]&amp;bug_severity=[% col.0 FILTER uri %]">
- [% col.0 FILTER html %]</a>
- </td>
- <td align="right">
- [% col.1 FILTER html %]
- </td>
- <td width="70%">
- [% INCLUDE bar_graph count = col.1 %]
- </td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+ <div id="severity_counts"></div>
<br>
<a name="assignee"></a>
<b>Assignee</b>
(<a href="#top">back to top</a>)
- <div id="assignee_counts">
- <table id="assignee_counts_table" border="0" cellspacing="3" cellpadding="0">
- <thead>
- <tr>
- <th>Name</th>
- <th>Count</th>
- <th>Percentage</th>
- </tr>
- </thead>
- <tbody>
- [% FOREACH col = by_assignee %]
- <tr>
- <td>
- [% IF user.id %]
- <a href="[% bug_link FILTER html %]&amp;emailassigned_to1=1&amp;emailtype1=exact&amp;email1=[% col.0.email FILTER uri %]">
- [% col.0.email FILTER html %]</a>
- [% ELSE %]
- [% col.0.realname || "No Name" FILTER html %]
- [% END %]
- </td>
- <td>
- [% col.1 FILTER html %]
- </td>
- <td width="70%">
- [% INCLUDE bar_graph count = col.1 %]
- </td>
- </tr>
- [% END %]
- </tbody>
- </table>
- </div>
+ <div id="assignee_counts"></div>
</div>
diff --git a/extensions/ProductDashboard/web/js/components.js b/extensions/ProductDashboard/web/js/components.js
new file mode 100644
index 000000000..538b15457
--- /dev/null
+++ b/extensions/ProductDashboard/web/js/components.js
@@ -0,0 +1,75 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0.
+ */
+
+YUI({
+ base: 'js/yui3/',
+ combine: false
+}).use("datatable", "datatable-sort", function(Y) {
+ if (typeof PD.updated_recently != 'undefined') {
+ var columns = [
+ { key:"id", label:"ID", sortable:true, allowHTML: true,
+ formatter: '<a href="show_bug.cgi?id={value}" target="_blank">{value}</a>' },
+ { key:"bug_status", label:"Status", sortable:true },
+ { key:"version", label:"Version", sortable:true },
+ { key:"component", label:"Component", sortable:true },
+ { key:"severity", label:"Severity", sortable:true },
+ { key:"summary", label:"Summary", sortable:false },
+ ];
+
+ var updatedRecentlyDataTable = new Y.DataTable({
+ columns: columns,
+ data: PD.updated_recently
+ });
+ updatedRecentlyDataTable.render("#updated_recently");
+
+ if (typeof PD.past_due != 'undefined') {
+ var pastDueDataTable = new Y.DataTable({
+ columns: columns,
+ data: PD.past_due
+ });
+ pastDueDataTable.render('#past_due');
+ }
+ }
+
+ if (typeof PD.component_counts != 'undefined') {
+ var summary_url = '<a href="page.cgi?id=productdashboard.html&amp;product=' +
+ encodeURIComponent(PD.product_name) + '&bug_status=' +
+ encodeURIComponent(PD.bug_status) + '&tab=components';
+
+ var columns = [
+ { key:"name", label:"Name", sortable:true, allowHTML: true,
+ formatter: summary_url + '&component={value}">{value}</a>' },
+ { key:"count", label:"Count", sortable:true },
+ { key:"percentage", label:"Percentage", sortable:false, allowHTML: true,
+ formatter: '<div class="percentage"><div class="bar" style="width:{value}%"></div><div class="percent">{value}%</div></div>' },
+ { key:"link", label:"Link", sortable:false, allowHTML: true }
+ ];
+
+ var componentsDataTable = new Y.DataTable({
+ columns: columns,
+ data: PD.component_counts
+ });
+ componentsDataTable.render("#component_counts");
+
+ columns[0].formatter = summary_url + '&version={value}">{value}</a>';
+ var versionsDataTable = new Y.DataTable({
+ columns: columns,
+ data: PD.version_counts
+ });
+ versionsDataTable.render('#version_counts');
+
+ if (typeof PD.milestone_counts != 'undefined') {
+ columns[0].formatter = summary_url + '&target_milestone={value}">{value}</a>';
+ var milestonesDataTable = new Y.DataTable({
+ columns: columns,
+ data: PD.milestone_counts
+ });
+ milestonesDataTable.render('#milestone_counts');
+ }
+ }
+});
diff --git a/extensions/ProductDashboard/web/js/duplicates.js b/extensions/ProductDashboard/web/js/duplicates.js
new file mode 100644
index 000000000..5e3193a65
--- /dev/null
+++ b/extensions/ProductDashboard/web/js/duplicates.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0.
+ */
+
+YUI({
+ base: 'js/yui3/',
+ combine: false
+}).use("datatable", "datatable-sort", function (Y) {
+ var column_defs = [
+ { key:"id", label:"ID", sortable:true, allowHTML: true,
+ formatter: '<a href="show_bug.cgi?id={value}" target="_blank">{value}</a>' },
+ { key:"count", label:"Count", sortable:true },
+ { key:"status", label:"Status", sortable:true },
+ { key:"version", label:"Version", sortable:true },
+ { key:"component", label:"Component", sortable:true },
+ { key:"severity", label:"Severity", sortable:true },
+ { key:"summary", label:"Summary", sortable:false },
+ ];
+
+ var duplicatesDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.duplicates
+ }).render('#duplicates');
+});
diff --git a/extensions/ProductDashboard/web/js/popularity.js b/extensions/ProductDashboard/web/js/popularity.js
new file mode 100644
index 000000000..b78b67867
--- /dev/null
+++ b/extensions/ProductDashboard/web/js/popularity.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0.
+ */
+
+YUI({
+ base: 'js/yui3/',
+ combine: false
+}).use("datatable", "datatable-sort", function (Y) {
+ var column_defs = [
+ { key:"id", label:"ID", sortable:true, allowHTML: true,
+ formatter: '<a href="show_bug.cgi?id={value}" target="_blank">{value}</a>' },
+ { key:"count", label:"Count", sortable:true },
+ { key:"status", label:"Status", sortable:true },
+ { key:"version", label:"Version", sortable:true },
+ { key:"component", label:"Component", sortable:true },
+ { key:"severity", label:"Severity", sortable:true },
+ { key:"summary", label:"Summary", sortable:false },
+ ];
+
+ var popularityDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.popularity
+ }).render('#popularity');
+});
diff --git a/extensions/ProductDashboard/web/js/productdashboard.js b/extensions/ProductDashboard/web/js/productdashboard.js
deleted file mode 100644
index 56bc451ff..000000000
--- a/extensions/ProductDashboard/web/js/productdashboard.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * This Source Code Form is "Incompatible With Secondary Licenses", as
- * defined by the Mozilla Public License, v. 2.0.
- */
-
-YAHOO.namespace('ProductDashboard');
-
-var PD = YAHOO.ProductDashboard;
-
-PD.addStatListener = function (div_name, table_name, column_defs, fields, options) {
- YAHOO.util.Event.addListener(window, "load", function() {
- YAHOO.example.StatsFromMarkup = new function() {
- this.myDataSource = new YAHOO.util.DataSource(YAHOO.util.Dom.get(table_name));
- this.myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
- this.myDataSource.responseSchema = { fields:fields };
- this.myDataTable = new YAHOO.widget.DataTable(div_name, column_defs, this.myDataSource, options);
- this.myDataTable.subscribe("rowMouseoverEvent", this.myDataTable.onEventHighlightRow);
- this.myDataTable.subscribe("rowMouseoutEvent", this.myDataTable.onEventUnhighlightRow);
- };
- });
-}
-
-// Custom sort handler to sort by bug id inside an anchor tag
-PD.sortBugIdLinks = function (a, b, desc) {
- // Deal with empty values
- if (!YAHOO.lang.isValue(a)) {
- return (!YAHOO.lang.isValue(b)) ? 0 : 1;
- }
- else if(!YAHOO.lang.isValue(b)) {
- return -1;
- }
- // Now we need to pull out the ID text and convert to Numbers
- // First we do 'a'
- var container = document.createElement("bug_id_link");
- container.innerHTML = a.getData("id");
- var anchors = container.getElementsByTagName("a");
- var text = anchors[0].textContent;
- if (text === undefined) text = anchors[0].innerText;
- var new_a = new Number(text);
- // Then we do 'b'
- container.innerHTML = b.getData("id");
- anchors = container.getElementsByTagName("a");
- text = anchors[0].textContent;
- if (text == undefined) text = anchors[0].innerText;
- var new_b = new Number(text);
-
- if (!desc) {
- return YAHOO.util.Sort.compare(new_a, new_b);
- }
- else {
- return YAHOO.util.Sort.compare(new_b, new_a);
- }
-}
-
-// Custom sort handler for bug severities
-PD.sortBugSeverity = function (a, b, desc) {
- // Deal with empty values
- if (!YAHOO.lang.isValue(a)) {
- return (!YAHOO.lang.isValue(b)) ? 0 : 1;
- }
- else if(!YAHOO.lang.isValue(b)) {
- return -1;
- }
-
- var new_a = new Number(severities[YAHOO.lang.trim(a.getData('bug_severity'))]);
- var new_b = new Number(severities[YAHOO.lang.trim(b.getData('bug_severity'))]);
-
- if (!desc) {
- return YAHOO.util.Sort.compare(new_a, new_b);
- }
- else {
- return YAHOO.util.Sort.compare(new_b, new_a);
- }
-}
-
-// Custom sort handler for bug priorities
-PD.sortBugPriority = function (a, b, desc) {
- // Deal with empty values
- if (!YAHOO.lang.isValue(a)) {
- return (!YAHOO.lang.isValue(b)) ? 0 : 1;
- }
- else if(!YAHOO.lang.isValue(b)) {
- return -1;
- }
-
- var new_a = new Number(priorities[YAHOO.lang.trim(a.getData('priority'))]);
- var new_b = new Number(priorities[YAHOO.lang.trim(b.getData('priority'))]);
-
- if (!desc) {
- return YAHOO.util.Sort.compare(new_a, new_b);
- }
- else {
- return YAHOO.util.Sort.compare(new_b, new_a);
- }
-}
diff --git a/extensions/ProductDashboard/web/js/recents.js b/extensions/ProductDashboard/web/js/recents.js
new file mode 100644
index 000000000..84e1758b6
--- /dev/null
+++ b/extensions/ProductDashboard/web/js/recents.js
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0.
+ */
+
+YUI({
+ base: 'js/yui3/',
+ combine: false
+}).use("datatable", "datatable-sort", function (Y) {
+ var column_defs = [
+ { key:"id", label:"ID", sortable:true, allowHTML: true,
+ formatter: '<a href="show_bug.cgi?id={value}" target="_blank">{value}</a>' },
+ { key:"status", label:"Status", sortable:true },
+ { key:"version", label:"Version", sortable:true },
+ { key:"component", label:"Component", sortable:true },
+ { key:"severity", label:"Severity", sortable:true },
+ { key:"summary", label:"Summary", sortable:false },
+ ];
+
+ var recentlyOpenedDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.recents.opened
+ }).render('#recently_opened');
+
+ var recentlyClosedDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.recents.closed
+ }).render('#recently_closed');
+});
diff --git a/extensions/ProductDashboard/web/js/roadmap.js b/extensions/ProductDashboard/web/js/roadmap.js
new file mode 100644
index 000000000..1bef5b091
--- /dev/null
+++ b/extensions/ProductDashboard/web/js/roadmap.js
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0.
+ */
+
+YUI({
+ base: 'js/yui3/',
+ combine: false
+}).use("datatable", "datatable-sort", function (Y) {
+ var column_defs = [
+ { key: 'name', label: 'Name', sortable: true },
+ { key: 'percentage', label: 'Percentage', sortable: false, allowHTML: true,
+ formatter: '<div class="percentage"><div class="bar" style="width:{value}%"></div><div class="percent">{value}%</div></div>' },
+ { key: 'link', label: 'Links', allowHTML: true, sortable: false }
+ ];
+
+ var roadmapDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.roadmap,
+ }).render('#bug_milestones');
+});
diff --git a/extensions/ProductDashboard/web/js/summary.js b/extensions/ProductDashboard/web/js/summary.js
new file mode 100644
index 000000000..59d000d7b
--- /dev/null
+++ b/extensions/ProductDashboard/web/js/summary.js
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0.
+ */
+
+YUI({
+ base: 'js/yui3/',
+ combine: false
+}).use("datatable", "datatable-sort", function (Y) {
+ var column_defs = [
+ { key: 'name', label: 'Name', sortable: true },
+ { key: 'count', label: 'Count', sortable: true },
+ { key: 'percentage', label: 'Percentage', sortable: true, allowHTML: true,
+ formatter: '<div class="percentage"><div class="bar" style="width:{value}%"></div><div class="percent">{value}%</div></div>' },
+ { key: 'link', label: 'Link', allowHTML: true }
+ ];
+
+ var bugsCountDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.summary.bug_counts
+ }).render('#bug_counts');
+
+ var statusCountsDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.summary.status_counts
+ }).render('#status_counts');
+
+ var priorityCountsDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.summary.priority_counts
+ }).render('#priority_counts');
+
+ var severityCountsDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.summary.severity_counts
+ }).render('#severity_counts');
+
+ var assigneeCountsDataTable = new Y.DataTable({
+ columns: column_defs,
+ data: PD.summary.assignee_counts
+ }).render('#assignee_counts');
+});
diff --git a/extensions/ProductDashboard/web/styles/productdashboard.css b/extensions/ProductDashboard/web/styles/productdashboard.css
index 1e821fa11..c0c45cf38 100644
--- a/extensions/ProductDashboard/web/styles/productdashboard.css
+++ b/extensions/ProductDashboard/web/styles/productdashboard.css
@@ -23,3 +23,23 @@
padding-bottom: 5px;
margin-bottom: 10px;
}
+
+.percentage {
+ position:relative;
+ width: 200px;
+ border: 1px solid rgb(203, 203, 203);
+ position: relative;
+ padding: 3px;
+}
+
+.bar{
+ background-color: #00ff00;
+ height: 20px;
+}
+
+.percent{
+ position: absolute;
+ display: inline-block;
+ top: 3px;
+ left: 48%;
+}