diff --git a/app/controllers/admin/clients_controller.rb b/app/controllers/admin/clients_controller.rb
new file mode 100644
index 0000000..9d8ce6e
--- /dev/null
+++ b/app/controllers/admin/clients_controller.rb
@@ -0,0 +1,38 @@
+class Admin::ClientsController < Admin::BaseController
+ before_action :fetch_client, only: [:show]
+
+ # Shows all available clients
+ #
+ # GET /admin/clients
+ def index
+ @clients = Client.includes(:jobs).all
+ @client_ids = @clients.map(&:id)
+ fetch_jobs_info
+ end
+
+ # Shows a specific client
+ #
+ # GET /admin/clients/1
+ def show
+ @client_ids = [@client.id]
+ get_logs
+ get_charts
+ end
+
+ private
+
+ # Fetches the client based on the given id
+ def fetch_client
+ @client = Client.find(params[:id])
+ end
+
+ def fetch_jobs_info
+ @stats = JobStats.new
+ end
+
+ def get_charts
+ days_ago = params.fetch(:days_back, 7).to_i rescue 7
+ @job_status = ChartGenerator.job_statuses(@client_ids, days_ago)
+ @job_stats = ChartGenerator.job_stats(@client_ids, days_ago - 1)
+ end
+end
diff --git a/app/views/admin/clients/_client.html.erb b/app/views/admin/clients/_client.html.erb
new file mode 100644
index 0000000..bfc21bf
--- /dev/null
+++ b/app/views/admin/clients/_client.html.erb
@@ -0,0 +1,12 @@
+
+ <%= link_to "##{client.id}", admin_client_path(client) %> |
+ <%= link_to client.name, admin_client_path(client) %> |
+ <%= client.uname %> |
+ <%= @stats.active_jobs[client.id] || 0 %> |
+ <%= @stats.last_jobs[client.id] %> |
+ <%= client.file_retention_days %> |
+ <%= client.job_retention_days %> |
+ <%= number_to_human_size(@stats.jobs_sizes[client.id]) %> |
+ <%= number_by_magnitude(@stats.jobs_files[client.id]) %> |
+ <%= client.auto_prune_human %> |
+
diff --git a/app/views/admin/clients/_client_details.html.erb b/app/views/admin/clients/_client_details.html.erb
new file mode 100644
index 0000000..161b7bd
--- /dev/null
+++ b/app/views/admin/clients/_client_details.html.erb
@@ -0,0 +1,44 @@
+
+
+
+
+ Name |
+ <%= @client.name %> |
+
+
+ Uname |
+ <%= @client.uname %> |
+
+
+ Active Jobs |
+ <%= @client.running_jobs %> |
+
+
+ Last Backup |
+
+ <%= @client.last_job_date_formatted %>
+ |
+
+
+ File Retention |
+ <%= @client.file_retention_days %> days |
+
+
+ Job Retention |
+ <%= @client.job_retention_days %> days |
+
+
+ Total Space Used |
+ <%= number_to_human_size @client.backup_jobs_size %> |
+
+
+ Files count |
+ <%= number_by_magnitude(@client.files_count) %> |
+
+
+ Auto Prune |
+ <%= @client.auto_prune_human %> |
+
+
+
+
diff --git a/app/views/admin/clients/_client_graphs.html.erb b/app/views/admin/clients/_client_graphs.html.erb
new file mode 100644
index 0000000..0d1a682
--- /dev/null
+++ b/app/views/admin/clients/_client_graphs.html.erb
@@ -0,0 +1,18 @@
+
+
+
+
+
+ <%= bootstrap_form_tag(url: path, method: :get, layout: :inline) do |f| %>
+ <%= f.select(:days_back, [['1 week', 7], ['2 weeks', 14], ['1 month', 30]]) %>
+ <%= f.submit 'See Stats', class: "btn btn-primary" %>
+ <% end %>
+
+
+
+<%= baas_chart('jobs_status', @job_status) %>
+<%= baas_chart('jobs_stats', @job_stats) %>
diff --git a/app/views/admin/clients/_job.html.erb b/app/views/admin/clients/_job.html.erb
new file mode 100644
index 0000000..f10eda8
--- /dev/null
+++ b/app/views/admin/clients/_job.html.erb
@@ -0,0 +1,7 @@
+
+ <%= job.name %> |
+ <%= job.job_type %> |
+ <%= job.fileset.try(:name) %> |
+ <%= job.schedule_human %> |
+ <%= I18n.l(job.created_at, format: :long) %> |
+
diff --git a/app/views/admin/clients/_jobs.html.erb b/app/views/admin/clients/_jobs.html.erb
new file mode 100644
index 0000000..d7a4d0f
--- /dev/null
+++ b/app/views/admin/clients/_jobs.html.erb
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Name |
+ Type |
+ Fileset |
+ Schedule |
+ Created |
+
+
+
+ <%= render partial: 'job', collection: @client.persisted_jobs, object: :job %>
+
+
+
+
diff --git a/app/views/admin/clients/_log.html.erb b/app/views/admin/clients/_log.html.erb
new file mode 100644
index 0000000..589a415
--- /dev/null
+++ b/app/views/admin/clients/_log.html.erb
@@ -0,0 +1,8 @@
+
+ <%= log.log_id %> |
+ <%= log.job.name %> |
+ <%= log.time_formatted %> |
+
+<%= log.log_text %>
+ |
+
diff --git a/app/views/admin/clients/_logs.html.erb b/app/views/admin/clients/_logs.html.erb
new file mode 100644
index 0000000..adcfe45
--- /dev/null
+++ b/app/views/admin/clients/_logs.html.erb
@@ -0,0 +1,23 @@
+
+
+
Logs
+
+
+
+
+
+
+
+ LogId |
+ Job |
+ Time |
+ Text |
+
+
+
+ <%= render partial: 'log', collection: @logs %>
+
+
+
+
+
diff --git a/app/views/admin/clients/_recent_job.html.erb b/app/views/admin/clients/_recent_job.html.erb
new file mode 100644
index 0000000..109626b
--- /dev/null
+++ b/app/views/admin/clients/_recent_job.html.erb
@@ -0,0 +1,11 @@
+
+ <%= recent_job.name %> |
+ <%= recent_job.job_id %> |
+ <%= recent_job.level_human %> |
+ <%= recent_job.fileset %> |
+ <%= recent_job.start_time_formatted %> |
+ <%= recent_job.end_time_formatted %> |
+ <%= number_to_human_size(recent_job.job_bytes) %> |
+ <%= number_by_magnitude(recent_job.job_files) %> |
+ <%= recent_job.status_human %>
+ |
diff --git a/app/views/admin/clients/_recent_jobs.html.erb b/app/views/admin/clients/_recent_jobs.html.erb
new file mode 100644
index 0000000..a444a8e
--- /dev/null
+++ b/app/views/admin/clients/_recent_jobs.html.erb
@@ -0,0 +1,22 @@
+
+
+
+
+
+ Name |
+ JobId |
+ Level |
+ Fileset |
+ Started At |
+ Finished At |
+ Bytes |
+ Files |
+ Status |
+
+
+
+ <%= render partial: 'recent_job', collection: @client.recent_jobs %>
+
+
+
+
diff --git a/app/views/admin/clients/index.html.erb b/app/views/admin/clients/index.html.erb
new file mode 100644
index 0000000..98e580f
--- /dev/null
+++ b/app/views/admin/clients/index.html.erb
@@ -0,0 +1,24 @@
+Bacula Clients
+
+
+
+
+
+ ClientId |
+ Name |
+ Uname |
+ Active Jobs |
+ Last Backup |
+ FileRetention (days) |
+ JobRetention (days) |
+ Space Used |
+ File count |
+ AutoPrune |
+
+
+
+
+ <%= render partial: 'client', collection: @clients %>
+
+
+
diff --git a/app/views/admin/clients/show.html.erb b/app/views/admin/clients/show.html.erb
new file mode 100644
index 0000000..f30a0c7
--- /dev/null
+++ b/app/views/admin/clients/show.html.erb
@@ -0,0 +1,44 @@
+<%= notice %>
+<% if @client.host %>
+
+ <%= link_to 'Manage Client', 'admin_host_path(@client.host)', class: "btn btn-primary", role: "button" %>
+
+<% end %>
+
+<%= @client.name %>
+
+
+
+
Client Details
+
+
+
Bacula Jobs
+
+
+
+
+ <%= render partial: 'client_details' %>
+
+
+ <% if @client.host %>
+ <%= render partial: 'jobs' %>
+ <% end %>
+
+
+
+ <%= render partial: 'recent_jobs' %>
+
+
+
+
+
+
+<%= render partial: 'client_graphs', locals: { path: admin_client_path(@client) } %>
+
+
+
+<%= render partial: 'logs' %>
diff --git a/app/views/shared/_nav.html.erb b/app/views/shared/_nav.html.erb
index fefa4ff..654d8cc 100644
--- a/app/views/shared/_nav.html.erb
+++ b/app/views/shared/_nav.html.erb
@@ -1,34 +1,35 @@
diff --git a/config/routes.rb b/config/routes.rb
index d8f978d..d2fd087 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,28 +1,30 @@
Rails.application.routes.draw do
root 'clients#index'
resources :clients, only: [:index, :show]
resources :hosts, only: [:new, :create, :show, :edit, :update, :destroy] do
member do
post :submit_config
get :restore
post :run_restore
delete :revoke
end
resources :jobs, only: [:new, :create, :show, :edit, :update, :destroy] do
member do
patch :toggle_enable
post :backup_now
end
end
resources :filesets, only: [:show, :new, :create, :destroy]
resources :schedules, only: [:show, :new, :edit, :create, :update, :destroy]
end
namespace :admin do
get '/' => 'base#index'
+
+ resources :clients, only: [:index, :show]
end
end