diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ac73f21d..038e49f2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,10 @@ jobs: uses: ruby/setup-ruby@v1 with: bundler-cache: true + - name: Install ImageMagick + uses: mfinelli/setup-imagemagick@v6 + with: + cache: true - name: Run tests env: RAILS_ENV: test diff --git a/Gemfile.lock b/Gemfile.lock index 4bfc23356..346d9d752 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,47 +1,47 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8.5) - actionpack (= 7.0.8.5) - activesupport (= 7.0.8.5) + actioncable (7.0.8.7) + actionpack (= 7.0.8.7) + activesupport (= 7.0.8.7) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8.5) - actionpack (= 7.0.8.5) - activejob (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionmailbox (7.0.8.7) + actionpack (= 7.0.8.7) + activejob (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8.5) - actionpack (= 7.0.8.5) - actionview (= 7.0.8.5) - activejob (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionmailer (7.0.8.7) + actionpack (= 7.0.8.7) + actionview (= 7.0.8.7) + activejob (= 7.0.8.7) + activesupport (= 7.0.8.7) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.8.5) - actionview (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionpack (7.0.8.7) + actionview (= 7.0.8.7) + activesupport (= 7.0.8.7) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8.5) - actionpack (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + actiontext (7.0.8.7) + actionpack (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8.5) - activesupport (= 7.0.8.5) + actionview (7.0.8.7) + activesupport (= 7.0.8.7) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) @@ -60,55 +60,55 @@ GEM kaminari (~> 1.0, >= 1.2.1) railties (>= 6.1, < 7.1) ransack (>= 2.1.1, < 4) - activejob (7.0.8.5) - activesupport (= 7.0.8.5) + activejob (7.0.8.7) + activesupport (= 7.0.8.7) globalid (>= 0.3.6) - activemodel (7.0.8.5) - activesupport (= 7.0.8.5) - activerecord (7.0.8.5) - activemodel (= 7.0.8.5) - activesupport (= 7.0.8.5) - activestorage (7.0.8.5) - actionpack (= 7.0.8.5) - activejob (= 7.0.8.5) - activerecord (= 7.0.8.5) - activesupport (= 7.0.8.5) + activemodel (7.0.8.7) + activesupport (= 7.0.8.7) + activerecord (7.0.8.7) + activemodel (= 7.0.8.7) + activesupport (= 7.0.8.7) + activestorage (7.0.8.7) + actionpack (= 7.0.8.7) + activejob (= 7.0.8.7) + activerecord (= 7.0.8.7) + activesupport (= 7.0.8.7) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.8.5) + activesupport (7.0.8.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) arbre (1.7.0) activesupport (>= 3.0.0) ruby2_keywords (>= 0.0.2) ast (2.4.2) - autoprefixer-rails (10.4.16.0) + autoprefixer-rails (10.4.19.0) execjs (~> 2) aws-eventstream (1.3.0) - aws-partitions (1.887.0) - aws-sdk-core (3.191.0) + aws-partitions (1.1029.0) + aws-sdk-core (3.214.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.77.0) - aws-sdk-core (~> 3, >= 3.191.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.143.0) - aws-sdk-core (~> 3, >= 3.191.0) + aws-sdk-kms (1.96.0) + aws-sdk-core (~> 3, >= 3.210.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.176.1) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) base64 (0.2.0) bcrypt (3.1.20) - bigdecimal (3.1.7) + bigdecimal (3.1.9) bindex (0.8.1) - bootsnap (1.18.3) + bootsnap (1.18.4) msgpack (~> 1.2) bootstrap-sass (3.4.1) autoprefixer-rails (>= 5.2.1) @@ -127,14 +127,14 @@ GEM concurrent-ruby (1.3.4) connection_pool (2.4.1) crass (1.0.6) - database_cleaner (2.0.2) + database_cleaner (2.1.0) database_cleaner-active_record (>= 2, < 3) - database_cleaner-active_record (2.1.0) + database_cleaner-active_record (2.2.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - date (3.3.4) - devise (4.9.3) + date (3.4.1) + devise (4.9.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) @@ -143,39 +143,40 @@ GEM devise-i18n (1.11.1) devise (>= 4.9.0) diff-lcs (1.5.1) - docile (1.4.0) + docile (1.4.1) domain_name (0.6.20240107) dotenv (2.7.6) dotenv-rails (2.7.6) dotenv (= 2.7.6) railties (>= 3.2) - erubi (1.13.0) + erubi (1.13.1) et-orbi (1.2.11) tzinfo - execjs (2.9.1) + execjs (2.10.0) fabrication (2.31.0) faker (2.23.0) i18n (>= 1.8.11, < 2) - ffi (1.16.3) + ffi (1.17.0-arm64-darwin) + ffi (1.17.0-x86_64-linux-gnu) formtastic (4.0.0) actionpack (>= 5.2.0) formtastic_i18n (0.7.0) fugit (1.11.1) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) - gli (2.21.1) + gli (2.22.0) globalid (1.2.1) activesupport (>= 6.1) has_scope (0.7.2) actionpack (>= 4.1) activesupport (>= 4.1) http-accept (1.7.0) - http-cookie (1.0.5) + http-cookie (1.0.8) domain_name (~> 0.5) http_accept_language (2.1.1) i18n (1.14.6) concurrent-ruby (~> 1.0) - image_processing (1.12.2) + image_processing (1.13.0) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) inherited_resources (1.14.0) @@ -190,7 +191,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.7.1) + json (2.9.1) json_translate (4.0.1) activerecord (>= 4.2.0) kaminari (1.2.2) @@ -215,7 +216,8 @@ GEM i18n (>= 0.7, < 2) json (>= 1.7.7) rest-client (>= 1.8.0) - loofah (2.22.0) + logger (1.6.4) + loofah (2.23.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -226,14 +228,15 @@ GEM marcel (1.0.4) matrix (0.4.2) method_source (1.1.0) - mime-types (3.5.2) + mime-types (3.6.0) + logger mime-types-data (~> 3.2015) - mime-types-data (3.2023.1205) - mini_magick (4.12.0) + mime-types-data (3.2024.1203) + mini_magick (4.13.2) mini_mime (1.1.5) - minitest (5.25.1) - msgpack (1.7.2) - net-imap (0.5.0) + minitest (5.25.4) + msgpack (1.7.5) + net-imap (0.5.6) date net-protocol net-pop (0.1.2) @@ -243,51 +246,51 @@ GEM net-smtp (0.5.0) net-protocol netrc (0.11.0) - nio4r (2.7.3) - nokogiri (1.16.7-arm64-darwin) + nio4r (2.7.4) + nokogiri (1.18.0-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.18.0-x86_64-linux-gnu) racc (~> 1.4) orm_adapter (0.5.0) - parallel (1.24.0) - parser (3.3.0.5) + parallel (1.26.3) + parser (3.3.6.0) ast (~> 2.4.1) racc pdf-core (0.10.0) - pg (1.5.4) - pg_search (2.3.6) - activerecord (>= 5.2) - activesupport (>= 5.2) + pg (1.5.9) + pg_search (2.3.7) + activerecord (>= 6.1) + activesupport (>= 6.1) prawn (2.5.0) matrix (~> 0.4) pdf-core (~> 0.10.0) ttfunk (~> 1.8) prawn-table (0.2.2) prawn (>= 1.3.0, < 3.0.0) - public_suffix (5.0.4) - puma (6.4.3) + public_suffix (6.0.1) + puma (6.5.0) nio4r (~> 2.0) pundit (2.1.1) activesupport (>= 3.0.0) raabro (1.4.0) racc (1.8.1) - rack (2.2.10) - rack-test (2.1.0) + rack (2.2.11) + rack-test (2.2.0) rack (>= 1.3) - rails (7.0.8.5) - actioncable (= 7.0.8.5) - actionmailbox (= 7.0.8.5) - actionmailer (= 7.0.8.5) - actionpack (= 7.0.8.5) - actiontext (= 7.0.8.5) - actionview (= 7.0.8.5) - activejob (= 7.0.8.5) - activemodel (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + rails (7.0.8.7) + actioncable (= 7.0.8.7) + actionmailbox (= 7.0.8.7) + actionmailer (= 7.0.8.7) + actionpack (= 7.0.8.7) + actiontext (= 7.0.8.7) + actionview (= 7.0.8.7) + activejob (= 7.0.8.7) + activemodel (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) bundler (>= 1.15.0) - railties (= 7.0.8.5) + railties (= 7.0.8.7) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -296,15 +299,15 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) - rails-i18n (7.0.8) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + rails-i18n (7.0.10) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) - railties (7.0.8.5) - actionpack (= 7.0.8.5) - activesupport (= 7.0.8.5) + railties (7.0.8.7) + actionpack (= 7.0.8.7) + activesupport (= 7.0.8.7) method_source rake (>= 12.2) thor (~> 1.0) @@ -317,7 +320,7 @@ GEM i18n rdiscount (2.2.7.3) redis (4.8.1) - regexp_parser (2.9.0) + regexp_parser (2.10.0) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) @@ -326,46 +329,45 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rexml (3.3.6) - strscan - rspec-core (3.13.0) + rexml (3.4.0) + rspec-core (3.13.2) rspec-support (~> 3.13.0) - rspec-expectations (3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.0) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (6.1.1) + rspec-rails (6.1.5) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) - rspec-core (~> 3.12) - rspec-expectations (~> 3.12) - rspec-mocks (~> 3.12) - rspec-support (~> 3.12) - rspec-support (3.13.0) - rubocop (1.60.2) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.2) + rubocop (1.69.2) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.30.0, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.36.2, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.30.0) - parser (>= 3.2.1.0) - rubocop-rails (2.23.1) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.37.0) + parser (>= 3.3.1.0) + rubocop-rails (2.28.0) activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) - rubocop-ast (>= 1.30.0, < 2.0) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (1.13.0) - ruby-vips (2.2.0) + ruby-vips (2.2.2) ffi (~> 1.12) + logger ruby2_keywords (0.0.5) rubyzip (2.3.2) sassc (2.4.0) @@ -377,8 +379,9 @@ GEM sprockets-rails tilt select2-rails (4.0.13) - selenium-webdriver (4.17.0) + selenium-webdriver (4.27.0) base64 (~> 0.2) + logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) @@ -399,26 +402,27 @@ GEM docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-html (0.12.3) + simplecov-html (0.13.1) simplecov_json_formatter (0.1.4) sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - strscan (3.1.0) thor (1.3.2) - tilt (2.3.0) - timeout (0.4.1) + tilt (2.5.0) + timeout (0.4.3) ttfunk (1.8.0) bigdecimal (~> 3.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - uglifier (4.2.0) + uglifier (4.2.1) execjs (>= 0.3.0, < 3) - unicode-display_width (2.5.0) + unicode-display_width (3.1.3) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) warden (1.2.9) rack (>= 2.0.9) web-console (4.1.0) @@ -426,7 +430,7 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - websocket (1.2.10) + websocket (1.2.11) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -436,6 +440,7 @@ GEM PLATFORMS arm64-darwin-22 + arm64-darwin-23 x86_64-linux DEPENDENCIES diff --git a/app/admin/organization.rb b/app/admin/organization.rb index 2d8fe6dcc..37364efba 100644 --- a/app/admin/organization.rb +++ b/app/admin/organization.rb @@ -23,6 +23,7 @@ column :posts do |organization| organization.posts.count end + column :highlighted actions end @@ -37,6 +38,7 @@ form do |f| f.inputs do + f.input :highlighted, hint: "Highlighted Time Banks will appear first" f.input :name f.input :email f.input :web @@ -71,7 +73,8 @@ def destroy filter :neighborhood filter :created_at filter :updated_at + filter :highlighted permit_params :name, :email, :web, :phone, :city, :neighborhood, - :address, :description, :public_opening_times, :logo + :address, :description, :public_opening_times, :logo, :highlighted end diff --git a/app/admin/petition.rb b/app/admin/petition.rb index 9a1bcab4d..77e41bdd7 100644 --- a/app/admin/petition.rb +++ b/app/admin/petition.rb @@ -1,5 +1,15 @@ ActiveAdmin.register Petition do - actions :index + actions :index, :destroy + + controller do + def destroy + if resource.accepted? + redirect_to admin_petitions_path, alert: "ACCEPTED petitions can't be deleted" + else + super + end + end + end index do id_column @@ -9,6 +19,7 @@ column :status do |petition| petition.status.upcase end + actions end filter :organization diff --git a/app/admin/user.rb b/app/admin/user.rb index dc0de5e09..f34aed6ce 100644 --- a/app/admin/user.rb +++ b/app/admin/user.rb @@ -16,6 +16,9 @@ redirect_to action: :index end + scope :all + scope :without_memberships + index do selectable_column column do |user| @@ -29,6 +32,7 @@ column :posts do |u| u.posts.count end + column :created_at actions end diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 3c4ad3f5e..f20205cbf 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -302,6 +302,10 @@ ul.statistics li{ padding-left: 1.5rem; } +.alert a { + text-decoration: underline; +} + // fields that contain an error .field_with_errors{ color: red; diff --git a/app/assets/stylesheets/application/member-card.scss b/app/assets/stylesheets/application/member-card.scss index 6592e33f6..ac9d1f003 100644 --- a/app/assets/stylesheets/application/member-card.scss +++ b/app/assets/stylesheets/application/member-card.scss @@ -1,14 +1,37 @@ -.to-member-card { +.member-cards { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: stretch; + gap: 20px; + margin: 10px 0; + + @media(max-width: $screen-lg-min) { + display: block; + } +} + +.member-card__wrapper { + width: 48%; + + @media (max-width: $screen-lg-min) { + width: 100%; + } +} + +.member-card { & { - margin: 16px 0; width: 100%; max-width: 600px; - height: 178px; + min-height: 178px; background-color: white; box-shadow: 0 0 3px $palette-grey; - display: inline-block; + display: flex; + flex-direction: column; + height: 100%; + border-radius: 4px; - @media(max-width: $screen-sm-min) { + @media(max-width: $screen-lg-min) { height: auto; margin: 10px 0; } @@ -19,6 +42,7 @@ display: flex; background-color: $palette-dark-turkey; padding: 10px 20px; + border-radius: 4px 4px 0 0; } &__avatar { @@ -48,13 +72,16 @@ &__body { & { padding: 16px 20px; + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; } &__description { margin-bottom: 10px; color: #666; font-size: 15px; - height: 44px; @media(max-width: $screen-sm-min) { height: auto; @@ -63,6 +90,7 @@ &__items { display: flex; + flex-wrap: wrap; @media(max-width: $screen-sm-min) { display: block; @@ -71,11 +99,11 @@ } &__item { - & { - margin-right: 20px; - font-size: 14px; - color: grey; - } + margin-right: 20px; + font-size: 14px; + color: gray; + overflow-wrap: break-word; + white-space: normal; a { color: grey; diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index a8823c0c8..e62d199d0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -53,7 +53,7 @@ def after_sign_in_path_for(user) elsif user.members.present? users_path else - page_path("about") + organizations_path end end diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index ad585bb44..b5454a76a 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -6,6 +6,8 @@ def destroy toggle_active_posts @member.destroy + OrganizationNotifier.member_deleted(@member.user.username, current_organization).deliver_later + redirect_to request.referer.include?(organizations_path) ? organizations_path : manage_users_path end diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index 37daeafcc..7fc2ec660 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -10,7 +10,7 @@ def index organizations = Organization.where.not(id: @user_organizations&.pluck(:id)) organizations = organizations.search_by_query(params[:q]) if params[:q].present? - @organizations = organizations.page(params[:page]).per(25) + @organizations = organizations.order(highlighted: :desc).page(params[:page]).per(25) end def show diff --git a/app/controllers/petitions_controller.rb b/app/controllers/petitions_controller.rb index 8dc4d432b..d39d88396 100644 --- a/app/controllers/petitions_controller.rb +++ b/app/controllers/petitions_controller.rb @@ -3,15 +3,14 @@ class PetitionsController < ApplicationController def create petition = Petition.new petition_params - petition.status = "pending" if petition.save - OrganizationNotifier.new_petition(petition).deliver_now - OrganizationNotifier.petition_sent(petition).deliver_now + OrganizationNotifier.new_petition(petition).deliver_later + OrganizationNotifier.petition_sent(petition).deliver_later flash[:notice] = t('petitions.application_status', status: t("petitions.status.sent")) else - flash[:error] = t('errors.internal_server_error.description') + flash[:error] = petition.errors.full_messages.to_sentence end redirect_back fallback_location: organization_path(petition.organization) @@ -25,14 +24,14 @@ def update petition.user.add_to_organization(petition.organization) if status == 'accepted' flash[:notice] = t('petitions.application_status', status: t("petitions.status.#{status}")) else - flash[:error] = t('errors.internal_server_error.description') + flash[:error] = petition.errors.full_messages.to_sentence end redirect_to manage_petitions_path end def manage - @status = params[:status] || 'pending' + @status = params[:status] || Petition::DEFAULT_STATUS @users = User.joins(:petitions).where(petitions: { organization_id: current_organization.id, status: @status }).page(params[:page]).per(20) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index eff524c3e..675b12cc6 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -113,4 +113,8 @@ def alert_class(alert) 'alert-info' end end + + def show_no_membership_warning? + current_user&.no_membership_warning? && !devise_controller? + end end diff --git a/app/jobs/membership_warning_job.rb b/app/jobs/membership_warning_job.rb new file mode 100644 index 000000000..981b62477 --- /dev/null +++ b/app/jobs/membership_warning_job.rb @@ -0,0 +1,11 @@ +class MembershipWarningJob < ActiveJob::Base + queue_as :cron + + def perform + User.without_memberships.find_each do |user| + if user.created_at.to_date == 15.days.ago.to_date && user.no_membership_warning? + OrganizationNotifier.no_membership_warning(user).deliver_now + end + end + end +end diff --git a/app/mailers/organization_notifier.rb b/app/mailers/organization_notifier.rb index 5827711f5..2625b98e3 100644 --- a/app/mailers/organization_notifier.rb +++ b/app/mailers/organization_notifier.rb @@ -4,7 +4,6 @@ class OrganizationNotifier < ActionMailer::Base def recent_posts(posts, locale, users) @offers = posts.where(type: "Offer").take(10) @inquiries = posts.where(type: "Inquiry").take(10) - @organization_name = posts.take.organization.name I18n.with_locale(locale) do @@ -15,11 +14,13 @@ def recent_posts(posts, locale, users) def new_petition(petition) @user = petition.user organization = petition.organization + org_managers = organization.all_managers + @organization_name = organization.name - I18n.with_locale(locale) do + I18n.with_locale(org_managers.first&.locale) do mail( - subject: 'New Application', - bcc: organization.users.joins(:members).where(members: { manager: true }).pluck(:email).uniq + subject: "New Application - #{organization.name}", + bcc: org_managers.pluck(:email).uniq ) end end @@ -27,11 +28,32 @@ def new_petition(petition) def petition_sent(petition) @organization_name = petition.organization.name - I18n.with_locale(locale) do + I18n.with_locale(petition.user.locale) do mail( subject: 'Application sent correctly', to: petition.user.email ) end end + + def member_deleted(username, organization) + @username = username + org_managers = organization.all_managers + + I18n.with_locale(org_managers.first&.locale) do + mail( + subject: "Membership deleted - #{organization.name}", + bcc: org_managers.pluck(:email).uniq + ) + end + end + + def no_membership_warning(user) + I18n.with_locale(user.locale) do + mail( + subject: "Do not forget to join a Timebank", + to: user.email + ) + end + end end diff --git a/app/models/organization.rb b/app/models/organization.rb index cec867101..90847ee3b 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -33,6 +33,10 @@ class Organization < ApplicationRecord before_validation :ensure_url after_create :create_account + def to_s + "#{name}" + end + def all_transfers_with_accounts all_transfers. includes(movements: { account: :accountable }). @@ -40,8 +44,8 @@ def all_transfers_with_accounts distinct end - def to_s - "#{name}" + def all_managers + users.where(members: { manager: true }) end def display_name_with_uid diff --git a/app/models/petition.rb b/app/models/petition.rb index 24ad28376..5297e4f8e 100644 --- a/app/models/petition.rb +++ b/app/models/petition.rb @@ -1,6 +1,10 @@ class Petition < ApplicationRecord - enum status: %i[pending accepted declined] + DEFAULT_STATUS = "pending" + + enum status: %i[pending accepted declined], _default: DEFAULT_STATUS belongs_to :user belongs_to :organization + + validates :user_id, uniqueness: { scope: :organization_id } end diff --git a/app/models/user.rb b/app/models/user.rb index 2877fb8e2..c63bc6f95 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -38,6 +38,7 @@ class User < ApplicationRecord accepts_nested_attributes_for :members, allow_destroy: true default_scope { order("users.id ASC") } + scope :without_memberships, -> { where.missing(:members) } scope :actives, -> { joins(:members).where(members: { active: true }) } scope :online_active, -> { where("sign_in_count > 0") } scope :notifications, -> { where(notifications: true) } @@ -93,6 +94,14 @@ def active?(organization) organization && !!(as_member_of(organization).try :active) end + def memberships? + members.any? + end + + def no_membership_warning? + confirmed? && terms_accepted_at.present? && !memberships? + end + def member(organization) members.where(organization_id: organization).first end diff --git a/app/services/push_notifications/broadcast.rb b/app/services/push_notifications/broadcast.rb index 9681ea9d2..2b3ef2eb0 100644 --- a/app/services/push_notifications/broadcast.rb +++ b/app/services/push_notifications/broadcast.rb @@ -1,3 +1,5 @@ +require "net/http" + module PushNotifications class Broadcast class PostError < ::StandardError; end diff --git a/app/views/layouts/_messages.html.erb b/app/views/application/_flash_messages.html.erb similarity index 100% rename from app/views/layouts/_messages.html.erb rename to app/views/application/_flash_messages.html.erb diff --git a/app/views/application/_no_membership_warning.html.erb b/app/views/application/_no_membership_warning.html.erb new file mode 100644 index 000000000..8a2c552ea --- /dev/null +++ b/app/views/application/_no_membership_warning.html.erb @@ -0,0 +1,3 @@ +
<%= t(".resend_instructions_description") %>
- <%= render 'layouts/messages' %> + <%= render 'application/flash_messages' %> <%= show_error_messages!(resource) %> <%= form_for resource, url: confirmation_path(resource_name), html: { method: :post } do |f| %><%= t(".forgot_question_description") %>
- <%= render 'layouts/messages' %> + <%= render 'application/flash_messages' %> <%= show_error_messages!(resource) %> <%= form_for resource, url: password_path(resource_name), html: { method: :post } do |f| %><%= t(".resend_instructions_description") %>
- <%= render 'layouts/messages' %> + <%= render 'application/flash_messages' %> <%= show_error_messages!(resource) %> <%= form_for resource, url: unlock_path(resource_name), html: { method: :post } do |f| %>