Improved admin UI

shrike
Eugen Rochko 2016-12-13 13:42:10 +01:00
parent 668013265c
commit 76ec907993
23 changed files with 259 additions and 49 deletions

View File

@ -45,6 +45,7 @@ gem 'rack-timeout-puma'
gem 'sidekiq' gem 'sidekiq'
gem 'ledermann-rails-settings' gem 'ledermann-rails-settings'
gem 'pg_search' gem 'pg_search'
gem 'simple-navigation'
gem 'react-rails' gem 'react-rails'
gem 'browserify-rails' gem 'browserify-rails'

View File

@ -353,6 +353,8 @@ GEM
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)
redis (~> 3.2, >= 3.2.1) redis (~> 3.2, >= 3.2.1)
simple-navigation (4.0.3)
activesupport (>= 2.3.2)
simple_form (3.2.1) simple_form (3.2.1)
actionpack (> 4, < 5.1) actionpack (> 4, < 5.1)
activemodel (> 4, < 5.1) activemodel (> 4, < 5.1)
@ -458,6 +460,7 @@ DEPENDENCIES
sass-rails (~> 5.0) sass-rails (~> 5.0)
sdoc (~> 0.4.0) sdoc (~> 0.4.0)
sidekiq sidekiq
simple-navigation
simple_form simple_form
simplecov simplecov
uglifier (>= 1.3.0) uglifier (>= 1.3.0)

View File

@ -26,6 +26,12 @@ const Notifications = React.createClass({
trackScroll: React.PropTypes.bool trackScroll: React.PropTypes.bool
}, },
getDefaultProps () {
return {
trackScroll: true
};
},
mixins: [PureRenderMixin], mixins: [PureRenderMixin],
componentWillMount () { componentWillMount () {

View File

@ -0,0 +1,105 @@
.admin-wrapper {
width: 100%;
height: 100%;
position: fixed;
background: #1a1c23;
overflow-y: scroll;
.sidebar {
width: 240px;
position: fixed;
left: 0;
height: 100%;
background: #282c37;
.logo {
display: block;
margin: 40px auto;
width: 100px;
height: 100px;
}
ul {
list-style: none;
a {
display: block;
padding: 15px 25px;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
transition: all 200ms linear;
i.fa {
margin-right: 5px;
}
&:hover {
color: #fff;
background-color: darken(#282c37, 5%);
transition: all 100ms linear;
}
&.selected {
color: #fff;
background-color: #2b90d9;
&:hover {
background-color: lighten(#2b90d9, 5%);
}
}
}
}
}
.content {
margin-left: 240px;
padding: 15px;
}
}
.filters {
display: flex;
margin-bottom: 20px;
padding-left: 8px;
.filter-subset {
flex: 0 0 auto;
margin-right: 40px;
ul {
margin-top: 5px;
list-style: none;
li {
display: inline-block;
margin-right: 5px;
}
}
strong {
font-weight: 500;
text-transform: uppercase;
font-size: 12px;
}
a {
display: inline-block;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
text-transform: uppercase;
font-size: 12px;
font-weight: 500;
border-bottom: 2px solid #282c37;
&:hover {
color: #fff;
border-bottom: 2px solid lighten(#282c37, 5%);
}
&.selected {
color: #2b90d9;
border-bottom: 2px solid #2b90d9;
}
}
}
}

View File

@ -235,3 +235,4 @@ body {
@import 'components'; @import 'components';
@import 'about'; @import 'about';
@import 'tables'; @import 'tables';
@import 'admin';

View File

@ -7,15 +7,15 @@
th, td { th, td {
padding: 8px; padding: 8px;
line-height: 1.42857143; line-height: 18px;
vertical-align: top; vertical-align: top;
border-top: 1px solid #ddd; border-top: 1px solid #282c37;
text-align: left; text-align: left;
} }
& > thead > tr > th { & > thead > tr > th {
vertical-align: bottom; vertical-align: bottom;
border-bottom: 2px solid #ddd; border-bottom: 2px solid #282c37;
border-top: 0; border-top: 0;
font-weight: 500; font-weight: 500;
} }
@ -24,6 +24,10 @@
font-weight: 500; font-weight: 500;
} }
& > tbody > tr:nth-child(odd) > td, & > tbody > tr:nth-child(odd) > th {
background: lighten(#1a1c23, 2%);
}
a { a {
color: #2b90d9; color: #2b90d9;
text-decoration: underline; text-decoration: underline;
@ -38,20 +42,20 @@ samp {
font-family: 'Roboto Mono', monospace; font-family: 'Roboto Mono', monospace;
} }
.filters { a.table-action-link {
list-style: none; text-decoration: none;
margin-bottom: 20px;
li {
display: inline-block; display: inline-block;
} margin-right: 5px;
padding: 0 10px;
a { color: rgba(255, 255, 255, 0.7);
color: #2b90d9; font-weight: 500;
text-decoration: underline;
&:hover { &:hover {
text-decoration: none; color: #fff;
} }
i.fa {
font-weight: 400;
margin-right: 5px;
} }
} }

View File

@ -4,7 +4,7 @@ class Admin::AccountsController < ApplicationController
before_action :require_admin! before_action :require_admin!
before_action :set_account, except: :index before_action :set_account, except: :index
layout 'public' layout 'admin'
def index def index
@accounts = Account.alphabetic.paginate(page: params[:page], per_page: 40) @accounts = Account.alphabetic.paginate(page: params[:page], per_page: 40)

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class Admin::DomainBlocksController < ApplicationController
before_action :require_admin!
layout 'admin'
def index
@blocks = DomainBlock.paginate(page: params[:page], per_page: 40)
end
def create
end
end

View File

@ -3,7 +3,7 @@
class Admin::PubsubhubbubController < ApplicationController class Admin::PubsubhubbubController < ApplicationController
before_action :require_admin! before_action :require_admin!
layout 'public' layout 'admin'
def index def index
@subscriptions = Subscription.order('id desc').includes(:account).paginate(page: params[:page], per_page: 40) @subscriptions = Subscription.order('id desc').includes(:account).paginate(page: params[:page], per_page: 40)

View File

@ -1,2 +1,15 @@
# frozen_string_literal: true
module Admin::AccountsHelper module Admin::AccountsHelper
def filter_params(more_params)
params.permit(:local, :remote, :by_domain, :silenced, :suspended, :recent).merge(more_params)
end
def filter_link_to(text, more_params)
link_to text, filter_params(more_params), class: params.merge(more_params).compact == params.compact ? 'selected' : ''
end
def table_link_to(icon, text, path)
link_to safe_join([fa_icon(icon), text]), path, class: 'table-action-link'
end
end end

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
module Admin::DomainBlocksHelper
end

View File

@ -1,2 +1,4 @@
# frozen_string_literal: true
module Admin::PubsubhubbubHelper module Admin::PubsubhubbubHelper
end end

View File

@ -1,9 +1,24 @@
%ul.filters - content_for :page_title do
%li= link_to 'Local', admin_accounts_path(local: '1') Accounts
%li= link_to 'Remote', admin_accounts_path(remote: '1')
%li= link_to 'Silenced', admin_accounts_path(silenced: '1') .filters
%li= link_to 'Suspended', admin_accounts_path(suspended: '1') .filter-subset
%li= link_to 'Most recent', admin_accounts_path(recent: '1') %strong Location
%ul
%li= filter_link_to 'All', local: nil, remote: nil
%li= filter_link_to 'Local', local: '1', remote: nil
%li= filter_link_to 'Remote', remote: '1', local: nil
.filter-subset
%strong Moderation
%ul
%li= filter_link_to 'All', silenced: nil, suspended: nil
%li= filter_link_to 'Silenced', silenced: '1'
%li= filter_link_to 'Suspended', suspended: '1'
.filter-subset
%strong Order
%ul
%li= filter_link_to 'Alphabetic', recent: nil
%li= filter_link_to 'Most recent', recent: '1'
%table.table %table.table
%thead %thead
@ -38,6 +53,9 @@
%i.fa.fa-check %i.fa.fa-check
- else - else
%i.fa.fa-times %i.fa.fa-times
%td= link_to 'Edit', admin_account_path(account.id) %td
= table_link_to 'circle', 'Open in web', web_path("accounts/#{account.id}")
= table_link_to 'globe', 'Open public', TagManager.instance.url_for(account)
= table_link_to 'pencil', 'Edit', admin_account_path(account.id)
= will_paginate @accounts, pagination_options = will_paginate @accounts, pagination_options

View File

@ -0,0 +1,14 @@
- content_for :page_title do
Domain Blocks
%table.table
%thead
%tr
%th Domain
%tbody
- @blocks.each do |block|
%tr
%td
%samp= block.domain
= will_paginate @blocks, pagination_options

View File

@ -1,3 +1,6 @@
- content_for :page_title do
PubSubHubbub
%table.table %table.table
%thead %thead
%tr %tr

View File

@ -0,0 +1,11 @@
- content_for :content do
.admin-wrapper
.sidebar
= link_to root_path do
= image_tag 'logo.png', class: 'logo'
= render_navigation
.content
= yield
= render template: "layouts/application"

11
config/navigation.rb Normal file
View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
SimpleNavigation::Configuration.run do |navigation|
navigation.items do |primary|
primary.item :accounts, safe_join([fa_icon('users fw'), 'Accounts']), admin_accounts_url
primary.item :pubsubhubbubs, safe_join([fa_icon('paper-plane-o fw'), 'PubSubHubbub']), admin_pubsubhubbub_index_url
primary.item :domain_blocks, safe_join([fa_icon('lock fw'), 'Domain Blocks']), admin_domain_blocks_url
primary.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url
primary.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url
end
end

View File

@ -6,8 +6,8 @@ Rails.application.routes.draw do
mount ActionCable.server, at: 'cable' mount ActionCable.server, at: 'cable'
authenticate :user, lambda { |u| u.admin? } do authenticate :user, lambda { |u| u.admin? } do
mount Sidekiq::Web, at: 'sidekiq' mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq
mount PgHero::Engine, at: 'pghero' mount PgHero::Engine, at: 'pghero', as: :pghero
end end
use_doorkeeper do use_doorkeeper do
@ -46,6 +46,7 @@ Rails.application.routes.draw do
namespace :admin do namespace :admin do
resources :pubsubhubbub, only: [:index] resources :pubsubhubbub, only: [:index]
resources :domain_blocks, only: [:index, :create]
resources :accounts, only: [:index, :show, :update] do resources :accounts, only: [:index, :show, :update] do
member do member do

View File

@ -0,0 +1,14 @@
require 'rails_helper'
RSpec.describe Admin::DomainBlocksController, type: :controller do
before do
sign_in Fabricate(:user, admin: true), scope: :user
end
describe 'GET #index' do
it 'returns http success' do
get :index
expect(response).to have_http_status(:success)
end
end
end

View File

@ -1,15 +1,5 @@
require 'rails_helper' require 'rails_helper'
# Specs in this file have access to a helper object that includes
# the Admin::AccountsHelper. For example:
#
# describe Admin::AccountsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
RSpec.describe Admin::AccountsHelper, type: :helper do RSpec.describe Admin::AccountsHelper, type: :helper do
pending "add some examples to (or delete) #{__FILE__}"
end end

View File

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Admin::DomainBlocksHelper, type: :helper do
end

View File

@ -1,15 +1,5 @@
require 'rails_helper' require 'rails_helper'
# Specs in this file have access to a helper object that includes
# the Admin::PubsubhubbubHelper. For example:
#
# describe Admin::PubsubhubbubHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
RSpec.describe Admin::PubsubhubbubHelper, type: :helper do RSpec.describe Admin::PubsubhubbubHelper, type: :helper do
pending "add some examples to (or delete) #{__FILE__}"
end end

View File

@ -17,7 +17,7 @@ RSpec.describe Formatter do
end end
it 'contains a link' do it 'contains a link' do
expect(subject).to match('<a rel="nofollow noopener" href="http://google.com">google.com</a>') expect(subject).to match('<a rel="nofollow noopener" target="_blank" href="http://google.com">google.com</a>')
end end
end end