diff --git a/app/controllers/admin/contacts_controller.rb b/app/controllers/admin/contacts_controller.rb index 2fd4f9f7a..094954e64 100644 --- a/app/controllers/admin/contacts_controller.rb +++ b/app/controllers/admin/contacts_controller.rb @@ -18,15 +18,11 @@ def update @contact = Contact.find(params[:id]) @location = Location.find(params[:location_id]) - respond_to do |format| - if @contact.update(params[:contact]) - format.html do - redirect_to [:admin, @location, @contact], - notice: 'Contact was successfully updated.' - end - else - format.html { render :edit } - end + if @contact.update(params[:contact]) + flash[:notice] = 'Contact was successfully updated.' + redirect_to [:admin, @location, @contact] + else + render :edit end end @@ -46,27 +42,19 @@ def create @location = Location.find(params[:location_id]) @contact = @location.contacts.new(params[:contact]) - respond_to do |format| - if @contact.save - format.html do - redirect_to admin_location_path(@location), - notice: "Contact '#{@contact.name}' was successfully created." - end - else - format.html { render :new } - end + if @contact.save + flash[:notice] = "Contact '#{@contact.name}' was successfully created." + redirect_to admin_location_path(@location) + else + render :new end end def destroy contact = Contact.find(params[:id]) contact.destroy - respond_to do |format| - format.html do - redirect_to admin_location_path(contact.location), - notice: "Contact '#{contact.name}' was successfully deleted." - end - end + redirect_to admin_location_path(contact.location), + notice: "Contact '#{contact.name}' was successfully deleted." end end end diff --git a/app/controllers/admin/organization_contacts_controller.rb b/app/controllers/admin/organization_contacts_controller.rb new file mode 100644 index 000000000..09c35cd4d --- /dev/null +++ b/app/controllers/admin/organization_contacts_controller.rb @@ -0,0 +1,60 @@ +class Admin + class OrganizationContactsController < ApplicationController + before_action :authenticate_admin! + layout 'admin' + + def edit + @organization = Organization.find(params[:organization_id]) + @contact = Contact.find(params[:id]) + @admin_decorator = AdminDecorator.new(current_admin) + + unless @admin_decorator.allowed_to_access_organization?(@organization) + redirect_to admin_dashboard_path, + alert: "Sorry, you don't have access to that page." + end + end + + def update + @contact = Contact.find(params[:id]) + @organization = Organization.find(params[:organization_id]) + + if @contact.update(params[:contact]) + flash[:notice] = 'Contact was successfully updated.' + redirect_to [:admin, @organization, @contact] + else + render :edit + end + end + + def new + @admin_decorator = AdminDecorator.new(current_admin) + @organization = Organization.find(params[:organization_id]) + + unless @admin_decorator.allowed_to_access_organization?(@organization) + redirect_to admin_dashboard_path, + alert: "Sorry, you don't have access to that page." + end + + @contact = Contact.new + end + + def create + @organization = Organization.find(params[:organization_id]) + @contact = @organization.contacts.new(params[:contact]) + + if @contact.save + flash[:notice] = "Contact '#{@contact.name}' was successfully created." + redirect_to admin_organization_path(@organization) + else + render :new + end + end + + def destroy + contact = Contact.find(params[:id]) + contact.destroy + redirect_to admin_organization_path(contact.organization), + notice: "Contact '#{contact.name}' was successfully deleted." + end + end +end diff --git a/app/controllers/api/v1/organizations_controller.rb b/app/controllers/api/v1/organizations_controller.rb index c21e4e14e..4663d7422 100644 --- a/app/controllers/api/v1/organizations_controller.rb +++ b/app/controllers/api/v1/organizations_controller.rb @@ -6,7 +6,7 @@ class OrganizationsController < ApplicationController include CustomErrors def index - orgs = Organization.page(params[:page]).per(params[:per_page]) + orgs = Organization.includes(:contacts).page(params[:page]).per(params[:per_page]) render json: orgs, status: 200 generate_pagination_headers(orgs) end diff --git a/app/models/contact.rb b/app/models/contact.rb index 0cf61619d..0659ee34e 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -4,6 +4,7 @@ class Contact < ActiveRecord::Base attr_accessible :department, :email, :name, :title, :phones_attributes belongs_to :location, touch: true + belongs_to :organization has_many :phones, dependent: :destroy accepts_nested_attributes_for :phones, diff --git a/app/models/organization.rb b/app/models/organization.rb index 70020eadd..486ba4b71 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -7,6 +7,7 @@ class Organization < ActiveRecord::Base has_many :locations, dependent: :destroy has_many :programs, dependent: :destroy + has_many :contacts, dependent: :destroy validates :name, presence: { message: I18n.t('errors.messages.blank_for_org') }, diff --git a/app/serializers/location_serializer.rb b/app/serializers/location_serializer.rb index 838f9d2f8..31a3a86d4 100644 --- a/app/serializers/location_serializer.rb +++ b/app/serializers/location_serializer.rb @@ -11,7 +11,7 @@ class LocationSerializer < ActiveModel::Serializer has_many :services has_many :regular_schedules has_many :holiday_schedules - has_one :organization + has_one :organization, serializer: SummarizedOrganizationSerializer def url api_location_url(object) diff --git a/app/serializers/locations_organization_serializer.rb b/app/serializers/locations_organization_serializer.rb new file mode 100644 index 000000000..307fe34f8 --- /dev/null +++ b/app/serializers/locations_organization_serializer.rb @@ -0,0 +1,13 @@ +class LocationsOrganizationSerializer < ActiveModel::Serializer + attributes :id, :accreditations, :alternate_name, :date_incorporated, + :description, :email, :funding_sources, :licenses, :name, :slug, + :website, :url, :locations_url + + def url + api_organization_url(object) + end + + def locations_url + api_org_locations_url(object) + end +end diff --git a/app/serializers/locations_serializer.rb b/app/serializers/locations_serializer.rb index 92d4adafc..e2ee2063a 100644 --- a/app/serializers/locations_serializer.rb +++ b/app/serializers/locations_serializer.rb @@ -4,7 +4,7 @@ class LocationsSerializer < ActiveModel::Serializer :updated_at, :urls, :contacts_url, :services_url, :url has_one :address - has_one :organization + has_one :organization, serializer: LocationsOrganizationSerializer has_many :phones def contacts_url diff --git a/app/serializers/organization_serializer.rb b/app/serializers/organization_serializer.rb index b7ef491f3..a59948db6 100644 --- a/app/serializers/organization_serializer.rb +++ b/app/serializers/organization_serializer.rb @@ -3,6 +3,8 @@ class OrganizationSerializer < ActiveModel::Serializer :description, :email, :funding_sources, :licenses, :name, :slug, :website, :url, :locations_url + has_many :contacts + def url api_organization_url(object) end diff --git a/app/serializers/summarized_organization_serializer.rb b/app/serializers/summarized_organization_serializer.rb new file mode 100644 index 000000000..7fd23b36d --- /dev/null +++ b/app/serializers/summarized_organization_serializer.rb @@ -0,0 +1,7 @@ +class SummarizedOrganizationSerializer < ActiveModel::Serializer + attributes :id, :alternate_name, :name, :slug, :url + + def url + api_organization_url(object) + end +end diff --git a/app/views/admin/contacts/forms/_new_org_contact.html.haml b/app/views/admin/contacts/forms/_new_org_contact.html.haml new file mode 100644 index 000000000..20e5b0181 --- /dev/null +++ b/app/views/admin/contacts/forms/_new_org_contact.html.haml @@ -0,0 +1,8 @@ += render 'admin/contacts/forms/fields', f: f + +.save-box.navbar-default + %p + = 'Creating contact for' + %strong + = "#{@organization.name}" + = f.submit 'Create contact', class: 'btn btn-success', data: { disable_with: 'Please wait...' } diff --git a/app/views/admin/locations/forms/_contact_fields.html.haml b/app/views/admin/locations/forms/_contact_fields.html.haml deleted file mode 100644 index 7179da6de..000000000 --- a/app/views/admin/locations/forms/_contact_fields.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -= field_set_tag do - .form-group - = f.label :name, 'Name' - .row - .col-md-6 - = f.text_field :name, maxlength: 255, class: 'form-control' - .form-group - = f.label :title, 'Title' - .row - .col-md-6 - = f.text_field :title, maxlength: 255, class: 'form-control' - .form-group - = f.label :email, 'Email' - .row - .col-md-6 - = f.text_field :email, maxlength: 255, class: 'form-control' - .form-group - = f.label :department, 'Department' - .row - .col-md-6 - = f.text_field :department, maxlength: 255, class: 'form-control' - = f.hidden_field :_destroy - - = link_to 'Delete this contact permanently', '#', class: 'btn btn-danger delete_association' - %hr diff --git a/app/views/admin/locations/forms/_contacts.html.haml b/app/views/admin/locations/forms/_contacts.html.haml deleted file mode 100644 index fdf631bc3..000000000 --- a/app/views/admin/locations/forms/_contacts.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -.inst-box.contacts - %header - %strong - Contacts - %p.desc - Who are the main points of contact at the location? - = f.fields_for :contacts do |builder| - = render 'admin/locations/forms/contact_fields', f: builder - = link_to_add_fields 'Add a new contact', f, :contacts diff --git a/app/views/admin/locations/forms/_phone_fields.html.haml b/app/views/admin/locations/forms/_phone_fields.html.haml index e7ebe7a39..3ea82e505 100644 --- a/app/views/admin/locations/forms/_phone_fields.html.haml +++ b/app/views/admin/locations/forms/_phone_fields.html.haml @@ -18,7 +18,7 @@ - else = f.select :number_type, Phone.number_type.options, {}, class: 'form-control' .form-group - = f.label :extension, 'Extension (for example: x101)' + = f.label :extension, 'Extension (numbers only)' .row .col-md-4 = f.text_field :extension, maxlength: 8, class: 'form-control' diff --git a/app/views/admin/organization_contacts/edit.html.haml b/app/views/admin/organization_contacts/edit.html.haml new file mode 100644 index 000000000..012e2abfb --- /dev/null +++ b/app/views/admin/organization_contacts/edit.html.haml @@ -0,0 +1,4 @@ +.content-box + %h2= "#{@contact.try(:name)} / #{@organization.name}" += form_for [:admin, @organization, @contact], html: { class: 'edit_entry' } do |f| + = render 'admin/contacts/form', f: f diff --git a/app/views/admin/organization_contacts/new.html.haml b/app/views/admin/organization_contacts/new.html.haml new file mode 100644 index 000000000..658d5e4dd --- /dev/null +++ b/app/views/admin/organization_contacts/new.html.haml @@ -0,0 +1,5 @@ +.content-box + %h1 Create a new contact + += form_for [:admin, @organization, @contact], html: { method: :post, class: 'edit_entry' } do |f| + = render 'admin/contacts/forms/new_org_contact', f: f diff --git a/app/views/admin/organizations/_form.html.haml b/app/views/admin/organizations/_form.html.haml index 3412e08d3..60d93e5b5 100644 --- a/app/views/admin/organizations/_form.html.haml +++ b/app/views/admin/organizations/_form.html.haml @@ -1,5 +1,16 @@ = render 'admin/organizations/forms/fields', f: f +.content-box + %h2= 'Contacts' + - if @organization.contacts.present? + = 'Click a Contact below to view and update it:' + %p + - @organization.contacts.each do |contact| + = link_to contact.name, edit_admin_organization_contact_path(@organization, contact) + %br + %p + = link_to 'Add a new contact', new_admin_organization_contact_path(@organization), class: 'btn btn-primary' + .danger-zone %header %strong diff --git a/config/routes.rb b/config/routes.rb index 4bd89bb25..d3236aec5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -19,7 +19,9 @@ resources :contacts, except: [:show, :index] end - resources :organizations, except: :show + resources :organizations, except: :show do + resources :contacts, except: [:show, :index], controller: 'organization_contacts' + end resources :programs, except: :show resources :services, only: :index @@ -32,6 +34,7 @@ get 'locations/:location_id/contacts/:id', to: 'contacts#edit' get 'locations/:id', to: 'locations#edit' get 'organizations/:id', to: 'organizations#edit' + get 'organizations/:organization_id/contacts/:id', to: 'organization_contacts#edit' get 'programs/:id', to: 'programs#edit' end end diff --git a/data/ohana_api_development.dump b/data/ohana_api_development.dump index e2a0d7564..9338262f7 100644 Binary files a/data/ohana_api_development.dump and b/data/ohana_api_development.dump differ diff --git a/db/migrate/20141106215928_add_organization_ref_to_contacts.rb b/db/migrate/20141106215928_add_organization_ref_to_contacts.rb new file mode 100644 index 000000000..f2a22c2af --- /dev/null +++ b/db/migrate/20141106215928_add_organization_ref_to_contacts.rb @@ -0,0 +1,5 @@ +class AddOrganizationRefToContacts < ActiveRecord::Migration + def change + add_reference :contacts, :organization, index: true + end +end diff --git a/db/structure.sql b/db/structure.sql index d6dd05eca..be0c790ea 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -243,7 +243,8 @@ CREATE TABLE contacts ( email text, created_at timestamp without time zone, updated_at timestamp without time zone, - department character varying(255) + department character varying(255), + organization_id integer ); @@ -977,6 +978,13 @@ CREATE UNIQUE INDEX index_categories_services_on_service_id_and_category_id ON c CREATE INDEX index_contacts_on_location_id ON contacts USING btree (location_id); +-- +-- Name: index_contacts_on_organization_id; Type: INDEX; Schema: public; Owner: -; Tablespace: +-- + +CREATE INDEX index_contacts_on_organization_id ON contacts USING btree (organization_id); + + -- -- Name: index_friendly_id_slugs_on_slug_and_sluggable_type; Type: INDEX; Schema: public; Owner: -; Tablespace: -- @@ -1319,3 +1327,5 @@ INSERT INTO schema_migrations (version) VALUES ('20141030012617'); INSERT INTO schema_migrations (version) VALUES ('20141030204742'); +INSERT INTO schema_migrations (version) VALUES ('20141106215928'); + diff --git a/spec/api/get_location_spec.rb b/spec/api/get_location_spec.rb index 025fdc093..3f8388563 100644 --- a/spec/api/get_location_spec.rb +++ b/spec/api/get_location_spec.rb @@ -134,23 +134,14 @@ it 'includes the serialized organization association' do org = @location.organization - locations_url = api_org_locations_url(org) serialized_organization = { 'id' => @location.organization.id, - 'accreditations' => [], 'alternate_name' => nil, - 'date_incorporated' => nil, - 'description' => 'Organization created for testing purposes', - 'email' => nil, - 'funding_sources' => [], - 'licenses' => [], - 'locations_url' => locations_url, 'name' => 'Parent Agency', 'slug' => 'parent-agency', - 'url' => api_organization_url(org), - 'website' => nil + 'url' => api_organization_url(org) } expect(json['organization']).to eq(serialized_organization) diff --git a/spec/api/get_locations_spec.rb b/spec/api/get_locations_spec.rb index d2f0bfd92..b27a73a7b 100644 --- a/spec/api/get_locations_spec.rb +++ b/spec/api/get_locations_spec.rb @@ -163,6 +163,12 @@ expect(json.first.keys).to include('organization') end + it 'does not include contacts within Organization' do + get api_locations_url(subdomain: ENV['API_SUBDOMAIN']) + org_keys = json.first['organization'].keys + expect(org_keys).to_not include('contacts') + end + it 'includes the correct url attribute' do loc_url = json.first['url'] diff --git a/spec/features/admin/contacts/delete_contact_spec.rb b/spec/features/admin/contacts/delete_contact_spec.rb new file mode 100644 index 000000000..a04261884 --- /dev/null +++ b/spec/features/admin/contacts/delete_contact_spec.rb @@ -0,0 +1,19 @@ +require 'rails_helper' + +feature 'Delete contact' do + background do + @location = create(:location) + @location.contacts.create!(attributes_for(:contact)) + login_super_admin + visit '/admin/locations/vrs-services' + click_link 'Moncef Belyamani' + end + + scenario 'when deleting contact' do + find_link('Permanently delete this contact').click + using_wait_time 1 do + expect(current_path).to eq admin_location_path(@location) + expect(page).not_to have_link 'Moncef Belyamani' + end + end +end diff --git a/spec/features/admin/organizations/create_contact_spec.rb b/spec/features/admin/organizations/create_contact_spec.rb new file mode 100644 index 000000000..5874f5ca2 --- /dev/null +++ b/spec/features/admin/organizations/create_contact_spec.rb @@ -0,0 +1,68 @@ +require 'rails_helper' + +feature 'Create a new contact for organization' do + background do + create(:organization) + login_super_admin + visit '/admin/organizations/parent-agency' + click_link 'Add a new contact' + end + + scenario 'with all required fields' do + fill_in 'contact_name', with: 'New VRS Services contact' + click_button 'Create contact' + click_link 'New VRS Services contact' + + expect(find_field('contact_name').value).to eq 'New VRS Services contact' + end + + scenario 'without any required fields' do + click_button 'Create contact' + expect(page).to have_content "Name can't be blank for Contact" + end + + scenario 'with email' do + fill_in 'contact_name', with: 'New VRS Services contact' + fill_in 'contact_email', with: 'foo@bar.com' + click_button 'Create contact' + click_link 'New VRS Services contact' + + expect(find_field('contact_email').value).to eq 'foo@bar.com' + end + + scenario 'with department' do + fill_in 'contact_name', with: 'New VRS Services contact' + fill_in 'contact_department', with: 'new department' + click_button 'Create contact' + click_link 'New VRS Services contact' + + expect(find_field('contact_department').value).to eq 'new department' + end + + scenario 'with title' do + fill_in 'contact_name', with: 'New VRS Services contact' + fill_in 'contact_title', with: 'CTO' + click_button 'Create contact' + click_link 'New VRS Services contact' + + expect(find_field('contact_title').value).to eq 'CTO' + end + + # Contacts use the same form for adding phone numbers as Locations, + # and the form is already tested there. We just need to make sure + # that the form is present for Contacts. + scenario 'with phone' do + expect(page).to have_link 'Add a new phone' + end +end + +describe 'when admin does not have access to the organization' do + it 'denies access to create a new contact' do + create(:organization) + login_admin + + visit('/admin/organizations/parent-agency/contacts/new') + + expect(page).to have_content "Sorry, you don't have access to that page." + end +end diff --git a/spec/features/admin/organizations/delete_contact_spec.rb b/spec/features/admin/organizations/delete_contact_spec.rb new file mode 100644 index 000000000..a22d0b7a2 --- /dev/null +++ b/spec/features/admin/organizations/delete_contact_spec.rb @@ -0,0 +1,19 @@ +require 'rails_helper' + +feature 'Delete contact' do + background do + @org = create(:organization) + @org.contacts.create!(attributes_for(:contact)) + login_super_admin + visit '/admin/organizations/parent-agency' + click_link 'Moncef Belyamani' + end + + scenario 'when deleting contact' do + find_link('Permanently delete this contact').click + using_wait_time 1 do + expect(current_path).to eq admin_organization_path(@org) + expect(page).not_to have_link 'Moncef Belyamani' + end + end +end diff --git a/spec/features/admin/organizations/update_contact_name_spec.rb b/spec/features/admin/organizations/update_contact_name_spec.rb new file mode 100644 index 000000000..b3ada4c2a --- /dev/null +++ b/spec/features/admin/organizations/update_contact_name_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +feature 'Update name' do + background do + org = create(:organization) + org.contacts.create!(attributes_for(:contact)) + login_super_admin + visit '/admin/organizations/parent-agency' + click_link 'Moncef Belyamani' + end + + scenario 'with empty name' do + fill_in 'contact_name', with: '' + click_button 'Save changes' + expect(page).to have_content "Name can't be blank for Contact" + end + + scenario 'with valid name' do + fill_in 'contact_name', with: 'Monfresh' + click_button 'Save changes' + expect(page).to have_content 'Contact was successfully updated.' + expect(find_field('contact_name').value).to eq 'Monfresh' + end +end diff --git a/spec/models/contact_spec.rb b/spec/models/contact_spec.rb index e0f40a4fe..355917643 100644 --- a/spec/models/contact_spec.rb +++ b/spec/models/contact_spec.rb @@ -12,6 +12,7 @@ it { is_expected.to allow_mass_assignment_of(:title) } it { is_expected.to belong_to(:location).touch(true) } + it { is_expected.to belong_to(:organization) } it { is_expected.to have_many(:phones).dependent(:destroy) } it { is_expected.to accept_nested_attributes_for(:phones).allow_destroy(true) } diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb index 90857a3e3..b260b9ad0 100644 --- a/spec/models/organization_spec.rb +++ b/spec/models/organization_spec.rb @@ -21,6 +21,7 @@ it { is_expected.to have_many(:locations).dependent(:destroy) } it { is_expected.to have_many(:programs).dependent(:destroy) } + it { is_expected.to have_many(:contacts).dependent(:destroy) } it do is_expected.to validate_presence_of(:name).