Skip to content

Acceptance between administrators #790

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: time-between-banks-base
Choose a base branch
from

Conversation

gmartincor
Copy link
Collaborator

grabacion-de-pantalla-2025-04-14-a-las-92316_CkLB7uBF.1.mp4

Alliance Feature for Time Banks

This new feature that allows time banks to establish alliances with each other. This functionality extends the existing organization relationship model to enable time banks to collaborate more effectively.

The implementation includes:

  • A migration to create the organization_alliances table which tracks relationships between time banks.Only organization administrators can manage alliances.

  • New models and controllers to manage the alliance lifecycle (request, accept/reject, end alliance).

  • UI components integrated into the existing organizations views, allowing administrators to:

    • Request alliances with other time banks
    • Accept or reject incoming alliance requests
    • View and manage their current alliances
    • End active alliances when needed
  • Several tests to ensure the functionality works as expected.

@gmartincor gmartincor requested a review from Copilot April 14, 2025 07:34
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 16 out of 17 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • db/structure.sql: Language not supported

actions: "Acciones"
sent: "Enviadas"
received: "Recibidas"
pending: "Pending"
Copy link
Preview

Copilot AI Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Spanish translation for 'pending' remains in English. Consider updating it (e.g., to 'Pendiente' or a more context-appropriate term) for consistency.

Suggested change
pending: "Pending"
pending: "Pendiente"

Copilot uses AI. Check for mistakes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gmartincor I think our Our colleague Copilot is right

Comment on lines 26 to 27
target_organization_id: params[:organization_alliance][:target_organization_id],
status: "pending"
Copy link
Preview

Copilot AI Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the create action, the target_organization_id is accessed directly from params instead of using the alliance_params method. Consider refactoring to use strong parameters (alliance_params) to enhance consistency and security.

Suggested change
target_organization_id: params[:organization_alliance][:target_organization_id],
status: "pending"
target_organization_id: alliance_params[:target_organization_id],

Copilot uses AI. Check for mistakes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gmartincor another point for Copilot

@gmartincor gmartincor changed the base branch from develop to time-between-banks-base April 14, 2025 08:05
@franpb14
Copy link
Collaborator

franpb14 commented Apr 23, 2025

Hi @gmartincor , I've been a bit busy lately and I continue busy, so I'll be slow in the PRs review, sorry for that 🙏.

Thanks for the contribution!

@@ -5,24 +5,20 @@
it "validates content_type" do
temp_file = Tempfile.new('test.txt')
organization.logo.attach(io: File.open(temp_file.path), filename: 'test.txt')

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert this change since it only generates

expect(organization).to be_invalid

temp_file = Tempfile.new('test.svg')
organization.logo.attach(io: File.open(temp_file.path), filename: 'test.svg')

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

expect(organization).to be_invalid

temp_file = Tempfile.new('test.png')
organization.logo.attach(io: File.open(temp_file.path), filename: 'test.png')

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

expect(organization).to be_valid
end
end

describe '#display_id' do
subject { organization.display_id }

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@franpb14
Copy link
Collaborator

image
remember that this means that you didn't let an empty space at the end of the file, it is a good practice to have it

end

def create
@alliance = OrganizationAlliance.new(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no need for @ivar here I think

end

def update
authorize @alliance
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we have before_action :authorize_admin we don't need an authorize here

end

def destroy
authorize @alliance
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Comment on lines 70 to 73
unless current_user.manages?(current_organization)
flash[:error] = t("organization_alliances.not_authorized")
redirect_to root_path
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code could be prettier using a guard clause:

if current_user.manages?(current_organization) return

flash[:error] = t("organization_alliances.not_authorized")
redirect_to root_pat

Comment on lines 27 to 28
has_many :source_alliances, class_name: "OrganizationAlliance", foreign_key: "source_organization_id", dependent: :destroy
has_many :target_alliances, class_name: "OrganizationAlliance", foreign_key: "target_organization_id", dependent: :destroy
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this naming is clear enough, maybe:

initiated_alliances / received_alliances
outgoing_alliances / incoming_alliances

what do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I´m agree with you, I have changed source_alliances by source_alliances and target_alliances by received_alliances.

Thank you very much for your advice @franpb14 :)

Comment on lines 71 to 77
def pending_sent_alliances
source_alliances.pending
end

def pending_received_alliances
target_alliances.pending
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not combine this with an or like the other 2?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've implemented your suggestion to combine the methods for pending alliances using the or operator, just like we do with accepted and rejected alliances.

To make this change, I not only modified the Organization model by creating the pending_alliances method, but I also updated the controller organization_alliances_controller to use this new method:

# Before:
current_organization.pending_sent_alliances.includes(:source_organization, :target_organization) +
current_organization.pending_received_alliances.includes(:source_organization, :target_organization)

# After:
current_organization.pending_alliances.includes(:source_organization, :target_organization)

Comment on lines 3 to 4
alliance = record
user.manages?(alliance.source_organization) || user.manages?(alliance.target_organization)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to understand, why do we need this? and also this repeated code could be an auxiliar function

Comment on lines +3 to +4
<div class="row">
<div class="col-12 col-sm-12 col-md-12 col-lg-12">
Copy link
Collaborator

@franpb14 franpb14 Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

col-12 is enough, since you are not giving other sizes

Comment on lines 59 to 63
<% if is_sender %>
<%= t('organization_alliances.sent') %>
<% else %>
<%= t('organization_alliances.received') %>
<% end %>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be just this:
<%= t("organization_alliances.#{is_sender ? 'sent' : 'received'}'") %>

Comment on lines 52 to 53
<% is_sender = (alliance.source_organization_id == current_organization.id) %>
<% other_org = is_sender ? alliance.target_organization : alliance.source_organization %>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this code to a helper, the views should have the less ruby code as possible

db/structure.sql Outdated
@@ -1,25 +1,13 @@
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this whole file there a bunch of changes not related with your migration and it could break, probably because the database version, make sure you only leave what's important and don't change anything else.

Comment on lines +12 to +42
let!(:pending_sent) {
OrganizationAlliance.create!(
source_organization: organization,
target_organization: other_organization,
status: "pending"
)
}

let!(:pending_received) {
OrganizationAlliance.create!(
source_organization: Fabricate(:organization),
target_organization: organization,
status: "pending"
)
}

let!(:accepted) {
OrganizationAlliance.create!(
source_organization: organization,
target_organization: Fabricate(:organization),
status: "accepted"
)
}

let!(:rejected) {
OrganizationAlliance.create!(
source_organization: organization,
target_organization: Fabricate(:organization),
status: "rejected"
)
}
Copy link
Collaborator

@franpb14 franpb14 Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use these let at the beggining, create a fabricator for this model and if the ! after let is not needed don't use it

end

it "sets flash error if alliance cannot be created" do
# Try to create alliance with self which is invalid
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not use comments for that, it is already described in the test

Comment on lines +92 to +98
let!(:alliance) {
OrganizationAlliance.create!(
source_organization: Fabricate(:organization),
target_organization: organization,
status: "pending"
)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same move this to the beggining

Comment on lines +130 to +135
let!(:alliance) {
OrganizationAlliance.create!(
source_organization: organization,
target_organization: other_organization
)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, you probably will be able to reuse some

let(:other_organization) { Fabricate(:organization) }

around do |example|
I18n.with_locale(:en) do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strange, I'm not sure about this

Comment on lines +87 to +103
@pending_alliance = OrganizationAlliance.create!(
source_organization: organization,
target_organization: other_organization,
status: "pending"
)

@accepted_alliance = OrganizationAlliance.create!(
source_organization: Fabricate(:organization),
target_organization: Fabricate(:organization),
status: "accepted"
)

@rejected_alliance = OrganizationAlliance.create!(
source_organization: Fabricate(:organization),
target_organization: Fabricate(:organization),
status: "rejected"
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be let at the beggining

Comment on lines +80 to +83
alliance = OrganizationAlliance.create!(
source_organization: organization,
target_organization: other_organization
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same use the fabricator at the beginning in this file

Comment on lines +102 to +136
@pending_sent = OrganizationAlliance.create!(
source_organization: organization,
target_organization: other_organization,
status: "pending"
)

@pending_received = OrganizationAlliance.create!(
source_organization: third_organization,
target_organization: organization,
status: "pending"
)

@accepted_sent = OrganizationAlliance.create!(
source_organization: organization,
target_organization: Fabricate(:organization),
status: "accepted"
)

@accepted_received = OrganizationAlliance.create!(
source_organization: Fabricate(:organization),
target_organization: organization,
status: "accepted"
)

@rejected_sent = OrganizationAlliance.create!(
source_organization: organization,
target_organization: Fabricate(:organization),
status: "rejected"
)

@rejected_received = OrganizationAlliance.create!(
source_organization: Fabricate(:organization),
target_organization: organization,
status: "rejected"
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@franpb14
Copy link
Collaborator

I think that's all for now, I'd like to give it a second go when it's all fixed 😄

gmartincor added a commit that referenced this pull request Apr 24, 2025
@gmartincor gmartincor force-pushed the acceptance-between-administrators branch from 2687a1a to 13cd2c4 Compare April 24, 2025 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants