From cf142084f3ed5872051a268ebf2f982433b46b06 Mon Sep 17 00:00:00 2001 From: Jose Farias Date: Wed, 9 Oct 2024 11:30:32 -0400 Subject: [PATCH 1/2] Add debug gem --- Gemfile | 1 + Gemfile.lock | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index bdda4c9a7..ebbfb69cb 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ gem "bcrypt", "~> 3.1.7" gem "rqrcode" group :development, :test do + gem "debug" gem "brakeman", require: false gem "rubocop-rails-omakase", require: false end diff --git a/Gemfile.lock b/Gemfile.lock index f097342ab..614910582 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -126,6 +126,9 @@ GEM connection_pool (2.4.1) crass (1.0.6) date (3.3.4) + debug (1.9.2) + irb (~> 1.10) + reline (>= 0.3.8) drb (2.2.1) erubi (1.13.0) globalid (1.2.1) @@ -319,6 +322,7 @@ DEPENDENCIES bootsnap brakeman capybara + debug importmap-rails propshaft puma (>= 5.0) @@ -335,4 +339,4 @@ RUBY VERSION ruby 3.3.4p94 BUNDLED WITH - 2.5.11 + 2.5.21 From dd1752de2a7bed4087d6f6d86497b9bcf4882777 Mon Sep 17 00:00:00 2001 From: Jose Farias Date: Wed, 9 Oct 2024 11:53:05 -0400 Subject: [PATCH 2/2] Implement bucket access form --- app/assets/stylesheets/filters.css | 10 ++++ .../buckets/accesses_controller.rb | 17 ------- app/controllers/buckets_controller.rb | 22 ++++++++- app/helpers/translations_helper.rb | 3 +- .../controllers/filter_controller.js | 46 +++++++++++++++++++ app/models/bucket/accessible.rb | 34 ++++++++------ app/models/user.rb | 1 + app/views/bubbles/_filters.html.erb | 5 ++ app/views/buckets/_user_toggle.html.erb | 24 ++++++++++ app/views/buckets/edit.html.erb | 35 ++++++++++++-- config/environments/development.rb | 2 +- test/controllers/buckets_controller_test.rb | 18 ++++++-- test/models/.keep | 0 test/models/bucket_test.rb | 7 +-- 14 files changed, 177 insertions(+), 47 deletions(-) delete mode 100644 app/controllers/buckets/accesses_controller.rb create mode 100644 app/javascript/controllers/filter_controller.js create mode 100644 app/views/buckets/_user_toggle.html.erb delete mode 100644 test/models/.keep diff --git a/app/assets/stylesheets/filters.css b/app/assets/stylesheets/filters.css index 47d3edb13..df9ea05cd 100644 --- a/app/assets/stylesheets/filters.css +++ b/app/assets/stylesheets/filters.css @@ -8,6 +8,16 @@ } } +.filter--active { + li { + display: none; + + &.selected { + display: flex; + } + } +} + .filter__button { --hover-size: 0; diff --git a/app/controllers/buckets/accesses_controller.rb b/app/controllers/buckets/accesses_controller.rb deleted file mode 100644 index 9421e5ba4..000000000 --- a/app/controllers/buckets/accesses_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -class Buckets::AccessesController < ApplicationController - include BucketScoped - - def edit - @users = @bucket.users - end - - def update - @bucket.update_access [ Current.user, *find_users ] - redirect_to edit_bucket_access_url(@bucket) - end - - private - def find_users - Current.account.users.active.where(id: params[:user_ids]) - end -end diff --git a/app/controllers/buckets_controller.rb b/app/controllers/buckets_controller.rb index bc6ffe8c7..275496794 100644 --- a/app/controllers/buckets_controller.rb +++ b/app/controllers/buckets_controller.rb @@ -15,10 +15,16 @@ class BucketsController < ApplicationController end def edit + selected_user_ids = @bucket.users.pluck :id + @selected_users, @unselected_users = User.active.alphabetically.partition { |user| selected_user_ids.include? user.id } end def update - @bucket.update!(bucket_params) + @bucket.transaction do + @bucket.update! bucket_params + @bucket.accesses.revise granted: grantees, revoked: revokees + end + redirect_to bucket_bubbles_url(@bucket) end @@ -29,10 +35,22 @@ class BucketsController < ApplicationController private def set_bucket - @bucket = Current.user.buckets.find(params[:id]) + @bucket = Current.user.buckets.find params[:id] end def bucket_params params.require(:bucket).permit(:name) end + + def grantees + Current.account.users.active.where id: grantee_ids + end + + def revokees + @bucket.users.where.not id: grantee_ids + end + + def grantee_ids + params.fetch :user_ids, [] + end end diff --git a/app/helpers/translations_helper.rb b/app/helpers/translations_helper.rb index 5898b3a88..094145099 100644 --- a/app/helpers/translations_helper.rb +++ b/app/helpers/translations_helper.rb @@ -2,7 +2,8 @@ module TranslationsHelper TRANSLATIONS = { user_name: { "🇺🇸": "Enter your name", "🇪🇸": "Introduce tu nombre", "🇫🇷": "Entrez votre nom", "🇮🇳": "अपना नाम दर्ज करें", "🇩🇪": "Geben Sie Ihren Namen ein", "🇧🇷": "Insira seu nome" }, email_address: { "🇺🇸": "Enter your email address", "🇪🇸": "Introduce tu correo electrónico", "🇫🇷": "Entrez votre adresse courriel", "🇮🇳": "अपना ईमेल पता दर्ज करें", "🇩🇪": "Geben Sie Ihre E-Mail-Adresse ein", "🇧🇷": "Insira seu endereço de email" }, - password: { "🇺🇸": "Enter your password", "🇪🇸": "Introduce tu contraseña", "🇫🇷": "Saisissez votre mot de passe", "🇮🇳": "अपना पासवर्ड दर्ज करें", "🇩🇪": "Geben Sie Ihr Passwort ein", "🇧🇷": "Insira sua senha" } + password: { "🇺🇸": "Enter your password", "🇪🇸": "Introduce tu contraseña", "🇫🇷": "Saisissez votre mot de passe", "🇮🇳": "अपना पासवर्ड दर्ज करें", "🇩🇪": "Geben Sie Ihr Passwort ein", "🇧🇷": "Insira sua senha" }, + bucket_name: { "🇺🇸": "Give it a name", "🇪🇸": "Dale un nombre", "🇫🇷": "Donnez lui un nom", "🇮🇳": "इसे एक नाम दें", "🇩🇪": "Gib ihm einen Namen", "🇧🇷": "Dê-lhe um nome" } } def translations_for(translation_key) diff --git a/app/javascript/controllers/filter_controller.js b/app/javascript/controllers/filter_controller.js new file mode 100644 index 000000000..815b05952 --- /dev/null +++ b/app/javascript/controllers/filter_controller.js @@ -0,0 +1,46 @@ +import { Controller } from "@hotwired/stimulus" +import { debounce } from "helpers/timing_helpers" + +export default class extends Controller { + static targets = [ "list" ] + static classes = [ "active", "selected" ] + + initialize() { + this.filter = debounce(this.filter.bind(this), 300) + } + + connect() { + this.element.focus() + } + + filter(event) { + this.#reset() + + if (event.target.value != "") { + this.#selectMatches(event.target.value) + this.#activate() + } + } + + #reset() { + this.#deactivate() + + this.listTarget.querySelectorAll(`.${this.selectedClass}`).forEach((element) => { + element.classList.remove(this.selectedClass) + }) + } + + #activate() { + this.listTarget.classList.add(this.activeClass) + } + + #deactivate() { + this.listTarget.classList.remove(this.activeClass) + } + + #selectMatches(value) { + this.listTarget.querySelectorAll(`[data-value*=${value.toLowerCase()}]`).forEach((element) => { + element.classList.add(this.selectedClass) + }) + } +} diff --git a/app/models/bucket/accessible.rb b/app/models/bucket/accessible.rb index 4b4e7c031..0f3c0d04a 100644 --- a/app/models/bucket/accessible.rb +++ b/app/models/bucket/accessible.rb @@ -2,22 +2,26 @@ module Bucket::Accessible extend ActiveSupport::Concern included do - has_many :accesses, dependent: :destroy + has_many :accesses, dependent: :delete_all do + def revise(granted: [], revoked: []) + transaction do + grant_to granted + revoke_from revoked + end + end + + private + def grant_to(users) + Access.insert_all Array(users).collect { |user| { bucket_id: proxy_association.owner.id, user_id: user.id } } + end + + def revoke_from(users) + destroy_by user: users + end + end + has_many :users, through: :accesses - after_create -> { grant_access(creator) } - end - - def update_access(users) - transaction do - grant_access(users) - accesses.where.not(user: Array(users)).delete_all - end - end - - def grant_access(users) - Array(users).each do |user| - accesses.create_or_find_by!(user: user) - end + after_create -> { accesses.grant_to(creator) } end end diff --git a/app/models/user.rb b/app/models/user.rb index 5ff9bd7a0..2d1c5ce6f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -19,6 +19,7 @@ class User < ApplicationRecord normalizes :email_address, with: ->(value) { value.strip.downcase } scope :active, -> { where(active: true) } + scope :alphabetically, -> { order("LOWER(name)") } def initials name.to_s.scan(/\b\p{L}/).join.upcase diff --git a/app/views/bubbles/_filters.html.erb b/app/views/bubbles/_filters.html.erb index 20a2254b5..a98fe7771 100644 --- a/app/views/bubbles/_filters.html.erb +++ b/app/views/bubbles/_filters.html.erb @@ -34,6 +34,11 @@ <% end %> + + <%= link_to edit_bucket_url(bucket), class: "btn btn--plain", style: "font-size: 0.25em;" do %> + <%= image_tag "pencil.svg", aria: { hidden: true }, size: 24 %> + Edit + <% end %> diff --git a/app/views/buckets/_user_toggle.html.erb b/app/views/buckets/_user_toggle.html.erb new file mode 100644 index 000000000..f1e3a2f23 --- /dev/null +++ b/app/views/buckets/_user_toggle.html.erb @@ -0,0 +1,24 @@ +
  • +
    + <%= avatar_tag user, loading: :lazy %> +
    + +
    +
    + <%= user.name %> +
    +
    + + + + <% if user == Current.user %> + <%= hidden_field_tag "user_ids[]", user.id, id: nil %> + <%= image_tag "check.svg", size: 20, class: "colorize--black flex-item-no-shrink", aria: { hidden: "true" } %> + <% else %> + + <% end %> +
  • diff --git a/app/views/buckets/edit.html.erb b/app/views/buckets/edit.html.erb index 5105b5dab..7272b7cc1 100644 --- a/app/views/buckets/edit.html.erb +++ b/app/views/buckets/edit.html.erb @@ -2,7 +2,7 @@ <% content_for :header do %>