diff --git a/app/controllers/admin/accepted_offers_controller.rb b/app/controllers/admin/accepted_offers_controller.rb index e753c0f..9fe706c 100755 --- a/app/controllers/admin/accepted_offers_controller.rb +++ b/app/controllers/admin/accepted_offers_controller.rb @@ -2,6 +2,12 @@ class Admin::AcceptedOffersController < ApplicationController layout "admin" before_filter :auth_admin + def show + @offer = Offer.find(params[:offer_id]) + @accepted_offer = @offer.accepted_offers.find(params[:id]) + @documents = @accepted_offer.documents + end + def upload_devis @accepted_offer = AcceptedOffer.find(params[:id]) diff --git a/app/controllers/admin/contact_messages_controller.rb b/app/controllers/admin/contact_messages_controller.rb new file mode 100755 index 0000000..bcd7b1a --- /dev/null +++ b/app/controllers/admin/contact_messages_controller.rb @@ -0,0 +1,39 @@ +class Admin::ContactMessagesController < ApplicationController + layout "admin" + before_filter :auth_admin + + + def create + + @contact_message = ContactMessage.new + @contact_message.assign_attributes(message_params) + @contact_message.admin = current_admin + @contact_message.read_by_admin = true + + if @contact_message.save + flash[:notice] = "Commentaire envoyé." + return redirect_to :back + else + flash[:error] = "Votre commentaire n'a pas pu être envoyé." + return render 'index' + end + + end + + + def destroy + @message = ContactMessage.find(params['id']) + if @message.destroy + flash[:notice] = "message supprimé" + else + flash[:error] = "Impossible de supprimer ce message" + end + + redirect_to :back + end + + def message_params + params.require(:contact_message).permit(:content, :contact_id) + end + +end diff --git a/app/controllers/admin/conversations_controller.rb b/app/controllers/admin/conversations_controller.rb new file mode 100755 index 0000000..d8d9c02 --- /dev/null +++ b/app/controllers/admin/conversations_controller.rb @@ -0,0 +1,29 @@ +class Admin::ConversationsController < ApplicationController + layout "admin" + before_filter :auth_admin + + + def index + + @conversations = ContactMessage.select([ + "count(CASE WHEN contact_messages.read_by_admin = FALSE THEN 1 END ) as unread_count", + "max(created_at) as last_at", + "count(contact_id) as messages_count", + "contact_messages.*"]) + .group(:contact_id) + .order(created_at: :desc) + .page(params[:page]) + + + end + + def show + @customer = Customer.find(params[:id]) + ContactMessage.where(contact_id: params[:id]).update_all(read_by_admin: true) + @contact_messages = ContactMessage.where('customer_id = ? OR contact_id = ?', params[:id], params[:id]) + .order(created_at: :desc).page params[:page] + + end + + +end diff --git a/app/controllers/admin/documents_controller.rb b/app/controllers/admin/documents_controller.rb new file mode 100755 index 0000000..7895118 --- /dev/null +++ b/app/controllers/admin/documents_controller.rb @@ -0,0 +1,113 @@ +class Admin::DocumentsController < ApplicationController + layout "admin" + before_filter :auth_admin + before_filter :load_offer + + def create + + @accepted_offer.documents.create({title: params[:document][:title]}) + + + if @accepted_offer.save + flash[:success] = "Document créé" + else + flash[:error] = "Impossible de créer le document" + end + redirect_to :back + end + + def upload_document + if !params[:document] + flash[:error] = "Vous devez sélectionner un fichier" + else + + @document = @accepted_offer.documents.find(params[:document_id]) + @document.document = params[:document] + if @document.save + @document.upload_document! if @document.not_available? + @document.save + flash[:success] = "Document chargé" + else + flash[:error] = "Impossible de charger le document" + end + end + redirect_to :back + end + + def upload_returned_document + if !params[:returned_document] + flash[:error] = "Vous devez sélectionner un fichier" + else + + @document = @accepted_offer.documents.find(params[:document_id]) + @document.returned_document = params[:returned_document] + if @document.save + @document.state = :document_returned + @document.save + flash[:success] = "Document chargé" + else + flash[:error] = "Impossible de charger le document" + end + end + redirect_to :back + end + + + def load_offer + @offer = Offer.find(params[:offer_id]) + @accepted_offer = @offer.accepted_offers.find(params[:accepted_offer_id]) + end + + def download + @document = @accepted_offer.documents.find(params[:document_id]) + send_file @document.document.file.path + end + + def download_returned + @document = @accepted_offer.documents.find(params[:document_id]) + send_file @document.returned_document.file.path + end + + def delete + @document = @accepted_offer.documents.find(params[:document_id]) + if @document.remove_document! + flash[:success] = "Fichier supprimé" + else + flash[:error] = "Impossible de supprimer le fichier" + end + redirect_to :back + end + + def destroy + @document = @accepted_offer.documents.find(params[:document_id]) + if @document.destroy + flash[:success] = "Document Supprimé" + else + flash[:error] = "Impossible de supprimer le document" + end + redirect_to :back + end + + def delete_returned + @document = @accepted_offer.documents.find(params[:document_id]) + if @document.remove_returned_document! + flash[:success] = "Fichier supprimé" + else + flash[:error] = "Impossible de supprimer le fichier" + end + redirect_to :back + end + + def verify_returned + @document = @accepted_offer.documents.find(params[:document_id]) + @document.state = :document_verified + + + if @document.save + flash[:success] = "Document vérifié" + else + flash[:error] = "Impossible vérifier le document" + end + redirect_to :back + end +end diff --git a/app/controllers/admin/needs_controller.rb b/app/controllers/admin/needs_controller.rb index bc23bc6..d99982d 100755 --- a/app/controllers/admin/needs_controller.rb +++ b/app/controllers/admin/needs_controller.rb @@ -145,7 +145,7 @@ class Admin::NeedsController < ApplicationController def accept @need = Need.find(params[:id]) if @need.offers.length < 1 - flash[:error] = "Vous devez créer au moins une offre avant de passer ce besoin en négocié" + flash[:error] = "Vous devez créer au moins une proposition avant de passer ce besoin en négocié" else if @need.accept! flash[:notice] = "Le besoin est maintenant négocié" diff --git a/app/controllers/admin/offers_controller.rb b/app/controllers/admin/offers_controller.rb index 3d2645d..7f19471 100755 --- a/app/controllers/admin/offers_controller.rb +++ b/app/controllers/admin/offers_controller.rb @@ -65,7 +65,7 @@ class Admin::OffersController < ApplicationController @offer = Offer.new(offer_params) if @offer.save @need.offers << @offer - flash[:notice] = "Offre créée avec succès." + flash[:notice] = "Proposition créée avec succès." @offer.validate! redirect_to admin_need_offers_path(@need) else @@ -83,7 +83,7 @@ class Admin::OffersController < ApplicationController def update @offer = Offer.find(params[:id]) if @offer.update_attributes(offer_params) - flash[:notice] = "Offre modifiée avec succès." + flash[:notice] = "Proposition modifiée avec succès." if params[:need_id] @need = Need.find(params[:need_id]) redirect_to admin_need_offers_path(@need) @@ -99,9 +99,9 @@ class Admin::OffersController < ApplicationController @offer = Offer.find(params[:id]) if(@offer.destroy) - flash[:notice] = "Offre supprimée avec succès." + flash[:notice] = "Proposition supprimée avec succès." else - flash[:error] = "Impossible de supprimer cette offre" + flash[:error] = "Impossible de supprimer cette proposition" end if params[:need_id] diff --git a/app/controllers/public/accepted_offers_controller.rb b/app/controllers/public/accepted_offers_controller.rb index bf3a7fa..61a7b74 100755 --- a/app/controllers/public/accepted_offers_controller.rb +++ b/app/controllers/public/accepted_offers_controller.rb @@ -13,9 +13,10 @@ class Public::AcceptedOffersController < ApplicationController @accepted_offer.save send_file @accepted_offer.devis.file.path end + end - - + def index + @accepted_offer = AcceptedOffer.find(params[:id]) end end diff --git a/app/controllers/public/contact_messages_controller.rb b/app/controllers/public/contact_messages_controller.rb new file mode 100755 index 0000000..78bfcdb --- /dev/null +++ b/app/controllers/public/contact_messages_controller.rb @@ -0,0 +1,49 @@ +class Public::ContactMessagesController < ApplicationController + layout "public" + + def index + ContactMessage.update_all(read_by_customer: true) + @contact_message = ContactMessage.new + @contact_messages = ContactMessage.where("customer_id = ? OR contact_id = ?", current_customer.id,current_customer.id ) + .order(created_at: :desc) + .page params[:page] + + + end + + def create + + @contact_message = ContactMessage.new + @contact_message.assign_attributes(message_params) + @contact_message.customer = current_customer + @contact_message.contact = current_customer + @contact_message.read_by_customer = true + + if @contact_message.save + flash[:notice] = "Commentaire envoyé." + return redirect_to :back + else + flash[:error] = "Votre commentaire n'a pas pu être envoyé." + return render 'index' + end + + + + + end + + def destroy + @message = ContactMessage.find(params['id']) + if @message.destroy + flash[:notice] = "message supprimé" + else + flash[:error] = "Impossible de supprimer ce message" + end + + redirect_to :back + end + + def message_params + params.require(:contact_message).permit(:content) + end +end diff --git a/app/controllers/public/offers_controller.rb b/app/controllers/public/offers_controller.rb index 272705c..a58cf70 100755 --- a/app/controllers/public/offers_controller.rb +++ b/app/controllers/public/offers_controller.rb @@ -6,14 +6,19 @@ class Public::OffersController < ApplicationController def accept if @offer.customers.include?(current_customer) - flash[:error] = "Vous avez déjà accepter cette offre" + flash[:error] = "Vous avez déjà accepter cette proposition" return redirect_back_or_default :root end @accepted_offer = AcceptedOffer.new @accepted_offer.customer = current_customer @accepted_offer.offer = @offer + + @document = Document.new + @document.title = "Proposition" + @accepted_offer.documents << @document + if @accepted_offer.save - flash[:notice] = "Offre acceptée avec succès !" + flash[:notice] = "Proposition acceptée avec succès !" redirect_back_or_default :root end end diff --git a/app/models/accepted_offer.rb b/app/models/accepted_offer.rb index 51aeec9..a40bb60 100755 --- a/app/models/accepted_offer.rb +++ b/app/models/accepted_offer.rb @@ -2,6 +2,7 @@ class AcceptedOffer < ActiveRecord::Base include Workflow belongs_to :customer belongs_to :offer + has_many :documents, dependent: :destroy mount_uploader :devis, DevisUploader validates :customer, :presence => true validates :offer, :presence => true @@ -9,43 +10,28 @@ class AcceptedOffer < ActiveRecord::Base workflow_column :state workflow do - state :waiting_devis do - event :upload_devis, :transitions_to => :devis_available + state :waiting_documents do + event :complete_documents, :transitions_to => :devis_available end - state :devis_available do - event :download_devis, :transitions_to => :devis_downloaded - end - state :devis_downloaded do - event :receive_devis, :transitions_to => :devis_received - end - - state :devis_received + state :documents_completed end def human_state case state - when 'waiting_devis' - "Création du devis en cours..." - when 'devis_available' - "Devis disponible" - when 'devis_downloaded' - "En attente de réception du devis signé" - when 'devis_received' - "Devis reçu et signé" + when 'waiting_documents' + "En attente des documents à retourner" + when 'documents_completed' + "Documents retournés et vérifiés" end end def human_admin_state case state - when 'waiting_devis' - "En attente de création du devis" - when 'devis_available' - "Devis disponible pour le client mais pas encore téléchargé" - when 'devis_downloaded' - "Dévis téléchargé par le client, en attente de réception du devis signé" - when 'devis_received' - "Devis reçu et signé" + when 'waiting_documents' + "En attente des documents à retourner" + when 'documents_completed' + "Documents retournés et vérifiés" end end end diff --git a/app/models/admin.rb b/app/models/admin.rb index af97647..e41ff59 100755 --- a/app/models/admin.rb +++ b/app/models/admin.rb @@ -33,6 +33,10 @@ class Admin < ActiveRecord::Base AdminMailer.delay.password_reset(self) end + def fullname + "#{firstname.capitalize} #{name.upcase}" + end + def generate_token(column) begin self[column] = SecureRandom.urlsafe_base64 diff --git a/app/models/contact_message.rb b/app/models/contact_message.rb new file mode 100755 index 0000000..28a39c4 --- /dev/null +++ b/app/models/contact_message.rb @@ -0,0 +1,33 @@ +class ContactMessage < ActiveRecord::Base + belongs_to :customer + belongs_to :admin + + belongs_to :contact, class_name: "Customer" + + paginates_per 5 + acts_as_paranoid + + validates :content, :presence => true, length: {within: 1..1024} + + def conversation_id + if customer + customer.id + elsif contact + contact.id + else + nil + end + end + + def author_name + if customer + customer.fullname + elsif admin + admin.fullname + " (Administrateur)" + else + nil + end + end + + +end diff --git a/app/models/document.rb b/app/models/document.rb new file mode 100755 index 0000000..a71fb01 --- /dev/null +++ b/app/models/document.rb @@ -0,0 +1,61 @@ +class Document < ActiveRecord::Base + include Workflow + + belongs_to :accepted_offer + + mount_uploader :document, DocumentUploader + mount_uploader :returned_document, DocumentUploader + + acts_as_paranoid + workflow_column :state + + validates :title, :presence => true + + + workflow do + state :not_available do + event :upload_document, :transitions_to => :document_available + end + state :document_available do + event :download_document, :transitions_to => :document_downloaded + end + state :document_downloaded do + event :return_document, :transitions_to => :document_returned + end + state :document_returned do + event :verify, :transitions_to => :document_verified + end + state :document_verified + end + + def human_state + case state + when 'not_available' + "Pas encore disponible, patientez..." + when 'document_available' + "Disponible" + when 'document_downloaded' + "Téléchargé, à retourner signé" + when 'document_returned' + "Retourné, en attente de vérification" + when 'document_verified' + "Retourné et vérifié" + end + end + + def human_admin_state + case state + when 'not_available' + "Vous devez charger le document" + when 'document_available' + "Disponible et en attente de téléchargement par le client" + when 'document_downloaded' + "Téléchargé par le client, en attente de retour" + when 'document_returned' + "Retourné par le client, en attente de vérification" + when 'document_verified' + "Retourné et vérifié" + end + end + +end diff --git a/app/uploaders/document_uploader.rb b/app/uploaders/document_uploader.rb new file mode 100755 index 0000000..6421947 --- /dev/null +++ b/app/uploaders/document_uploader.rb @@ -0,0 +1,30 @@ +# encoding: utf-8 + +class DocumentUploader < CarrierWave::Uploader::Base + + include Rails.application.routes.url_helpers + + + def filename + "#{mounted_as}-#{friendly_filename(model.title)}.#{file.extension}" if original_filename.present? + end + + def store_dir + "#{Rails.root}/private_medias/documents/#{model.id}/#{mounted_as}" + end + + def extension_white_list + %w(pdf) + end + + def url + #download_admin_offer_accepted_offer_path(model.offer, model) + end + + def friendly_filename(filename) + filename.gsub(/[^\w\s_-]+/, '') + .gsub(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2') + .gsub(/\s+/, '_') + end + +end diff --git a/app/views/admin/accepted_offers/_documents.html.haml b/app/views/admin/accepted_offers/_documents.html.haml new file mode 100755 index 0000000..ef97ef6 --- /dev/null +++ b/app/views/admin/accepted_offers/_documents.html.haml @@ -0,0 +1,10 @@ +%tr + %td + Titre du document + %td + Document + %td + Document retourné + %td{style:"text-align:center"} + Etat + %td{style:"text-align:righ"} diff --git a/app/views/admin/accepted_offers/index.html.haml b/app/views/admin/accepted_offers/index.html.haml new file mode 100755 index 0000000..f71d6f2 --- /dev/null +++ b/app/views/admin/accepted_offers/index.html.haml @@ -0,0 +1 @@ +ezrf diff --git a/app/views/admin/accepted_offers/show.html.haml b/app/views/admin/accepted_offers/show.html.haml new file mode 100755 index 0000000..f291a68 --- /dev/null +++ b/app/views/admin/accepted_offers/show.html.haml @@ -0,0 +1,72 @@ + +%h1 + = "Gestion des documents pour l'utilisateur #{@accepted_offer.customer.fullname} sur la proposition #{@accepted_offer.offer.need.title}" + + +%h3 + = "Ajouter un document" + +%br +=semantic_form_for [:admin, @offer, @accepted_offer, Document.new] do |f| + .content + + =f.inputs do + =f.input :title, :label => "Titre du document : " + + .actions= f.submit "Créer le document", :class => "btn btn-primary" +%br +%h3 + = "Liste des documents (#{@accepted_offer.documents.where(state: :document_verified).count} sur #{@accepted_offer.documents.count} vérifiés)" +%table.table.admin-table.table-hover.table-striped + %thead.rows_header + %tr + %th + Titre du document + %th{style:"text-align:center"} + Fichier à retourner par le client + %th{style:"text-align:center"} + Fichier retourné par le client + %th{style:"text-align:left"} + État du document + %th{style:"text-align:right"} + + + + %tbody.rows + -@documents.each do |document| + %tr{class: document.document_verified? ? "success" : ""} + %td + =document.title + %td{style:"text-align:center"} + -if !document.document? + = form_tag admin_offer_accepted_offer_document_upload_document_path(@offer, @accepted_offer, document), name: :document, method: :post, multipart: true do + %span.btn.btn-default.btn-file + ="Ouvrir (PDF)" + = file_field_tag :document + = submit_tag("Charger" , class:"btn btn-primary") + -else + =link_to i(:"download"), admin_offer_accepted_offer_document_download_path(@offer, @accepted_offer, document),title: "Télécharger le document" + -if !document.document_verified? + =link_to i(:"remove"), admin_offer_accepted_offer_document_delete_path(@offer, @accepted_offer, document), title: "Supprimer le fichier chargé", :data => {:confirm => 'Voulez-vous vraiment supprimer le fichier chargé ?'} + + %td{style:"text-align:center"} + -if !document.not_available? + -if !document.returned_document? + = form_tag admin_offer_accepted_offer_document_upload_returned_document_path(@offer, @accepted_offer, document), name: :returned_document, method: :post, multipart: true do + %span.btn.btn-default.btn-file + ="Ouvrir (PDF)" + = file_field_tag :returned_document + = submit_tag("Charger manuellement" , class:"btn btn-primary") + -else + =link_to i(:"download"), admin_offer_accepted_offer_document_download_returned_path(@offer, @accepted_offer, document),title: "Télécharger le document" + -if !document.document_verified? + =link_to i(:"remove"), admin_offer_accepted_offer_document_delete_returned_path(@offer, @accepted_offer, document), title: "Supprimer le fichier chargé", :data => {:confirm => 'Voulez-vous vraiment supprimer le fichier chargé ?'} + -if document.document_returned? + =link_to ic(:"check"), admin_offer_accepted_offer_document_verify_returned_path(@offer, @accepted_offer, document), :data => {:confirm => 'Voulez-vous vraiment marquer le document retourné par le client comme vérifié et signé ?'}, title: "Marquer le document comme vérifié et signé" + + -else + Charger un document d'abord + %td{style:"text-align:left"} + =document.human_admin_state + %td{style:"text-align:right"} + =link_to i(:"trash"), admin_offer_accepted_offer_document_destroy_path(@offer, @accepted_offer, document), title: "Supprimer le document", :data => {:confirm => 'Voulez-vous vraiment supprimer ce document ?'} diff --git a/app/views/admin/contact_messages/_conversation.html.haml b/app/views/admin/contact_messages/_conversation.html.haml new file mode 100755 index 0000000..d043e91 --- /dev/null +++ b/app/views/admin/contact_messages/_conversation.html.haml @@ -0,0 +1,9 @@ +%tr + %td + + %td + =contact_message.content + %td + =contact_message.created_at + %td{:style => "width:100px"} + =link_to i(:"eye"), admin_conversation_path(contact_message.conversation_id), class: "btn" diff --git a/app/views/admin/conversations/_contact_message.html.haml b/app/views/admin/conversations/_contact_message.html.haml new file mode 100755 index 0000000..2f492fd --- /dev/null +++ b/app/views/admin/conversations/_contact_message.html.haml @@ -0,0 +1,6 @@ +.white.padding.message-item{style:contact_message.admin ? "background-color:#e8e8e8" : "background-color:#b7e7ff"} + + %h4= ic(:"user") + " " + contact_message.author_name + %p.info=ic(:"clock-o") + " Posté le #{contact_message.created_at.strftime('%d/%m/%Y à %H:%M')}, il y a #{time_ago_in_words(contact_message.created_at)}" + + %p= contact_message.content diff --git a/app/views/admin/conversations/_conversation.html.haml b/app/views/admin/conversations/_conversation.html.haml new file mode 100755 index 0000000..523d4c6 --- /dev/null +++ b/app/views/admin/conversations/_conversation.html.haml @@ -0,0 +1,15 @@ +%tr + %td + = link_to contact_message.contact.fullname + " (#{contact_message.contact.organisation})", edit_admin_customer_path( contact_message.contact) + %td + ="#{contact_message.last_at.strftime('%d/%m/%Y à %H:%M')}, il y a #{time_ago_in_words(contact_message.last_at)}" + %td{style:"text-align:center"} + =contact_message.messages_count + %td{style:"text-align:center"} + -if contact_message.unread_count > 0 + %span.badge{style:"background-color:#D9534F"} + =contact_message.unread_count + -else + Aucun + %td{:style => "width:100px;text-align:right"} + =link_to i(:"comments"), admin_conversation_path(contact_message.conversation_id) diff --git a/app/views/admin/conversations/index.html.haml b/app/views/admin/conversations/index.html.haml new file mode 100755 index 0000000..731be3b --- /dev/null +++ b/app/views/admin/conversations/index.html.haml @@ -0,0 +1,25 @@ +%h1 + Liste des conversations avec les utilisateurs + + +%table.table.admin-table.table-hover.table-striped + %thead.rows_header + + %tr + %th + Utilisateur + + %th + Date du dernier message + %th{style:"text-align:center"} + Nombre de messages + %th{style:"text-align:center"} + Messages non lus + %th{:style => "width:100px"} +   + + %tbody.rows + + =render collection: @conversations, partial: 'conversation', as: :contact_message + +.pagination.pull-right= paginate @conversations diff --git a/app/views/admin/conversations/show.html.haml b/app/views/admin/conversations/show.html.haml new file mode 100755 index 0000000..f99c31c --- /dev/null +++ b/app/views/admin/conversations/show.html.haml @@ -0,0 +1,21 @@ + +.center.white.row + + .row + %h1 + = ic(:"comments") + " Conversation avec " + = link_to @customer.fullname,edit_admin_customer_path(@customer) + = semantic_form_for [:admin, ContactMessage.new], :html => {id: :contact_message_form, :method => :post } do |f| + + %h4 Répondre + = f.inputs do + = f.input :content, as: :text, label: false, rows: 5, :input_html => {:style => "height:100px;"} + = f.input :contact_id, as: :hidden, value: @customer.id + = f.submit "Envoyer", :class => "btn btn-square btn-primary pull-right" + + .row{style:"margin-top:20px;"} + .padding + =render collection: @contact_messages, partial: 'contact_message' + + .pull-right + .pagination= paginate @contact_messages diff --git a/app/views/admin/needs/_need.html.haml b/app/views/admin/needs/_need.html.haml index 49c47de..2a82bfc 100755 --- a/app/views/admin/needs/_need.html.haml +++ b/app/views/admin/needs/_need.html.haml @@ -22,7 +22,7 @@ -if(need.verified?) = link_to i(:"comments"), negociate_admin_need_path(need), title: "Passer en négociation", :data => {:confirm => 'Voulez-vous vraiment passer ce besoin en négociation'} -if(need.negociating?) - = link_to i(:"thumbs-up"), accept_admin_need_path(need), title: "Passer en négocié et créer une offre", :data => {:confirm => 'Voulez-vous vraiment passer ce besoin en négocié ?'} + = link_to i(:"thumbs-up"), accept_admin_need_path(need), title: "Passer en négocié et créer une proposition", :data => {:confirm => 'Voulez-vous vraiment passer ce besoin en négocié ?'} = link_to i(:"thumbs-down"), reject_admin_need_path(need), title: "Passer en négociation échouée", :data => {:confirm => 'Voulez-vous vraiment passer ce besoin en négociation échouée'} = link_to i(:"trash"), [:admin, need], :data => {:confirm => 'Voulez-vous vraiment supprimer ce besoin ?'}, :method => :delete = link_to i(:pencil), edit_admin_need_path(need) diff --git a/app/views/admin/needs/index.html.haml b/app/views/admin/needs/index.html.haml index 14bac25..ad7234c 100755 --- a/app/views/admin/needs/index.html.haml +++ b/app/views/admin/needs/index.html.haml @@ -51,7 +51,7 @@ %th{style: 'text-align:center' } Commentaires/Intérêts %th{style: 'text-align:center' } - Offres + Propositions %th Statut %th{:style => "width:100px"} diff --git a/app/views/admin/offers/_offer.html.haml b/app/views/admin/offers/_offer.html.haml index 1334256..8b72aca 100755 --- a/app/views/admin/offers/_offer.html.haml +++ b/app/views/admin/offers/_offer.html.haml @@ -15,10 +15,10 @@ %td.actions{:style => "width:150px;text-align:right"} -if @need - = link_to i(:"cog"), accepted_admin_offer_path(offer), title: "Gérer les offres par client" - = link_to i(:"trash"), [:admin,@need, offer], :data => {:confirm => 'Voulez-vous vraiment supprimer cette offre ?'}, :method => :delete + = link_to i(:"cog"), accepted_admin_offer_path(offer), title: "Gérer les propositions par client" + = link_to i(:"trash"), [:admin,@need, offer], :data => {:confirm => 'Voulez-vous vraiment supprimer cette proposition ?'}, :method => :delete = link_to i(:pencil), edit_admin_need_offer_path(@need, offer) -else - = link_to i(:"cog"), accepted_admin_offer_path(offer), title: "Gérer les offres par client" - = link_to i(:"trash"), [:admin, offer], :data => {:confirm => 'Voulez-vous vraiment supprimer cette offre ?'}, :method => :delete + = link_to i(:"cog"), accepted_admin_offer_path(offer), title: "Gérer les propositions par client" + = link_to i(:"trash"), [:admin, offer], :data => {:confirm => 'Voulez-vous vraiment supprimer cette proposition ?'}, :method => :delete = link_to i(:pencil), edit_admin_offer_path(offer) diff --git a/app/views/admin/offers/accepted.html.haml b/app/views/admin/offers/accepted.html.haml index ed0b93b..1df31b3 100755 --- a/app/views/admin/offers/accepted.html.haml +++ b/app/views/admin/offers/accepted.html.haml @@ -1,5 +1,5 @@ %h1 - = "Gestion des offres par client pour le besoin " + = "Gestion des propositions par client pour le besoin " = @offer.need.title @@ -10,12 +10,12 @@ =pluralize(@offer.need.wishes.length, "Personne") %h4 - = "Offres acceptées: " + = "Propositions acceptées: " =pluralize(@offer.accepted_offers.length, "Personne") %h4 - = "Prix de l'offre: " + = "Prix proposé: " =number_to_currency(@offer.price, locale: :fr) @@ -37,16 +37,16 @@ %th Email %th{style:"text-align:center"} - Offre acceptée? + Propositions acceptée? %th{style:"text-align:center"} - Devis + Documents %th{style:"text-align:center"} État %tbody.rows -@offer.need.wishes.each do |wish| -accepted_offer = @offer.accepted_offers.where(customer_id: wish.customer.id).first - -if accepted_offer && accepted_offer.devis_received? + -if accepted_offer && accepted_offer.documents_completed? -class_name = "success" %tr{class: class_name} %td @@ -56,41 +56,26 @@ =wish.customer.firstname + " " + wish.customer.name %td -if wish.customer.phone - = i("phone") + " #{wish.customer.phone}" + = ic("phone") + " #{wish.customer.phone}" %td -if wish.customer.email - = link_to i("envelope-o") + " #{wish.customer.email}", 'mailto:' + wish.customer.email + = link_to ic("envelope-o") + " #{wish.customer.email}", 'mailto:' + wish.customer.email %td{style:"text-align:center"} -if accepted_offer - =i(:"check") + " Acceptée" + =ic(:"check") + " Acceptée" -else \- %td{style:"text-align:center"} -if accepted_offer + =link_to ic(:file) + " #{accepted_offer.documents.where(state: :document_verified).count} / #{accepted_offer.documents.count}", admin_offer_accepted_offer_path(@offer, accepted_offer), style:"display:inline-block", class: "btn btn-primary btn-xs" - -if accepted_offer.devis? - -if accepted_offer.devis_downloaded? - =link_to "Reçu et signé", received_admin_offer_accepted_offer_path(@offer, accepted_offer), :data => {:confirm => 'Voulez-vous vraiment marquer le devis comme reçu et signé ?'}, style:"display:inline-block", class: "btn btn-success btn-sm" - - =link_to "Télécharger le devis", accepted_offer.devis.url, style:"display:inline-block", class: "btn btn-primary btn-sm" - =link_to "Supprimer le devis", delete_admin_offer_accepted_offer_path(@offer, accepted_offer), style:"display:inline-block", class: "btn btn-danger btn-sm" - -else - - = form_tag upload_devis_admin_offer_accepted_offer_path(@offer, accepted_offer), name: :devis, method: :post, multipart: true do - %span.btn.btn-default.btn-file - ="..." - = file_field_tag :devis - = submit_tag("Charger" , class:"btn btn-primary") - - -else - \- %td{style:"text-align:center"} -if accepted_offer =accepted_offer.human_admin_state -else - Offre pas encore acceptée + Pas encore acceptée :javascript diff --git a/app/views/admin/offers/edit.html.haml b/app/views/admin/offers/edit.html.haml index 812d77d..f908625 100755 --- a/app/views/admin/offers/edit.html.haml +++ b/app/views/admin/offers/edit.html.haml @@ -1,4 +1,4 @@ %h1 - Modifier une offre + Modifier une proposition =render :partial => "form" diff --git a/app/views/admin/offers/index.html.haml b/app/views/admin/offers/index.html.haml index 6af0cdb..6f0d0f2 100755 --- a/app/views/admin/offers/index.html.haml +++ b/app/views/admin/offers/index.html.haml @@ -1,33 +1,33 @@ -if @need %h1 - Gestion des offres pour le besoin + Gestion des propositions pour le besoin %strong= @need.title %p.alert.alert-info - Seul les offres concernant le besoin + Seul les propositions concernant le besoin %strong= @need.title sont affichées sur cette page. %br - Pour voir toutes les offres, aller dans l'onglet - %strong Gestion des offres + Pour voir toutes les propositions, aller dans l'onglet + %strong Gestion des propositions depuis la barre de navigation -else %h1 - Gestion des offres + Gestion des propositions %p.alert.alert-info - Cette page affiche toutes les offres existantes. + Cette page affiche toutes les propositions existantes. %br - Pour créer une offre, il faut d'abord choisir un besoin. Pour cela, passer par l'onglet + Pour créer une proposition, il faut d'abord choisir un besoin. Pour cela, passer par l'onglet %strong Gestion des besoins puis cliquer sur le bouton %strong= ic(:gift) en face du besoin de votre choix. -if @offers.length < 1 - Aucune offre actuellement + Aucune proposition actuellement -if @need %br %br - =link_to "Ajouter une offre", new_admin_need_offer_path(@need),class:"btn btn-primary" + =link_to "Ajouter une proposition", new_admin_need_offer_path(@need),class:"btn btn-primary" %br -else .row @@ -38,7 +38,7 @@ %tr %th - Besoin concerné par l'offre + Besoin concerné par la proposition %th Fournisseur %th @@ -46,7 +46,7 @@ %th{style:"text-align:center;"} Clients Intéressés %th{style:"text-align:center;"} - Offres Acceptées + Propositions Acceptées %th{:style => "width:100px"}   @@ -57,7 +57,7 @@ .pagination.pull-right= paginate @offers .col-md-2 -if @need - =link_to "Ajouter une offre", new_admin_need_offer_path(@need),class:"btn btn-primary btn-block" + =link_to "Ajouter une proposition", new_admin_need_offer_path(@need),class:"btn btn-primary btn-block" %br = semantic_form_for :search, :html => {id: :search_form, :method => :get } do |f| = f.inputs do diff --git a/app/views/admin/offers/new.html.haml b/app/views/admin/offers/new.html.haml index d22684c..26aeaee 100755 --- a/app/views/admin/offers/new.html.haml +++ b/app/views/admin/offers/new.html.haml @@ -1,4 +1,4 @@ %h1 - Créer une offre pour le besoin + Créer une proposition pour le besoin %strong= @need.title =render :partial => "form" diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 3b8883d..62d0f42 100755 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -42,13 +42,17 @@ %li= link_to "Clients", admin_customers_path - unvalidated_need_count = Need.where(state: 'created').count -if unvalidated_need_count > 0 - %li= link_to content_tag(:span,unvalidated_need_count , class: 'badge') + " Besoins", admin_needs_path + %li= link_to content_tag(:span,unvalidated_need_count , style:"background-color:#D9534F", class: 'badge') + " Besoins", admin_needs_path -else %li= link_to " Besoins", admin_needs_path %li= link_to " Catégories", admin_need_categories_path - %li= link_to " Offres", admin_offers_path - + %li= link_to " Propositions", admin_offers_path + - unread_messages = ContactMessage.where(read_by_admin: false).count + -if unread_messages > 0 + %li=link_to content_tag(:span,unread_messages , style:"background-color:#D9534F", class: 'badge') + " Contacts", admin_conversations_path + -else + %li= link_to " Contacts", admin_conversations_path %ul.nav.navbar-nav.navbar-right diff --git a/app/views/public/accepted_offers/index.html.haml b/app/views/public/accepted_offers/index.html.haml new file mode 100755 index 0000000..81062c7 --- /dev/null +++ b/app/views/public/accepted_offers/index.html.haml @@ -0,0 +1,4 @@ + +.center.white.row + .row.gutter + %h1= i(:"file") + " Documents à retourner pour la proposition #{@accepted_offer.offer.need.title}" diff --git a/app/views/public/contact_messages/_contact_message.html.haml b/app/views/public/contact_messages/_contact_message.html.haml new file mode 100755 index 0000000..8fe7d60 --- /dev/null +++ b/app/views/public/contact_messages/_contact_message.html.haml @@ -0,0 +1,6 @@ +.white.padding.message-item{style:contact_message.admin ? "background-color:#b7e7ff" : "background-color:#e8e8e8"} + + %h4= i(:"user") + " " + contact_message.author_name + %p.info=i(:"clock-o") + " Posté le #{contact_message.created_at.strftime('%d/%m/%Y à %H:%M')}, il y a #{time_ago_in_words(contact_message.created_at)}" + + %p= contact_message.content diff --git a/app/views/public/contact_messages/index.html.haml b/app/views/public/contact_messages/index.html.haml new file mode 100755 index 0000000..37208f3 --- /dev/null +++ b/app/views/public/contact_messages/index.html.haml @@ -0,0 +1,17 @@ + +.center.white.row + .row.gutter + %h1= i(:"comments") + " Nous contacter" + = semantic_form_for [:public, @contact_message ], :html => {id: :contact_message_form, :method => :post } do |f| + + %h4 Écrire un nouveau message + = 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 + .padding + =render collection: @contact_messages, partial: 'contact_message' + + .pagination= paginate @contact_messages diff --git a/app/views/public/my_account/index.html.haml b/app/views/public/my_account/index.html.haml index 986e77b..1c77496 100755 --- a/app/views/public/my_account/index.html.haml +++ b/app/views/public/my_account/index.html.haml @@ -47,10 +47,10 @@ =link_to "Modifier mes infos", public_edit_infos_path, :class => "btn btn-primary" .padding.center.white %h3 - Mes offres acceptées + Mes propositions acceptées %div.alert.alert-info %p - Voici la liste de toutes les offres que vous avez acceptées. C'est ici que vous pouvez télécharger les devis quand ils sont disponibles. + Voici la liste de toutes les propositions que vous avez acceptées. C'est ici que vous pouvez télécharger les devis quand ils sont disponibles. %p Une fois le devis téléchargé, vous devez nous le renvoyer signé. @@ -59,7 +59,7 @@ .pagination= paginate @accepted_offers, param_name: 'page_offers' -else %p - Vous n'avez pas encore accepté d'offre + Vous n'avez pas encore accepté de proposition .padding.center.white %h3 Mes besoins @@ -67,9 +67,9 @@ %p Votre liste de besoins comprend tout les besoins pour lesquels vous avez signalé un intérêt. %p - Signaler votre intérêt vous permet d'acceder à des offres intéressantes si nous décidons par la suite de négocier ce besoin auprès de nos fournisseurs. + Signaler votre intérêt vous permet d'acceder à des propositions intéressantes si nous décidons par la suite de négocier ce besoin auprès de nos fournisseurs. %p - Signaler un intérêt ne vous engage en rien, vous serez engagé seulement après avoir accepté une offre que nous vous proposons. + Signaler un intérêt ne vous engage en rien, vous serez engagé seulement après avoir accepté une proposition que nous vous proposons. %p Remarque: Vous pouvez signaler/supprimer un intérêt pour un besoin uniquement quand celui-ci est en sondage. -if @wishes.length > 0 diff --git a/app/views/public/needs/_accepted_offer.html.haml b/app/views/public/needs/_accepted_offer.html.haml index 32492e0..f7ad432 100755 --- a/app/views/public/needs/_accepted_offer.html.haml +++ b/app/views/public/needs/_accepted_offer.html.haml @@ -1,4 +1,4 @@ --if(accepted_offer.devis_received?) +-if(accepted_offer.documents_completed?) -class_name="success" -else -class_name="warning" @@ -10,12 +10,7 @@ =link_to accepted_offer.offer.need.title, public_need_path(accepted_offer.offer.need) %td =number_to_currency(accepted_offer.offer.price, locale: :fr) - %td - =accepted_offer.offer.supplier %td{style: 'text-align:center'} =accepted_offer.human_state %td{style: 'text-align:right'} - -if accepted_offer.devis? - =link_to i(:"download") + " Télécharger", download_devis_public_accepted_offer_path(accepted_offer), class: "btn btn-primary" - -else - Pas encore disponible + =link_to i(:"file") + " #{accepted_offer.documents.where(state: :document_verified).count} / #{accepted_offer.documents.count}", public_documents_path(accepted_offer), class: "btn btn-sm btn-primary" diff --git a/app/views/public/needs/_accepted_offers_index.html.haml b/app/views/public/needs/_accepted_offers_index.html.haml index 5f97fb9..e43fd91 100755 --- a/app/views/public/needs/_accepted_offers_index.html.haml +++ b/app/views/public/needs/_accepted_offers_index.html.haml @@ -5,11 +5,10 @@ Titre du besoin %th Prix négocié - %th - Fournisseur + %th{style: 'text-align:center'} État %th{style: 'text-align:right'} - Devis + Documents à retourner %tbody =render partial: "public/needs/accepted_offer", collection: @accepted_offers, as: :accepted_offer diff --git a/app/views/public/needs/_need_item.html.haml b/app/views/public/needs/_need_item.html.haml index 72e771f..45111cb 100755 --- a/app/views/public/needs/_need_item.html.haml +++ b/app/views/public/needs/_need_item.html.haml @@ -58,7 +58,7 @@ -elsif(need.negociated?) -if(need.customers.include?(current_customer)) - =link_to i(:"download") +" Voir les offres", public_need_path(need), class: "btn btn-success pull-right" + =link_to i(:"download") +" Voir les propositions", public_need_path(need), class: "btn btn-success pull-right" -else =link_to i(:"times-circle") + " Trop tard pour vous !", public_need_path(need) , class: "btn btn-danger pull-right" diff --git a/app/views/public/needs/show.html.haml b/app/views/public/needs/show.html.haml index a8bff4a..86977f7 100755 --- a/app/views/public/needs/show.html.haml +++ b/app/views/public/needs/show.html.haml @@ -43,13 +43,13 @@ -if @need.customers.include?(current_customer) .alert.alert-success %h3= i(:"check") + ' Vous êtes intéressé par ce besoin' - %p Marquer votre interêt pour un besoin vous permettra d'accéder à des offres intéressantes si nous décidons par la suite de négocier ce besoin auprès de nos fournisseurs. + %p Marquer votre interêt pour un besoin vous permettra d'accéder à des propositions intéressantes si nous décidons par la suite de négocier ce besoin auprès de nos fournisseurs. -else .alert.alert-info %h3 Vous avez aussi ce besoin ? signalez-le nous ! %p Vous pouvez marquer votre interêt pour ce besoin en cliquant sur le bouton Ça m'intéresse ! - %p Si il y a un nombre suffisant de personnes avec ce même besoin, une négociation sera entamée auprès de nos fournisseurs afin de vous faire une offre au meilleur prix. - %p Vous ensuite libre d'accepter ou non l'offre + %p Si il y a un nombre suffisant de personnes avec ce même besoin, une négociation sera entamée auprès de nos fournisseurs afin de vous faire une proposition au meilleur prix. + %p Vous ensuite libre d'accepter ou non la proposition -elsif @need.negociating? -if @need.customers.include?(current_customer) @@ -57,8 +57,8 @@ %h3 Négociation en cours... %p Ce besoin a suscité un interêt suffisant pour que nous engagions une négociation afin de vous proposer cet article au meilleur prix. %p Vous avez marqué votre interêt pour ce besoin et vous serez donc prevenu dès que la négociation sera terminée. - %p Une ou plusieurs offres vous seront faites et vous serez libre de les accepter ou non. - %p Attention, accepter l'offre vous engage à payer. + %p Une ou plusieurs propositions vous seront faites et vous serez libre de les accepter ou non. + %p Attention, accepter la proposition vous engage à payer. -else .alert.alert-warning %h3 Négociation en cours... @@ -70,8 +70,8 @@ .alert.alert-success %h3 Négociation terminée %p= "Nous avons négocié ce besoin à partir de #{number_to_currency(offers.first.price, locale: :fr)}" - %p Vous avez maintenant le choix d'accepter ou refuser les offres proposées. - %p Attention, accepter l'offre vous engage à payer. + %p Vous avez maintenant le choix d'accepter ou refuser la proposition. + %p Attention, accepter la proposition vous engage à payer. -else .alert.alert-success %h3 Négociation terminée @@ -98,24 +98,23 @@ -elsif @need.negociated? -if @need.offers.length > 0 - %h2= i(:"gift") + " Les offres négociées" + %h2= i(:"gift") + " Les propositions négociées" -@need.offers.each do |offer| .offer .price =number_to_currency(offer.price, locale: :fr) - .supplier{style: "position:absolute;bottom:-5px;"} - ="Avec #{offer.supplier}" + -if offer.need.customers.include?(current_customer) -if !offer.customers.include?(current_customer) .accept-offer - =link_to i(:"check") + " Accepter l'offre", accept_public_need_offer_path(@need, offer), data: {confirm: "Voulez-vous vraiment accepter cette offre ? Attention, cette action vous engage à payer la somme proposée."}, class: "btn btn-lg btn-success " + =link_to i(:"check") + " Accepter la proposition", accept_public_need_offer_path(@need, offer), data: {confirm: "Voulez-vous vraiment accepter cette proposition ? Attention, cette action vous engage à payer la somme proposée."}, class: "btn btn-lg btn-success " -else .offer-accepted - =i(:"check") + " Offre Acceptée" + =i(:"check") + " Propositions Acceptée" .my-account-link - Consulter vos offres depuis la rubrique + Consulter vos propositions acceptées depuis la rubrique =link_to "Mon compte", public_my_account_path -else .offer-not-aceptable diff --git a/app/views/public/shared/_menu.html.haml b/app/views/public/shared/_menu.html.haml index 3dc4dac..0316d7e 100755 --- a/app/views/public/shared/_menu.html.haml +++ b/app/views/public/shared/_menu.html.haml @@ -5,6 +5,8 @@ %ul %li=link_to ic(:star)+" Besoins", public_needs_path, :class => "btn" + - unread_message = ContactMessage.where(contact_id: current_customer.id, read_by_customer: false).count + %li=link_to ic(:comment)+" Nous contacter" + (unread_message > 0 ? " (#{unread_message})" : ""), public_contact_messages_path, :class => "btn" %li=link_to ic(:user)+" Mon compte", public_my_account_path, :class => "btn" %li=link_to "Se déconnecter", logout_public_customers_auths_path, :class => "btn" diff --git a/config/routes.rb b/config/routes.rb index ccf186f..9f2b9bb 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -90,7 +90,8 @@ Rails.application.routes.draw do resources :accepted_offers do member do - get :download_devis + resources :documents + end end resources :needs do @@ -105,6 +106,10 @@ Rails.application.routes.draw do end end + resources :contact_messages do + + end + end @@ -265,13 +270,32 @@ Rails.application.routes.draw do get :reject end end + + resources :conversations do + + end + + resources :contact_messages do + + end resources :offers do resources :accepted_offers do + resources :documents do + post :upload_document + post :upload_returned_document + get :download + get :download_returned + get :delete + get :delete_returned + get :verify_returned + get :destroy + end member do post :upload_devis get :download get :delete get :received + end end diff --git a/db/migrate/20160307105901_create_contact_message.rb b/db/migrate/20160307105901_create_contact_message.rb new file mode 100755 index 0000000..43ea7c1 --- /dev/null +++ b/db/migrate/20160307105901_create_contact_message.rb @@ -0,0 +1,14 @@ +class CreateContactMessage < ActiveRecord::Migration + def change + create_table :contact_messages do |t| + t.references :customer + t.references :admin + t.references :contact + t.datetime :deleted_at + t.boolean :read_by_admin, default: false + t.boolean :read_by_customer, default: false + t.text :content + t.timestamps null: false + end + end +end diff --git a/db/migrate/20160308112733_create_document.rb b/db/migrate/20160308112733_create_document.rb new file mode 100755 index 0000000..576051b --- /dev/null +++ b/db/migrate/20160308112733_create_document.rb @@ -0,0 +1,13 @@ +class CreateDocument < ActiveRecord::Migration + def change + create_table :documents do |t| + t.references :accepted_offer + t.datetime :deleted_at + t.string :title + t.string :state + t.string :document + t.string :returned_document + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 53c35a0..1107aa4 100755 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160215170354) do +ActiveRecord::Schema.define(version: 20160308112733) do create_table "accepted_offers", force: :cascade do |t| t.datetime "created_at", null: false @@ -145,6 +145,18 @@ ActiveRecord::Schema.define(version: 20160215170354) do t.datetime "updated_at" end + create_table "contact_messages", force: :cascade do |t| + t.integer "customer_id", limit: 4 + t.integer "admin_id", limit: 4 + t.integer "contact_id", limit: 4 + t.datetime "deleted_at" + t.boolean "read_by_admin", limit: 1, default: false + t.boolean "read_by_customer", limit: 1, default: false + t.text "content", limit: 65535 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "customer_newsgroups", force: :cascade do |t| t.integer "customer_id", limit: 4 t.integer "newsgroup_id", limit: 4 @@ -224,6 +236,17 @@ ActiveRecord::Schema.define(version: 20160215170354) do t.datetime "updated_at" end + create_table "documents", force: :cascade do |t| + t.integer "accepted_offer_id", limit: 4 + t.datetime "deleted_at" + t.string "title", limit: 255 + t.string "state", limit: 255 + t.string "document", limit: 255 + t.string "returned_document", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "download_contents", force: :cascade do |t| t.string "title", limit: 255 t.string "style", limit: 255