diff --git a/Gemfile b/Gemfile
index 0aaccff5..fe30e152 100644
--- a/Gemfile
+++ b/Gemfile
@@ -111,3 +111,5 @@ gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem "coffee-script"
gem "pundit", "~> 2.2"
+
+gem "madmin", "~> 1.2"
diff --git a/Gemfile.lock b/Gemfile.lock
index 87fd9ac1..6ef7be0a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -168,6 +168,9 @@ GEM
loofah (2.19.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
+ madmin (1.2.7)
+ pagy (>= 3.5, < 6.0)
+ rails (>= 6.0.3)
mail (2.8.1)
mini_mime (>= 0.1.1)
net-imap
@@ -220,6 +223,8 @@ GEM
actionpack (>= 4.2)
omniauth (~> 2.0)
orm_adapter (0.5.0)
+ pagy (5.10.1)
+ activesupport
parallel (1.22.1)
parser (3.2.1.1)
ast (~> 2.4.1)
@@ -409,6 +414,7 @@ DEPENDENCIES
jquery-rails
jquery-ui-rails (~> 5.0, >= 5.0.5)
listen (~> 3.7)
+ madmin (~> 1.2)
matrix
mimemagic (~> 0.3.8)
newrelic_rpm
diff --git a/app/controllers/madmin/application_controller.rb b/app/controllers/madmin/application_controller.rb
new file mode 100644
index 00000000..85dbf73d
--- /dev/null
+++ b/app/controllers/madmin/application_controller.rb
@@ -0,0 +1,9 @@
+module Madmin
+ class ApplicationController < Madmin::BaseController
+ before_action :ensure_admin
+
+ def ensure_admin
+ redirect_to "/" if current_user.nil? || !current_user.admin
+ end
+ end
+end
diff --git a/app/controllers/madmin/estimates_controller.rb b/app/controllers/madmin/estimates_controller.rb
new file mode 100644
index 00000000..15e49bac
--- /dev/null
+++ b/app/controllers/madmin/estimates_controller.rb
@@ -0,0 +1,4 @@
+module Madmin
+ class EstimatesController < Madmin::ResourceController
+ end
+end
diff --git a/app/controllers/madmin/projects_controller.rb b/app/controllers/madmin/projects_controller.rb
new file mode 100644
index 00000000..7547e930
--- /dev/null
+++ b/app/controllers/madmin/projects_controller.rb
@@ -0,0 +1,4 @@
+module Madmin
+ class ProjectsController < Madmin::ResourceController
+ end
+end
diff --git a/app/controllers/madmin/stories_controller.rb b/app/controllers/madmin/stories_controller.rb
new file mode 100644
index 00000000..20651828
--- /dev/null
+++ b/app/controllers/madmin/stories_controller.rb
@@ -0,0 +1,4 @@
+module Madmin
+ class StoriesController < Madmin::ResourceController
+ end
+end
diff --git a/app/controllers/madmin/users_controller.rb b/app/controllers/madmin/users_controller.rb
new file mode 100644
index 00000000..323ea7e0
--- /dev/null
+++ b/app/controllers/madmin/users_controller.rb
@@ -0,0 +1,4 @@
+module Madmin
+ class UsersController < Madmin::ResourceController
+ end
+end
diff --git a/app/madmin/resources/estimate_resource.rb b/app/madmin/resources/estimate_resource.rb
new file mode 100644
index 00000000..15ab46d2
--- /dev/null
+++ b/app/madmin/resources/estimate_resource.rb
@@ -0,0 +1,24 @@
+class EstimateResource < Madmin::Resource
+ # Attributes
+ attribute :id, form: false
+ attribute :best_case_points
+ attribute :worst_case_points
+
+ # Associations
+ attribute :story
+ attribute :user
+
+ # Uncomment this to customize the display name of records in the admin area.
+ # def self.display_name(record)
+ # record.name
+ # end
+
+ # Uncomment this to customize the default sort column and direction.
+ # def self.default_sort_column
+ # "created_at"
+ # end
+ #
+ # def self.default_sort_direction
+ # "desc"
+ # end
+end
diff --git a/app/madmin/resources/project_resource.rb b/app/madmin/resources/project_resource.rb
new file mode 100644
index 00000000..966c2b9f
--- /dev/null
+++ b/app/madmin/resources/project_resource.rb
@@ -0,0 +1,26 @@
+class ProjectResource < Madmin::Resource
+ # Attributes
+ attribute :id, form: false
+ attribute :title
+ attribute :status
+
+ # Associations
+ attribute :stories
+ attribute :estimates
+ attribute :users
+ attribute :parent
+ attribute :projects
+
+ def self.display_name(record)
+ record.title.truncate(20)
+ end
+
+ # Uncomment this to customize the default sort column and direction.
+ # def self.default_sort_column
+ # "created_at"
+ # end
+ #
+ # def self.default_sort_direction
+ # "desc"
+ # end
+end
diff --git a/app/madmin/resources/story_resource.rb b/app/madmin/resources/story_resource.rb
new file mode 100644
index 00000000..1926fd52
--- /dev/null
+++ b/app/madmin/resources/story_resource.rb
@@ -0,0 +1,26 @@
+class StoryResource < Madmin::Resource
+ # Attributes
+ attribute :id, form: false
+ attribute :title
+ attribute :description
+ attribute :real_score
+ attribute :extra_info
+
+ # Associations
+ attribute :project
+ attribute :estimates
+ attribute :users
+
+ def self.display_name(record)
+ record.title.truncate(30)
+ end
+
+ # Uncomment this to customize the default sort column and direction.
+ # def self.default_sort_column
+ # "created_at"
+ # end
+ #
+ # def self.default_sort_direction
+ # "desc"
+ # end
+end
diff --git a/app/madmin/resources/user_resource.rb b/app/madmin/resources/user_resource.rb
new file mode 100644
index 00000000..4139fd06
--- /dev/null
+++ b/app/madmin/resources/user_resource.rb
@@ -0,0 +1,23 @@
+class UserResource < Madmin::Resource
+ # Attributes
+ attribute :id, form: false
+ attribute :email
+ attribute :name
+ attribute :admin
+
+ # Associations
+ attribute :estimates
+
+ def self.display_name(record)
+ record.name.truncate(12)
+ end
+
+ # Uncomment this to customize the default sort column and direction.
+ # def self.default_sort_column
+ # "created_at"
+ # end
+ #
+ # def self.default_sort_direction
+ # "desc"
+ # end
+end
diff --git a/app/views/layouts/madmin/application.html.erb b/app/views/layouts/madmin/application.html.erb
new file mode 100644
index 00000000..674a9501
--- /dev/null
+++ b/app/views/layouts/madmin/application.html.erb
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+ Madmin: <%= Rails.application.class %>
+
+
+ <%= csrf_meta_tags %>
+
+ <%= render "javascript" %>
+
+
+
+
+
+ <%#= render "flashes" -%>
+ <%= yield %>
+
+
+
+
diff --git a/app/views/madmin/application/_form.html.erb b/app/views/madmin/application/_form.html.erb
new file mode 100644
index 00000000..3d2b4337
--- /dev/null
+++ b/app/views/madmin/application/_form.html.erb
@@ -0,0 +1,23 @@
+<%= form_with model: [:madmin, record], url: (record.persisted? ? resource.show_path(record) : resource.index_path), local: true do |form| %>
+ <% if form.object.errors.any? %>
+
+
There was <%= pluralize form.object.errors.full_messages.count, "error" %> with your submission
+
+ <% form.object.errors.full_messages.each do |message| %>
+
<%= message %>
+ <% end %>
+
+ <% end %>
+
+ <% resource.attributes.values.each do |attribute| %>
+ <% next if attribute.field.nil? %>
+ <% next unless attribute.field.visible?(action_name) %>
+ <% next unless attribute.field.visible?(:form) %>
+
+
+ <%= render partial: attribute.field.to_partial_path("form"), locals: { field: attribute.field, record: record, form: form, resource: resource } %>
+
+ <% end %>
+
+ <%= form.submit class: "bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow" %>
+<% end %>
diff --git a/app/views/madmin/application/_javascript.html.erb b/app/views/madmin/application/_javascript.html.erb
new file mode 100644
index 00000000..fb8d4fd5
--- /dev/null
+++ b/app/views/madmin/application/_javascript.html.erb
@@ -0,0 +1,107 @@
+
+<%= stylesheet_link_tag "https://unpkg.com/flatpickr/dist/flatpickr.min.css", "data-turbo-track": "reload" %>
+<%= stylesheet_link_tag "https://unpkg.com/trix/dist/trix.css", "data-turbo-track": "reload" %>
+<%= stylesheet_link_tag "https://unpkg.com/tom-select/dist/css/tom-select.min.css", "data-turbo-track": "reload" %>
+
+
+
+
+
diff --git a/app/views/madmin/application/_navigation.html.erb b/app/views/madmin/application/_navigation.html.erb
new file mode 100644
index 00000000..54f5240a
--- /dev/null
+++ b/app/views/madmin/application/_navigation.html.erb
@@ -0,0 +1,32 @@
+
+
+
+
Madmin
+ <% if main_app.respond_to?(:root_url) %>
+ <%= link_to main_app.root_url, class: "block p-2 rounded hover:bg-gray-200", data: { turbo: false } do %>
+ ← Back to App
+ <% end %>
+ <% end %>
+
+
+
+
+ Open main menu
+
+
+
+
+
+
+
+
+
+ <% Madmin.resources.each do |resource| %>
+ <%= nav_link_to resource.friendly_name.pluralize, resource.index_path, class: "block p-2 rounded hover:bg-gray-100", starts_with: resource.index_path, active_class: "font-bold text-black" %>
+ <% end %>
+
+
+ <%= link_to "View Madmin on GitHub", "https://github.com/excid3/madmin", target: :_blank, class: "block p-2 rounded text-gray-500 hover:bg-gray-100" %>
+
+
+
diff --git a/app/views/madmin/application/edit.html.erb b/app/views/madmin/application/edit.html.erb
new file mode 100644
index 00000000..0d3c8a51
--- /dev/null
+++ b/app/views/madmin/application/edit.html.erb
@@ -0,0 +1,7 @@
+
+ <%= link_to resource.friendly_name.pluralize, resource.index_path, class: "text-indigo-500" %>
+ /
+ Edit <%= link_to resource.display_name(@record), resource.show_path(@record), class: "text-indigo-500" %>
+
+
+<%= render partial: "form", locals: { record: @record, resource: resource } %>
diff --git a/app/views/madmin/application/index.html.erb b/app/views/madmin/application/index.html.erb
new file mode 100644
index 00000000..0e17c2a7
--- /dev/null
+++ b/app/views/madmin/application/index.html.erb
@@ -0,0 +1,58 @@
+
+
<%= resource.friendly_name.pluralize %>
+
+
+
+
+
+ <% if resource.scopes.any? %>
+ <%= link_to "All", resource.index_path, class: class_names("p-2 rounded", {"bg-gray-100" => params[:scope].blank?}) %>
+ <% end %>
+
+ <% resource.scopes.each do |scope| %>
+ <%= link_to scope.to_s.humanize, resource.index_path(scope: scope), class: class_names("p-2 rounded", {"bg-gray-100" => params[:scope] == scope.to_s}) %>
+ <% end %>
+
+
+
+
+
+ <% resource.attributes.values.each do |attribute| %>
+ <% next if attribute.field.nil? %>
+ <% next unless attribute.field.visible?(action_name) %>
+
+ <%= sortable attribute.name, attribute.name.to_s.titleize %>
+ <% end %>
+ Actions
+
+
+
+
+ <% @records.each do |record| %>
+
+ <% resource.attributes.values.each do |attribute| %>
+ <% next if attribute.field.nil? %>
+ <% next unless attribute.field.visible?(action_name) %>
+ <%= render partial: attribute.field.to_partial_path("index"), locals: { field: attribute.field, record: record, resource: resource } %>
+ <% end %>
+
+
+ <%= link_to "View", resource.show_path(record), class: "text-indigo-500" %>
+ <%# = link_to "Edit", resource.edit_path(record), class: "text-indigo-500" %>
+
+
+ <% end %>
+
+
+
+<%== render(partial: 'madmin/pagy/nav', locals: { pagy: @pagy }) if @pagy.pages > 1 %>
diff --git a/app/views/madmin/application/new.html.erb b/app/views/madmin/application/new.html.erb
new file mode 100644
index 00000000..39a5bd81
--- /dev/null
+++ b/app/views/madmin/application/new.html.erb
@@ -0,0 +1,7 @@
+
+ <%= link_to resource.friendly_name.pluralize, resource.index_path, class: "text-indigo-500" %>
+ /
+ New <%= resource.friendly_name %>
+
+
+<%= render partial: "form", locals: { record: @record, resource: resource } %>
diff --git a/app/views/madmin/application/show.html.erb b/app/views/madmin/application/show.html.erb
new file mode 100644
index 00000000..d83dd3a2
--- /dev/null
+++ b/app/views/madmin/application/show.html.erb
@@ -0,0 +1,31 @@
+
+
+ <%= link_to resource.friendly_name.pluralize, resource.index_path, class: "text-indigo-500" %>
+ /
+ <%= link_to resource.display_name(@record), resource.show_path(@record), class: "text-indigo-500 font-bold" %>
+
+
+
+
+ <%# = link_to "Edit", resource.edit_path(@record), class: "block bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow" %>
+
+ <%# = button_to "Delete", resource.show_path(@record), method: :delete, data: { turbo_confirm: "Are you sure?" }, class: "bg-white hover:bg-gray-100 text-red-500 font-semibold py-2 px-4 border border-red-500 rounded shadow pointer-cursor" %>
+
+
+
+
+ <% resource.attributes.values.each do |attribute| %>
+ <% next if attribute.field.nil? %>
+ <% next unless attribute.field.visible?(action_name) %>
+
+
+
+ <%= attribute.field.attribute_name.to_s.titleize %>
+
+
+
+ <%= render partial: attribute.field.to_partial_path("show"), locals: { field: attribute.field, record: @record, resource: resource } %>
+
+
+ <% end %>
+
diff --git a/config/routes.rb b/config/routes.rb
index 151bf369..17744076 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,5 @@
Rails.application.routes.draw do
+ draw :madmin
devise_for :users, controllers: {omniauth_callbacks: "callbacks"}
authenticated do
diff --git a/config/routes/madmin.rb b/config/routes/madmin.rb
new file mode 100644
index 00000000..9f95e715
--- /dev/null
+++ b/config/routes/madmin.rb
@@ -0,0 +1,8 @@
+# Below are the routes for madmin
+namespace :madmin do
+ resources :projects, except: [:update, :edit, :create]
+ resources :stories
+ resources :estimates, except: [:update, :edit, :create]
+ resources :users, except: [:update, :edit, :create]
+ root to: "dashboard#show"
+end