implement Inventory page feature
This commit is contained in:
@@ -10,6 +10,7 @@ use HTML::Entities (); # to ensure dependency is met
|
||||
use Netdisco::Web::AuthN;
|
||||
use Netdisco::Web::Search;
|
||||
use Netdisco::Web::Device;
|
||||
use Netdisco::Web::Inventory;
|
||||
|
||||
hook 'before_template' => sub {
|
||||
my $tokens = shift;
|
||||
|
||||
@@ -33,7 +33,7 @@ post '/login' => sub {
|
||||
}
|
||||
}
|
||||
}
|
||||
redirect '/?failed=1';
|
||||
redirect uri_for('/', {failed => 1});
|
||||
};
|
||||
|
||||
get '/logout' => sub {
|
||||
|
||||
23
Netdisco/lib/Netdisco/Web/Inventory.pm
Normal file
23
Netdisco/lib/Netdisco/Web/Inventory.pm
Normal file
@@ -0,0 +1,23 @@
|
||||
package Netdisco::Web::Inventory;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use Dancer::Plugin::DBIC;
|
||||
|
||||
get '/inventory' => sub {
|
||||
template 'inventory', {
|
||||
models => scalar schema('netdisco')->resultset('Device')->search({},{
|
||||
select => [ 'vendor', 'model', { count => 'ip' } ],
|
||||
as => [qw/vendor model count/],
|
||||
group_by => [qw/vendor model/],
|
||||
order_by => [{-asc => 'vendor'}, {-desc => 'count'}, {-asc => 'model'}],
|
||||
}),
|
||||
releases => scalar schema('netdisco')->resultset('Device')->search({},{
|
||||
select => [ 'os', 'os_ver', { count => 'ip' } ],
|
||||
as => [qw/os os_ver count/],
|
||||
group_by => [qw/os os_ver/],
|
||||
order_by => [{-asc => 'os'}, {-desc => 'count'}, {-asc => 'os_ver'}],
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
true;
|
||||
@@ -185,10 +185,12 @@ get '/search' => sub {
|
||||
schema('netdisco')->resultset('Device')->get_distinct('vendor')
|
||||
]);
|
||||
|
||||
params->{'q'} ||= '_'; # FIXME a cheat Inventory, for now
|
||||
|
||||
my $q = param('q');
|
||||
if ($q and not param('tab')) {
|
||||
if (not param('tab')) {
|
||||
if (not $q) {
|
||||
redirect uri_for('/');
|
||||
}
|
||||
|
||||
# pick most likely tab for initial results
|
||||
if ($q =~ m/^\d+$/) {
|
||||
params->{'tab'} = 'vlan';
|
||||
|
||||
@@ -17,11 +17,16 @@ body {
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
/* on both main content and sidebar, hidden overflow is weird and wrong?! */
|
||||
/* on both main content and sidebar, default is hidden */
|
||||
.tab-content {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* ajax results should fill all available */
|
||||
.tab-content table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* various styles to adjust the hero box used for homepage + login */
|
||||
|
||||
@@ -38,6 +43,35 @@ body {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* styles for Reports */
|
||||
|
||||
/* from Bootstrap doc style sheet */
|
||||
.nd_show-grid [class*="span"] {
|
||||
background-color: cornsilk;
|
||||
text-align: center;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
min-height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* styles for Inventory */
|
||||
|
||||
#nd_dev_age_form {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.nd_inv_tbl_head {
|
||||
text-align: center;
|
||||
color: lightSlateGray;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* results table links */
|
||||
|
||||
|
||||
BIN
Netdisco/public/img/glyphicons-halflings-white.png
Normal file
BIN
Netdisco/public/img/glyphicons-halflings-white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
BIN
Netdisco/public/img/glyphicons-halflings.png
Normal file
BIN
Netdisco/public/img/glyphicons-halflings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
@@ -31,11 +31,6 @@
|
||||
<a class="close" data-dismiss="alert">×</a>
|
||||
Log in to the Demo with username "demo" and password "demo".
|
||||
</div>
|
||||
[% ELSE %]
|
||||
<div class="alert alert-success fade in">
|
||||
<a class="close" data-dismiss="alert">×</a>
|
||||
Hit <strong>Enter</strong> in the Search box to view the current Inventory (<em>temporary feature</em>).
|
||||
</div>
|
||||
[% END %]
|
||||
<div class="hero-unit">
|
||||
<h2>Welcome to Netdisco</h2>
|
||||
|
||||
62
Netdisco/views/inventory.tt
Normal file
62
Netdisco/views/inventory.tt
Normal file
@@ -0,0 +1,62 @@
|
||||
<div class="container">
|
||||
[% IF models.count %]
|
||||
<div class="row">
|
||||
<div class="span6">
|
||||
<h3 class="nd_inv_tbl_head">By Platform</h3>
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Vendor</th>
|
||||
<th>Model</th>
|
||||
<th>Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
[% FOREACH platform IN models.all %]
|
||||
<tr>
|
||||
<th>
|
||||
<a class="nd_stealthlink"
|
||||
href="[% uri_for('/search') %]?tab=device&matchall=on&vendor=[% platform.vendor %]">
|
||||
[% platform.vendor %]</a>
|
||||
</th>
|
||||
<th>
|
||||
<a class="nd_linkcell"
|
||||
href="[% uri_for('/search') %]?tab=device&matchall=on&model=[% platform.model %]">
|
||||
[% platform.model %]</a>
|
||||
</th>
|
||||
<th>[% platform.get_column('count') %]</th>
|
||||
</tr>
|
||||
[% END %]
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="span6">
|
||||
<h3 class="nd_inv_tbl_head">By Software Release</h3>
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>OS</th>
|
||||
<th>Version</th>
|
||||
<th>Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
[% FOREACH release IN releases.all %]
|
||||
<tr>
|
||||
<th>[% release.os %]</th>
|
||||
<th>
|
||||
<a class="nd_linkcell"
|
||||
href="[% uri_for('/search') %]?tab=device&matchall=on&os_ver=[% release.os_ver %]">
|
||||
[% release.os_ver %]</a>
|
||||
</th>
|
||||
<th>[% release.get_column('count') %]</th>
|
||||
</tr>
|
||||
[% END %]
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
[% ELSE %]
|
||||
<div class="span4 alert alert-info">No devices found. Do you need to run a Discover?</div>
|
||||
[% END %]
|
||||
</div>
|
||||
@@ -43,12 +43,13 @@
|
||||
<a class="brand" href="[% uri_for('/') %]">Netdisco</a>
|
||||
[% IF session.user %]
|
||||
<ul class="nav">
|
||||
<li class="active"><a href="[% uri_for('/') %]">Home</a></li>
|
||||
<li[% ' class="active"' IF vars('nav') == 'inventory' %]>
|
||||
<a href="[% uri_for('/inventory') %]">Inventory</a>
|
||||
</li>
|
||||
[% IF more_dd.size %]
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
More <b class="caret"></b>
|
||||
</a>
|
||||
More <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
[% FOREACH title IN more_dd.keys.sort %]
|
||||
<li><a href="[% uri_for(more_dd.$title) %]">[% title %]</a></li>
|
||||
@@ -59,14 +60,13 @@
|
||||
</ul>
|
||||
<form class="navbar-search pull-left" method="get" action="[% uri_for('/search') %]">
|
||||
<input placeholder="Find Anything" class="search-query span3" id="nq" name="q" type="text"/>
|
||||
<img id="navsearchgo" class="navbar_icon" src="[% uri_base %]/images/crystalclear_mag.png"/>
|
||||
<i id="navsearchgo" class="icon-search icon-white navbar_icon"></i>
|
||||
</form>
|
||||
<ul class="nav pull-right">
|
||||
<li class="nd_navbartext">Logged in as </li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
[% session.user %] <b class="caret"></b>
|
||||
</a>
|
||||
[% session.user %] <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
[% FOREACH item IN user_dd %]
|
||||
<li><a href="[% uri_for(item.link) %]">[% item.title %]</a></li>
|
||||
|
||||
31
Netdisco/views/report.tt
Normal file
31
Netdisco/views/report.tt
Normal file
@@ -0,0 +1,31 @@
|
||||
<div class="container">
|
||||
<div class="row nd_show-grid">
|
||||
<div class="span10 offset1">
|
||||
<form id="nd_dev_age_form" class="form-inline">
|
||||
Find Devices
|
||||
<select name="age_type" class="span2">
|
||||
<option value="first">First Discovered</option>
|
||||
<option value="last" selected="selected">Last Updated</option>
|
||||
</select>
|
||||
<select name="age_bool" class="span2">
|
||||
<option value="in">less than</option>
|
||||
<option value="not_in" selected="selected">more than</option>
|
||||
</select>
|
||||
<select name="age_num" class="span1">
|
||||
[% FOREACH count IN [1..32] %]
|
||||
<option[% ' selected="selected"' IF count == 3 %]>[% count %]</option>
|
||||
[% END %]
|
||||
</select>
|
||||
<select name="age_unit" class="span2">
|
||||
<option>days</option>
|
||||
<option>weeks</option>
|
||||
<option selected="selected">months</option>
|
||||
<option>years</option>
|
||||
</select>
|
||||
ago.
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="icon-search icon-white"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,14 +65,12 @@
|
||||
<li>
|
||||
<span rel="tooltip" data-placement="left"
|
||||
data-offset="5" title="Free if Down for this period of time">
|
||||
<select id="nd_days_select" name="age_num"/>
|
||||
[% SET count = 1 %]
|
||||
[% WHILE count < 32 %]
|
||||
<select id="nd_days_select" name="age_num">
|
||||
[% FOREACH count IN [1..32] %]
|
||||
<option[% ' selected="selected"' IF params.age_num == count %]>[% count %]</option>
|
||||
[% SET count = count + 1 %]
|
||||
[% END %]
|
||||
</select>
|
||||
<select id="nd_age_select" name="age_unit"/>
|
||||
<select id="nd_age_select" name="age_unit">
|
||||
[% FOREACH unit IN [ 'days', 'weeks', 'months', 'years' ] %]
|
||||
<option[% ' selected="selected"' IF params.age_unit == unit %]>[% unit %]</option>
|
||||
[% END %]
|
||||
|
||||
Reference in New Issue
Block a user