Skip to content

Commit 88c808b

Browse files
committed
alliance between diffents timebanks
1 parent 52c1771 commit 88c808b

File tree

14 files changed

+517
-94
lines changed

14 files changed

+517
-94
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
class OrganizationAlliancesController < ApplicationController
2+
before_action :authenticate_user!
3+
before_action :member_should_exist_and_be_active
4+
before_action :authorize_admin
5+
before_action :find_alliance, only: [:update, :destroy]
6+
7+
def index
8+
@status = params[:status] || "pending"
9+
10+
@alliances = case @status
11+
when "pending"
12+
current_organization.pending_sent_alliances.includes(:source_organization, :target_organization) +
13+
current_organization.pending_received_alliances.includes(:source_organization, :target_organization)
14+
when "accepted"
15+
current_organization.accepted_alliances.includes(:source_organization, :target_organization)
16+
when "rejected"
17+
current_organization.rejected_alliances.includes(:source_organization, :target_organization)
18+
else
19+
[]
20+
end
21+
end
22+
23+
def create
24+
@alliance = OrganizationAlliance.new(
25+
source_organization: current_organization,
26+
target_organization_id: params[:organization_alliance][:target_organization_id],
27+
status: "pending"
28+
)
29+
30+
if @alliance.save
31+
flash[:notice] = t("organization_alliances.created")
32+
else
33+
flash[:error] = @alliance.errors.full_messages.to_sentence
34+
end
35+
36+
redirect_back fallback_location: organizations_path
37+
end
38+
39+
def update
40+
authorize @alliance
41+
42+
if @alliance.update(status: params[:status])
43+
flash[:notice] = t("organization_alliances.updated")
44+
else
45+
flash[:error] = @alliance.errors.full_messages.to_sentence
46+
end
47+
48+
redirect_to organization_alliances_path
49+
end
50+
51+
def destroy
52+
authorize @alliance
53+
54+
if @alliance.destroy
55+
flash[:notice] = t("organization_alliances.destroyed")
56+
else
57+
flash[:error] = t("organization_alliances.error_destroying")
58+
end
59+
60+
redirect_to organization_alliances_path
61+
end
62+
63+
private
64+
65+
def find_alliance
66+
@alliance = OrganizationAlliance.find(params[:id])
67+
end
68+
69+
def authorize_admin
70+
unless current_user.manages?(current_organization)
71+
flash[:error] = t("organization_alliances.not_authorized")
72+
redirect_to root_path
73+
end
74+
end
75+
76+
def alliance_params
77+
params.require(:organization_alliance).permit(:target_organization_id)
78+
end
79+
end

app/models/organization.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class Organization < ApplicationRecord
2424
has_many :inquiries
2525
has_many :documents, as: :documentable, dependent: :destroy
2626
has_many :petitions, dependent: :delete_all
27+
has_many :source_alliances, class_name: "OrganizationAlliance", foreign_key: "source_organization_id", dependent: :destroy
28+
has_many :target_alliances, class_name: "OrganizationAlliance", foreign_key: "target_organization_id", dependent: :destroy
2729

2830
validates :name, presence: true, uniqueness: true
2931

@@ -61,6 +63,27 @@ def display_id
6163
account.accountable_id
6264
end
6365

66+
def alliance_with(organization)
67+
source_alliances.find_by(target_organization: organization) ||
68+
target_alliances.find_by(source_organization: organization)
69+
end
70+
71+
def pending_sent_alliances
72+
source_alliances.pending
73+
end
74+
75+
def pending_received_alliances
76+
target_alliances.pending
77+
end
78+
79+
def accepted_alliances
80+
source_alliances.accepted.or(target_alliances.accepted)
81+
end
82+
83+
def rejected_alliances
84+
source_alliances.rejected.or(target_alliances.rejected)
85+
end
86+
6487
def ensure_reg_number_seq!
6588
update_column(:reg_number_seq, members.maximum(:member_uid))
6689
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class OrganizationAlliance < ApplicationRecord
2+
belongs_to :source_organization, class_name: "Organization"
3+
belongs_to :target_organization, class_name: "Organization"
4+
5+
enum status: { pending: 0, accepted: 1, rejected: 2 }
6+
7+
validates :source_organization_id, presence: true
8+
validates :target_organization_id, presence: true
9+
validates :target_organization_id, uniqueness: { scope: :source_organization_id }
10+
validate :cannot_ally_with_self
11+
12+
scope :pending, -> { where(status: "pending") }
13+
scope :accepted, -> { where(status: "accepted") }
14+
scope :rejected, -> { where(status: "rejected") }
15+
16+
private
17+
18+
def cannot_ally_with_self
19+
if source_organization_id == target_organization_id
20+
errors.add(:base, "Cannot create an alliance with yourself")
21+
end
22+
end
23+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class OrganizationAlliancePolicy < ApplicationPolicy
2+
def update?
3+
alliance = record
4+
user.manages?(alliance.source_organization) || user.manages?(alliance.target_organization)
5+
end
6+
7+
def destroy?
8+
alliance = record
9+
user.manages?(alliance.source_organization) || user.manages?(alliance.target_organization)
10+
end
11+
end

app/views/application/menus/_organization_listings_menu.html.erb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
<%= t('petitions.applications') %>
1717
<% end %>
1818
</li>
19+
<li>
20+
<%= link_to organization_alliances_path, class: "dropdown-item" do %>
21+
<%= glyph :globe %>
22+
<%= t "application.navbar.organization_alliances" %>
23+
<% end %>
24+
</li>
1925
<li>
2026
<%= link_to offers_path(org: current_organization), class: "dropdown-item" do %>
2127
<%= glyph :link %>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<h1><%= t('organization_alliances.title') %></h1>
2+
3+
<div class="row">
4+
<div class="col-12 col-sm-12 col-md-12 col-lg-12">
5+
<ul class="nav nav-pills actions-menu">
6+
<li class="nav-item">
7+
<%= link_to organization_alliances_path(status: 'pending'), class: "nav-link #{'active' if @status == 'pending'}" do %>
8+
<%= glyph :time %>
9+
<%= t('organization_alliances.status.pending') %>
10+
<% end %>
11+
</li>
12+
<li class="nav-item">
13+
<%= link_to organization_alliances_path(status: 'accepted'), class: "nav-link #{'active' if @status == 'accepted'}" do %>
14+
<%= glyph :ok %>
15+
<%= t('organization_alliances.status.accepted') %>
16+
<% end %>
17+
</li>
18+
<li class="nav-item">
19+
<%= link_to organization_alliances_path(status: 'rejected'), class: "nav-link #{'active' if @status == 'rejected'}" do %>
20+
<%= glyph :remove %>
21+
<%= t('organization_alliances.status.rejected') %>
22+
<% end %>
23+
</li>
24+
<li class="nav-item ms-auto">
25+
<%= link_to organizations_path, class: "text-primary" do %>
26+
<%= glyph :search %>
27+
<%= t('organization_alliances.search_organizations') %>
28+
<% end %>
29+
</li>
30+
</ul>
31+
</div>
32+
</div>
33+
34+
<div class="row">
35+
<div class="col-md-12">
36+
<div class="card">
37+
<div class="card-body table-responsive">
38+
<table class="table table-hover table-sm">
39+
<thead>
40+
<tr>
41+
<th><%= t('organization_alliances.organization') %></th>
42+
<th><%= t('organization_alliances.city') %></th>
43+
<th><%= t('organization_alliances.members') %></th>
44+
<th><%= t('organization_alliances.type') %></th>
45+
<% if @status != 'rejected' %>
46+
<th><%= t('organization_alliances.actions') %></th>
47+
<% end %>
48+
</tr>
49+
</thead>
50+
<tbody>
51+
<% @alliances.each do |alliance| %>
52+
<% is_sender = (alliance.source_organization_id == current_organization.id) %>
53+
<% other_org = is_sender ? alliance.target_organization : alliance.source_organization %>
54+
<tr>
55+
<td><%= link_to other_org.name, other_org %></td>
56+
<td><%= other_org.city %></td>
57+
<td><%= other_org.members.count %></td>
58+
<td>
59+
<% if is_sender %>
60+
<%= t('organization_alliances.sent') %>
61+
<% else %>
62+
<%= t('organization_alliances.received') %>
63+
<% end %>
64+
</td>
65+
<% if @status == 'pending' %>
66+
<td>
67+
<% if is_sender %>
68+
<%= link_to t('organization_alliances.cancel_request'),
69+
organization_alliance_path(alliance),
70+
method: :delete,
71+
class: 'btn btn-danger',
72+
data: { confirm: t('organization_alliances.confirm_cancel') } %>
73+
<% else %>
74+
<div class="btn-group" role="group" aria-label="Alliance actions">
75+
<%= link_to t('organization_alliances.accept'),
76+
organization_alliance_path(alliance, status: 'accepted'),
77+
method: :put,
78+
class: 'btn btn-success me-2' %>
79+
<%= link_to t('organization_alliances.reject'),
80+
organization_alliance_path(alliance, status: 'rejected'),
81+
method: :put,
82+
class: 'btn btn-danger' %>
83+
</div>
84+
<% end %>
85+
</td>
86+
<% elsif @status == 'accepted' %>
87+
<td>
88+
<%= link_to t('organization_alliances.end_alliance'),
89+
organization_alliance_path(alliance),
90+
method: :delete,
91+
class: 'btn btn-danger',
92+
data: { confirm: t('organization_alliances.confirm_end') } %>
93+
</td>
94+
<% end %>
95+
</tr>
96+
<% end %>
97+
</tbody>
98+
</table>
99+
</div>
100+
</div>
101+
</div>
102+
</div>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<% if current_user&.manages?(current_organization) && organization != current_organization %>
2+
<% alliance = current_organization.alliance_with(organization) %>
3+
<% if alliance.nil? %>
4+
<%= link_to t('organization_alliances.request_alliance'),
5+
organization_alliances_path(organization_alliance: { target_organization_id: organization.id }),
6+
method: :post,
7+
class: 'btn btn-secondary',
8+
aria: { label: t('organization_alliances.request_alliance_for', org: organization.name) } %>
9+
<% elsif alliance.pending? %>
10+
<span class="badge rounded-pill bg-secondary"><%= t('organization_alliances.pending') %></span>
11+
<% elsif alliance.accepted? %>
12+
<span class="badge rounded-pill bg-success"><%= t('organization_alliances.active') %></span>
13+
<% elsif alliance.rejected? %>
14+
<span class="badge rounded-pill bg-danger"><%= t('organization_alliances.rejected') %></span>
15+
<% end %>
16+
<% end %>

app/views/organizations/_organizations_row.html.erb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@
77
<td>
88
<%= render "organizations/petition_button", organization: org %>
99
</td>
10-
</tr>
10+
<% if current_user&.manages?(current_organization) %>
11+
<td>
12+
<%= render "organizations/alliance_button", organization: org %>
13+
</td>
14+
<% end %>
15+
</tr>

app/views/organizations/index.html.erb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
<th><%= t '.neighborhood' %></th>
2626
<th><%= t '.web' %></th>
2727
<th><%= t '.member_count' %></th>
28-
<th></th>
28+
<th><%= t '.membership' %></th>
29+
<% if current_user&.manages?(current_organization) %>
30+
<th><%= t '.alliance' %></th>
31+
<% end %>
2932
</tr>
3033
</thead>
3134
<tbody>
@@ -37,4 +40,4 @@
3740
<%= paginate @organizations %>
3841
</div>
3942
</div>
40-
</div>
43+
</div>

config/locales/en.yml

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ en:
141141
last_login: Last login
142142
offer_public_link: Offers public link
143143
organizations: Organizations
144+
organization_alliances: Organizations
144145
reports: Reports
145146
sign_out: Logout
146147
statistics: Statistics
@@ -370,6 +371,35 @@ en:
370371
show:
371372
give_time_for: Time transfer for this offer
372373
offered_by: Offered by
374+
organization_alliances:
375+
title: "Organization Alliances"
376+
created: "Alliance request sent"
377+
updated: "Alliance status updated"
378+
destroyed: "Alliance has been ended"
379+
error_destroying: "Could not end alliance"
380+
not_authorized: "You are not authorized to manage alliances"
381+
organization: "Organization"
382+
city: "City"
383+
members: "Members"
384+
type: "Type"
385+
actions: "Actions"
386+
sent: "Sent"
387+
received: "Received"
388+
pending: "Pending"
389+
active: "Active"
390+
rejected: "Rejected"
391+
request_alliance: "Request alliance"
392+
cancel_request: "Cancel request"
393+
accept: "Accept"
394+
reject: "Reject"
395+
end_alliance: "End alliance"
396+
confirm_cancel: "Are you sure you want to cancel this alliance request?"
397+
confirm_end: "Are you sure you want to end this alliance?"
398+
search_organizations: "Search organizations"
399+
status:
400+
pending: "Pending Requests"
401+
accepted: "Active Alliances"
402+
rejected: "Rejected Requests"
373403
organization_notifier:
374404
member_deleted:
375405
body: User %{username} has unsubscribed from the organization.
@@ -382,6 +412,8 @@ en:
382412
give_time: Give time to
383413
index:
384414
member_count: Number of users
415+
membership: "Membership"
416+
alliance: "Alliance"
385417
new:
386418
new: New bank
387419
show:
@@ -588,4 +620,4 @@ en:
588620
last: Last
589621
next: Next
590622
previous: Previous
591-
truncate: Truncate
623+
truncate: Truncate

0 commit comments

Comments
 (0)