From 51341cd60ac696a04536072f3c418c06fdc2ca14 Mon Sep 17 00:00:00 2001 From: Alicia Date: Thu, 21 Mar 2024 15:24:29 -0600 Subject: [PATCH 01/48] Remove workaround after devise upgrade --- config/initializers/devise.rb | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index e128034d5..7b994f840 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -1,21 +1,5 @@ # frozen_string_literal: true -class TurboFailureApp < Devise::FailureApp - def respond - if request_format == :turbo_stream - redirect - else - super - end - end - - def skip_format? - %w[html turbo_stream */*].include? request_format.to_s - end -end - -# frozen_string_literal: true - # Assuming you have not yet modified this file, each configuration option below # is set to its default value. Note that some are commented out while others # are not: uncommented lines are intended to protect your configuration from @@ -34,7 +18,6 @@ def skip_format? # ==> Controller configuration # Configure the parent class to the devise controllers. - config.parent_controller = "TurboController" # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer, @@ -279,7 +262,6 @@ def skip_format? # should add them to the navigational formats lists. # # The "*/*" below is required to match Internet Explorer requests. - config.navigational_formats = ["*/*", :html, :turbo_stream] # The default HTTP method used to sign out a resource. Default is :delete. config.sign_out_via = :delete @@ -293,11 +275,10 @@ def skip_format? # If you want to use other strategies, that are not supported by Devise, or # change the failure app, you can configure them inside the config.warden block. # - config.warden do |manager| - manager.failure_app = TurboFailureApp - # manager.intercept_401 = false - # manager.default_strategies(scope: :user).unshift :some_external_strategy - end + + # ==> Configure responses to match Hotwire/Turbo behavior. + config.responder.error_status = :unprocessable_entity + config.responder.redirect_status = :see_other # ==> Mountable engine configurations # When using Devise inside an engine, let's call it `MyEngine`, and this engine From d07203632c71b0d2ad43da04bec9d12431414743 Mon Sep 17 00:00:00 2001 From: Alicia Date: Thu, 21 Mar 2024 16:19:08 -0600 Subject: [PATCH 02/48] Share location and display it in navbar --- app/components/navbar/component.html.slim | 14 ++++-- app/components/navbar/component.rb | 11 +---- app/controllers/application_controller.rb | 5 ++ .../controllers/geolocation_controller.js | 49 +++++++++++++++++++ app/models/search.rb | 1 + app/views/layouts/application.html.erb | 4 +- 6 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 app/javascript/controllers/geolocation_controller.js diff --git a/app/components/navbar/component.html.slim b/app/components/navbar/component.html.slim index 65d22c5c9..ea5170940 100644 --- a/app/components/navbar/component.html.slim +++ b/app/components/navbar/component.html.slim @@ -10,20 +10,24 @@ nav id="main-navbar" data-controller="navbar--component" class="#{non_sticky_pat = link_to root_path = inline_svg_tag 'logo.svg', size:'130*32', class:'mr-2 mid:mr-7 lg:mr-11' div class="flex w-full" - li class="mr-2 mid:mr-7 lg:mr-11 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'].include?("/search") ? "text-seafoam border-seafoam" : ""}" + li class="mr-1 mid:mr-5 lg:mr-9 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'].include?("/search") ? "text-seafoam border-seafoam" : ""}" = link_to 'Search', search_path - li class="mr-2 mid:mr-7 lg:mr-11 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'] == "/nonprofit" ? "text-seafoam border-seafoam" : ""}" + li class="mr-1 mid:mr-5 lg:mr-9 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'] == "/nonprofit" ? "text-seafoam border-seafoam" : ""}" = link_to 'Add a Nonprofit', new_nonprofit_request_path - li class="mr-2 mid:mr-7 lg:mr-11 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'] == "/about_us" ? "text-seafoam border-seafoam" : ""}" + li class="mr-1 mid:mr-5 lg:mr-9 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'] == "/about_us" ? "text-seafoam border-seafoam" : ""}" = link_to 'About Us', about_us_path - li class="mr-2 mid:mr-7 lg:mr-11 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'].include?("/discover") ? "text-seafoam border-seafoam" : ""}" + li class="mr-1 mid:mr-5 lg:mr-9 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'].include?("/discover") ? "text-seafoam border-seafoam" : ""}" = link_to 'Discover', discover_path - li class="mr-2 mid:mr-7 lg:mr-11 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'] == "/donate" ? "text-seafoam border-seafoam" : ""}" + li class="mr-1 mid:mr-5 lg:mr-9 text-sm-underlined-color font-medium border-b-2 border-transparent #{request.env['PATH_INFO'] == "/donate" ? "text-seafoam border-seafoam" : ""}" = link_to donate_path, class:"flex" do div class='mr-2' | Donate div class='flex items-center' = inline_svg_tag 'heart.svg', size:'9*9', class:'mt-1 fill-current text-red' + li class="flex items-center mr-2 font-medium border-b-2 cursor-pointer text-sm-underlined-color text-seafoam border-seafoam" data-action="click->geolocation#getCurrentPosition" + = inline_svg_tag "location-dot.svg", size:"9*9", class:"mr-1 fill-current text-seafoam" + span data-geolocation-target="currentLocation" + = @current_location ul class='flex items-center' - if @signed_in li = link_to "My Account", my_account_path, class:"text-base font-bold mr-1 lg:mr-6 border-b-2 border-transparent #{request.env['PATH_INFO'] == "/my_account" ? "text-seafoam border-seafoam" : ""}" diff --git a/app/components/navbar/component.rb b/app/components/navbar/component.rb index a927ef688..a45a77299 100644 --- a/app/components/navbar/component.rb +++ b/app/components/navbar/component.rb @@ -1,10 +1,6 @@ -# frozen_string_literal: true - -# navbar view component -# rubocop:disable Style/ClassAndModuleChildren -# rubocop:disable Lint/MissingSuper class Navbar::Component < ApplicationViewComponent - def initialize(signed_in:) + def initialize(signed_in:, current_location:) + @current_location = current_location @signed_in = signed_in end @@ -12,6 +8,3 @@ def non_sticky_paths request.env["PATH_INFO"] == "/searches" || request.env["PATH_INFO"] == "/my_account" end end - -# rubocop:enable Style/ClassAndModuleChildren -# rubocop:enable Lint/MissingSuper diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5ad119d21..6898307f7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -58,4 +58,9 @@ def user_not_authorized flash[:error] = "You are not allowed to perform this action" redirect_to root_path end + + def current_location + cookies[:city] || Search::DEFAULT_CITY + end + helper_method :current_location end diff --git a/app/javascript/controllers/geolocation_controller.js b/app/javascript/controllers/geolocation_controller.js new file mode 100644 index 000000000..4eaa1ab85 --- /dev/null +++ b/app/javascript/controllers/geolocation_controller.js @@ -0,0 +1,49 @@ +import { Controller } from "@hotwired/stimulus"; + + const options = { + enableHighAccuracy: true, + timeout: 5000, + maximumAge: 0 + }; + +export default class extends Controller { + static targets = [ "currentLocation" ] + + connect() { + this.currentCity = this.currentLocationTarget.innerText + } + + async success(position) { + let city; + const coordinates = position.coords; + document.cookie = `latitude=${coordinates.latitude}`; + document.cookie = `longitude=${coordinates.longitude}`; + city = await this.findNearestCity(coordinates) + this.updateNavbarLocation(city); + } + + async findNearestCity(coordinates) { + let response; + const geocoder = new google.maps.Geocoder() + const coords= { lat: coordinates.latitude, lng: coordinates.longitude } + response = await geocoder.geocode({ location: coords }) + if (response.results[0]) { + this.currentCity = response.results[0].address_components[3].long_name + document.cookie = `city=${this.currentCity}`; + } else { + console.warning('No location found'); + } + } + + error(err) { + console.warn(`ERROR(${err.code}): ${err.message}`) + } + + async getCurrentPosition() { + navigator.geolocation.getCurrentPosition(this.success.bind(this), this.error, options); + } + + updateNavbarLocation() { + this.currentLocationTarget.innerText = this.currentCity + } +} \ No newline at end of file diff --git a/app/models/search.rb b/app/models/search.rb index 68c120859..d0235a7a5 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -5,6 +5,7 @@ class Search KEYWORD_SEARCH_TYPE = "keyword" FILTER_SEARCH_TYPE = "filter" + DEFAULT_CITY = "Nashville" attr_accessor :keyword, :results, :distance, :city, :state, :zipcode, :beneficiary_groups, :services, :causes, :open_now, :open_weekends, diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index d157ebd91..20341c8b5 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -36,12 +36,12 @@ - <%= render Navbar::Component.new(signed_in: user_signed_in?) %> + <%= render Navbar::Component.new(signed_in: user_signed_in?, current_location: current_location) %>
<%= render "shared/flash_messages" %> From fd30f953fad78d249b2c3f4ff5cea2493b5fedcf Mon Sep 17 00:00:00 2001 From: Alicia Date: Fri, 22 Mar 2024 17:52:41 -0600 Subject: [PATCH 03/48] Navbar dropdown --- app/components/navbar/component.html.slim | 15 +++++++++++---- app/components/navbar/component.rb | 1 + ...ontroller.js => custom_dropdown_controller.js} | 0 .../controllers/extend_dropdown_controller.js | 2 +- app/javascript/controllers/index.js | 3 ++- app/models/search.rb | 1 + 6 files changed, 16 insertions(+), 6 deletions(-) rename app/javascript/controllers/{dropdown_controller.js => custom_dropdown_controller.js} (100%) diff --git a/app/components/navbar/component.html.slim b/app/components/navbar/component.html.slim index ea5170940..57f7a2a8a 100644 --- a/app/components/navbar/component.html.slim +++ b/app/components/navbar/component.html.slim @@ -24,10 +24,17 @@ nav id="main-navbar" data-controller="navbar--component" class="#{non_sticky_pat | Donate div class='flex items-center' = inline_svg_tag 'heart.svg', size:'9*9', class:'mt-1 fill-current text-red' - li class="flex items-center mr-2 font-medium border-b-2 cursor-pointer text-sm-underlined-color text-seafoam border-seafoam" data-action="click->geolocation#getCurrentPosition" - = inline_svg_tag "location-dot.svg", size:"9*9", class:"mr-1 fill-current text-seafoam" - span data-geolocation-target="currentLocation" - = @current_location + li class="relative flex items-center inline-block mr-2 font-medium cursor-pointer " data-controller="dropdown" data-action="click@window->dropdown#hide touchstart@window->dropdown#hide keydown.up->dropdown#previousItem keydown.down->dropdown#nextItem keydown.esc->dropdown#hide" + button data-action="text-sm-underlined-color text-seafoam border-seafoam dropdown#toggle:stop click->geolocation#getCurrentPosition" class="flex items-center" + = inline_svg_tag "location-dot.svg", size:"9*9", class:"h-7 mr-1 fill-current text-seafoam" + p data-geolocation-target="currentLocation" + = @current_location + div data-dropdown-target="menu" class="absolute right-0 z-10 flex hidden w-screen mt-5 top-4 max-w-max" + div class="overflow-hidden text-sm bg-white border rounded shadow-lg w-42" + span class="flex items-center block px-4 py-2 font-medium text-gray-3 border-gray-5 hover:bg-seafoam focus:bg-seafoam" + | Search near me + - @locations.each do |location| + = link_to location, location, class:"block px-4 py-2 text-gray-3 hover:bg-seafoam", data: { action:"click->geolocation#update" } ul class='flex items-center' - if @signed_in li = link_to "My Account", my_account_path, class:"text-base font-bold mr-1 lg:mr-6 border-b-2 border-transparent #{request.env['PATH_INFO'] == "/my_account" ? "text-seafoam border-seafoam" : ""}" diff --git a/app/components/navbar/component.rb b/app/components/navbar/component.rb index a45a77299..dae971e69 100644 --- a/app/components/navbar/component.rb +++ b/app/components/navbar/component.rb @@ -1,6 +1,7 @@ class Navbar::Component < ApplicationViewComponent def initialize(signed_in:, current_location:) @current_location = current_location + @locations = Search::AVAILABLE_CITIES @signed_in = signed_in end diff --git a/app/javascript/controllers/dropdown_controller.js b/app/javascript/controllers/custom_dropdown_controller.js similarity index 100% rename from app/javascript/controllers/dropdown_controller.js rename to app/javascript/controllers/custom_dropdown_controller.js diff --git a/app/javascript/controllers/extend_dropdown_controller.js b/app/javascript/controllers/extend_dropdown_controller.js index 2ec745099..68185614e 100644 --- a/app/javascript/controllers/extend_dropdown_controller.js +++ b/app/javascript/controllers/extend_dropdown_controller.js @@ -1,4 +1,4 @@ -import Dropdown from './dropdown_controller.js' +import Dropdown from './custom_dropdown_controller.js' // Extend Dropdown to close it when you press ESC. diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index f0dc97211..8744cb896 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -5,11 +5,12 @@ import { Application } from "@hotwired/stimulus" import { definitionsFromContext } from "@hotwired/stimulus-webpack-helpers" import Carousel from 'stimulus-carousel' import { Autocomplete } from "stimulus-autocomplete" - +import { Dropdown } from "tailwindcss-stimulus-components" const application = Application.start() application.register('carousel', Carousel) application.register('autocomplete', Autocomplete) +application.register('dropdown', Dropdown) const context = require.context("controllers", true, /_controller\.js$/) const contextComponents = require.context("../../components", true, /_controller\.js$/) application.load( diff --git a/app/models/search.rb b/app/models/search.rb index d0235a7a5..afea354bd 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -6,6 +6,7 @@ class Search KEYWORD_SEARCH_TYPE = "keyword" FILTER_SEARCH_TYPE = "filter" DEFAULT_CITY = "Nashville" + AVAILABLE_CITIES = ["Nashville", "Atlantic City"].freeze attr_accessor :keyword, :results, :distance, :city, :state, :zipcode, :beneficiary_groups, :services, :causes, :open_now, :open_weekends, From 529740e7c3521a9fc9f3b614fa70e1083688962d Mon Sep 17 00:00:00 2001 From: Alicia Date: Sat, 23 Mar 2024 14:45:02 -0600 Subject: [PATCH 04/48] Style and fix navbar dropdown behavior --- app/assets/images/my-location.svg | 1 + app/components/navbar/component.html.slim | 18 ++++++----- .../controllers/geolocation_controller.js | 32 ++++++++++++++----- 3 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 app/assets/images/my-location.svg diff --git a/app/assets/images/my-location.svg b/app/assets/images/my-location.svg new file mode 100644 index 000000000..6e482b7d6 --- /dev/null +++ b/app/assets/images/my-location.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/components/navbar/component.html.slim b/app/components/navbar/component.html.slim index 57f7a2a8a..a6276098d 100644 --- a/app/components/navbar/component.html.slim +++ b/app/components/navbar/component.html.slim @@ -24,17 +24,19 @@ nav id="main-navbar" data-controller="navbar--component" class="#{non_sticky_pat | Donate div class='flex items-center' = inline_svg_tag 'heart.svg', size:'9*9', class:'mt-1 fill-current text-red' - li class="relative flex items-center inline-block mr-2 font-medium cursor-pointer " data-controller="dropdown" data-action="click@window->dropdown#hide touchstart@window->dropdown#hide keydown.up->dropdown#previousItem keydown.down->dropdown#nextItem keydown.esc->dropdown#hide" - button data-action="text-sm-underlined-color text-seafoam border-seafoam dropdown#toggle:stop click->geolocation#getCurrentPosition" class="flex items-center" - = inline_svg_tag "location-dot.svg", size:"9*9", class:"h-7 mr-1 fill-current text-seafoam" - p data-geolocation-target="currentLocation" + li class="relative flex items-center inline-block mr-2 font-medium cursor-pointer w-36 " data-controller="dropdown" data-action="click@window->dropdown#hide touchstart@window->dropdown#hide keydown.up->dropdown#previousItem keydown.down->dropdown#nextItem keydown.esc->dropdown#hide" + button data-action="text-sm-underlined-color dropdown#toggle:stop" class="flex items-center border-b-2 text-seafoam border-seafoam" + = inline_svg_tag "location-dot.svg", size:"9*9", class:"h-7 mr-3 fill-current" + p data-geolocation-target="currentLocation" class="" = @current_location div data-dropdown-target="menu" class="absolute right-0 z-10 flex hidden w-screen mt-5 top-4 max-w-max" - div class="overflow-hidden text-sm bg-white border rounded shadow-lg w-42" - span class="flex items-center block px-4 py-2 font-medium text-gray-3 border-gray-5 hover:bg-seafoam focus:bg-seafoam" - | Search near me + ul class="overflow-hidden text-sm bg-white border rounded shadow-lg w-42" - @locations.each do |location| - = link_to location, location, class:"block px-4 py-2 text-gray-3 hover:bg-seafoam", data: { action:"click->geolocation#update" } + li class="block px-4 py-2 text-gray-3 hover:bg-seafoam" data-action="click->geolocation#updateLocation" + = location + span class="flex items-center block px-4 py-2 font-medium text-gray-3 border-gray-5 hover:bg-seafoam focus:bg-seafoam" data-action="click->geolocation#getCurrentPosition" + = inline_svg_tag "my-location.svg", size:"12*12", class:"h-7 mr-2 fill-current text-gray-3" + | Search near me ul class='flex items-center' - if @signed_in li = link_to "My Account", my_account_path, class:"text-base font-bold mr-1 lg:mr-6 border-b-2 border-transparent #{request.env['PATH_INFO'] == "/my_account" ? "text-seafoam border-seafoam" : ""}" diff --git a/app/javascript/controllers/geolocation_controller.js b/app/javascript/controllers/geolocation_controller.js index 4eaa1ab85..d20a7a340 100644 --- a/app/javascript/controllers/geolocation_controller.js +++ b/app/javascript/controllers/geolocation_controller.js @@ -10,16 +10,20 @@ export default class extends Controller { static targets = [ "currentLocation" ] connect() { - this.currentCity = this.currentLocationTarget.innerText + this.latitude = document.cookie.split('; ').find(row => row.startsWith('latitude=')).split('=')[1] + this.longitude = document.cookie.split('; ').find(row => row.startsWith('longitude=')).split('=')[1] + this.currentCity = document.cookie.split('; ').find(row => row.startsWith('city=')).split('=')[1] } async success(position) { - let city; const coordinates = position.coords; document.cookie = `latitude=${coordinates.latitude}`; document.cookie = `longitude=${coordinates.longitude}`; - city = await this.findNearestCity(coordinates) - this.updateNavbarLocation(city); + this.latitude = coordinates.latitude + this.longitude = coordinates.longitude + this.currentCity = await this.findNearestCity(coordinates) + this.rememberLocation() + this.updateNavbarLocation(); } async findNearestCity(coordinates) { @@ -28,8 +32,8 @@ export default class extends Controller { const coords= { lat: coordinates.latitude, lng: coordinates.longitude } response = await geocoder.geocode({ location: coords }) if (response.results[0]) { - this.currentCity = response.results[0].address_components[3].long_name - document.cookie = `city=${this.currentCity}`; + + return response.results[0].address_components[3].long_name } else { console.warning('No location found'); } @@ -43,7 +47,19 @@ export default class extends Controller { navigator.geolocation.getCurrentPosition(this.success.bind(this), this.error, options); } + rememberLocation() { + document.cookie = `latitude=${this.latitude}` + document.cookie = `longitude=${this.longitude}` + document.cookie = `city=${this.currentCity}` + } + updateNavbarLocation() { - this.currentLocationTarget.innerText = this.currentCity + this.currentLocationTarget.innerText = this.currentCity + } + + updateLocation(event) { + this.currentCity = event.target.innerText + this.rememberLocation() + this.updateNavbarLocation() } -} \ No newline at end of file +} From 58ae33f428b625cc36e3947f07b31817c854dd32 Mon Sep 17 00:00:00 2001 From: Alan Soto Date: Thu, 13 Oct 2022 15:16:31 -0500 Subject: [PATCH 05/48] Create random locations within 1000 Us cities --- app/queries/locations/filter_query.rb | 3 +- db/seeds.rb | 5 +++ lib/assets/us_cities_coords.xlsx | Bin 0 -> 76573 bytes lib/tasks/populate.rake | 45 ++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 lib/assets/us_cities_coords.xlsx diff --git a/app/queries/locations/filter_query.rb b/app/queries/locations/filter_query.rb index 56eabda18..f2a09b558 100644 --- a/app/queries/locations/filter_query.rb +++ b/app/queries/locations/filter_query.rb @@ -12,8 +12,7 @@ class FilterQuery class << self def call(params = {}, locations = Location.active) scope = locations - scope = geo_near(scope, starting_coordinates(params[:lat], params[:lon]), params[:distance]) - scope = by_address(scope, params[:address]) + # scope = by_address(scope, params[:address]) scope = by_cause(scope, params[:causes]) scope = by_service(scope, params[:services]) scope = by_beneficiary_groups_served(scope, params[:beneficiary_groups]) diff --git a/db/seeds.rb b/db/seeds.rb index 220879368..01f654c57 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -49,4 +49,9 @@ phone.number = "222-333-4444" main = false end + # Create random location around cities in US + Rake::Task['populate:random_locations'].invoke + + # Create organizations and causes association + Rake::Task['populate:seed_organizations_causes'].invoke end diff --git a/lib/assets/us_cities_coords.xlsx b/lib/assets/us_cities_coords.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ebfd3305bf80a81dbff88b443430c92a1b81447b GIT binary patch literal 76573 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�*<1H$>Z9(Q2G}nTw%Cb7dX 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�|`~YKt|HjzUojy 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 literal 0 HcmV?d00001 diff --git a/lib/tasks/populate.rake b/lib/tasks/populate.rake index baf48c3b6..9979dd8c2 100644 --- a/lib/tasks/populate.rake +++ b/lib/tasks/populate.rake @@ -18,4 +18,49 @@ namespace :populate do end end end + + desc "Seed organizations and causes association" + task seed_organizations_causes: :environment do + Organization.all.each do |organization| + OrganizationCause.create!(organization: organization, cause: Cause.all.sample) + organization.update!(active: true) + end + end + + desc "Locations cities arround US" + task random_locations: :environment do + def random_point_in_disk(max_radius) + r = max_radius * (rand**0.5) + theta = rand * 2 * Math::PI + [r * Math.cos(theta), r * Math.sin(theta)] + end + + wb = Roo::Spreadsheet.open "./lib/assets/us_cities_coords.xlsx" + sheet = wb.sheet(0) + cities_coords = sheet.parse(place_name: "place_name", latitude: "latitude", longitude: "longitude", clean: true) + + cities_coords.each do |city| + 2.times do + lat = city[:latitude].to_f + lng = city[:longitude].to_f + max_radius = 1000 # mts + earth_radius = 6371 # km + one_degree = earth_radius * 2 * Math::PI / 360 * 1000 # 1° latitude in meters + + dx, dy = random_point_in_disk(max_radius) + random_lat = lat + (dy / one_degree) + random_lng = lng + (dx / (one_degree * Math.cos(lat * Math::PI / 180))) + + Location.create!( + organization_id: Organization.first.id, + name: "Hanas corps", + address: "Anywhere within US", + latitude: random_lat, + longitude: random_lng, + physical: true, + offer_services: false + ) + end + end + end end From 57e6ef19e01c3d68b902611800c8e3d31299b66f Mon Sep 17 00:00:00 2001 From: Alicia Date: Sun, 24 Mar 2024 10:29:52 -0600 Subject: [PATCH 06/48] Search filters --- app/models/search.rb | 22 ++++++++++++++++------ lib/tasks/populate.rake | 1 - 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/models/search.rb b/app/models/search.rb index afea354bd..5fe8bd127 100644 --- a/app/models/search.rb +++ b/app/models/search.rb @@ -22,18 +22,28 @@ def save end def execute_search - filters = { + @results = Location.where(id: Locations::FilterQuery.call(filters, Location.active).pluck(:id)) + @results = keyword.present? ? Locations::KeywordQuery.call({keyword: keyword}, @results) : @results + end + + private + + def filters + { address: {city: city.presence, state: state.presence, zipcode: zipcode.presence}, open_now: ActiveModel::Type::Boolean.new.cast(open_now), open_weekends: ActiveModel::Type::Boolean.new.cast(open_weekends), beneficiary_groups: beneficiary_groups, services: services, - causes: causes, - distance: distance.presence&.to_i, - lat: lat.presence&.to_f, lon: lon.presence&.to_f + causes: causes } + end - @results = Location.where(id: Locations::FilterQuery.call(filters, Location.active).pluck(:id)) - @results = keyword.present? ? Locations::KeywordQuery.call({keyword: keyword}, @results) : @results + def geo_filters + { + distance: distance.presence&.to_i, + lat: lat.presence&.to_f, + lon: lon.presence&.to_f + } end end diff --git a/lib/tasks/populate.rake b/lib/tasks/populate.rake index 9979dd8c2..1ca30d1f7 100644 --- a/lib/tasks/populate.rake +++ b/lib/tasks/populate.rake @@ -57,7 +57,6 @@ namespace :populate do address: "Anywhere within US", latitude: random_lat, longitude: random_lng, - physical: true, offer_services: false ) end From bbd8b1593406096bdf3860f56884320ac662c000 Mon Sep 17 00:00:00 2001 From: Alicia Date: Sun, 24 Mar 2024 22:08:03 -0600 Subject: [PATCH 07/48] Use hidden inputs in search form --- .../controllers/geolocation_controller.js | 20 ++++++++++++++----- app/views/home/index.html.slim | 3 +++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/javascript/controllers/geolocation_controller.js b/app/javascript/controllers/geolocation_controller.js index d20a7a340..65db189ae 100644 --- a/app/javascript/controllers/geolocation_controller.js +++ b/app/javascript/controllers/geolocation_controller.js @@ -7,12 +7,12 @@ import { Controller } from "@hotwired/stimulus"; }; export default class extends Controller { - static targets = [ "currentLocation" ] + static targets = [ "currentLocation", "formLatitude", "formLongitude" ] connect() { - this.latitude = document.cookie.split('; ').find(row => row.startsWith('latitude=')).split('=')[1] - this.longitude = document.cookie.split('; ').find(row => row.startsWith('longitude=')).split('=')[1] - this.currentCity = document.cookie.split('; ').find(row => row.startsWith('city=')).split('=')[1] + this.latitude = this.findInCookie("latitude") + this.longitude = this.findInCookie("longitude") + this.currentCity = this.findInCookie("city") } async success(position) { @@ -24,6 +24,7 @@ export default class extends Controller { this.currentCity = await this.findNearestCity(coordinates) this.rememberLocation() this.updateNavbarLocation(); + this.updateForm() } async findNearestCity(coordinates) { @@ -32,7 +33,6 @@ export default class extends Controller { const coords= { lat: coordinates.latitude, lng: coordinates.longitude } response = await geocoder.geocode({ location: coords }) if (response.results[0]) { - return response.results[0].address_components[3].long_name } else { console.warning('No location found'); @@ -61,5 +61,15 @@ export default class extends Controller { this.currentCity = event.target.innerText this.rememberLocation() this.updateNavbarLocation() + this.updateForm() + } + + updateForm() { + this.formLongitudeTarget.value = this.longitude + this.formLatitudeTarget.value = this.latitude + } + + findInCookie(key) { + document.cookie?.split('; ')?.find(row => row.startsWith(`${key}=`))?.split('=')[1] || null } } diff --git a/app/views/home/index.html.slim b/app/views/home/index.html.slim index 77c63332b..a9b5df79a 100644 --- a/app/views/home/index.html.slim +++ b/app/views/home/index.html.slim @@ -25,6 +25,9 @@ main div class="relative w-full sm:max-w-xl mb-7 sm:mb-0" = f.text_field :keyword, autocomplete: "search", class:"c-input pl-10 m-0 w-full", placeholder: "Try \"Mental Health Nonprofits\"", data: { test_id: "home_search_input" } = inline_svg_tag 'search-icon.svg', class:"absolute top-1/3 left-4" + div + = f.hidden_field :lat, value: "", data: { geolocation_target: "formLatitude" } + = f.hidden_field :lon, value: "", data: { geolocation_target: "formLongitude" } div = f.submit "Search", class:"c-button mx-10 mt-6", data: { test_id: "home_search_btn" } section class="pt-6 text-gray-2 sm:pt-10" From 7097df06d17d8e0502b6c46d935e12838817812d Mon Sep 17 00:00:00 2001 From: Alicia Date: Sun, 24 Mar 2024 22:24:34 -0600 Subject: [PATCH 08/48] Update form with chosen city --- .../controllers/geolocation_controller.js | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/app/javascript/controllers/geolocation_controller.js b/app/javascript/controllers/geolocation_controller.js index 65db189ae..67adbf76e 100644 --- a/app/javascript/controllers/geolocation_controller.js +++ b/app/javascript/controllers/geolocation_controller.js @@ -6,6 +6,10 @@ import { Controller } from "@hotwired/stimulus"; maximumAge: 0 }; + const CITIES = { + "Nashville" : { latitude: 36.1627, longitude: -86.7816 }, + "Atlantic City" : { latitude: 39.3643, longitude: -74.4229 }, + } export default class extends Controller { static targets = [ "currentLocation", "formLatitude", "formLongitude" ] @@ -15,16 +19,21 @@ export default class extends Controller { this.currentCity = this.findInCookie("city") } + async getCurrentPosition() { + navigator.geolocation.getCurrentPosition(this.success.bind(this), this.error, options); + } + async success(position) { const coordinates = position.coords; - document.cookie = `latitude=${coordinates.latitude}`; - document.cookie = `longitude=${coordinates.longitude}`; this.latitude = coordinates.latitude this.longitude = coordinates.longitude this.currentCity = await this.findNearestCity(coordinates) this.rememberLocation() - this.updateNavbarLocation(); - this.updateForm() + this.updateDOM() + } + + error(err) { + console.warn(`ERROR(${err.code}): ${err.message}`) } async findNearestCity(coordinates) { @@ -39,29 +48,22 @@ export default class extends Controller { } } - error(err) { - console.warn(`ERROR(${err.code}): ${err.message}`) - } - - async getCurrentPosition() { - navigator.geolocation.getCurrentPosition(this.success.bind(this), this.error, options); - } - rememberLocation() { document.cookie = `latitude=${this.latitude}` document.cookie = `longitude=${this.longitude}` document.cookie = `city=${this.currentCity}` } - updateNavbarLocation() { + updateNavbar() { this.currentLocationTarget.innerText = this.currentCity } updateLocation(event) { this.currentCity = event.target.innerText + this.latitude = CITIES[this.currentCity].latitude + this.longitude = CITIES[this.currentCity].longitude this.rememberLocation() - this.updateNavbarLocation() - this.updateForm() + this.updateDOM() } updateForm() { @@ -69,6 +71,11 @@ export default class extends Controller { this.formLatitudeTarget.value = this.latitude } + updateDOM() { + this.updateNavbar() + this.updateForm() + } + findInCookie(key) { document.cookie?.split('; ')?.find(row => row.startsWith(`${key}=`))?.split('=')[1] || null } From 81889dd8ed3d22aa85c3a296dbfb237512972245 Mon Sep 17 00:00:00 2001 From: Alicia Date: Mon, 25 Mar 2024 09:56:34 -0600 Subject: [PATCH 09/48] Update city in both navbar and search pills --- app/components/navbar/component.html.slim | 2 +- .../search_pills/component.html.slim | 19 ++++++++++--------- app/components/search_pills/component.rb | 3 ++- .../controllers/geolocation_controller.js | 8 +++++--- app/views/home/index.html.slim | 4 ++-- app/views/searches/show.html.slim | 4 ++-- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/app/components/navbar/component.html.slim b/app/components/navbar/component.html.slim index a6276098d..72db94f99 100644 --- a/app/components/navbar/component.html.slim +++ b/app/components/navbar/component.html.slim @@ -27,7 +27,7 @@ nav id="main-navbar" data-controller="navbar--component" class="#{non_sticky_pat li class="relative flex items-center inline-block mr-2 font-medium cursor-pointer w-36 " data-controller="dropdown" data-action="click@window->dropdown#hide touchstart@window->dropdown#hide keydown.up->dropdown#previousItem keydown.down->dropdown#nextItem keydown.esc->dropdown#hide" button data-action="text-sm-underlined-color dropdown#toggle:stop" class="flex items-center border-b-2 text-seafoam border-seafoam" = inline_svg_tag "location-dot.svg", size:"9*9", class:"h-7 mr-3 fill-current" - p data-geolocation-target="currentLocation" class="" + p data-geolocation-target="currentLocation" = @current_location div data-dropdown-target="menu" class="absolute right-0 z-10 flex hidden w-screen mt-5 top-4 max-w-max" ul class="overflow-hidden text-sm bg-white border rounded shadow-lg w-42" diff --git a/app/components/search_pills/component.html.slim b/app/components/search_pills/component.html.slim index 7c24fac11..4f36fb908 100644 --- a/app/components/search_pills/component.html.slim +++ b/app/components/search_pills/component.html.slim @@ -2,50 +2,51 @@ div class="w-full bg-gray-9" div data-controller="tabs" data-tabs-active-tab=("border-b-4 border-blue-medium") div class="flex" / Clear-Counter button - span class="flex justify-center items-center w-14 pl-5 py-3 pr-2 text-xs bg-blue-pale" + span class="flex items-center justify-center py-3 pl-5 pr-2 text-xs w-14 bg-blue-pale" span class="inline-flex hidden items-center px-1 py-0.5 border border-blue-medium rounded-full bg-white" data-search-target="pillsCounterWrapper" span data-search-target="pillsCounter" class="mr-0.5" button type="button" data-action="search#clearCheckedPills" = inline_svg_tag "x-icon.svg", class: 'h-2 w-2 fill-current stroke-current stroke-1 text-blue-medium ml-1 relative' = inline_svg_tag "solid_filters.svg", class: 'h-4 w-4 fill-current text-gray-2 -ml-0.5 relative', data: { 'search-target': "filtersIcon" } / Tabs - ul class="flex flex-1 gap-x-6 pl-5 pr-6 text-sm list-none overflow-x-auto overflow-y-hidden bg-blue-pale text-gray-2" + ul class="flex flex-1 pl-5 pr-6 overflow-x-auto overflow-y-hidden text-sm list-none gap-x-6 bg-blue-pale text-gray-2" - @tabs_labels.each do |tab_label| li data-action="click->tabs#change" data-tabs-target="tab" a class="inline-block py-3 whitespace-nowrap" href="#" = tab_label li data-action="click->modal#open" button class="inline-block py-3 whitespace-nowrap" type="button" id="advanced-filters-button" | Advanced Filters - span class="relative hidden w-2 h-2 rounded-full bg-salmon ml-1 mb-2" id="appliedIcon" + span class="relative hidden w-2 h-2 mb-2 ml-1 rounded-full bg-salmon" id="appliedIcon" / Panels div class="bg-white" / Causes - div class="flex flex-wrap gap-x-2 gap-y-3 hidden max-h-28 py-4 px-6 border-l border-b border-r overflow-y-auto" data-tabs-target="panel" + div class="flex flex-wrap hidden px-6 py-4 overflow-y-auto border-b border-l border-r gap-x-2 gap-y-3 max-h-28" data-tabs-target="panel" - @causes.each do |cause| = render SearchPills::Pill::Component.new(name: "search[causes][]", value: cause.name, checked: @params.dig(:search, :causes)&.include?(cause.name), options: { multiple: true }) = render SearchPills::MoreFiltersButton::Component.new / Location div class="flex flex-col hidden border-b md:flex-row md:items-center" data-tabs-target="panel" - div class="py-4 pl-6 text-gray-2 text-sm" + div class="py-4 pl-6 text-sm text-gray-2" span class="inline-flex items-center" = inline_svg_tag "location-dot.svg", class: "h-3 w-3 fill-current text-blue-medium mr-1" - | Nashville + p data-geolocation-target="currentLocation" + = @current_location div class="flex w-full md:w-auto md:py-3.5 md:pl-7" - @radii_in_miles.each do |radius| = render SearchPills::Button::Component.new(name: "search[distance]", value: miles_to_km(radius), checked: @params.dig(:search, :distance) == miles_to_km(radius).to_s, copy: radius == "Any" ? radius : "#{radius} mi") / Services - div class="flex flex-wrap gap-x-2 gap-y-3 hidden max-h-28 py-4 px-6 border-l border-b border-r overflow-y-auto" data-tabs-target="panel" + div class="flex flex-wrap hidden px-6 py-4 overflow-y-auto border-b border-l border-r gap-x-2 gap-y-3 max-h-28" data-tabs-target="panel" - @services.each do |service| = render SearchPills::Pill::Component.new(name: "search[services][#{service.cause.name}][]", value: service.name, checked: @params.dig('search', 'services', service.cause.name)&.include?(service.name), options: { multiple: true }) = render SearchPills::MoreFiltersButton::Component.new / Populations Served - div class="flex flex-wrap gap-x-2 gap-y-3 hidden max-h-28 py-4 px-6 border-l border-b border-r overflow-y-auto" data-tabs-target="panel" + div class="flex flex-wrap hidden px-6 py-4 overflow-y-auto border-b border-l border-r gap-x-2 gap-y-3 max-h-28" data-tabs-target="panel" - @beneficiary_subcategories.each do |subcategory| = render SearchPills::Pill::Component.new(name: "search[beneficiary_groups][#{subcategory.beneficiary_group.name}][]", value: subcategory.name, checked: @params.dig('search', 'beneficiary_groups', subcategory.beneficiary_group.name)&.include?(subcategory.name), options: { multiple: true }) = render SearchPills::MoreFiltersButton::Component.new / Hours - div class="flex hidden gap-x-2 w-full py-4 px-6 border-l border-b border-r" data-tabs-target="panel" + div class="flex hidden w-full px-6 py-4 border-b border-l border-r gap-x-2" data-tabs-target="panel" = render SearchPills::Pill::Component.new(name: "search[open_now]", value: true, checked: @params.dig('search', 'open_now') == 'true') do | Open Now = render SearchPills::Pill::Component.new(name: "search[open_weekends]", value: true, checked: @params.dig('search', 'open_weekends') == 'true') do diff --git a/app/components/search_pills/component.rb b/app/components/search_pills/component.rb index 2f4fc757c..51a9965f7 100644 --- a/app/components/search_pills/component.rb +++ b/app/components/search_pills/component.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true class SearchPills::Component < ApplicationViewComponent - def initialize(causes:, services:, beneficiary_subcategories:, params:) + def initialize(causes:, services:, current_location:, beneficiary_subcategories:, params:) @causes = causes @services = services + @current_location = current_location @beneficiary_subcategories = beneficiary_subcategories @params = params @tabs_labels = ["Causes", "Location", "Services", "Populations Served", "Hours"] diff --git a/app/javascript/controllers/geolocation_controller.js b/app/javascript/controllers/geolocation_controller.js index 67adbf76e..37b2599a3 100644 --- a/app/javascript/controllers/geolocation_controller.js +++ b/app/javascript/controllers/geolocation_controller.js @@ -54,8 +54,10 @@ export default class extends Controller { document.cookie = `city=${this.currentCity}` } - updateNavbar() { - this.currentLocationTarget.innerText = this.currentCity + updateCity() { + for (let target of this.currentLocationTargets) { + target.innerText = this.currentCity + } } updateLocation(event) { @@ -72,7 +74,7 @@ export default class extends Controller { } updateDOM() { - this.updateNavbar() + this.updateCity() this.updateForm() } diff --git a/app/views/home/index.html.slim b/app/views/home/index.html.slim index a9b5df79a..1b2dcd2fa 100644 --- a/app/views/home/index.html.slim +++ b/app/views/home/index.html.slim @@ -26,8 +26,8 @@ main = f.text_field :keyword, autocomplete: "search", class:"c-input pl-10 m-0 w-full", placeholder: "Try \"Mental Health Nonprofits\"", data: { test_id: "home_search_input" } = inline_svg_tag 'search-icon.svg', class:"absolute top-1/3 left-4" div - = f.hidden_field :lat, value: "", data: { geolocation_target: "formLatitude" } - = f.hidden_field :lon, value: "", data: { geolocation_target: "formLongitude" } + = f.hidden_field :lat, value: Locations::GeolocationQuery::DEFAULT_LOCATION[:latitude], data: { geolocation_target: "formLatitude" } + = f.hidden_field :lon, value: Locations::GeolocationQuery::DEFAULT_LOCATION[:longitude], data: { geolocation_target: "formLongitude" } div = f.submit "Search", class:"c-button mx-10 mt-6", data: { test_id: "home_search_btn" } section class="pt-6 text-gray-2 sm:pt-10" diff --git a/app/views/searches/show.html.slim b/app/views/searches/show.html.slim index 012406613..2bca5149e 100644 --- a/app/views/searches/show.html.slim +++ b/app/views/searches/show.html.slim @@ -11,11 +11,11 @@ 'search-target': 'form'}) do |f| = render SearchBar::Component.new(form: f, search: @search) = turbo_frame_tag "search-pills" do - = render SearchPills::Component.new(causes: @top_10_causes, services: @top_10_services, beneficiary_subcategories: @top_10_beneficiary_subcategories, params: params) + = render SearchPills::Component.new(causes: @top_10_causes, services: @top_10_services, beneficiary_subcategories: @top_10_beneficiary_subcategories, params: params, current_location: @current_location) = turbo_frame_tag "search-locations" do div class="flex flex-row justify-center w-full md:h-85vh" data-controller="tabs" data-tabs-active-tab="border-blue-dark bg-blue-dark text-white" data-tabs-inactive-tab="border-gray-6 bg-transparent text-gray-3" div class="flex flex-row justify-center w-full min-h-full" - div class="flex flex-col w-full md:border-r max-w-sm border-gray-7" + div class="flex flex-col w-full max-w-sm md:border-r border-gray-7" div class="fixed inset-0 flex items-center justify-center hidden min-h-screen overflow-y-auto animated fadeIn" data-action="click->modal#closeBackground keyup@window->modal#closeWithKeyboard" data-modal-target="container" style=("z-index: 9999;") div class="relative w-full max-h-full max-w-375px" = render partial: 'filter', locals: { form: f, search: @search, services: @services, causes: @causes, beneficiary_groups: @beneficiary_groups } From c3f68538a43bd1915bea2d4001f1368fbb56d410 Mon Sep 17 00:00:00 2001 From: Alicia Date: Mon, 25 Mar 2024 10:16:55 -0600 Subject: [PATCH 10/48] Add coordinates in search form --- app/components/search_bar/component.html.erb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/components/search_bar/component.html.erb b/app/components/search_bar/component.html.erb index e68c45c0d..4baa09f38 100644 --- a/app/components/search_bar/component.html.erb +++ b/app/components/search_bar/component.html.erb @@ -18,6 +18,9 @@ bg-gradient-to-r from-blue-gradient-2 to-blue-gradient-1 top-20 md:top-22.75 md: } ) %> + <%= @form.hidden_field :lat, value: Locations::GeolocationQuery::DEFAULT_LOCATION[:latitude], data: { geolocation_target: "formLatitude" } %> + <%= @form.hidden_field :lon, value: Locations::GeolocationQuery::DEFAULT_LOCATION[:longitude], data: { geolocation_target: "formLongitude" } %> + +

+
    + <% @locations.each do |location| %> +
  • + <%= location %> +
  • + <% end %> +
  • + <%= inline_svg_tag "my-location.svg", size:"12*12", class:"h-7 mr-2 fill-current text-gray-3" %> + Search near me +
  • +
+
+
<%= @form.hidden_field :lat, value: @current_location[:latitude], data: { geolocation_target: "formLatitude" } %> <%= @form.hidden_field :lon, value: @current_location[:longitude], data: { geolocation_target: "formLongitude" } %> - - <%# Search Button %> <%= @form.submit( 'Search', diff --git a/app/views/home/index.html.slim b/app/views/home/index.html.slim index 6f3631811..c65036f54 100644 --- a/app/views/home/index.html.slim +++ b/app/views/home/index.html.slim @@ -22,11 +22,12 @@ main | Search for listings of nonprofit organizations based on your needs. = form_with model: @search, url: search_path, method: :get do |f| .c-form class="py-0 mx-auto sm:px-3 sm:flex-col sm:justify-center sm:max-w-4xl" - div class="relative flex w-full p-0 rounded-md c-input sm:max-w-xl mb-7 sm:mb-0" - = f.text_field :keyword, autocomplete: "search", class:"border-none rounded-l-md pl-10 m-0 w-full", placeholder: "Try \"Mental Health Nonprofits\"", data: { test_id: "home_search_input" } - = inline_svg_tag 'search-icon.svg', class:"absolute top-1/3 left-4" + div class="relative flex w-full p-0 divide-x rounded-md c-input sm:max-w-xl mb-7 sm:mb-0" + div class="relative flex w-full" + = f.text_field :keyword, autocomplete: "search", class:"border-none rounded-l-md pl-10 m-0 w-full", placeholder: "Try \"Mental Health Nonprofits\"", data: { test_id: "home_search_input" } + = inline_svg_tag 'search-icon.svg', class:"absolute top-1/3 left-4" div class="flex m-0 font-medium bg-white cursor-pointer w-52 rounded-r-md" data-controller="dropdown" data-action="click@window->dropdown#hide touchstart@window->dropdown#hide keydown.up->dropdown#previousItem keydown.down->dropdown#nextItem keydown.esc->dropdown#hide" - button type="button" class="flex items-center pl-2 border-l-gray-4 w-52" data-action="dropdown#toggle:stop" + button type="button" class="flex items-center pl-2 w-52 border-l-gray" data-action="dropdown#toggle:stop" = inline_svg_tag "location-dot.svg", size:"9*9", class:"h-7 mr-2 fill-current" p data-geolocation-target="currentLocation" = @current_location[:city] From f385e4879cf2c4db5844818d54e2c6adf592b187 Mon Sep 17 00:00:00 2001 From: Alicia Date: Fri, 29 Mar 2024 12:07:04 -0600 Subject: [PATCH 24/48] Trigger form submission on location change --- app/javascript/controllers/geolocation_controller.js | 6 ++++++ app/javascript/controllers/search_controller.js | 10 ++++++++++ app/views/layouts/application.html.erb | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/javascript/controllers/geolocation_controller.js b/app/javascript/controllers/geolocation_controller.js index ba2e252d5..c0ac7755e 100644 --- a/app/javascript/controllers/geolocation_controller.js +++ b/app/javascript/controllers/geolocation_controller.js @@ -68,5 +68,11 @@ export default class extends Controller { this.currentLocationTargets.forEach(target => target.innerText = this.currentCity); this.formLongitudeTarget.value = this.longitude; this.formLatitudeTarget.value = this.latitude; + + // Dispatch a custom event indicating the location has ch + const event = new CustomEvent('locationUpdated', { + detail: { latitude: this.latitude, longitude: this.longitude } + }); + window.dispatchEvent(event); } } diff --git a/app/javascript/controllers/search_controller.js b/app/javascript/controllers/search_controller.js index b000145af..0775e0216 100644 --- a/app/javascript/controllers/search_controller.js +++ b/app/javascript/controllers/search_controller.js @@ -23,6 +23,8 @@ export default class extends Controller { useDispatch(this) this.updatePillsCounter() this.updateRadioButtonsClass() + + window.addEventListener('locationUpdated', this.handleLocationUpdate.bind(this)); } initialize() { @@ -208,4 +210,12 @@ export default class extends Controller { this.updateFiltersState() this.submitForm() } + + handleLocationUpdate(event) { + this.submitForm(); + } + + disconnect() { + window.removeEventListener('locationUpdated', this.handleLocationUpdate.bind(this)); + } } diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index f3bc837af..c4058b510 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -33,7 +33,7 @@ data-action="turbo:click@document->disable-turbo#disableTurboForTargetUrls"> - <%= render Navbar::Component.new(signed_in: user_signed_in?, current_location: @current_location) %> + <%= render Navbar::Component.new(signed_in: user_signed_in?, current_location: @current_location, locations: @locations) %>
<%= render "shared/flash_messages" %> From 62b8ed447c2196d8677a7b99d0ed5e94d94331e2 Mon Sep 17 00:00:00 2001 From: Alicia Date: Fri, 29 Mar 2024 12:10:34 -0600 Subject: [PATCH 25/48] Move navbar dropdown to right corner --- app/components/navbar/component.html.slim | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/components/navbar/component.html.slim b/app/components/navbar/component.html.slim index 10e99965b..b4634ea77 100644 --- a/app/components/navbar/component.html.slim +++ b/app/components/navbar/component.html.slim @@ -24,25 +24,25 @@ nav id="main-navbar" data-controller="navbar--component" class="#{non_sticky_pat | Donate div class='flex items-center' = inline_svg_tag 'heart.svg', size:'9*9', class:'mt-1 fill-current text-red' - li class="relative flex items-center inline-block mr-2 font-medium cursor-pointer w-36 " data-controller="dropdown" data-action="click@window->dropdown#hide touchstart@window->dropdown#hide keydown.up->dropdown#previousItem keydown.down->dropdown#nextItem keydown.esc->dropdown#hide" - button data-action="dropdown#toggle:stop" class="flex items-center border-b-2 text-sm-underlined-color text-seafoam border-seafoam" - = inline_svg_tag "location-dot.svg", size:"9*9", class:"h-7 mr-3 fill-current" - p data-geolocation-target="currentLocation" - = @current_location[:city] - div data-dropdown-target="menu" class="absolute right-0 z-10 flex hidden w-screen mt-5 top-4 max-w-max" - ul class="overflow-hidden text-sm bg-white border rounded shadow-lg w-42" - - @locations.each do |location| - li class="block px-4 py-2 text-gray-3 hover:bg-seafoam" data-action="click->geolocation#updateLocation" - = location - li class="flex items-center block px-4 py-1 font-medium text-gray-3 border-gray-5 hover:bg-seafoam focus:bg-seafoam" data-action="click->geolocation#getCurrentPosition" - = inline_svg_tag "my-location.svg", size:"12*12", class:"h-7 mr-2 fill-current text-gray-3" - | Search near me ul class='flex items-center' - if @signed_in li = link_to "My Account", my_account_path, class:"text-base font-bold mr-1 lg:mr-6 border-b-2 border-transparent #{request.env['PATH_INFO'] == "/my_account" ? "text-seafoam border-seafoam" : ""}" - else li = link_to "Sign in", signin_path, class:'text-base font-bold mr-2 mid:mr-4 lg:mr-6' li = link_to "Create Account", signup_path, class:'c-button py-2.5 px-5 text-white bg-blue-medium' + li class="relative flex items-center inline-block mx-2 font-medium cursor-pointer w-36 " data-controller="dropdown" data-action="click@window->dropdown#hide touchstart@window->dropdown#hide keydown.up->dropdown#previousItem keydown.down->dropdown#nextItem keydown.esc->dropdown#hide" + button data-action="dropdown#toggle:stop" class="flex items-center border-b-2 text-sm-underlined-color text-seafoam border-seafoam" + = inline_svg_tag "location-dot.svg", size:"9*9", class:"h-7 mr-3 fill-current" + p data-geolocation-target="currentLocation" + = @current_location[:city] + div data-dropdown-target="menu" class="absolute right-0 z-10 flex hidden w-screen mt-5 top-4 max-w-max" + ul class="overflow-hidden text-sm bg-white border rounded shadow-lg w-42" + - @locations.each do |location| + li class="block px-4 py-2 text-gray-3 hover:bg-seafoam" data-action="click->geolocation#updateLocation" + = location + li class="flex items-center block px-4 py-1 font-medium text-gray-3 border-gray-5 hover:bg-seafoam focus:bg-seafoam" data-action="click->geolocation#getCurrentPosition" + = inline_svg_tag "my-location.svg", size:"12*12", class:"h-7 mr-2 fill-current text-gray-3" + | Search near me .sidebar class='fixed top-0 bottom-0 left-0 right-0 z-10 hidden h-screen overflow-y-hidden bg-gray-9' data-navbar--component-target="sideNavBar" section class="pt-8 pb-4 px-7 #{ @signed_in ? 'h-4/5' : 'h-3/4' }" From b68ffbce4b95f5cc329cb564f7e26f9e5ffd4b8a Mon Sep 17 00:00:00 2001 From: Alicia Date: Fri, 29 Mar 2024 12:19:14 -0600 Subject: [PATCH 26/48] Fix seeds --- db/seeds.rb | 4 ++-- lib/tasks/populate.rake | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/seeds.rb b/db/seeds.rb index 01f654c57..9f6d66c75 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -50,8 +50,8 @@ main = false end # Create random location around cities in US - Rake::Task['populate:random_locations'].invoke + Rake::Task["populate:random_locations"].invoke # Create organizations and causes association - Rake::Task['populate:seed_organizations_causes'].invoke + Rake::Task["populate:seed_organizations_causes"].invoke end diff --git a/lib/tasks/populate.rake b/lib/tasks/populate.rake index 57972fb9a..42a3e4909 100644 --- a/lib/tasks/populate.rake +++ b/lib/tasks/populate.rake @@ -21,7 +21,7 @@ namespace :populate do desc "Seed organizations and causes association" task seed_organizations_causes: :environment do - Organization.all.each do |organization| + Organization.all.find_each do |organization| OrganizationCause.create!(organization: organization, cause: Cause.all.sample) organization.update!(active: true) end From ceaddd49fd919da9efb0894bc7782c7177edd4a3 Mon Sep 17 00:00:00 2001 From: Alicia Date: Fri, 5 Apr 2024 15:55:06 -0600 Subject: [PATCH 27/48] Fix city dropdown responsiveness --- app/components/search_bar/component.html.erb | 17 +++--- app/views/home/index.html.slim | 58 ++++++++++---------- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/app/components/search_bar/component.html.erb b/app/components/search_bar/component.html.erb index c81dbd83c..d92944323 100644 --- a/app/components/search_bar/component.html.erb +++ b/app/components/search_bar/component.html.erb @@ -1,9 +1,8 @@ -