Improved admin UI
This commit is contained in:
		
							
								
								
									
										1
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Gemfile
									
									
									
									
									
								
							| @@ -45,6 +45,7 @@ gem 'rack-timeout-puma' | ||||
| gem 'sidekiq' | ||||
| gem 'ledermann-rails-settings' | ||||
| gem 'pg_search' | ||||
| gem 'simple-navigation' | ||||
|  | ||||
| gem 'react-rails' | ||||
| gem 'browserify-rails' | ||||
|   | ||||
| @@ -353,6 +353,8 @@ GEM | ||||
|       connection_pool (~> 2.2, >= 2.2.0) | ||||
|       rack-protection (>= 1.5.0) | ||||
|       redis (~> 3.2, >= 3.2.1) | ||||
|     simple-navigation (4.0.3) | ||||
|       activesupport (>= 2.3.2) | ||||
|     simple_form (3.2.1) | ||||
|       actionpack (> 4, < 5.1) | ||||
|       activemodel (> 4, < 5.1) | ||||
| @@ -458,6 +460,7 @@ DEPENDENCIES | ||||
|   sass-rails (~> 5.0) | ||||
|   sdoc (~> 0.4.0) | ||||
|   sidekiq | ||||
|   simple-navigation | ||||
|   simple_form | ||||
|   simplecov | ||||
|   uglifier (>= 1.3.0) | ||||
|   | ||||
| @@ -26,6 +26,12 @@ const Notifications = React.createClass({ | ||||
|     trackScroll: React.PropTypes.bool | ||||
|   }, | ||||
|  | ||||
|   getDefaultProps () { | ||||
|     return { | ||||
|       trackScroll: true | ||||
|     }; | ||||
|   }, | ||||
|  | ||||
|   mixins: [PureRenderMixin], | ||||
|  | ||||
|   componentWillMount () { | ||||
|   | ||||
							
								
								
									
										105
									
								
								app/assets/stylesheets/admin.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								app/assets/stylesheets/admin.scss
									
									
									
									
									
										Normal 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; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -235,3 +235,4 @@ body { | ||||
| @import 'components'; | ||||
| @import 'about'; | ||||
| @import 'tables'; | ||||
| @import 'admin'; | ||||
|   | ||||
| @@ -7,15 +7,15 @@ | ||||
|  | ||||
|   th, td { | ||||
|     padding: 8px; | ||||
|     line-height: 1.42857143; | ||||
|     line-height: 18px; | ||||
|     vertical-align: top; | ||||
|     border-top: 1px solid #ddd; | ||||
|     border-top: 1px solid #282c37; | ||||
|     text-align: left; | ||||
|   } | ||||
|  | ||||
|   & > thead > tr > th { | ||||
|     vertical-align: bottom; | ||||
|     border-bottom: 2px solid #ddd; | ||||
|     border-bottom: 2px solid #282c37; | ||||
|     border-top: 0; | ||||
|     font-weight: 500; | ||||
|   } | ||||
| @@ -24,6 +24,10 @@ | ||||
|     font-weight: 500; | ||||
|   } | ||||
|  | ||||
|   & > tbody > tr:nth-child(odd) > td, & > tbody > tr:nth-child(odd) > th { | ||||
|     background: lighten(#1a1c23, 2%); | ||||
|   } | ||||
|  | ||||
|   a { | ||||
|     color: #2b90d9; | ||||
|     text-decoration: underline; | ||||
| @@ -38,20 +42,20 @@ samp { | ||||
|   font-family: 'Roboto Mono', monospace; | ||||
| } | ||||
|  | ||||
| .filters { | ||||
|   list-style: none; | ||||
|   margin-bottom: 20px; | ||||
| a.table-action-link { | ||||
|   text-decoration: none; | ||||
|   display: inline-block; | ||||
|   margin-right: 5px; | ||||
|   padding: 0 10px; | ||||
|   color: rgba(255, 255, 255, 0.7); | ||||
|   font-weight: 500; | ||||
|  | ||||
|   li { | ||||
|     display: inline-block; | ||||
|   &:hover { | ||||
|     color: #fff; | ||||
|   } | ||||
|  | ||||
|   a { | ||||
|     color: #2b90d9; | ||||
|     text-decoration: underline; | ||||
|  | ||||
|     &:hover { | ||||
|       text-decoration: none; | ||||
|     } | ||||
|   i.fa { | ||||
|     font-weight: 400; | ||||
|     margin-right: 5px; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ class Admin::AccountsController < ApplicationController | ||||
|   before_action :require_admin! | ||||
|   before_action :set_account, except: :index | ||||
|  | ||||
|   layout 'public' | ||||
|   layout 'admin' | ||||
|  | ||||
|   def index | ||||
|     @accounts = Account.alphabetic.paginate(page: params[:page], per_page: 40) | ||||
|   | ||||
							
								
								
									
										14
									
								
								app/controllers/admin/domain_blocks_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/controllers/admin/domain_blocks_controller.rb
									
									
									
									
									
										Normal 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 | ||||
| @@ -3,7 +3,7 @@ | ||||
| class Admin::PubsubhubbubController < ApplicationController | ||||
|   before_action :require_admin! | ||||
|  | ||||
|   layout 'public' | ||||
|   layout 'admin' | ||||
|  | ||||
|   def index | ||||
|     @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 | ||||
|   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 | ||||
|   | ||||
							
								
								
									
										4
									
								
								app/helpers/admin/domain_blocks_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/helpers/admin/domain_blocks_helper.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| module Admin::DomainBlocksHelper | ||||
| end | ||||
| @@ -1,2 +1,4 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| module Admin::PubsubhubbubHelper | ||||
| end | ||||
|   | ||||
| @@ -1,9 +1,24 @@ | ||||
| %ul.filters | ||||
|   %li= link_to 'Local', admin_accounts_path(local: '1') | ||||
|   %li= link_to 'Remote', admin_accounts_path(remote: '1') | ||||
|   %li= link_to 'Silenced', admin_accounts_path(silenced: '1') | ||||
|   %li= link_to 'Suspended', admin_accounts_path(suspended: '1') | ||||
|   %li= link_to 'Most recent', admin_accounts_path(recent: '1') | ||||
| - content_for :page_title do | ||||
|   Accounts | ||||
|  | ||||
| .filters | ||||
|   .filter-subset | ||||
|     %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 | ||||
|   %thead | ||||
| @@ -38,6 +53,9 @@ | ||||
|             %i.fa.fa-check | ||||
|           - else | ||||
|             %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 | ||||
|   | ||||
							
								
								
									
										14
									
								
								app/views/admin/domain_blocks/index.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/views/admin/domain_blocks/index.html.haml
									
									
									
									
									
										Normal 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 | ||||
| @@ -1,3 +1,6 @@ | ||||
| - content_for :page_title do | ||||
|   PubSubHubbub | ||||
|  | ||||
| %table.table | ||||
|   %thead | ||||
|     %tr | ||||
|   | ||||
							
								
								
									
										11
									
								
								app/views/layouts/admin.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/views/layouts/admin.html.haml
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										11
									
								
								config/navigation.rb
									
									
									
									
									
										Normal 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 | ||||
| @@ -6,8 +6,8 @@ Rails.application.routes.draw do | ||||
|   mount ActionCable.server, at: 'cable' | ||||
|  | ||||
|   authenticate :user, lambda { |u| u.admin? } do | ||||
|     mount Sidekiq::Web, at: 'sidekiq' | ||||
|     mount PgHero::Engine, at: 'pghero' | ||||
|     mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq | ||||
|     mount PgHero::Engine, at: 'pghero', as: :pghero | ||||
|   end | ||||
|  | ||||
|   use_doorkeeper do | ||||
| @@ -46,6 +46,7 @@ Rails.application.routes.draw do | ||||
|  | ||||
|   namespace :admin do | ||||
|     resources :pubsubhubbub, only: [:index] | ||||
|     resources :domain_blocks, only: [:index, :create] | ||||
|  | ||||
|     resources :accounts, only: [:index, :show, :update] do | ||||
|       member do | ||||
|   | ||||
							
								
								
									
										14
									
								
								spec/controllers/admin/domain_blocks_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								spec/controllers/admin/domain_blocks_controller_spec.rb
									
									
									
									
									
										Normal 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 | ||||
| @@ -1,15 +1,5 @@ | ||||
| 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 | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
|  | ||||
| end | ||||
|   | ||||
							
								
								
									
										5
									
								
								spec/helpers/admin/domain_blocks_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/helpers/admin/domain_blocks_helper_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe Admin::DomainBlocksHelper, type: :helper do | ||||
|  | ||||
| end | ||||
| @@ -1,15 +1,5 @@ | ||||
| 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 | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
|  | ||||
| end | ||||
|   | ||||
| @@ -17,7 +17,7 @@ RSpec.describe Formatter do | ||||
|     end | ||||
|  | ||||
|     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 | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user