diff --git a/Gemfile b/Gemfile
index 98feafb..39ba4b6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -111,3 +111,5 @@ group :test do
gem 'vcr'
gem 'webmock'
end
+
+gem "administrate", "~> 0.20.1"
diff --git a/Gemfile.lock b/Gemfile.lock
index 360902d..edfc3aa 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -86,6 +86,14 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
+ administrate (0.20.1)
+ actionpack (>= 6.0, < 8.0)
+ actionview (>= 6.0, < 8.0)
+ activerecord (>= 6.0, < 8.0)
+ jquery-rails (~> 4.6.0)
+ kaminari (~> 1.2.2)
+ sassc-rails (~> 2.1)
+ selectize-rails (~> 0.6)
aes_key_wrap (1.1.0)
annotate (3.2.0)
activerecord (>= 3.2, < 8.0)
@@ -178,6 +186,10 @@ GEM
jbuilder (2.12.0)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
+ jquery-rails (4.6.0)
+ rails-dom-testing (>= 1, < 3)
+ railties (>= 4.2.0)
+ thor (>= 0.14, < 2.0)
json (2.7.2)
json-jwt (1.16.6)
activesupport (>= 4.2)
@@ -186,6 +198,18 @@ GEM
bindata
faraday (~> 2.0)
faraday-follow_redirects
+ kaminari (1.2.2)
+ activesupport (>= 4.1.0)
+ kaminari-actionview (= 1.2.2)
+ kaminari-activerecord (= 1.2.2)
+ kaminari-core (= 1.2.2)
+ kaminari-actionview (1.2.2)
+ actionview
+ kaminari-core (= 1.2.2)
+ kaminari-activerecord (1.2.2)
+ activerecord
+ kaminari-core (= 1.2.2)
+ kaminari-core (1.2.2)
language_server-protocol (3.17.0.3)
llhttp-ffi (0.5.0)
ffi-compiler (~> 1.0)
@@ -349,6 +373,7 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
+ selectize-rails (0.12.6)
selenium-webdriver (4.23.0)
base64 (~> 0.2)
logger (~> 1.4)
@@ -429,6 +454,7 @@ PLATFORMS
x86_64-linux
DEPENDENCIES
+ administrate (~> 0.20.1)
annotate
bootsnap
cancancan
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb
new file mode 100644
index 0000000..8449926
--- /dev/null
+++ b/app/controllers/admin/application_controller.rb
@@ -0,0 +1,36 @@
+# All Administrate controllers inherit from this
+# `Administrate::ApplicationController`, making it the ideal place to put
+# authentication logic or other before_actions.
+#
+# If you want to add pagination or other controller-level concerns,
+# you're free to overwrite the RESTful controller actions.
+module Admin
+ class ApplicationController < Administrate::ApplicationController
+ before_action :require_user
+ before_action :authorize_user
+
+ private
+
+ def authorize_user
+ return if authorize_action?(resource_name, action_name)
+
+ redirect_to root_path, alert: 'Not authorized'
+ end
+
+ def authorize_action?(resource, action)
+ can? action, resource
+ end
+
+ def require_user
+ return if current_user
+
+ redirect_to root_path, alert: 'Please sign in to continue'
+ end
+
+ # Override this value to specify the number of elements to display at a time
+ # on index pages. Defaults to 20.
+ # def records_per_page
+ # params[:per_page] || 20
+ # end
+ end
+end
diff --git a/app/controllers/admin/search_events_controller.rb b/app/controllers/admin/search_events_controller.rb
new file mode 100644
index 0000000..de79091
--- /dev/null
+++ b/app/controllers/admin/search_events_controller.rb
@@ -0,0 +1,46 @@
+module Admin
+ class SearchEventsController < Admin::ApplicationController
+ # Overwrite any of the RESTful controller actions to implement custom behavior
+ # For example, you may want to send an email after a foo is updated.
+ #
+ # def update
+ # super
+ # send_foo_updated_email(requested_resource)
+ # end
+
+ # Override this method to specify custom lookup behavior.
+ # This will be used to set the resource for the `show`, `edit`, and `update`
+ # actions.
+ #
+ # def find_resource(param)
+ # Foo.find_by!(slug: param)
+ # end
+
+ # The result of this lookup will be available as `requested_resource`
+
+ # Override this if you have certain roles that require a subset
+ # this will be used to set the records shown on the `index` action.
+ #
+ # def scoped_resource
+ # if current_user.super_admin?
+ # resource_class
+ # else
+ # resource_class.with_less_stuff
+ # end
+ # end
+
+ # Override `resource_params` if you want to transform the submitted
+ # data before it's persisted. For example, the following would turn all
+ # empty values into nil values. It uses other APIs such as `resource_class`
+ # and `dashboard`:
+ #
+ # def resource_params
+ # params.require(resource_class.model_name.param_key).
+ # permit(dashboard.permitted_attributes(action_name)).
+ # transform_values { |value| value == "" ? nil : value }
+ # end
+
+ # See https://administrate-demo.herokuapp.com/customizing_controller_actions
+ # for more information
+ end
+end
diff --git a/app/controllers/admin/stats_controller.rb b/app/controllers/admin/stats_controller.rb
new file mode 100644
index 0000000..dc139dc
--- /dev/null
+++ b/app/controllers/admin/stats_controller.rb
@@ -0,0 +1,10 @@
+module Admin
+ class StatsController < Admin::ApplicationController
+ def index
+ @stats = {
+ term_count: Term.count,
+ search_event_count: SearchEvent.count,
+ }
+ end
+ end
+end
diff --git a/app/controllers/admin/terms_controller.rb b/app/controllers/admin/terms_controller.rb
new file mode 100644
index 0000000..9f2780c
--- /dev/null
+++ b/app/controllers/admin/terms_controller.rb
@@ -0,0 +1,46 @@
+module Admin
+ class TermsController < Admin::ApplicationController
+ # Overwrite any of the RESTful controller actions to implement custom behavior
+ # For example, you may want to send an email after a foo is updated.
+ #
+ # def update
+ # super
+ # send_foo_updated_email(requested_resource)
+ # end
+
+ # Override this method to specify custom lookup behavior.
+ # This will be used to set the resource for the `show`, `edit`, and `update`
+ # actions.
+ #
+ # def find_resource(param)
+ # Foo.find_by!(slug: param)
+ # end
+
+ # The result of this lookup will be available as `requested_resource`
+
+ # Override this if you have certain roles that require a subset
+ # this will be used to set the records shown on the `index` action.
+ #
+ # def scoped_resource
+ # if current_user.super_admin?
+ # resource_class
+ # else
+ # resource_class.with_less_stuff
+ # end
+ # end
+
+ # Override `resource_params` if you want to transform the submitted
+ # data before it's persisted. For example, the following would turn all
+ # empty values into nil values. It uses other APIs such as `resource_class`
+ # and `dashboard`:
+ #
+ # def resource_params
+ # params.require(resource_class.model_name.param_key).
+ # permit(dashboard.permitted_attributes(action_name)).
+ # transform_values { |value| value == "" ? nil : value }
+ # end
+
+ # See https://administrate-demo.herokuapp.com/customizing_controller_actions
+ # for more information
+ end
+end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
new file mode 100644
index 0000000..2c4ab7d
--- /dev/null
+++ b/app/controllers/admin/users_controller.rb
@@ -0,0 +1,46 @@
+module Admin
+ class UsersController < Admin::ApplicationController
+ # Overwrite any of the RESTful controller actions to implement custom behavior
+ # For example, you may want to send an email after a foo is updated.
+ #
+ # def update
+ # super
+ # send_foo_updated_email(requested_resource)
+ # end
+
+ # Override this method to specify custom lookup behavior.
+ # This will be used to set the resource for the `show`, `edit`, and `update`
+ # actions.
+ #
+ # def find_resource(param)
+ # Foo.find_by!(slug: param)
+ # end
+
+ # The result of this lookup will be available as `requested_resource`
+
+ # Override this if you have certain roles that require a subset
+ # this will be used to set the records shown on the `index` action.
+ #
+ # def scoped_resource
+ # if current_user.super_admin?
+ # resource_class
+ # else
+ # resource_class.with_less_stuff
+ # end
+ # end
+
+ # Override `resource_params` if you want to transform the submitted
+ # data before it's persisted. For example, the following would turn all
+ # empty values into nil values. It uses other APIs such as `resource_class`
+ # and `dashboard`:
+ #
+ # def resource_params
+ # params.require(resource_class.model_name.param_key).
+ # permit(dashboard.permitted_attributes(action_name)).
+ # transform_values { |value| value == "" ? nil : value }
+ # end
+
+ # See https://administrate-demo.herokuapp.com/customizing_controller_actions
+ # for more information
+ end
+end
diff --git a/app/dashboards/search_event_dashboard.rb b/app/dashboards/search_event_dashboard.rb
new file mode 100644
index 0000000..cd4e096
--- /dev/null
+++ b/app/dashboards/search_event_dashboard.rb
@@ -0,0 +1,66 @@
+require "administrate/base_dashboard"
+
+class SearchEventDashboard < Administrate::BaseDashboard
+ # ATTRIBUTE_TYPES
+ # a hash that describes the type of each of the model's fields.
+ #
+ # Each different type represents an Administrate::Field object,
+ # which determines how the attribute is displayed
+ # on pages throughout the dashboard.
+ ATTRIBUTE_TYPES = {
+ id: Field::Number,
+ source: Field::String,
+ term: Field::BelongsTo,
+ created_at: Field::DateTime,
+ updated_at: Field::DateTime,
+ }.freeze
+
+ # COLLECTION_ATTRIBUTES
+ # an array of attributes that will be displayed on the model's index page.
+ #
+ # By default, it's limited to four items to reduce clutter on index pages.
+ # Feel free to add, remove, or rearrange items.
+ COLLECTION_ATTRIBUTES = %i[
+ id
+ source
+ term
+ created_at
+ ].freeze
+
+ # SHOW_PAGE_ATTRIBUTES
+ # an array of attributes that will be displayed on the model's show page.
+ SHOW_PAGE_ATTRIBUTES = %i[
+ id
+ source
+ term
+ created_at
+ updated_at
+ ].freeze
+
+ # FORM_ATTRIBUTES
+ # an array of attributes that will be displayed
+ # on the model's form (`new` and `edit`) pages.
+ FORM_ATTRIBUTES = %i[
+ source
+ term
+ ].freeze
+
+ # COLLECTION_FILTERS
+ # a hash that defines filters that can be used while searching via the search
+ # field of the dashboard.
+ #
+ # For example to add an option to search for open resources by typing "open:"
+ # in the search field:
+ #
+ # COLLECTION_FILTERS = {
+ # open: ->(resources) { resources.where(open: true) }
+ # }.freeze
+ COLLECTION_FILTERS = {}.freeze
+
+ # Overwrite this method to customize how search events are displayed
+ # across all pages of the admin dashboard.
+ #
+ # def display_resource(search_event)
+ # "SearchEvent ##{search_event.id}"
+ # end
+end
diff --git a/app/dashboards/stat_dashboard.rb b/app/dashboards/stat_dashboard.rb
new file mode 100644
index 0000000..d455329
--- /dev/null
+++ b/app/dashboards/stat_dashboard.rb
@@ -0,0 +1,5 @@
+require 'administrate/custom_dashboard'
+
+class StatDashboard < Administrate::CustomDashboard
+ resource "Overview"
+end
diff --git a/app/dashboards/term_dashboard.rb b/app/dashboards/term_dashboard.rb
new file mode 100644
index 0000000..b75dbff
--- /dev/null
+++ b/app/dashboards/term_dashboard.rb
@@ -0,0 +1,66 @@
+require "administrate/base_dashboard"
+
+class TermDashboard < Administrate::BaseDashboard
+ # ATTRIBUTE_TYPES
+ # a hash that describes the type of each of the model's fields.
+ #
+ # Each different type represents an Administrate::Field object,
+ # which determines how the attribute is displayed
+ # on pages throughout the dashboard.
+ ATTRIBUTE_TYPES = {
+ id: Field::Number,
+ phrase: Field::String,
+ search_events: Field::HasMany,
+ created_at: Field::DateTime,
+ updated_at: Field::DateTime,
+ }.freeze
+
+ # COLLECTION_ATTRIBUTES
+ # an array of attributes that will be displayed on the model's index page.
+ #
+ # By default, it's limited to four items to reduce clutter on index pages.
+ # Feel free to add, remove, or rearrange items.
+ COLLECTION_ATTRIBUTES = %i[
+ id
+ phrase
+ search_events
+ created_at
+ ].freeze
+
+ # SHOW_PAGE_ATTRIBUTES
+ # an array of attributes that will be displayed on the model's show page.
+ SHOW_PAGE_ATTRIBUTES = %i[
+ id
+ phrase
+ search_events
+ created_at
+ updated_at
+ ].freeze
+
+ # FORM_ATTRIBUTES
+ # an array of attributes that will be displayed
+ # on the model's form (`new` and `edit`) pages.
+ FORM_ATTRIBUTES = %i[
+ phrase
+ search_events
+ ].freeze
+
+ # COLLECTION_FILTERS
+ # a hash that defines filters that can be used while searching via the search
+ # field of the dashboard.
+ #
+ # For example to add an option to search for open resources by typing "open:"
+ # in the search field:
+ #
+ # COLLECTION_FILTERS = {
+ # open: ->(resources) { resources.where(open: true) }
+ # }.freeze
+ COLLECTION_FILTERS = {}.freeze
+
+ # Overwrite this method to customize how terms are displayed
+ # across all pages of the admin dashboard.
+ #
+ # def display_resource(term)
+ # "Term ##{term.id}"
+ # end
+end
diff --git a/app/dashboards/user_dashboard.rb b/app/dashboards/user_dashboard.rb
new file mode 100644
index 0000000..7fe9659
--- /dev/null
+++ b/app/dashboards/user_dashboard.rb
@@ -0,0 +1,69 @@
+require "administrate/base_dashboard"
+
+class UserDashboard < Administrate::BaseDashboard
+ # ATTRIBUTE_TYPES
+ # a hash that describes the type of each of the model's fields.
+ #
+ # Each different type represents an Administrate::Field object,
+ # which determines how the attribute is displayed
+ # on pages throughout the dashboard.
+ ATTRIBUTE_TYPES = {
+ id: Field::Number,
+ admin: Field::Boolean,
+ email: Field::String,
+ uid: Field::String,
+ created_at: Field::DateTime,
+ updated_at: Field::DateTime,
+ }.freeze
+
+ # COLLECTION_ATTRIBUTES
+ # an array of attributes that will be displayed on the model's index page.
+ #
+ # By default, it's limited to four items to reduce clutter on index pages.
+ # Feel free to add, remove, or rearrange items.
+ COLLECTION_ATTRIBUTES = %i[
+ id
+ admin
+ email
+ uid
+ ].freeze
+
+ # SHOW_PAGE_ATTRIBUTES
+ # an array of attributes that will be displayed on the model's show page.
+ SHOW_PAGE_ATTRIBUTES = %i[
+ id
+ admin
+ email
+ uid
+ created_at
+ updated_at
+ ].freeze
+
+ # FORM_ATTRIBUTES
+ # an array of attributes that will be displayed
+ # on the model's form (`new` and `edit`) pages.
+ FORM_ATTRIBUTES = %i[
+ admin
+ email
+ uid
+ ].freeze
+
+ # COLLECTION_FILTERS
+ # a hash that defines filters that can be used while searching via the search
+ # field of the dashboard.
+ #
+ # For example to add an option to search for open resources by typing "open:"
+ # in the search field:
+ #
+ # COLLECTION_FILTERS = {
+ # open: ->(resources) { resources.where(open: true) }
+ # }.freeze
+ COLLECTION_FILTERS = {}.freeze
+
+ # Overwrite this method to customize how users are displayed
+ # across all pages of the admin dashboard.
+ #
+ # def display_resource(user)
+ # "User ##{user.id}"
+ # end
+end
diff --git a/app/views/admin/stats/index.html.erb b/app/views/admin/stats/index.html.erb
new file mode 100644
index 0000000..abd17d9
--- /dev/null
+++ b/app/views/admin/stats/index.html.erb
@@ -0,0 +1,8 @@
+TACOS overview
+
There are <%= number_with_delimiter(@stats[:term_count]) %> unique terms.
+These terms have been received as part of <%= number_with_delimiter(@stats[:search_event_count]) %> search events.
+