Skip to content

Commit df12f2b

Browse files
author
pbmolini
committed
Basic listings
1 parent 8c27947 commit df12f2b

30 files changed

+600
-9
lines changed
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Place all the behaviors and hooks related to the matching controller here.
2+
# All this logic will automatically be available in application.js.
3+
# You can use CoffeeScript in this file: http://coffeescript.org/

app/assets/stylesheets/application.scss

+1
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@
4242
@import "messages";
4343
@import "transactions";
4444
@import "conversations";
45+
@import "listings";

app/assets/stylesheets/listings.scss

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
.listing-container {
2+
@extend .col;
3+
// background-color: snow;
4+
color: #111;
5+
padding-top: 2em;
6+
padding-bottom: 3.7em;
7+
8+
.listing-lang-select {
9+
@extend .text-muted;
10+
font-size: 0.8em;
11+
padding: 5px;
12+
}
13+
14+
.listing-title {
15+
margin: 0;
16+
h2 {
17+
font-size: 3.7em;
18+
font-weight: 400;
19+
margin-bottom: 0;
20+
}
21+
p.listing-type {
22+
font-size: 1em;
23+
text-transform: uppercase;
24+
margin: 0;
25+
font-weight: 500;
26+
}
27+
}
28+
29+
.listing-info {
30+
font-size: 1.3em;
31+
line-height: 1.5em;
32+
33+
.listing-author,
34+
.listing-date,
35+
.listing-place,
36+
.listing-comments,
37+
.listing-share,
38+
.listing-lang-select {
39+
@extend .text-muted;
40+
font-size: 0.8em;
41+
padding: 0;
42+
i.fa {
43+
padding-right: .5em;
44+
}
45+
a {
46+
text-decoration: none;
47+
color: inherit;
48+
&:hover {
49+
color: inherit;
50+
text-decoration: none;
51+
}
52+
}
53+
}
54+
55+
56+
.listing-comments {
57+
padding: 2.7em 0 .5em;
58+
}
59+
}
60+
61+
.listing-description {
62+
line-height: 1.5em;
63+
font-family: "Lora", serif;
64+
font-size: 1.3em;
65+
}
66+
67+
.listing-tags {
68+
color: $cf-primary-color
69+
}
70+
}
71+
72+
// Media queries
73+
74+
@media screen and (max-width: 991px) {
75+
.listing-container {
76+
.listing-info {
77+
.listing-comments {
78+
padding: 1.3em 0 .5em;
79+
}
80+
a.btn-block {
81+
display: none;
82+
}
83+
}
84+
.listing-description {
85+
figure {
86+
width: 100%;
87+
}
88+
}
89+
}
90+
}
91+
92+
@media screen and (max-width: 767px) {
93+
.listing-container {
94+
.listing-title, .listing-price {
95+
h2 {
96+
font-size: 2.19em;
97+
}
98+
}
99+
}
100+
}
101+
102+
@media screen and (max-width: 575px) {
103+
.listing-container .listing-description {
104+
font-size: 1.1em;
105+
}
106+
}

app/assets/stylesheets/stories/shared.scss

+1-5
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@
1919
}
2020
}
2121

22-
.story-share-url {
23-
font-family: monospace;
24-
}
25-
2622
.story-card-tag-link {
2723
font-size: .75rem;
2824
display: inline-block;
@@ -42,7 +38,7 @@ button#langSelectDropdownMenuButton {
4238
padding-top: 0;
4339
}
4440

45-
input#story-share-url {
41+
input#story-share-url, input#listing-share-url {
4642
font-family: monospace;
4743
width: 100%;
4844
border: 0;
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
class ListingsController < ApplicationController
2+
# This must stay **before** cancan methods, because they do a Listing.new(listing_params)
3+
# but new tags break it
4+
before_action :create_new_tags, only: [:create, :update]
5+
6+
load_and_authorize_resource
7+
before_action :set_commoner, only: [:new, :create, :destroy]
8+
9+
# GET /listings
10+
# GET /listings.json
11+
def index
12+
@listings = Listing.all
13+
end
14+
15+
# GET /listings/1
16+
# GET /listings/1.json
17+
def show
18+
end
19+
20+
# GET /listings/new
21+
def new
22+
@listing = @commoner.listings.build
23+
end
24+
25+
# GET /listings/1/edit
26+
def edit
27+
end
28+
29+
# POST /listings
30+
# POST /listings.json
31+
def create
32+
@listing = @commoner.listings.build(listing_params)
33+
respond_to do |format|
34+
if @listing.save
35+
format.html { redirect_to @listing, notice: 'Listing was successfully created.' }
36+
format.json { render :show, status: :created, location: @listing }
37+
else
38+
format.html { render :new }
39+
format.json { render json: @listing.errors, status: :unprocessable_entity }
40+
end
41+
end
42+
end
43+
44+
# PATCH/PUT /listings/1
45+
# PATCH/PUT /listings/1.json
46+
def update
47+
respond_to do |format|
48+
if @listing.update(listing_params)
49+
format.html { redirect_to @listing, notice: 'Listing was successfully updated.' }
50+
format.json { render :show, status: :ok, location: @listing }
51+
else
52+
format.html { render :edit }
53+
format.json { render json: @listing.errors, status: :unprocessable_entity }
54+
end
55+
end
56+
end
57+
58+
# DELETE /listings/1
59+
# DELETE /listings/1.json
60+
def destroy
61+
@listing.destroy
62+
respond_to do |format|
63+
format.html { redirect_to listings_url, notice: 'Listing was successfully destroyed.' }
64+
format.json { head :no_content }
65+
end
66+
end
67+
68+
private
69+
# Use callbacks to share common setup or constraints between actions.
70+
def set_listing
71+
@listing = Listing.find(params[:id])
72+
end
73+
74+
# Never trust parameters from the scary internet, only allow the white list through.
75+
def listing_params
76+
params.require(:listing).permit(:title, :description, :place, :min_price, :max_price, tag_ids: [])
77+
end
78+
79+
def set_commoner
80+
@commoner = params[:commoner_id].present? ? params[:commoner_id] : current_user.meta
81+
end
82+
83+
# This method creates the not-yet-existing tags and replaces
84+
# tag_ids with their ids.
85+
def create_new_tags
86+
if params[:listing][:tag_ids].present?
87+
params[:listing][:tag_ids].map! do |tag_id|
88+
if Tag.exists? tag_id
89+
tag_id
90+
else
91+
new_tag = Tag.create(name: tag_id.downcase)
92+
new_tag.id
93+
end
94+
end
95+
end
96+
end
97+
end

app/helpers/listings_helper.rb

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module ListingsHelper
2+
def listing_card_image_url(listing)
3+
relative_path = ''
4+
if listing.images.any?
5+
relative_path = listing.images.first.picture.card.url
6+
else
7+
relative_path = image_path 'card_default_img.jpg'
8+
end
9+
root_url(locale: nil) + relative_path
10+
end
11+
12+
def listing_price(listing)
13+
return "#{listing.min_price}-#{listing.max_price}cc"
14+
"#{listing.min_price}cc"
15+
end
16+
end

app/helpers/stories_helper.rb

+9-3
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,14 @@ def card_author(story)
8787
end
8888
end
8989

90-
def og_description_for(story)
91-
return strip_tags(@story.content).truncate(42) if story.content.present?
92-
strip_tags(@story.content_json.join).truncate(42)
90+
def og_description_for(obj)
91+
if obj.is_a? Story
92+
return strip_tags(obj.content).truncate(42) if obj.content.present?
93+
strip_tags(obj.content_json.join).truncate(42)
94+
elsif obj.is_a? Listing
95+
obj.description.truncate(42) if obj.description.present?
96+
else
97+
''
98+
end
9399
end
94100
end

app/models/ability.rb

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def initialize(user)
1313
can :read, Tag
1414
can :read, Comment
1515
can :read, Group
16+
can :read, Listing
1617
alias_action :create, :read, :update, :destroy, :to => :crud
1718
if user.is_commoner?
1819
commoner = user.meta
@@ -59,6 +60,8 @@ def initialize(user)
5960
can :create, Conversation
6061
can :read, Conversation, sender_id: commoner.id
6162
can :read, Conversation, recipient_id: commoner.id
63+
64+
can [:create, :update, :destroy], Listing
6265
end
6366
end
6467
end

app/models/commoner.rb

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class Commoner < ApplicationRecord
99
has_many :join_requests
1010
has_many :messages
1111
has_many :wallets, as: :walletable, dependent: :destroy
12+
has_many :listings
1213

1314
# http://guides.rubyonrails.org/association_basics.html#has-many-association-reference
1415
has_many :sender_conversations,

app/models/listing.rb

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class Listing < ApplicationRecord
2+
belongs_to :commoner
3+
has_and_belongs_to_many :tags
4+
has_many :images, as: :imageable, dependent: :destroy
5+
6+
validates :title, :description, :place, :min_price, presence: true
7+
end

app/models/tag.rb

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
class Tag < ApplicationRecord
22
extend FriendlyId
33
has_and_belongs_to_many :stories
4+
has_and_belongs_to_many :listings
45
validates :name, presence: true, uniqueness: { case_sensitive: false }
56
friendly_id :name, use: :slugged
67

app/views/layouts/_navbar.html.erb

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
<li class="nav-item active">
3232
<%= link_to s_('Navbar|Public Benefits'), stories_path(filter: :welfare_provision), class: 'nav-link' %>
3333
</li>
34+
<li class="nav-item active">
35+
<%= link_to s_('Navbar|Commonplace'), listings_path, class: 'nav-link' %>
36+
</li>
3437
<div class="navbar-cf-divider"></div>
3538
<li class="nav-item">
3639
<%= link_to s_('Navbar|About'), page_path(id: 'about'), class: 'nav-link' %>

app/views/listings/_fields.html.erb

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<%= f.input :title,
2+
required: false,
3+
label: _('Title') %>
4+
<%= f.input :place,
5+
required: false,
6+
label: _('Place') %>
7+
<div class="row">
8+
<div class="col">
9+
<%= f.input :min_price,
10+
required: false,
11+
label: _('Minimum price (in CC)'),
12+
placeholder: _('Specify an amount') %>
13+
</div>
14+
<div class="col">
15+
<%= f.input :max_price,
16+
label: _('Maximum price (in CC)'),
17+
placeholder: _('Specify an amount') %>
18+
</div>
19+
</div>
20+
<%= f.input :description,
21+
as: :text,
22+
required: false,
23+
label: _('Description') %>
24+
<%= f.association :tags,
25+
label: _('Tags'),
26+
# placeholder: "Managed in JS",
27+
hint: _('Use comma or press Enter to separate tags') %>
28+
<%= f.association :images, as: :file %>
29+
30+
<%= content_for :scripts do %>
31+
<script type="text/javascript">
32+
autosize($('#listing_description'));
33+
34+
// Select2 for tags
35+
$('#listing_tag_ids').select2({
36+
theme: 'bootstrap',
37+
placeholder: "<%= _('e.g. Lessons, Babysitting, etc.') %>",
38+
minimumResultsForSearch: -1,
39+
tags: true,
40+
tokenSeparators: [',']
41+
});
42+
$('#listing_tag_ids').on('select2:select', function (e) {
43+
// console.log(data.text);
44+
});
45+
</script>
46+
<% end %>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
json.extract! listing, :id, :commoner_id, :title, :description, :place, :min_price, :max_price, :created_at, :updated_at
2+
json.url listing_url(listing, format: :json)
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!-- Modal -->
2+
<div class="modal fade" id="shareModal" tabindex="-1" role="dialog" aria-labelledby="shareModalLabel" aria-hidden="true">
3+
<div class="modal-dialog" role="document">
4+
<div class="modal-content">
5+
<div class="modal-header">
6+
<h5 class="modal-title red-title" id="shareModalLabel"><%= s_('Modal title|Share this Listing') %></h5>
7+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
8+
<span aria-hidden="true">&times;</span>
9+
</button>
10+
</div>
11+
<div class="modal-body">
12+
<%= text_field_tag 'listing-share-url', request.url, { onClick: "this.setSelectionRange(0, this.value.length)" } %>
13+
<p class="text-muted">
14+
<small onClick="this.select();"><%= _('Commonfare.net is not linked to any external platform, so to share this listing on Facebook, Twitter, etc. you can copy the URL and paste it there.') %></small>
15+
</p>
16+
</div>
17+
<div class="modal-footer">
18+
<button type="button" class="btn btn-cf" data-clipboard-target="#listing-share-url" data-dismiss="modal"><%= _('Copy and close') %></button>
19+
</div>
20+
</div>
21+
</div>
22+
</div>
23+
24+
<%= content_for :scripts do %>
25+
<script type="text/javascript">
26+
// Instantiate ClipboardJs on the buttons
27+
new Clipboard('.btn');
28+
</script>
29+
<% end %>

0 commit comments

Comments
 (0)