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('layouts.application.no_memberhsip_warning', link: organizations_path).html_safe %> +
diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb index 4231f238a..d219ae20d 100644 --- a/app/views/devise/confirmations/new.html.erb +++ b/app/views/devise/confirmations/new.html.erb @@ -4,7 +4,7 @@

<%= t(".resend_instructions") %>

<%= 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| %>
diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb index 45ac98b79..cc362d33a 100644 --- a/app/views/devise/passwords/edit.html.erb +++ b/app/views/devise/passwords/edit.html.erb @@ -3,7 +3,7 @@

<%= t(".change_password") %>

- <%= render 'layouts/messages' %> + <%= render 'application/flash_messages' %> <%= show_error_messages!(resource) %> <%= form_for resource, url: password_path(resource_name), html: { method: :put } do |f| %> <%= f.hidden_field :reset_password_token %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index 582464ca9..b86ac8815 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -4,7 +4,7 @@

<%= t(".forgot_question") %>

<%= 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| %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 35673c42e..7ee90a473 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -2,7 +2,7 @@
- <%= render 'layouts/messages' %> + <%= render 'application/flash_messages' %> <%= show_error_messages!(resource) %> <%= form_for resource, url: session_path(resource_name) do |f| %>
diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb index 7dd14d1da..5cb658301 100644 --- a/app/views/devise/unlocks/new.html.erb +++ b/app/views/devise/unlocks/new.html.erb @@ -4,7 +4,7 @@

<%= t(".resend_instructions") %>

<%= 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| %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 1fc53ee74..34bd06dbb 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -15,8 +15,9 @@ <%= render 'navbar' %>
- <%= render 'layouts/messages' unless devise_controller? %> + <%= render 'application/flash_messages' unless devise_controller? %>
+ <%= render 'application/no_membership_warning' if show_no_membership_warning? %> <%= yield %>
<%= organization_logo %> diff --git a/app/views/organization_notifier/member_deleted.html.erb b/app/views/organization_notifier/member_deleted.html.erb new file mode 100644 index 000000000..7e1d11a92 --- /dev/null +++ b/app/views/organization_notifier/member_deleted.html.erb @@ -0,0 +1 @@ +<%= t("organization_notifier.member_deleted.body", username: @username) %> diff --git a/app/views/organization_notifier/new_petition.html.erb b/app/views/organization_notifier/new_petition.html.erb index 783cb9df4..a274388b1 100644 --- a/app/views/organization_notifier/new_petition.html.erb +++ b/app/views/organization_notifier/new_petition.html.erb @@ -1 +1 @@ -<%= t('petitions.new_body', username: @user.username, here_link: link_to(t('global.here'), manage_petitions_url)).html_safe %> +<%= t('petitions.new_body', username: @user.username, organization_name: @organization_name, here_link: link_to(t('global.here'), manage_petitions_url)).html_safe %> diff --git a/app/views/organization_notifier/no_membership_warning.html.erb b/app/views/organization_notifier/no_membership_warning.html.erb new file mode 100644 index 000000000..be077e238 --- /dev/null +++ b/app/views/organization_notifier/no_membership_warning.html.erb @@ -0,0 +1 @@ +<%= t('layouts.application.no_memberhsip_warning', link: organizations_url).html_safe %> diff --git a/app/views/users/_member_card.html.erb b/app/views/users/_member_card.html.erb index 3ed9f5109..5766a58ed 100644 --- a/app/views/users/_member_card.html.erb +++ b/app/views/users/_member_card.html.erb @@ -1,10 +1,10 @@ -
-
-
-
<%= image_tag avatar_url(member.user, 48) %>
-
+
+
+
+
<%= image_tag avatar_url(member.user, 48) %>
+

<%= member.link_to_self %>

-
+
<%= "##{member.member_uid} — " %> <% if member.last_sign_in_at %> <%= t('.active_ago', time: distance_of_time_in_words(Time.now, member.last_sign_in_at)) %> @@ -25,24 +25,24 @@
-
-
+
+
<%= sanitize strip_tags(markdown(member.description&.truncate(124))) %>
-
+
<% if member.phone.present? %> -
+
<%= phone_to member.phone %>
<% end %> <% if member.mail_to.present? %> -
+
<%= member.mail_to %>
<% end %> -
+
<%= "Balance: " %> <%= member.account_balance %>
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index bf73c674f..bafc004b8 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -20,7 +20,7 @@
-
+
<%= render partial: 'member_card', collection: @member_view_models, as: :member %>
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index f938920ca..b84f87a71 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -5,7 +5,7 @@
  • <%= link_to edit_user_path(@user) do %> <%= glyph :pencil, t('global.edit') %> - + <%= t "global.edit" %> <% end %>
  • <% end %> @@ -13,7 +13,7 @@
  • <%= link_to new_transfer_path(id: @user.id, destination_account_id: @member.account.id) do %> <%= glyph :time, t('global.give_time') %> - + <%= t "global.give_time" %> <% end %>
  • <% end %> diff --git a/config/application.rb b/config/application.rb index 810b4ce80..8df39d0fa 100644 --- a/config/application.rb +++ b/config/application.rb @@ -22,6 +22,9 @@ class Application < Rails::Application # ActiveJob configuration config.active_job.queue_adapter = :sidekiq + # ActionMailer background queues + config.action_mailer.deliver_later_queue_name = :mailers + # Use db/structure.sql with SQL as schema format # This is needed to store in the schema SQL statements not covered by the ORM config.active_record.schema_format = :sql @@ -29,9 +32,5 @@ class Application < Rails::Application # Guard against DNS rebinding attacks by permitting hosts # localhost is necessary for the docker image config.hosts = ENV.fetch('ALLOWED_HOSTS', 'localhost').split(' ') - # config.hosts << 'timeoverflow.local' - # config.hosts << 'staging.timeoverflow.org' - # config.hosts << 'www.timeoverflow.org' - # config.hosts << 'timeoverflow.org' end end diff --git a/config/locales/ca.yml b/config/locales/ca.yml index f30d500d6..557760dbb 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -319,6 +319,7 @@ ca: help: Ajuda login: Entra manage_memberships: Gestiona membres + no_memberhsip_warning: El teu registre es completarà un cop hagis sol·licitat unir-te a un Banc de Temps i el Banc de Temps accepti la teva sol·licitud. Si no sol·licites unir-te a un Banc de Temps en un termini de 30 dies, el teu usuari serà eliminat del sistema. report: report_title: INFORME locales: @@ -370,6 +371,8 @@ ca: give_time_for: Transferir temps per aquesta oferta offered_by: Oferents organization_notifier: + member_deleted: + body: L'usuari %{username} s'ha donat de baixa de l'organització. recent_posts: subject: Butlletí setmanal text1: 'Últimas ofertas publicadas:' @@ -393,8 +396,8 @@ ca: banner-title: Ets un Banc de Temps? empower-adbdt: ADBdT empower-adbdt-title: Associació pel Desenvolupament dels Bancs de Temps - empower-coopdevs: CoopDevs - empower-coopdevs-title: CoopDevs + empower-coopdevs: Coopdevs + empower-coopdevs-title: Coopdevs empower-github: Github empower-github-title: Github empower-showmap: veure mapa @@ -417,12 +420,12 @@ ca: title2: als Bancs de Temps petitions: application_sent: Sol·licitud enviada correctament - application_sent_body: Hola! La teva sol·licitud a %{organization_name} s'ha enviat correctament. + application_sent_body: Hola! La teva sol·licitud a %{organization_name} s'ha enviat correctament. Algú de l'equip del banc de temps es posarà en contacte amb tu per finalitzar el procés dalta. application_status: Sol·licitud %{status} applications: Sol·licituds apply: Sol·licita unir-te new: Nova sol·licitud - new_body: Hola! Nova petició de l'usuari %{username}. Gestiona les sol·licituds %{here_link}. + new_body: Hola, %{username} acaba de sol·licitar unir-se al Banc de Temps %{organization_name}. Pots accedir %{here_link} per acceptar o rebutjar la sol·licitud. Un cop acceptis la sol·licitud, l'usuari apareixerà automàticament a la teva llista d'usuaris. status: accepted: acceptada declined: rebutjada diff --git a/config/locales/en.yml b/config/locales/en.yml index 155a21940..36c5f5f57 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -319,6 +319,7 @@ en: help: Help login: Login manage_memberships: Manage memberships + no_memberhsip_warning: Your registration will be completed once you have requested to join a Timebank and the Timebank accepts your request. If you do not request to join a timebank within 30 days, your user will be deleted from the system. report: report_title: REPORT locales: @@ -370,6 +371,8 @@ en: give_time_for: Time transfer for this offer offered_by: Offered by organization_notifier: + member_deleted: + body: User %{username} has unsubscribed from the organization. recent_posts: subject: Newsletter text1: 'Latest offers published:' @@ -393,8 +396,8 @@ en: banner-title: Are you a Time Bank? empower-adbdt: ADBdT empower-adbdt-title: Association for the Development of Time Banks - empower-coopdevs: CoopDevs - empower-coopdevs-title: CoopDevs + empower-coopdevs: Coopdevs + empower-coopdevs-title: Coopdevs empower-github: Github empower-github-title: Github empower-showmap: see map @@ -422,7 +425,7 @@ en: applications: Applications apply: Apply to join new: New Application - new_body: Hello! New petition from user %{username}. Manage your applications %{here_link}. + new_body: Hello, %{username} has just requested to join %{organization_name} Timebank. You can enter %{here_link} to accept or reject the request. Once you accept the request the user will automatically appear on your users list. status: accepted: accepted declined: declined diff --git a/config/locales/es.yml b/config/locales/es.yml index da2015475..50ee7b641 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -319,6 +319,7 @@ es: help: Ayuda login: Entra manage_memberships: Gestionar membresia + no_memberhsip_warning: Tu registro se completará una vez que hayas solicitado unirte a un Banco de Tiempo y el Banco de Tiempo acepte tu solicitud. Si no solicitas unirte a un Banco de Tiempo dentro de 30 días, tu usuario será eliminado del sistema. report: report_title: INFORME locales: @@ -370,6 +371,8 @@ es: give_time_for: Transferir tiempo por esta oferta offered_by: Ofertantes organization_notifier: + member_deleted: + body: El usuario %{username} se ha dado de baja de la organización. recent_posts: subject: Boletín semanal text1: 'Últimas ofertas publicadas:' @@ -393,8 +396,8 @@ es: banner-title: "¿Eres un Banco de Tiempo?" empower-adbdt: ADBdT empower-adbdt-title: Asociación para el Desarrollo de los Bancos de Tiempo - empower-coopdevs: CoopDevs - empower-coopdevs-title: CoopDevs + empower-coopdevs: Coopdevs + empower-coopdevs-title: Coopdevs empower-github: Github empower-github-title: Github empower-showmap: ver mapa @@ -417,12 +420,12 @@ es: title2: los Bancos de Tiempo petitions: application_sent: Solicitud enviada correctamente - application_sent_body: 'Hola! Tu solicitud a %{organization_name} se ha enviado correctamente. ' + application_sent_body: Hola! Tu solicitud a %{organization_name} se ha enviado correctamente. Alguien del equipo del banco de tiempo se pondrá en contacto contigo para finalizar el proceso de alta. application_status: Solicitud %{status} applications: Solicitudes apply: Solicita unirte new: Nueva solicitud - new_body: Hola! Nueva petición del usuario %{username}. Gestiona las solicitudes %{here_link}. + new_body: Hola, %{username} acaba de solicitar unirse al Banco de Tiempo %{organization_name}. Puedes entrar %{here_link} para aceptar o rechazar la solicitud. Una vez que aceptes la solicitud, el usuario aparecerá automáticamente en tu lista de usuarios. status: accepted: aceptada declined: rechazada diff --git a/config/locales/eu.yml b/config/locales/eu.yml index b0723f2b6..d1c2dca46 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -65,7 +65,7 @@ eu: notifications: Jaso eposta jakinarazpenak organization: Erakundea phone: Telefonoa - postcode: + postcode: push_notifications: Jaso jakinarazpenak sakelakoan registration_date: Erregistratze-da registration_number: Erabiltzaile kodea @@ -274,7 +274,7 @@ eu: contact_details: Kontaktuaren datuak create: Sortu date: Datuak - decline: + decline: delete: Ezabatu demote: Ohiko erabiltzaile bihurtu edit: Aldaketak egin @@ -282,7 +282,7 @@ eu: filter: Iragazkia from: tik give_time: Denbora eman - here: + here: home: Hasiera information: Informazioa locales_header: Hizkuntza aldatu @@ -290,13 +290,13 @@ eu: more: 'Gehiago ikusi ' movements: Mugimenduak next: Hurrengoa - or: + or: promote: Administratzaile bihurtu reason: Arrazoia required_field: Derrigorrez bete beharrekoa save: Gorde search: Bilatu - search_location: + search_location: show: Erakutsi source_destination: Jatorria/ helmuga statistics: Estatistika @@ -313,12 +313,13 @@ eu: layouts: application: about: TimeOverflowri buruz - bdtnear: + bdtnear: edit_org: " %{organization} aldaketak egin" edit_profile: Nire profila aldatu help: Laguntza login: Sartu - manage_memberships: + manage_memberships: + no_memberhsip_warning: Zure erregistroa osatuko da Denbora Banku batera sartzeko eskaera egin eta Denbora Bankuak zure eskaera onartzen duenean. 30 eguneko epean Denbora Banku batera sartzeko eskaerarik egiten ez baduzu, zure erabiltzailea sistematik ezabatuko da. report: report_title: TXOSTENA locales: @@ -370,6 +371,8 @@ eu: give_time_for: Eskaintza honengatik denbora eman offered_by: Eskaintzaren emaileak organization_notifier: + member_deleted: + body: Erabiltzaileak %{username} erakundetik baja eman du. recent_posts: subject: Asteroko buletina text1: Argitaraturiko azken eskaintzak @@ -383,7 +386,7 @@ eu: new: Banku berria show: contact_information: kontaktuaren informazioa - join_timebank: + join_timebank: pages: about: app-mobile: Mugikorrerako Appa @@ -393,8 +396,8 @@ eu: banner-title: Denbora banku bat al zara? empower-adbdt: ADBdT empower-adbdt-title: Denbora Bankuen garapenerako elkartea - empower-coopdevs: CoopDevs - empower-coopdevs-title: CoopDevs + empower-coopdevs: Coopdevs + empower-coopdevs-title: Coopdevs empower-github: Github empower-github-title: Github empower-showmap: Mapa ikusi @@ -416,25 +419,25 @@ eu: title: Denbora bankuek diseinaturiko softwarea, title2: Denbora bankuentzat petitions: - application_sent: - application_sent_body: - application_status: - applications: - apply: - new: - new_body: + application_sent: + application_sent_body: + application_status: + applications: + apply: + new: + new_body: status: - accepted: - declined: - pending: - sent: - status_applications: + accepted: + declined: + pending: + sent: + status_applications: posts: show: info: "%{type} hau %{organization}(e)ri dagokio." reports: download: Jaitsi - download_all: + download_all: shared: movements: delete_reason: Zihur zaude azalpen hau ezabatu nahi duzula? @@ -521,12 +524,12 @@ eu: error_amount: Denbora 0 baino handiagoa izan behar da users: avatar: - change_your_image: - crop_the_image: - max_size_warning: + change_your_image: + crop_the_image: + max_size_warning: confirm_email: - email_sent: - please: + email_sent: + please: edit: edit_user: Erabiltzailea aldatu form: @@ -563,7 +566,7 @@ eu: from_to: Jatorria/ Helmuga inactive: "( Ez erabilgarri)" inactive_user: Erabiltzailea ez dago erabilgarri - invalid_format: + invalid_format: phone: one: Telefono zenbakia other: Telefono zenbakiak @@ -576,9 +579,9 @@ eu: active_warning: " %{user} erabiltzailearen kontuaren egoera aldatuko duzu" cancel_warning: " %{user} erabiltzailea D bankutik ezabatuko duzu" deactivate: Desaktibatu - delete_membership: + delete_membership: manage_warning: "%{user} erabiltzailearen onurak aldatuko dituzu" - sure_delete: + sure_delete: views: pagination: first: Lehenengoa diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 75a97ce3d..37937cc63 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -319,6 +319,7 @@ fr: help: Aide login: Connexion manage_memberships: Gérer les inscriptions + no_memberhsip_warning: Votre inscription sera finalisée une fois que vous aurez demandé à rejoindre une Banque de Temps et que la Banque de Temps aura accepté votre demande. Si vous ne demandez pas à rejoindre une Banque de Temps dans les 30 jours, votre compte sera supprimé du système. report: report_title: RAPPORT locales: @@ -370,6 +371,8 @@ fr: give_time_for: Donner du temps pour cette offre offered_by: Offert par organization_notifier: + member_deleted: + body: L'utilisateur %{username} s'est désabonné de l'organisation. recent_posts: subject: Newsletter text1: 'Dernières offres publiées :' @@ -393,8 +396,8 @@ fr: banner-title: Êtes-vous une Banque de temps ? empower-adbdt: ADBdT empower-adbdt-title: Association pour le développement des Banques de temps - empower-coopdevs: CoopDevs - empower-coopdevs-title: CoopDevs + empower-coopdevs: Coopdevs + empower-coopdevs-title: Coopdevs empower-github: Github empower-github-title: Github empower-showmap: Voir la map @@ -416,19 +419,19 @@ fr: title: L'application conçue par et pour title2: Banques de temps petitions: - application_sent: Demande envoyée avec succès - application_sent_body: Bonjour! votre demande à %{organization_name} a bien été envoyée. - application_status: Demande %{status} - applications: Applications - apply: Demande à nous rejoindre - new: Nouvelle demande - new_body: Bonjour! Nouvelle demande de %{username}. Gérer vos demandes %{here_link}. + application_sent: Demande d'inscription envoyée avec succès + application_sent_body: Bonjour! votre demande d'inscription à %{organization_name} a bien été envoyée. + application_status: Demande d'inscription %{status} + applications: Demandes d'inscription + apply: Demander à nous rejoindre + new: Nouvelle demande d'inscription + new_body: Bonjour, %{username} vient de demander à rejoindre la Banque de Temps %{organization_name}. Vous pouvez accéder %{here_link} pour accepter ou rejeter la demande. Une fois que vous acceptez la demande, l'utilisateur apparaîtra automatiquement dans votre liste d'utilisateurs. status: accepted: acceptée declined: déclinée pending: en attente sent: envoyée - status_applications: "%{status} demandes" + status_applications: Demandes d'inscription %{status} posts: show: info: Cette %{type} appartient à %{organization}. @@ -505,9 +508,9 @@ fr: alpha_grouped_index: maintitle: Tags disponibles terms: - accept: Accept + accept: Accepter show: - accept: Accept + accept: Accepter transfers: computation: hour: diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 16661d4ea..8f4911d66 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -57,7 +57,7 @@ gl: alt_phone: Teléfono alternativo created_at: Creado date_of_birth: Data de nacemento - deactivated: + deactivated: description: Descrición email: Correo electrónico gender: Xénero @@ -65,7 +65,7 @@ gl: notifications: Recibir notificacións por correo electrónico organization: Organización phone: Teléfono - postcode: + postcode: push_notifications: Recibir notificacións móbiles registration_date: Data de rexistro registration_number: Código de persoa usuaria @@ -144,7 +144,7 @@ gl: reports: Informes sign_out: Saír statistics: Estatísticas - statistics_all_transfers: + statistics_all_transfers: stats: Estatísticas tags: Etiquetas type_of_swaps: Tipo de transaccións @@ -274,15 +274,15 @@ gl: contact_details: Datos de contacto create: Crea date: Datos - decline: + decline: delete: Eliminar demote: Degradar a usuario/a normal edit: Actualización - enter_to_timebank: + enter_to_timebank: filter: Filtro from: Desde give_time: Transferencia de tempo - here: + here: home: Inicio information: Información locales_header: cambiar o idioma @@ -290,13 +290,13 @@ gl: more: Máis movements: Transaccións next: Próximo - or: + or: promote: Promover á persoa administradora reason: Razón required_field: "* Campo obrigatorio" save: Gardar search: Busca - search_location: + search_location: show: Amosar source_destination: Dende/ata statistics: Estatísticas @@ -313,12 +313,13 @@ gl: layouts: application: about: Sobre TimeOverflow - bdtnear: + bdtnear: edit_org: Actualización %{organization} edit_profile: Actualiza o meu perfil help: Axuda login: Acceder - manage_memberships: + manage_memberships: + no_memberhsip_warning: O teu rexistro completarase unha vez que solicites unirte a un Banco de Tempo e o Banco de Tempo acepte a túa solicitude. Se non solicitas unirte a un Banco de Tempo dentro de 30 días, o teu usuario será eliminado do sistema. report: report_title: INFORME locales: @@ -370,6 +371,8 @@ gl: give_time_for: Transferencia de tempo para esta oferta offered_by: Ofrecido por organization_notifier: + member_deleted: + body: O usuario %{username} deu de baixa na organización. recent_posts: subject: Boletín informativo text1: 'Últimas ofertas publicadas:' @@ -383,7 +386,7 @@ gl: new: Novo banco show: contact_information: Información de contacto - join_timebank: + join_timebank: pages: about: app-mobile: Aplicación móbil @@ -393,8 +396,8 @@ gl: banner-title: Es ti un Banco de Tempo? empower-adbdt: ADBdT empower-adbdt-title: Asociación para o Desenvolvemento de Bancos de Tempo - empower-coopdevs: CoopDevs - empower-coopdevs-title: CoopDevs + empower-coopdevs: Coopdevs + empower-coopdevs-title: Coopdevs empower-github: Github empower-github-title: Github empower-showmap: ver o mapa @@ -416,25 +419,25 @@ gl: title: O software deseñado por e para title2: Bancos de tempo petitions: - application_sent: - application_sent_body: - application_status: - applications: - apply: - new: - new_body: + application_sent: + application_sent_body: + application_status: + applications: + apply: + new: + new_body: status: - accepted: - declined: - pending: - sent: - status_applications: + accepted: + declined: + pending: + sent: + status_applications: posts: show: info: Este %{type} pertence a %{organization}. reports: download: Descarga - download_all: + download_all: shared: movements: delete_reason: Estás seguro/a de borrar este comentario? @@ -521,12 +524,12 @@ gl: error_amount: O tempo debe ser superior a 0 users: avatar: - change_your_image: - crop_the_image: - max_size_warning: + change_your_image: + crop_the_image: + max_size_warning: confirm_email: - email_sent: - please: + email_sent: + please: edit: edit_user: Actualizar persoa usuaria form: @@ -534,12 +537,12 @@ gl: give_time: give_time: Dálle tempo index: - account_deactivated: + account_deactivated: actions: Accións active_warning: Vas cambiar o estado da conta de usuario/a %{username} cancel_warning: Vas borrar a conta do Banco de tempo para o usuario/a %{username} create: Crea unha nova persoa usuaria - deactivated_warning: + deactivated_warning: manage_warning: Vas cambiar privilexios para o usuario/a %{username} members: Persoas usuarias user_created: O usuario/a %{uid} %{name} gardouse @@ -563,7 +566,7 @@ gl: from_to: De / Para inactive: "(Inactivo/a)" inactive_user: A persoa usuaria non está activa - invalid_format: + invalid_format: phone: one: Teléfono other: Teléfonos @@ -576,9 +579,9 @@ gl: active_warning: Vas cambiar o estado da conta de usuario/a para %{user} cancel_warning: Vas borrar a conta do Banco de tempo para o usuario/a %{user} deactivate: Desactivar - delete_membership: + delete_membership: manage_warning: Vas cambiar privilexios para o usuario/a %{user} - sure_delete: + sure_delete: views: pagination: first: Primeira diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 684f7f3ac..f21cb698f 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -57,7 +57,7 @@ pt-BR: alt_phone: Telefone alternativo created_at: Criado date_of_birth: Data de nascimento - deactivated: + deactivated: description: Descrição email: E-mail gender: Gênero @@ -65,7 +65,7 @@ pt-BR: notifications: Receber notificações organization: Organização phone: Telefone - postcode: + postcode: push_notifications: Receber notificações pelo celular registration_date: Data de ingresso registration_number: Código do usuário @@ -144,7 +144,7 @@ pt-BR: reports: Informes sign_out: Desconectar statistics: Estatísticas - statistics_all_transfers: + statistics_all_transfers: stats: Estatísticas tags: Etiquetas type_of_swaps: Tipos de trocas @@ -274,15 +274,15 @@ pt-BR: contact_details: Dados de contato create: Criar date: Data - decline: + decline: delete: Apagar demote: Converter em usuário normal edit: Modificar - enter_to_timebank: + enter_to_timebank: filter: Filtro from: De give_time: Transferir tempo - here: + here: home: Início information: Informação locales_header: Trocar idioma @@ -290,13 +290,13 @@ pt-BR: more: Ver mais movements: Movimentos next: Próximo - or: + or: promote: Converter em administrador reason: Razão required_field: "* Campo obrigatório" save: Guardar search: Pesquisar - search_location: + search_location: show: Mostrar source_destination: Origem/Destino statistics: Estatísticas @@ -313,12 +313,13 @@ pt-BR: layouts: application: about: Sobre TimeOverflow - bdtnear: + bdtnear: edit_org: Modificar %{organization} edit_profile: Modificar meu perfil help: Ajuda login: Entre - manage_memberships: + manage_memberships: + no_memberhsip_warning: Seu registro será concluído assim que você solicitar entrar em um Banco de Tempo e o Banco de Tempo aceitar sua solicitação. Se você não solicitar entrada em um Banco de Tempo dentro de 30 dias, seu usuário será excluído do sistema. report: report_title: INFORME locales: @@ -370,6 +371,8 @@ pt-BR: give_time_for: Transferir tempo para esta oferta offered_by: Ofertantes organization_notifier: + member_deleted: + body: O usuário %{username} cancelou a inscrição na organização. recent_posts: subject: Boletim semanal text1: 'Últimas ofertas publicadas:' @@ -383,7 +386,7 @@ pt-BR: new: Novo banco show: contact_information: Informação de contato - join_timebank: + join_timebank: pages: about: app-mobile: Aplicativo móvel @@ -393,8 +396,8 @@ pt-BR: banner-title: Você é um Banco de Tempo? empower-adbdt: ADBdt empower-adbdt-title: Associação para o Desenvolvimento dos Bancos de Tempo - empower-coopdevs: CoopDevs - empower-coopdevs-title: CoopDevs + empower-coopdevs: Coopdevs + empower-coopdevs-title: Coopdevs empower-github: Github empower-github-title: Github empower-showmap: ver mapa @@ -416,25 +419,25 @@ pt-BR: title: O software desenhado por e para title2: os Bancos de Tempo petitions: - application_sent: - application_sent_body: - application_status: - applications: - apply: - new: - new_body: + application_sent: + application_sent_body: + application_status: + applications: + apply: + new: + new_body: Olá, %{username} acabou de solicitar entrada no Banco de Tempo %{organization_name}. Você pode acessar %{here_link} para aceitar ou rejeitar a solicitação. Assim que você aceitar a solicitação, o usuário aparecerá automaticamente na sua lista de usuários. status: - accepted: - declined: - pending: - sent: - status_applications: + accepted: + declined: + pending: + sent: + status_applications: posts: show: info: Este %{type} pertence a %{organization}. reports: download: Baixar - download_all: + download_all: shared: movements: delete_reason: Tem certeza de que quer apagar este comentário? @@ -521,12 +524,12 @@ pt-BR: error_amount: O tempo deve ser maior que 0 users: avatar: - change_your_image: - crop_the_image: - max_size_warning: + change_your_image: + crop_the_image: + max_size_warning: confirm_email: - email_sent: - please: + email_sent: + please: edit: edit_user: Trocar usuário form: @@ -534,12 +537,12 @@ pt-BR: give_time: give_time: Dar Tempo a index: - account_deactivated: + account_deactivated: actions: Ações active_warning: Você mudará o status da conta para %{username} cancel_warning: Você deletará a conta do Banco de Tempo %{username} create: Criar novo usuário - deactivated_warning: + deactivated_warning: manage_warning: Você mudará privilégios para o usuário %{username} members: Membros user_created: Usuário %{uid} %{name} guardado @@ -563,7 +566,7 @@ pt-BR: from_to: Origem/Destino inactive: "(Inativo)" inactive_user: O usuário não está ativo - invalid_format: + invalid_format: phone: one: Telefone other: Telefones @@ -576,9 +579,9 @@ pt-BR: active_warning: Mudará o estado da conta do usuário %{user} cancel_warning: Eliminará o usuário do banco %{user} deactivate: Desativar - delete_membership: + delete_membership: manage_warning: Mudará os privilégios do usuário %{user} - sure_delete: + sure_delete: views: pagination: first: "« Primeira" diff --git a/config/schedule.yml b/config/schedule.yml index 7e85c3d5e..a841dc925 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -6,3 +6,7 @@ weekly_digest: cron: 'every monday at 9am' class: 'OrganizationNotifierJob' queue: cron +membership_warning: + cron: 'every day at 10am' + class: 'MembershipWarningJob' + queue: cron diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 25f4cd85c..e425e0c3b 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -2,3 +2,4 @@ :queues: - default - cron + - mailers diff --git a/db/migrate/20241230170753_add_highlighted_to_organizations.rb b/db/migrate/20241230170753_add_highlighted_to_organizations.rb new file mode 100644 index 000000000..a7f07ffc6 --- /dev/null +++ b/db/migrate/20241230170753_add_highlighted_to_organizations.rb @@ -0,0 +1,5 @@ +class AddHighlightedToOrganizations < ActiveRecord::Migration[7.0] + def change + add_column :organizations, :highlighted, :boolean, default: false + end +end diff --git a/db/structure.sql b/db/structure.sql index 1a912a796..5200e1ada 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -107,6 +107,7 @@ CREATE TABLE public.accounts ( -- CREATE SEQUENCE public.accounts_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -143,6 +144,7 @@ CREATE TABLE public.active_admin_comments ( -- CREATE SEQUENCE public.active_admin_comments_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -286,6 +288,7 @@ CREATE TABLE public.categories ( -- CREATE SEQUENCE public.categories_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -318,6 +321,7 @@ CREATE TABLE public.device_tokens ( -- CREATE SEQUENCE public.device_tokens_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -353,6 +357,7 @@ CREATE TABLE public.documents ( -- CREATE SEQUENCE public.documents_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -387,6 +392,7 @@ CREATE TABLE public.events ( -- CREATE SEQUENCE public.events_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -424,6 +430,7 @@ CREATE TABLE public.members ( -- CREATE SEQUENCE public.members_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -457,6 +464,7 @@ CREATE TABLE public.movements ( -- CREATE SEQUENCE public.movements_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -490,7 +498,8 @@ CREATE TABLE public.organizations ( address text, neighborhood character varying, city character varying, - domain character varying + domain character varying, + highlighted boolean DEFAULT false ); @@ -499,6 +508,7 @@ CREATE TABLE public.organizations ( -- CREATE SEQUENCE public.organizations_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -574,6 +584,7 @@ CREATE TABLE public.posts ( -- CREATE SEQUENCE public.posts_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -610,6 +621,7 @@ CREATE TABLE public.push_notifications ( -- CREATE SEQUENCE public.push_notifications_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -652,6 +664,7 @@ CREATE TABLE public.transfers ( -- CREATE SEQUENCE public.transfers_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -713,6 +726,7 @@ CREATE TABLE public.users ( -- CREATE SEQUENCE public.users_id_seq + AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -1305,7 +1319,7 @@ ALTER TABLE ONLY public.active_storage_attachments -- PostgreSQL database dump complete -- -SET search_path TO "$user",public; +SET search_path TO "$user", public; INSERT INTO "schema_migrations" (version) VALUES ('1'), @@ -1377,4 +1391,5 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230314233504'), ('20230401114456'), ('20231120164231'), -('20231120164346'); +('20231120164346'), +('20241230170753'); diff --git a/spec/controllers/petitions_controller_spec.rb b/spec/controllers/petitions_controller_spec.rb index 0d7bb916c..82c20c89f 100644 --- a/spec/controllers/petitions_controller_spec.rb +++ b/spec/controllers/petitions_controller_spec.rb @@ -12,13 +12,14 @@ expect do post :create, params: { user_id: user.id, organization_id: organization.id } end.to change(Petition, :count).by(1) + .and have_enqueued_mail(OrganizationNotifier).twice expect(response).to redirect_to(organizations_path) end end describe 'PUT #update' do before { login(admin.user) } - let(:petition) { Petition.create(user: user, organization: organization, status: 'pending') } + let(:petition) { Petition.create(user: user, organization: organization) } it 'decline the petition' do put :update, params: { status: 'declined', id: petition.id } @@ -41,7 +42,7 @@ allow(controller).to receive(:current_organization) { organization } login(admin.user) end - let!(:petition) { Petition.create(user: user, organization: organization, status: 'pending') } + let!(:petition) { Petition.create(user: user, organization: organization) } it 'populates a list of users with pending petitions' do get :manage diff --git a/spec/controllers/statistics_controller_spec.rb b/spec/controllers/statistics_controller_spec.rb index 9907d7bc0..d7c619927 100644 --- a/spec/controllers/statistics_controller_spec.rb +++ b/spec/controllers/statistics_controller_spec.rb @@ -45,7 +45,7 @@ it 'populates age_counts and gender_counts variables' do get :demographics - expect(assigns(:age_counts)).to eq({ "35-44" => 1 }) + expect(assigns(:age_counts)).to eq({ "45-54" => 1 }) expect(assigns(:gender_counts)).to eq({ "Otro" => 1 }) end end diff --git a/spec/features/transfer_spec.rb b/spec/features/transfer_spec.rb index 8dd8b3f6f..f6f2609ec 100644 --- a/spec/features/transfer_spec.rb +++ b/spec/features/transfer_spec.rb @@ -52,7 +52,7 @@ def navigate_to_member end def members_list - find('.to-member-cards') + find('.member-cards') end def transfer_form diff --git a/spec/jobs/membership_warning_job.rb b/spec/jobs/membership_warning_job.rb new file mode 100644 index 000000000..a9d59bc1f --- /dev/null +++ b/spec/jobs/membership_warning_job.rb @@ -0,0 +1,15 @@ +RSpec.describe MembershipWarningJob, type: :job do + let!(:org) { Fabricate(:organization) } + let!(:user) { Fabricate(:user) } + let!(:member) { Fabricate(:member, organization: org, user: user) } + let!(:user_with_no_memberships) { Fabricate(:user) } + let!(:user_with_no_memberships2) { Fabricate(:user, created_at: 15.days.ago) } + + describe '#perform' do + it "should send emails in user's locale" do + expect { + MembershipWarningJob.perform_now + }.to change { ActionMailer::Base.deliveries.count }.by(1) + end + end +end