Skip to content

Commit

Permalink
Merge branch 'main' into no-file-alert-1221
Browse files Browse the repository at this point in the history
  • Loading branch information
mgrigoriev8109 authored Dec 25, 2024
2 parents 3e3bdb8 + 3b5823a commit 2220f40
Show file tree
Hide file tree
Showing 26 changed files with 280 additions and 221 deletions.
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ gem "ransack"
gem "rails-controller-testing"

# Use Action Policy for authorization framework
gem "action_policy", "~> 0.7.2"
gem "action_policy", "~> 0.7.3"

# Use ViewComponent for our presenter pattern framework
gem "view_component", "~> 3.20"
gem "view_component", "~> 3.21"

# Use dry-types for defining types
gem "dry-types", "~> 1.7"
Expand Down
16 changes: 8 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
action_policy (0.7.2)
action_policy (0.7.3)
ruby-next-core (>= 1.0)
actioncable (8.0.1)
actionpack (= 8.0.1)
Expand Down Expand Up @@ -146,7 +146,7 @@ GEM
railties (>= 6.0.0)
sass-embedded (~> 1.63)
date (3.4.1)
debug (1.9.2)
debug (1.10.0)
irb (~> 1.10)
reline (>= 0.3.8)
devise (4.9.4)
Expand Down Expand Up @@ -277,7 +277,7 @@ GEM
activesupport (>= 6.0.0)
railties (>= 6.0.0)
io-console (0.8.0)
irb (1.14.2)
irb (1.14.3)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jbuilder (2.13.0)
Expand Down Expand Up @@ -310,7 +310,7 @@ GEM
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logger (1.6.3)
logger (1.6.4)
loofah (2.23.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
Expand Down Expand Up @@ -439,7 +439,7 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rdoc (6.9.1)
rdoc (6.10.0)
psych (>= 4.0.0)
regexp_parser (2.9.3)
reline (0.6.0)
Expand Down Expand Up @@ -542,7 +542,7 @@ GEM
unicode-emoji (4.0.4)
uri (1.0.2)
useragent (0.16.11)
view_component (3.20.0)
view_component (3.21.0)
activesupport (>= 5.2.0, < 8.1)
concurrent-ruby (~> 1.0)
method_source (~> 1.0)
Expand Down Expand Up @@ -570,7 +570,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
action_policy (~> 0.7.2)
action_policy (~> 0.7.3)
active_link_to
active_storage_validations
acts_as_tenant
Expand Down Expand Up @@ -625,7 +625,7 @@ DEPENDENCIES
traceroute
turbo-rails
tzinfo-data
view_component (~> 3.20)
view_component (~> 3.21)
web-console

RUBY VERSION
Expand Down
1 change: 1 addition & 0 deletions app/controllers/contacts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class ContactsController < ApplicationController

def new
@contact = Contact.new
@contact.email = current_user.email if current_user.present?
end

def create
Expand Down
1 change: 1 addition & 0 deletions app/controllers/feedback_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class FeedbackController < ApplicationController

def new
@feedback = Feedback.new
@feedback.email = current_user.email if current_user.present?
end

def create
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@ def index

def create
authorize! :external_form_upload, context: {organization: Current.organization}
import = Organizations::Importers::CsvImportService.new(params[:files]).call
render turbo_stream: turbo_stream.replace("results", partial: "organizations/staff/external_form_upload/upload_results", locals: {import: import})
file = params[:files]
@blob = ActiveStorage::Blob.create_and_upload!(io: file, filename: file.original_filename)

CsvImportJob.perform_later(@blob.signed_id, current_user.id)

flash.now[:notice] = t(".processing_file")
respond_to do |format|
format.turbo_stream
end
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions app/jobs/csv_import_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CsvImportJob < ApplicationJob
queue_as :default

def perform(blob_signed_id, current_user_id)
blob = ActiveStorage::Blob.find_signed(blob_signed_id)

Organizations::Importers::CsvImportService.new(blob, current_user_id).call
ensure
blob.purge_later
end
end
3 changes: 2 additions & 1 deletion app/models/contact.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ class Contact
include ActiveModel::Model
attr_accessor :name, :email, :message

validates :name, :email, :message, presence: true
validates :name, :email, presence: true
validates :message, presence: true, length: {maximum: 500}

# credit: https://medium.com/@limichelle21/building-and-debugging-a-contact-form-with-rails-mailgun-heroku-c0185b8bf419
end
2 changes: 1 addition & 1 deletion app/models/organization_account_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class OrganizationAccountRequest

before_validation :normalize_phone

validates :name, :email, :city_town, :country, :province_state, presence: true
validates :name, :email, :requester_name, :city_town, :country, :province_state, presence: true
validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
validates :phone_number, presence: true, phone: {possible: true, allow_blank: true}

Expand Down
9 changes: 5 additions & 4 deletions app/services/organizations/create_service.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# class to create a new location, organization, user, and staff account with role admin
# email is sent to admin user if all steps are successful
# Class to create a new location, organization, user, and staff account with role admin.
# An email is sent to admin user if all steps are successful.
# Be sure to use the Country and State codes from countries_states.yml
# call with Organizations::CreateService.new.signal(args)
# sample args:
# {
# location: {
# country: 'Mexico',
# city_town: 'La Ventana',
# country: 'MX',
# city_town: 'JAL',
# province_state: 'Baja'
# },
# organization: {
Expand Down
69 changes: 37 additions & 32 deletions app/services/organizations/importers/csv_import_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,66 @@ module Organizations
module Importers
class CsvImportService
Status = Data.define(:success?, :count, :no_match, :errors)
def initialize(file)
def initialize(file, current_user_id)
@file = file
@organization = Current.organization
@organization = ActsAsTenant.current_tenant
@current_user = User.find(current_user_id)
@count = 0
@no_match = []
@errors = []
end

def call
catch(:halt_import) do
validate_file

CSV.foreach(@file.to_path, headers: true, skip_blanks: true).with_index(2) do |row, index|
# Header may be different depending on which form applicaiton was used(e.g. google forms) or how it was created(User creates form with "Email Address")
email = row[@email_header].downcase
# Google forms uses "Timestamp", other services may use a different header
csv_timestamp = Time.parse(row["Timestamp"]) if row["Timestamp"].present?

person = Person.find_by(email:, organization: @organization)
previously_matched_form_submission = FormSubmission.where(person:, csv_timestamp:)

if person.nil?
@no_match << [index, email]
elsif previously_matched_form_submission.present?
next
else
ActiveRecord::Base.transaction do
create_form_answers(FormSubmission.create!(person:, csv_timestamp:), row)
@count += 1
@file.download do |data|
validate_file(data)
CSV.parse(data, headers: true, skip_blanks: true).each_with_index do |row, index|
# Header may be different depending on which form applicaiton was used(e.g. google forms) or how it was created(User creates form with "Email Address")
email = row[@email_header].downcase
# Google forms uses "Timestamp", other services may use a different header
csv_timestamp = Time.parse(row["Timestamp"]) if row["Timestamp"].present?

person = Person.find_by(email:, organization: @organization)
previously_matched_form_submission = FormSubmission.where(person:, csv_timestamp:)

if person.nil?
@no_match << [index + 2, email]
elsif previously_matched_form_submission.present?
next
else
ActiveRecord::Base.transaction do
create_form_answers(FormSubmission.create!(person:, csv_timestamp:), row)
@count += 1
end
end
rescue => e
@errors << [index + 2, e]
end
rescue => e
@errors << [index, e]
end
end
Status.new(@errors.empty?, @count, @no_match, @errors)
Turbo::StreamsChannel.broadcast_replace_to ["csv_import", @current_user],
target: "results",
partial: "organizations/staff/external_form_upload/upload_results",
locals: {
import: Status.new(@errors.empty?, @count, @no_match, @errors)
}
end

private

def validate_file
def validate_file(data)
raise FileTypeError unless @file.content_type == "text/csv"

first_row = CSV.foreach(@file.to_path).first
raise FileEmptyError if first_row.nil?
first_row = CSV.new(data).shift

raise TimestampColumnError unless first_row.include?("Timestamp")

email_headers = ["Email", "email", "Email Address", "email address"]
email_headers.each do |e|
@email_header = e if first_row.include?(e)
email_headers.each do |header|
@email_header = header if first_row.include?(header)
end
raise EmailColumnError unless @email_header
rescue FileTypeError, FileEmptyError, TimestampColumnError, EmailColumnError => e
@errors << e
rescue FileTypeError, TimestampColumnError, EmailColumnError => e
@errors << [1, e]
throw :halt_import
end

Expand Down
5 changes: 0 additions & 5 deletions app/views/layouts/adopter_foster_dashboard.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,6 @@
<i class="fe fe-power nav-icon"></i> Log Out
<% end %>
</li>
<li class="nav-item">
<%= active_link_to adopter_fosterer_dashboard_index_path, class: "nav-link" do %>
<i class="fe fe-trash nav-icon"></i> Delete Account
<% end %>
</li>
</ul>
</div>
</div>
Expand Down
20 changes: 10 additions & 10 deletions app/views/layouts/shared/_navbar.html.erb
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
<nav class="navbar navbar-expand-lg bg-transparent shadow-none px-0 py-3">
<div class="container-fluid px-4">
<div class="d-flex gap-2">
<% if Current.organization.avatar.attached? %>
<%= image_tag Current.organization.avatar, alt: current_organization_name, title: current_organization_name, height: 30 %>
<% end %>

<%= link_to home_index_path, class: 'navbar-brand fw-bold text-black' do %>
<%= Current.organization.name %>
<% end %>
</div>

<div class="d-flex align-items-center order-lg-3">
<div>
<button class="navbar-toggler collapsed me-1" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-default3" aria-controls="navbar-default3" aria-expanded="false" aria-label="Toggle navigation">
Expand All @@ -30,6 +20,16 @@
<% end %>
</div>

<div class="d-flex gap-2">
<% if Current.organization.avatar.attached? %>
<%= image_tag Current.organization.avatar, alt: current_organization_name, title: current_organization_name, height: 30 %>
<% end %>

<%= link_to home_index_path, class: 'navbar-brand fw-bold text-black d-none d-sm-block' do %>
<%= Current.organization.name %>
<% end %>
</div>

<!-- Collapse -->
<div class="justify-content-end collapse navbar-collapse" id="navbar-default3">
<ul class="navbar-nav">
Expand Down
2 changes: 1 addition & 1 deletion app/views/organizations/adoptable_pets/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
</div>
<div class="form-group">
<%= f.label :sex_eq, "Sex" %>
<%= f.select :sex_eq, ["Male", "Female"], {include_blank: 'All'}, class: "form-select" %>
<%= f.select :sex_eq, [["Male", "male"], ["Female", "female"]], {include_blank: 'All'}, class: "form-select" %>
</div>
<div class="form-group">
<%= f.label :breed_eq, "Breed" %>
Expand Down
5 changes: 5 additions & 0 deletions app/views/organizations/adoptable_pets/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@
<%= image_tag('green_check.png', height: '21') %>
<% end %>
</div>
<% if 'awaiting_data' == @adoption_application.status %>
<div class="mt-1">
<%= render partial: "shared/empty_state", locals: {text: "Please make sure you fill out the adoption questionnaire so staff have enough information to process your application."} %>
</div>
<% end %>
<% elsif allowed_to?(:manage?, @pet) %>
<%= link_to "Manage #{@pet.name}", staff_pet_path(@pet) %>
<%# FIXME: This section was missed during the remove of AdopterFosterProfile but I think it requires FormSubmission to be ready to refactor %>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
<div class="d-flex flex-column align-items-center mb-4">
<div class="my-4 mx-8">
<% if params[:dashboard] %>

<% if current_user.person.latest_form_submission.present? %>
<p class="fw-bold mb-0"><%= t('organizations.adopter_fosterer.form_instructions.previous_answers') %> <%= link_to t('general.here'), adopter_fosterer_form_answers_path %>.</p>
<p><%= t('organizations.adopter_fosterer.form_instructions.update_form_answers') %></p>
<section class="container d-flex flex-column py-6">
<div class="row justify-content-center gap-6">
<div class="col-8">
<% if params[:dashboard] %>
<% if current_user.person.latest_form_submission.present? %>
<p class="fw-bold mb-0"><%= t('organizations.adopter_fosterer.form_instructions.previous_answers') %> <%= link_to t('general.here'), adopter_fosterer_form_answers_path %>.</p>
<p><%= t('organizations.adopter_fosterer.form_instructions.update_form_answers') %></p>
<% else %>
<p><%= t("organizations.adopter_fosterer.form_instructions.dashboard", user_email: current_user.email) %></p>
<% end %>
<% else %>
<p><%= t("organizations.adopter_fosterer.form_instructions.dashboard", user_email: current_user.email) %></p>
<% end %>

<% else %>
<p><%= t(
"organizations.adopter_fosterer.form_instructions.index",
organization_name: Current.tenant.name,
) %></p>
<div>
<p class="fw-bold"><%= t(
"organizations.adopter_fosterer.form_instructions.index",
organization_name: Current.tenant.name,
email: current_user.email
) %></p>
<%= link_to "Go to my dashboard", adopter_fosterer_dashboard_index_path, class: "btn btn-primary" %>
</div>
<% end %>
<div class="d-flex justify-content-end gap-2 pb-2">
<%= link_to " #{t("general.view")} #{t("general.dogs")}",
adoptable_pets_path(species: "dog"),
class: "btn btn-primary" %>
<%= link_to " #{t("general.view")} #{t("general.cats")}",
adoptable_pets_path(species: "cat"),
class: "btn btn-primary" %>
</div>

<div class="row">
<iframe id="external-form"
src=<%= @form_url%>
width="90%"
height="645"
>Loading…</iframe>
</div>

</div>
<iframe id="external-form"
src=<%= @form_url%>
width="90%"
height="645"
>Loading…</iframe>
</div>
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div id="results" class="d-flex justify-content-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
Loading

0 comments on commit 2220f40

Please sign in to comment.