diff --git a/app/controllers/admin/geozones_controller.rb b/app/controllers/admin/geozones_controller.rb new file mode 100644 index 000000000..b73c13422 --- /dev/null +++ b/app/controllers/admin/geozones_controller.rb @@ -0,0 +1,49 @@ +class Admin::GeozonesController < Admin::BaseController + + respond_to :html + + load_and_authorize_resource + + def index + @geozones = Geozone.all.order("LOWER(name)") + end + + def new + end + + def edit + end + + def create + @geozone = Geozone.new(geozone_params) + + if @geozone.save + redirect_to admin_geozones_path + else + render :new + end + end + + def update + if @geozone.update(geozone_params) + redirect_to admin_geozones_path + else + render :edit + end + end + + def destroy + if @geozone.safe_to_destroy? + @geozone.destroy + redirect_to admin_geozones_path, notice: t('admin.geozones.delete.success') + else + redirect_to admin_geozones_path, flash: { error: t('admin.geozones.delete.error') } + end + end + + private + + def geozone_params + params.require(:geozone).permit(:name, :external_code, :census_code, :html_map_coordinates) + end +end diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index 0dfce6d3e..975ba7a8d 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -43,6 +43,7 @@ module Abilities can [:read, :update, :destroy, :summary], SpendingProposal can [:search, :edit, :update, :create, :index, :destroy], Banner + can [:index, :create, :edit, :update, :destroy], Geozone end end end diff --git a/app/models/geozone.rb b/app/models/geozone.rb index 787e2b7e2..7e38ce97d 100644 --- a/app/models/geozone.rb +++ b/app/models/geozone.rb @@ -1,9 +1,17 @@ class Geozone < ActiveRecord::Base + has_many :proposals has_many :spending_proposals + has_many :debates + has_many :users validates :name, presence: true def self.names Geozone.pluck(:name) end + def safe_to_destroy? + Geozone.reflect_on_all_associations(:has_many).all? do |association| + association.klass.where(geozone: self).empty? + end + end end diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index 7ea48b2c6..3d3e807c7 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -83,6 +83,12 @@ <% end %> +
  • > + <%= link_to admin_geozones_path do %> + <%= t('admin.menu.geozones') %> + <% end %> +
  • +
  • > <%= link_to admin_activity_path do %> <%= t('admin.menu.activity') %> diff --git a/app/views/admin/geozones/_errors.html.erb b/app/views/admin/geozones/_errors.html.erb new file mode 100644 index 000000000..d0f3b849f --- /dev/null +++ b/app/views/admin/geozones/_errors.html.erb @@ -0,0 +1,15 @@ + +<% if @geozone.errors.any? %> + +
    + + + + <%= @geozone.errors.count %> + <%= t("admin.geozones.errors.form.error", count: @geozone.errors.count) %> + +
    + +<% end %> diff --git a/app/views/admin/geozones/_form.html.erb b/app/views/admin/geozones/_form.html.erb new file mode 100644 index 000000000..e92b98c0b --- /dev/null +++ b/app/views/admin/geozones/_form.html.erb @@ -0,0 +1,29 @@ +<%= form_for [:admin, @geozone] do |f| %> + + <%= render 'errors' %> + +
    +
    + <%= f.label :name, t("admin.geozones.geozone.name") %> + <%= f.text_field :name, label: false %> +
    +
    + <%= f.label :html_map_coordinates, t("admin.geozones.geozone.coordinates") %> + <%= f.text_field :html_map_coordinates, label: false %> +
    +
    + <%= f.label :external_code, t("admin.geozones.geozone.external_code") %> + <%= f.text_field :external_code, label: false %> +
    +
    + <%= f.label :census_code, t("admin.geozones.geozone.census_code") %> + <%= f.text_field :census_code, label: false %> +
    +
    + +
    +
    + <%= f.submit(class: "button expanded", value: t("admin.geozones.edit.form.submit_button")) %> +
    +
    +<% end %> diff --git a/app/views/admin/geozones/edit.html.erb b/app/views/admin/geozones/edit.html.erb new file mode 100644 index 000000000..b6b8c3fd9 --- /dev/null +++ b/app/views/admin/geozones/edit.html.erb @@ -0,0 +1,13 @@ +
    + +
    + <%= link_to admin_geozones_path, class: "back" do %> + + <%= t("admin.geozones.edit.back") %> + <% end %> + +

    <%= t("admin.geozones.edit.editing") %>

    + + <%= render "form" %> +
    +
    diff --git a/app/views/admin/geozones/index.html.erb b/app/views/admin/geozones/index.html.erb new file mode 100644 index 000000000..5a524e46e --- /dev/null +++ b/app/views/admin/geozones/index.html.erb @@ -0,0 +1,33 @@ +<%= link_to t("admin.geozones.index.create"), + new_admin_geozone_path, class: "button success float-right" %> + +

    <%= t("admin.geozones.index.title") %>

    + + + + + + + + + + + + + + <% @geozones.each do |geozone| %> + + + + + + + + + <% end %> + +
    <%= t("admin.geozones.geozone.name") %><%= t("admin.geozones.geozone.external_code") %><%= t("admin.geozones.geozone.census_code") %><%= t("admin.geozones.geozone.coordinates") %>
    <%= geozone.name %><%= geozone.external_code %><%= geozone.census_code %><%= geozone.html_map_coordinates %> + <%= link_to t("admin.geozones.index.edit"), edit_admin_geozone_path(geozone), class: 'edit-banner button hollow' %> + + <%= link_to t("admin.geozones.index.delete"), admin_geozone_path(geozone), method: :delete, class: 'button hollow alert' %> +
    diff --git a/app/views/admin/geozones/new.html.erb b/app/views/admin/geozones/new.html.erb new file mode 100644 index 000000000..0d5080337 --- /dev/null +++ b/app/views/admin/geozones/new.html.erb @@ -0,0 +1,13 @@ +
    + +
    + <%= link_to admin_geozones_path, class: "back" do %> + + <%= t("admin.geozones.new.back") %> + <% end %> + +

    <%= t("admin.geozones.new.creating") %>

    + + <%= render "form" %> +
    +
    diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 2f73f64d8..370a06ccc 100755 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -97,6 +97,7 @@ en: admin: Admin menu banner: Manage banners debate_topics: Debate topics + geozones: Manage geozones hidden_comments: Hidden comments hidden_debates: Hidden debates hidden_proposals: Hidden proposals @@ -267,6 +268,33 @@ en: in_evaluation_count: In evaluation total_count: Total cost_for_geozone: Cost + geozones: + index: + title: Geozone + create: Create geozone + edit: Edit + delete: Delete + geozone: + name: Name + external_code: External code + census_code: Census code + coordinates: Coordinates + errors: + form: + error: + one: "prevented this geozone from being saved" + other: 'prevented this geozone from being saved' + edit: + form: + submit_button: Save changes + editing: Editing geozone + back: Go back + new: + back: Go back + creating: Create district + delete: + success: Geozone successfully deleted + error: This geozone can't be deleted since there are elements attached to it stats: show: stats_title: Stats diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 5aada1ce1..8819905d8 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -95,6 +95,7 @@ es: admin: Menú de administración banner: Gestionar banners debate_topics: Temas de debate + geozones: Gestionar distritos hidden_comments: Comentarios ocultos hidden_debates: Debates ocultos hidden_proposals: Propuestas ocultas @@ -265,6 +266,33 @@ es: in_evaluation_count: En evaluación total_count: Total cost_for_geozone: Coste total + geozones: + index: + title: Distritos + create: Crear un distrito + edit: Editar + delete: Borrar + geozone: + name: Nombre + external_code: Código externo + census_code: Código del censo + coordinates: Coordenadas + errors: + form: + error: + one: "error impidió guardar el distrito" + other: "errores impidieron guardar el distrito." + edit: + form: + submit_button: Guardar cambios + editing: Editando distrito + back: Volver + new: + back: Volver + creating: Crear distrito + delete: + success: Distrito borrado correctamente + error: No se puede borrar el distrito porque ya tiene elementos asociados stats: show: stats_title: Estadísticas diff --git a/config/routes.rb b/config/routes.rb index df1a7c6fb..885d155db 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -192,6 +192,8 @@ Rails.application.routes.draw do namespace :api do resource :stats, only: :show end + + resources :geozones, only: [:index, :new, :create, :edit, :update, :destroy] end namespace :moderation do diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb index 87dc94a2e..02e5f9f6d 100644 --- a/db/dev_seeds.rb +++ b/db/dev_seeds.rb @@ -34,7 +34,7 @@ Setting.create(key: 'per_page_code', value: "") Setting.create(key: 'comments_body_max_length', value: '1000') puts "Creating Geozones" -('A'..'Z').each{ |i| Geozone.create(name: "District #{i}") } +('A'..'Z').each { |i| Geozone.create(name: "District #{i}", external_code: i.ord, census_code: i.ord) } puts "Creating Users" diff --git a/spec/factories.rb b/spec/factories.rb index 7b446f3a0..c53f16be6 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -317,7 +317,8 @@ FactoryGirl.define do factory :geozone do sequence(:name) { |n| "District #{n}" } - census_code { '01' } + sequence(:external_code) { |n| "#{n}" } + sequence(:census_code) { |n| "#{n}" } end factory :banner do diff --git a/spec/features/admin/geozones_spec.rb b/spec/features/admin/geozones_spec.rb new file mode 100644 index 000000000..44f9b0d03 --- /dev/null +++ b/spec/features/admin/geozones_spec.rb @@ -0,0 +1,100 @@ +require 'rails_helper' + +feature 'Admin geozones' do + + background do + login_as(create(:administrator).user) + end + + scenario 'Show list of geozones' do + chamberi = create(:geozone, name: 'Chamberí') + retiro = create(:geozone, name: 'Retiro') + + visit admin_geozones_path + + expect(page).to have_content(chamberi.name) + expect(page).to have_content(retiro.name) + end + + scenario 'Create new geozone' do + visit admin_root_path + + within('#side_menu') { click_link 'Manage geozones' } + + click_link 'Create geozone' + + fill_in 'geozone_name', with: 'Fancy District' + fill_in 'geozone_external_code', with: 123 + fill_in 'geozone_census_code', with: 44 + + click_button 'Save changes' + + expect(page).to have_content 'Fancy District' + + visit admin_geozones_path + + expect(page).to have_content 'Fancy District' + end + + scenario 'Edit geozone with no associated elements' do + geozone = create(:geozone, name: 'Edit me!', census_code: '012') + + visit admin_geozones_path + + within("#geozone_#{geozone.id}") { click_link 'Edit' } + + fill_in 'geozone_name', with: 'New geozone name' + fill_in 'geozone_census_code', with: '333' + + click_button 'Save changes' + + within("#geozone_#{geozone.id}") do + expect(page).to have_content 'New geozone name' + expect(page).to have_content '333' + end + end + + scenario 'Edit geozone with associated elements' do + geozone = create(:geozone, name: 'Edit me!') + create(:proposal, title: 'Proposal with geozone', geozone: geozone) + + visit admin_geozones_path + + within("#geozone_#{geozone.id}") { click_link 'Edit' } + + fill_in 'geozone_name', with: 'New geozone name' + + click_button 'Save changes' + + within("#geozone_#{geozone.id}") do + expect(page).to have_content 'New geozone name' + end + end + + scenario 'Delete geozone with no associated elements' do + geozone = create(:geozone, name: 'Delete me!') + + visit admin_geozones_path + + within("#geozone_#{geozone.id}") { click_link 'Delete' } + + expect(page).to have_content 'Geozone successfully deleted' + expect(page).not_to have_content('Delete me!') + expect(Geozone.where(id: geozone.id)).to be_empty + end + + scenario 'Delete geozone with associated element' do + geozone = create(:geozone, name: 'Delete me!') + create(:proposal, geozone: geozone) + + visit admin_geozones_path + + within("#geozone_#{geozone.id}") { click_link 'Delete' } + + expect(page).to have_content "This geozone can't be deleted since there are elements attached to it" + + within("#geozone_#{geozone.id}") do + expect(page).to have_content 'Delete me!' + end + end +end diff --git a/spec/models/geozone_spec.rb b/spec/models/geozone_spec.rb index a29c4c918..1a7bf6b09 100644 --- a/spec/models/geozone_spec.rb +++ b/spec/models/geozone_spec.rb @@ -11,4 +11,30 @@ RSpec.describe Geozone, type: :model do geozone.name = nil expect(geozone).to_not be_valid end + + describe "#safe_to_destroy?" do + it "is true when not linked to other models" do + expect(geozone.safe_to_destroy?).to be_truthy + end + + it "is false when already linked to user" do + create(:user, geozone: geozone) + expect(geozone.safe_to_destroy?).to be_falsey + end + + it "is false when already linked to proposal" do + create(:proposal, geozone: geozone) + expect(geozone.safe_to_destroy?).to be_falsey + end + + it "is false when already linked to spending proposal" do + create(:spending_proposal, geozone: geozone) + expect(geozone.safe_to_destroy?).to be_falsey + end + + it "is false when already linked to debate" do + create(:debate, geozone: geozone) + expect(geozone.safe_to_destroy?).to be_falsey + end + end end