Adding search/order + Show need page + comments

This commit is contained in:
Nicolas VARROT 2015-12-04 14:34:12 +01:00
parent 22b86c191f
commit 1fd0e4c624
32 changed files with 504 additions and 149 deletions

View File

@ -42,7 +42,7 @@ group :development, :test do
gem 'web-console', '~> 2.0'
gem 'spring'
end
@ -70,3 +70,5 @@ gem "geocoder"
gem "paranoia", "~> 2.0"
gem 'workflow', '~> 1.2.0'
gem 'elasticsearch-model', git: 'git://github.com/elasticsearch/elasticsearch-rails.git'

View File

@ -1,3 +1,12 @@
GIT
remote: git://github.com/elasticsearch/elasticsearch-rails.git
revision: 5f32e484a6d0458f26dc9acfa9dd5fed4e5d6453
specs:
elasticsearch-model (0.1.8)
activesupport (> 3)
elasticsearch (> 0.4)
hashie
GEM
remote: https://rubygems.org/
specs:
@ -69,8 +78,18 @@ GEM
columnize (0.9.0)
debug_inspector (0.0.2)
debugger-linecache (1.2.0)
elasticsearch (1.0.14)
elasticsearch-api (= 1.0.14)
elasticsearch-transport (= 1.0.14)
elasticsearch-api (1.0.14)
multi_json
elasticsearch-transport (1.0.14)
faraday
multi_json
erubis (2.7.0)
execjs (2.3.0)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
formtastic (2.3.0)
actionpack (>= 3.0)
formtastic-bootstrap (3.0.0)
@ -87,6 +106,7 @@ GEM
haml (>= 3.1, < 5.0)
html2haml (>= 1.0.1)
railties (>= 4.0.1)
hashie (3.4.3)
highline (1.7.2)
hike (1.2.3)
html2haml (2.0.0)
@ -118,6 +138,7 @@ GEM
mini_portile (0.6.2)
minitest (5.5.1)
multi_json (1.10.1)
multipart-post (2.0.0)
mysql2 (0.3.18)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
@ -228,6 +249,7 @@ DEPENDENCIES
capistrano (= 2.15.5)
carrierwave
coffee-rails (~> 4.1.0)
elasticsearch-model!
formtastic (= 2.3.0)
formtastic-bootstrap
geocoder

View File

@ -22,9 +22,10 @@
@flash_delay = ->
$("#flashs").find(".alert").each ->
if !$(this).hasClass("delay")
$(this).addClass("delay")
$(this).delay(3000).fadeOut();
@ -35,60 +36,60 @@
return
bottom = 0
prev_link = ""
$("document").ready ->
flash_delay();
position_img_now = ->
# alert $("#large .large-img").outerHeight(false)
imgheight = $("#large .large-img").outerHeight(false) + $("#large h3").outerHeight(false)
imgheight = $("#large .large-img").outerHeight(false) + $("#large h3").outerHeight(false)
margintop = (( $(window).height() - imgheight) / 2 )
$("#large .large-img").css
"margin-top" :(margintop+"px")
#"width" : "100px"
position_img = ->
$("#large .large-img").one "load", ->
position_img_now()
$(".expandable_image").click ->
maxwidth = 1000
maxheight = 900
prev_link = $(this)
$("body").append "<div id='large'></div>"
title = false
$("#large").append "<div class='img_container first'></div>"
$("#large").append "<img src='/close.png' class='close_link' />"
$(".img_container.first").append "<img src="+$(this).attr("href")+" class='large-img' />"
if $(this).attr "title"
title = $(this).attr "title"
$(".img_container.first").append "<h3>"+title+"</h3>"
$("#large .large-img").one "load", ->
$("#large").fadeIn(500)
position_img();
@ -98,43 +99,43 @@ $("document").ready ->
else
$("#large .large-img").css
"max-height" : "85%"
if $(window).width() > (maxwidth+100)
$("#large .large-img").css
"max-width" : maxwidth
else
$("#large .large-img").css
"max-width" : "85%"
position_img();
false
$(".rea-gal a").click ->
maxwidth = 1000
maxheight = 900
prev_link = $(this)
$("body").append "<div id='large'></div>"
title = false
$("#large").append "<img src='/arrow-next.png' class='next' />"
$("#large").append "<img src='/arrow-prev.png' class='prev' />"
$("#large").append "<img src='/close.png' class='close_link' />"
$("#large").append "<div class='img_container first'></div>"
$(".img_container.first").append "<img src="+$(this).attr("href")+" class='large-img' />"
if $(this).attr "title"
title = $(this).attr "title"
$(".img_container.first").append "<h3>"+title+"</h3>"
$("#large .large-img").one "load", ->
$("#large").fadeIn(500)
position_img();
@ -144,221 +145,221 @@ $("document").ready ->
else
$("#large .large-img").css
"max-height" : "85%"
if $(window).width() > (maxwidth+100)
$("#large .large-img").css
"max-width" : maxwidth
else
$("#large .large-img").css
"max-width" : "85%"
position_img();
false
$("body").on "click", "#large", ->
$(this).fadeOut 300, ->
$(this).remove()
$("body").on "click" ,"#large .prev",->
if prev_link.prev("a").length > 0
link = prev_link.closest("a").prev("a")
else
link = prev_link.closest("div").children("a:last")
#titre = photo.find("h3")
$(".img_container.first").fadeOut 300, ->
$("#large h3").remove()
if link.attr "title"
title = link.attr "title"
$(".img_container.first").append "<h3>"+title+"</h3>"
$('#large .large-img').attr("src", link.attr("href"))
$("#large .large-img").one "load", ->
$(".img_container.first").fadeIn()
position_img();
prev_link = link
false
$("body").on "click" ,"#large .next",->
if prev_link.next("a").length > 0
link = prev_link.closest("a").next("a")
else
link = prev_link.closest("div").children("a:first")
#titre = photo.find("h3")
$(".img_container.first").fadeOut 300, ->
$("#large h3").remove()
if link.attr "title"
title = link.attr "title"
$(".img_container.first").append "<h3>"+title+"</h3>"
$('#large .large-img').attr("src", link.attr("href"))
$("#large .large-img").one "load", ->
$(".img_container.first").fadeIn()
position_img();
prev_link = link
false
$('.gal').bxSlider
adaptiveHeight: true,
auto: true,
speed:1000,
pause:5000,
left =0
top = 0
offset= 0
resize = ->
min_height = 0
min_height = $(window).height() - $(".top").height() - $(".top_home").height() - $(".bottom").height() - 30
$("#main").css("min-height", min_height+"px")
$("iframe").each ->
$(this).css
"height" : Math.round($(this).width()/ 1.77)+"px"
if $(window).width() > 1250
$(".infos .main").css
"position" : "static"
$(".bottom_image").css
"left" : "0px"
else
$(".infos .main").css
"position" : "relative"
$(".bottom_image").css
"left" : "-120px"
$("#large").css "min-height", ($(window).height()-30)+"px"
$(".bxslider").each ->
height = ($(window).height())
optimal_height = Math.round($(this).width()/ $(this).data("ratio"))
if optimal_height < height
height = optimal_height
$(this).find("li").css("max-height", height+"px")
position_img_now();
resize()
$('.bxslider').bxSlider
mode: 'fade'
captions: true
auto: true
resize()
resize_annonces = ->
$(".search_results").each ->
width = $(this).width()
small_width = (width - 4*14) / 2
small_height = small_width * (250 / 450)
big_width = width - 2*14
if width > 740
$(this).find(".annonce_list").css
"width" : small_width+"px"
"max-width" : small_width+"px"
$(this).find(".annonce_list .default_image").css
"height" : small_height+"px"
$(this).find(".annonce_list.big").css
"width" : big_width+"px"
"max-width" : big_width+"px"
$(this).find(".annonce_list.big .default_image").css
"height" : (small_height+90)+"px"
else
$(this).find(".annonce_list").css
"width" : big_width+"px"
"max-width" : big_width+"px"
$(this).find(".annonce_list .default_image").css
"height" : "height" : small_height+"px"
$(this).find(".annonce_list.big").css
"width" : big_width+"px"
"max-width" : big_width+"px"
$(this).find(".annonce_list.big .default_image").css
"height" : (small_height+90)+"px"
resize_annonces()
$(window).on "resize", ->
resize()
resize_annonces()
place_annonce_panel = ->
if $(window).scrollTop() > 230
$("#annonce_save").addClass("fixed")
else
$("#annonce_save").removeClass("fixed")
$(window).on "scroll", ->
place_annonce_panel()
place_annonce_panel()
top_height = $(window).height()
img_height = 0.0
initial_y = 0
$(document).on "click", ".tags label", ->
if $("#"+$(this).attr("for")).is(':checked')
if $("#"+$(this).attr("for")).is(':checked')
$(this).removeClass("active")
else
$(this).addClass("active")

View File

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

View File

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

View File

@ -0,0 +1,3 @@
// Place all the styles related to the public/messages controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,36 +1,98 @@
.btn-square{
border-radius: 0px;
}
.side-menu{
h3{
margin-top:0px;
}
}
.show-need{
position: relative;
.info{
color:rgb(163, 159, 159);
}
p.description{
margin-bottom:30px;
}
.message-item{
background: #eeeded;
}
.counters{
position:absolute;
left:0px;
bottom:0px;
padding-left:5px;
padding-bottom:5px;
.item{
display:inline;
margin-left:0px;
font-size:20px;
}
}
}
.need-item{
height: 150px;
background-color:#fff;
padding-top:30px;
p{
position:relative;
p.description{
font-size:12px;
color:rgb(163, 159, 159);
}
p.info{
font-size:10px;
color:rgb(163, 159, 159);
}
.counters{
position:absolute;
left:0px;
bottom:0px;
padding-left:5px;
padding-bottom:5px;
.item{
display:inline;
margin-left:10px;
font-size:18px;
}
}
span{
background-color: #ede8e8;
position:absolute;
padding:5px;
top:0px;
right:15px;
.time{
right:0px;
.info{
color:rgb(121, 120, 120);
text-align:right;
font-size:10px;
}
}
margin-bottom:30px;
h3{
margin-bottom:50px;
h4{
margin-top:0px;
margin-bottom:0px;
}
.btn{
border-radius: 0px;
position:absolute;
right:15px;
bottom:30px;
right:0px;
bottom:0px;
}
}

View File

@ -0,0 +1,3 @@
// Place all the styles related to the public/needs controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -40,7 +40,7 @@ class Admin::NeedsController < ApplicationController
flash[:notice] = "Besoin validé avec succès"
CustomerMailer.validate_need(@need).deliver
else
flash[:error] = "L'état actuel de ce besoin ne permet sa validation"
flash[:error] = "L'état actuel de ce besoin ne permet pas sa validation"
end
redirect_to admin_needs_path
end

View File

@ -0,0 +1,28 @@
class Public::MessagesController < ApplicationController
layout "public"
before_filter :auth_customer
def index
need = Need.find(params[:need_id])
end
def create
need = Need.find(params[:need_id])
@message = need.messages.create(comment_params)
@message.customer = current_customer
if(@message.save)
flash[:notice] = "Commentaire envoyé."
end
redirect_to public_need_path(need)
end
def comment_params
params.require(:message).permit(:content)
end
end

View File

@ -5,7 +5,58 @@ class Public::NeedsController < ApplicationController
before_filter :auth_customer
def index
# Get only public needs
@needs = Need.shared
# filters default value
params[:o] ||= 'created-desc'
# Include search in the query
if(params[:q] != '')
@needs = @needs.search(params[:q])
end
# Include order in the query
case params[:o]
when 'alpha-asc'
@needs = @needs.order(title: :asc)
when 'alpha-desc'
@needs = @needs.order(title: :desc)
when 'wishes-asc'
@needs = @needs.with_wishes_count.order('wishes_count ASC')
when 'wishes-desc'
@needs = @needs.with_wishes_count.order('wishes_count DESC')
when 'created-asc'
@needs = @needs.order(created_at: :asc)
when 'created-desc'
@needs = @needs.order(created_at: :desc)
when 'comments-asc'
@needs = @needs.with_messages_count.order('messages_count ASC')
when 'comments-desc'
@needs = @needs.with_messages_count.order('messages_count DESC')
end
# Paginate
@needs = @needs.page(params[:page]).per(6)
# Define order select options
@orders = {
"Les plus récents" => 'created-desc',
"Les plus anciens" => 'created-asc',
"Nombre d'intérêts décroissants" => 'wishes-desc',
"Nombre d'intérêts croissants" => 'wishes-asc' ,
"Nombre de commentaires décroissants" => 'comments-desc',
"Nombre de commentaires croissants" => 'comments-asc',
"Alphabétique (de A à Z)" => 'alpha-asc',
"Alphabétique (de Z à A)" => 'alpha-desc'
}
end
def new
@ -24,6 +75,13 @@ class Public::NeedsController < ApplicationController
@need = Need.find(params[:id])
end
def show
@need = Need.find(params[:id])
@comment = Message.new()
@comments = @need.messages.order(created_at: :desc).page params[:page]
end
def update
@need = Need.find(params[:id])
if @need.update_attributes(need_params)
@ -63,7 +121,7 @@ class Public::NeedsController < ApplicationController
flash[:notice] = "Vous avez signalé votre intérêt pour ce besoin"
else
@need.customers.delete(current_customer)
flash[:error] = "Vous n'être plus marqué comme intéressé par ce besoin"
flash[:error] = "Vous n'êtes maintenant plus intéressé par ce besoin"
end
redirect_to :back
end

View File

@ -0,0 +1,2 @@
module Public::MessagesHelper
end

View File

@ -0,0 +1,2 @@
module Public::NeedsHelper
end

View File

@ -3,7 +3,7 @@ class Customer < ActiveRecord::Base
# Relationships
has_many :owned_needs, foreign_key: 'author_id', class_name: 'Need'
has_many :wishes
has_many :wishes, dependent: :destroy
has_many :needs, -> { uniq }, through: :wishes
has_many :customer_favs
@ -141,6 +141,10 @@ class Customer < ActiveRecord::Base
end
def anonyme_nick
"Utilisateur#{id}"
end
def mlm_children_id_by_levels
self.mlm_detail_ids[:mlm_children_id_by_levels]
end

7
app/models/message.rb Normal file
View File

@ -0,0 +1,7 @@
class Message < ActiveRecord::Base
belongs_to :customer
belongs_to :need
paginates_per 5
validates :content, :presence => true, length: {within: 1..512}
end

View File

@ -1,21 +1,37 @@
require 'elasticsearch/model'
class Need < ActiveRecord::Base
include Workflow
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
scope :shared, -> {
where(state: ["verified", "negociating", "negociated", "failed"])
}
scope :top, -> { joins('left join wishes on wishes.need_id = needs.id')
scope :with_wishes_count, -> { joins('left join wishes on wishes.need_id = needs.id')
.select('needs.*, count(wishes.id) as wishes_count')
.group("needs.id")
.order("wishes_count DESC") }
}
scope :with_messages_count, -> { joins('left join messages on messages.need_id = needs.id')
.select('needs.*, count(messages.id) as messages_count')
.group("needs.id")
}
scope :search, -> (search) {
where('title LIKE ?', "%#{search}%")
}
workflow_column :state
max_paginates_per 10
acts_as_paranoid
has_many :wishes
has_many :wishes, dependent: :destroy
has_many :customers, -> { uniq }, through: :wishes
has_many :messages, dependent: :destroy
validates :title, :presence => true, length: {within: 4..128}
validates :description, presence: true, length: {maximum: 65535}
@ -46,7 +62,7 @@ class Need < ActiveRecord::Base
when 'created'
"En attente de validation"
when 'verified'
"Validé"
"Pas encore négocié"
when 'refused'
"Refusé"
when 'negociating'

View File

@ -1,4 +1,4 @@
- form_for @data_file, :url => admin_data_file_path(:id => @data_file.id, :file_folder_id => params[:file_folder_id], :manager => params[:manager], :multiple => params[:multiple]), :remote => true do |form|
- form_for @data_file, :url => admin_data_file_path(:id => @data_file.id, :file_folder_id => params[:file_folder_id], :manager => params[:manager], :multiple => params[:multiple]), :remote => true do |form|
%table{:style => "width:100%"}
%tr
%td{:style => "width:120px"} Nom :
@ -9,7 +9,4 @@
%tr.submit_tr
%td=link_to "Annuler", admin_data_file_path(:id => @data_file.id, :manager => params[:manager], :multiple => params[:multiple]), :remote => true
%td
=form.submit "Sauvegarder"

View File

@ -10,5 +10,3 @@
%tbody
=render @needs
.pagination= paginate @needs

View File

@ -0,0 +1,6 @@
.white.padding.message-item
%h4= i(:"user") + " " + message.customer.anonyme_nick
%p.info=i(:"clock-o") + " Posté le #{message.created_at.strftime('%d/%m/%Y à %H:%M')}, il y a #{time_ago_in_words(message.created_at)}"
%p= message.content

View File

@ -1,16 +1,25 @@
.col-md-6
.padding.need-item
%h3=need.title.upcase
%h4
=link_to need.title.upcase, public_need_path(need)
%p.info=i(:"clock-o") + " Ajouté il y a #{time_ago_in_words(need.created_at)} par #{need.author.anonyme_nick}"
%span
.time= i(:"clock-o") + " Ajouté il y a #{time_ago_in_words(need.created_at)}"
.info=i(:"info-circle") + " " + need.human_state
%p=truncate(need.description, length: 100)
%p.description=truncate(need.description, length: 100)
.counters
.item=i(:"hand-paper-o") + " " + need.wishes.length.to_s
.item=i(:"comment-o") + " " + need.messages.length.to_s
-if(need.customers.include?(current_customer))
=link_to i(:"hand-grab-o") + " Ça ne m'intéresse plus", wish_public_need_path(need) , :class => "btn btn-danger pull-right"
=link_to i(:"check") + " Intéressé", wish_public_need_path(need) , :class => "btn btn-success pull-right"
-else
=link_to i(:"hand-paper-o") + " Ça m'intéresse", wish_public_need_path(need) , :class => "btn btn-success pull-right"
=link_to i(:"hand-paper-o") + " Ça m'intéresse !", wish_public_need_path(need) , :class => "btn btn-primary pull-right"
.clear

View File

@ -4,6 +4,25 @@
=render collection: @needs, partial: 'need_item', as: :need
.clear
.pagination= paginate @needs
.row.col-md-3
.white
%h3 Sidebar
.white.side-menu
= semantic_form_for :search, :html => {id: :search_form, :method => :get } do |f|
%h4 Recherche
= f.inputs do
= f.input :q, :as => :search, label: false, input_html: {value: params[:q], :name => 'q' }, placeholder: "Rechercher un besoin"
=f.submit "Rechercher", :class => "btn btn-primary pull-right"
.clear
%h4 Ordonner par
= f.inputs do
= f.input :o, as: :order, selected: params[:o], input_html: {:name => 'o' }, label: false, :include_blank => false , :as => :select, :collection => @orders
.clear
:javascript
$('#search_o').change(function(){$('#search_form').submit()})

View File

@ -0,0 +1,38 @@
.center.white
.show-need
%h1= @need.title.upcase
%p.info=i(:"clock-o") + " Ajouté il y a #{time_ago_in_words(@need.created_at)} par #{@need.author.anonyme_nick}"
%p.description= @need.description
.clear
.counters
-if(@need.wishes.length > 0)
.item=i(:"hand-paper-o") + " " + " #{pluralize(@need.wishes.length, 'Organisation')} #{"intéressé".pluralize(@need.wishes.length)} par ce besoin"
-else
.item=i(:"hand-paper-o") + " Aucune organisation n'est intéressé par ce besoin"
-if(@need.customers.include?(current_customer))
=link_to i(:"check") + " Intéressé", wish_public_need_path(@need) , :class => "btn btn-square btn-lg btn-success pull-right"
-else
=link_to i(:"hand-paper-o") + " Ça m'intéresse !", wish_public_need_path(@need) , :class => "btn btn-square btn-lg btn-primary pull-right"
.clear
%hr
= semantic_form_for [:public, @need, @comment ], :html => {id: :message_form, :method => :post } do |f|
%h4 Poster un commentaire
= f.inputs do
= f.input :content, as: :text, label: false, rows: 5, :input_html => {:style => "height:100px;"}
=f.submit "Envoyer", :class => "btn btn-square btn-primary pull-right"
.clear
%h3= i(:"comment-o") + " #{pluralize(@need.messages.count, 'Commentaire')} pour ce besoin"
=render collection: @comments, partial: 'message'
.pagination= paginate @comments

View File

@ -89,6 +89,7 @@ Rails.application.routes.draw do
get 'my_account/reconfirm', :as => "reconfirm_email"
resources :needs do
resources :messages
member do
get 'wish', as: 'wish'
end

View File

@ -0,0 +1,5 @@
class AddIndexToNeed < ActiveRecord::Migration
def change
add_index :needs, :title
end
end

View File

@ -0,0 +1,11 @@
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.timestamps null: false
t.references :customer
t.references :need
t.text :content
end
end
end

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20151202165807) do
ActiveRecord::Schema.define(version: 20151203185210) do
create_table "admins", force: :cascade do |t|
t.string "name", limit: 255
@ -382,6 +382,14 @@ ActiveRecord::Schema.define(version: 20151202165807) do
t.datetime "updated_at"
end
create_table "messages", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "customer_id", limit: 4
t.integer "need_id", limit: 4
t.text "content", limit: 65535
end
create_table "needs", force: :cascade do |t|
t.string "title", limit: 255
t.text "description", limit: 65535
@ -394,6 +402,7 @@ ActiveRecord::Schema.define(version: 20151202165807) do
add_index "needs", ["author_id"], name: "index_needs_on_author_id", using: :btree
add_index "needs", ["deleted_at"], name: "index_needs_on_deleted_at", using: :btree
add_index "needs", ["title"], name: "index_needs_on_title", using: :btree
create_table "newsgroups", force: :cascade do |t|
t.string "name", limit: 255

View File

@ -0,0 +1,7 @@
require 'test_helper'
class MessagesControllerTest < ActionController::TestCase
# test "the truth" do
# assert true
# end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class Public::MessagesControllerTest < ActionController::TestCase
# test "the truth" do
# assert true
# end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class Public::NeedsControllerTest < ActionController::TestCase
# test "the truth" do
# assert true
# end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class PublicmessagesControllerTest < ActionController::TestCase
# test "the truth" do
# assert true
# end
end

11
test/fixtures/messages.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
# This model initially had no columns defined. If you add columns to the
# model remove the '{}' from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
one: {}
# column: value
#
two: {}
# column: value

View File

@ -0,0 +1,7 @@
require 'test_helper'
class MessageTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end