Improved admin UI
parent
668013265c
commit
76ec907993
1
Gemfile
1
Gemfile
|
@ -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'
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -235,3 +235,4 @@ body {
|
||||||
@import 'components';
|
@import 'components';
|
||||||
@import 'about';
|
@import 'about';
|
||||||
@import 'tables';
|
@import 'tables';
|
||||||
|
@import 'admin';
|
||||||
|
|
|
@ -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;
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
padding: 0 10px;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
li {
|
&:hover {
|
||||||
display: inline-block;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
i.fa {
|
||||||
color: #2b90d9;
|
font-weight: 400;
|
||||||
text-decoration: underline;
|
margin-right: 5px;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin::DomainBlocksHelper
|
||||||
|
end
|
|
@ -1,2 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Admin::PubsubhubbubHelper
|
module Admin::PubsubhubbubHelper
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -1,3 +1,6 @@
|
||||||
|
- content_for :page_title do
|
||||||
|
PubSubHubbub
|
||||||
|
|
||||||
%table.table
|
%table.table
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
|
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Admin::DomainBlocksHelper, type: :helper do
|
||||||
|
|
||||||
|
end
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Reference in New Issue