<%= form_for(@classified_listing) do |f| %>
+
- <% end %>
- <%= form_for(@classified_listing) do |f| %>
-
<% end %>
+ <% if @classified_listing.bumped_at %>
+ <%= form_for(@classified_listing) do |f| %>
+
+
+ <% if (@classified_listing.natural_expiration_date) < Date.today %>
+
+ (Expired <%= time_ago_in_words @classified_listing.natural_expiration_date %> ago)
+
+ <% else %>
+
+ (Expires in <%= time_ago_in_words @classified_listing.natural_expiration_date %>)
+
+ <% end %>
+
+ <%= f.submit "Bump Listing", class: "cta cta-main-listing-form" %>
+
+ <% end %>
+ <% end %>
<%= form_for(@classified_listing) do |f| %>
-
- Published: <%= @classified_listing.published %>
-
+ <% unless @classified_listing.published %>
+
+ This listing is not published
+
+ <% end %>
<% if @classified_listing.published == false %>
- <%#
- <%= f.submit "Publish Listing", class: "classified-listings-publish cta-main-listing-form" %>
- <%# <% else %>
+
+ <%= f.submit "Publish Listing", class: "cta classified-listings-publish cta-main-listing-form" %>
+ <% else %>
- <%= f.submit "Unpublish Listing", class: "classified-listings-unpublish cta-main-listing-form" %>
+ <%= f.submit "Unpublish Listing", class: "cta classified-listings-unpublish cta-main-listing-form" %>
<% end %>
<% end %>
diff --git a/app/views/classified_listings/new.html.erb b/app/views/classified_listings/new.html.erb
index 89046bcfa..be1d97c50 100644
--- a/app/views/classified_listings/new.html.erb
+++ b/app/views/classified_listings/new.html.erb
@@ -1,6 +1,5 @@
<% title "New Listing" %>
-
- <%= render 'form', classified_listing: @classified_listing %>
+
+ <%= render "form", classified_listing: @classified_listing %>
-
diff --git a/app/views/credits/index.html.erb b/app/views/credits/index.html.erb
index a1c715ce7..0459e4aea 100644
--- a/app/views/credits/index.html.erb
+++ b/app/views/credits/index.html.erb
@@ -34,6 +34,9 @@
Create a Listing
+
+ Go to Listings Dashboard
+
<% if @organizations.present? %>
diff --git a/app/views/dashboards/show.html.erb b/app/views/dashboards/show.html.erb
index 1f88598e8..3777a16de 100644
--- a/app/views/dashboards/show.html.erb
+++ b/app/views/dashboards/show.html.erb
@@ -23,6 +23,9 @@
Upload a Video 🎥
<% end %>
+
+ Create/Manage Listings
+
<%= select_tag "dashhboard_sort", options_for_select(sort_options, params[:sort]) %>
<% if @articles.any? {|article| article.archived } %>
<%= link_to "Show archived", "javascript:;", id: "toggleArchivedLink" %>
diff --git a/app/views/internal/classified_listings/index.html.erb b/app/views/internal/classified_listings/index.html.erb
index 576254916..176a49e81 100644
--- a/app/views/internal/classified_listings/index.html.erb
+++ b/app/views/internal/classified_listings/index.html.erb
@@ -28,7 +28,7 @@
<%= listing.category %>
<%= listing.cached_tag_list %>
<%= listing.published ? "Yes" : "No" %>
-
<%= time_ago_in_words(listing.bumped_at) %> ago
+
<%= listing.bumped_at ? time_ago_in_words(listing.bumped_at) + " ago" : "Draft" %>
diff --git a/config/routes.rb b/config/routes.rb
index 92c5753b3..1225b1f81 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -159,7 +159,7 @@ Rails.application.routes.draw do
resources :tag_adjustments, only: %i[create destroy]
resources :rating_votes, only: [:create]
resources :page_views, only: %i[create update]
- resources :classified_listings, path: :listings, only: %i[index new create edit update delete dashboard]
+ resources :classified_listings, path: :listings, only: %i[index new create edit update destroy dashboard]
resources :credits, only: %i[index new create] do
get "purchase", on: :collection, to: "credits#new"
end
@@ -177,6 +177,8 @@ Rails.application.routes.draw do
get "/listings/:category/:slug" => "classified_listings#index", as: :classified_listing_slug
get "/listings/:category/:slug/:view" => "classified_listings#index",
constraints: { view: /moderate/ }
+ get "/listings/:category/:slug/delete_confirm" => "classified_listings#delete_confirm"
+ delete "/listings/:category/:slug" => "classified_listings#destroy"
get "/notifications/:filter" => "notifications#index"
get "/notifications/:filter/:org_id" => "notifications#index"
get "/notification_subscriptions/:notifiable_type/:notifiable_id" => "notification_subscriptions#show"
diff --git a/spec/requests/classified_listings_spec.rb b/spec/requests/classified_listings_spec.rb
index 6af0c4fb1..79231e7c2 100644
--- a/spec/requests/classified_listings_spec.rb
+++ b/spec/requests/classified_listings_spec.rb
@@ -13,6 +13,18 @@ RSpec.describe "ClassifiedListings", type: :request do
}
}
end
+ let(:draft_params) do
+ {
+ classified_listing: {
+ title: "this a draft",
+ body_markdown: "something draft",
+ category: "cfp",
+ tag_list: "",
+ contact_via_connect: true,
+ action: "draft"
+ }
+ }
+ end
describe "GET /listings" do
let(:listing) { create(:classified_listing, user: user) }
@@ -56,6 +68,11 @@ RSpec.describe "ClassifiedListings", type: :request do
describe "GET /listings/new" do
before { sign_in user }
+ it "shows the option to create draft" do
+ get "/listings/new"
+ expect(response.body).to include "You will not be charged credits to save a draft."
+ end
+
context "when the user has no credits" do
it "shows the proper messages" do
get "/listings/new"
@@ -154,6 +171,11 @@ RSpec.describe "ClassifiedListings", type: :request do
expect(response.body).to redirect_to("/credits")
end
+ it "redirects to /listings/dashboard for listing draft" do
+ post "/listings", params: draft_params
+ expect(response).to redirect_to "/listings/dashboard"
+ end
+
it "redirects to /listings" do
post "/listings", params: listing_params
expect(response).to redirect_to "/listings"
@@ -165,6 +187,16 @@ RSpec.describe "ClassifiedListings", type: :request do
expect(user.credits.spent.size).to eq(listing_cost)
end
+ it "creates a listing draft under the org" do
+ org_admin = create(:user, :org_admin)
+ org_id = org_admin.organizations.first.id
+ Credit.create(organization_id: org_id)
+ draft_params[:classified_listing][:organization_id] = org_id
+ sign_in org_admin
+ post "/listings", params: draft_params
+ expect(ClassifiedListing.first.organization_id).to eq org_id
+ end
+
it "creates a listing under the org" do
org_admin = create(:user, :org_admin)
org_id = org_admin.organizations.first.id
@@ -175,6 +207,12 @@ RSpec.describe "ClassifiedListings", type: :request do
expect(ClassifiedListing.first.organization_id).to eq org_id
end
+ it "does not create a listing draft for an org not belonging to the user" do
+ org = create(:organization)
+ draft_params[:classified_listing][:organization_id] = org.id
+ expect { post "/listings", params: draft_params }.to raise_error(Pundit::NotAuthorizedError)
+ end
+
it "does not create a listing for an org not belonging to the user" do
org = create(:organization)
listing_params[:classified_listing][:organization_id] = org.id
@@ -188,6 +226,14 @@ RSpec.describe "ClassifiedListings", type: :request do
expect(spent_credit.spent_at).not_to be_nil
end
+ it "creates listing draft and does not subtract credits" do
+ allow(Credits::Buyer).to receive(:call).and_raise(ActiveRecord::Rollback)
+ expect do
+ post "/listings", params: draft_params
+ end.to change(ClassifiedListing, :count).by(1).
+ and change(user.credits.spent, :size).by(0)
+ end
+
it "does not create a listing or subtract credits if the purchase does not go through" do
allow(Credits::Buyer).to receive(:call).and_raise(ActiveRecord::Rollback)
expect do
@@ -209,11 +255,15 @@ RSpec.describe "ClassifiedListings", type: :request do
describe "PUT /listings/:id" do
let(:listing) { create(:classified_listing, user: user) }
+ let(:listing_draft) { create(:classified_listing, user: user) }
let(:organization) { create(:organization) }
let(:org_listing) { create(:classified_listing, user: user, organization: organization) }
+ let(:org_listing_draft) { create(:classified_listing, user: user, organization: organization) }
before do
sign_in user
+ listing_draft.update_columns(bumped_at: nil, published: false)
+ org_listing_draft.update_columns(bumped_at: nil, published: false)
end
context "when the bump action is called" do
@@ -272,5 +322,156 @@ RSpec.describe "ClassifiedListings", type: :request do
expect(org_listing.reload.bumped_at >= previous_bumped_at).to eq(true)
end
end
+
+ context "when the publish action is called" do
+ let(:params) { { classified_listing: { action: "publish" } } }
+
+ it "publishes a draft and charges user credits if first publish" do
+ cost = ClassifiedListing.cost_by_category(listing_draft.category)
+ create_list(:credit, cost, user: user)
+ expect do
+ put "/listings/#{listing_draft.id}", params: params
+ end.to change(user.credits.spent, :size).by(cost)
+ end
+
+ it "publishes a draft and ensures published column is true" do
+ cost = ClassifiedListing.cost_by_category(listing_draft.category)
+ create_list(:credit, cost, user: user)
+ put "/listings/#{listing_draft.id}", params: params
+ expect(listing_draft.reload.published).to eq(true)
+ end
+
+ it "publishes an org draft and charges org credits if first publish" do
+ cost = ClassifiedListing.cost_by_category(org_listing_draft.category)
+ create_list(:credit, cost, organization: organization)
+ expect do
+ put "/listings/#{org_listing_draft.id}", params: params
+ end.to change(organization.credits.spent, :size).by(cost)
+ end
+
+ it "publishes an org draft and ensures published column is true" do
+ cost = ClassifiedListing.cost_by_category(org_listing_draft.category)
+ create_list(:credit, cost, organization: organization)
+ put "/listings/#{org_listing_draft.id}", params: params
+ expect(org_listing_draft.reload.published).to eq(true)
+ end
+
+ it "publishes a draft that was charged and is within 30 days of bump doesn't charge credits" do
+ listing.update_column(:published, false)
+ expect do
+ put "/listings/#{listing.id}", params: params
+ end.to change(user.credits.spent, :size).by(0)
+ end
+
+ it "publishes a draft that was charged and is within 30 days of bump and successfully sets published as true" do
+ listing.update_column(:published, false)
+ put "/listings/#{listing.id}", params: params
+ expect(listing.reload.published).to eq(true)
+ end
+
+ it "fails to publish draft and doesn't charge credits" do
+ expect do
+ put "/listings/#{listing_draft.id}", params: params
+ end.to change(user.credits.spent, :size).by(0)
+ end
+
+ it "fails to publish draft and published remains false" do
+ put "/listings/#{listing_draft.id}", params: params
+ expect(listing_draft.reload.published).to eq(false)
+ end
+ end
+
+ context "when the unpublish action is called" do
+ let(:params) { { classified_listing: { action: "unpublish" } } }
+
+ it "unpublishes a published listing" do
+ put "/listings/#{listing.id}", params: params
+ expect(listing.reload.published).to eq(false)
+ end
+ end
+ end
+
+ describe "DEL /listings/:id" do
+ let!(:listing) { create(:classified_listing, user: user) }
+ let!(:listing_draft) { create(:classified_listing, user: user, bumped_at: nil, published: false) }
+ let(:organization) { create(:organization) }
+ let!(:org_listing) { create(:classified_listing, user: user, organization: organization) }
+ let!(:org_listing_draft) { create(:classified_listing, user: user, organization: organization, bumped_at: nil, published: false) }
+
+ before do
+ sign_in user
+ end
+
+ context "when deleting draft" do
+ it "redirect to dashboard" do
+ delete "/listings/#{listing_draft.id}"
+ expect(response).to redirect_to("/listings/dashboard")
+ end
+
+ it "have status 302 found" do
+ delete "/listings/#{listing_draft.id}"
+ expect(response).to have_http_status(:found)
+ end
+
+ it "decrease total listings count by 1" do
+ expect do
+ delete "/listings/#{listing_draft.id}"
+ end.to change(ClassifiedListing, :count).by(-1)
+ end
+ end
+
+ context "when deleting listing" do
+ it "redirect to dashboard" do
+ delete "/listings/#{listing.id}"
+ expect(response).to redirect_to("/listings/dashboard")
+ end
+
+ it "have status 302 found" do
+ delete "/listings/#{listing.id}"
+ expect(response).to have_http_status(:found)
+ end
+
+ it "decrease total listings count by 1" do
+ expect do
+ delete "/listings/#{listing.id}"
+ end.to change(ClassifiedListing, :count).by(-1)
+ end
+ end
+
+ context "when deleting org listing" do
+ it "redirect to dashboard" do
+ delete "/listings/#{org_listing_draft.id}"
+ expect(response).to redirect_to("/listings/dashboard")
+ end
+
+ it "have status 302 found" do
+ delete "/listings/#{org_listing_draft.id}"
+ expect(response).to have_http_status(:found)
+ end
+
+ it "decrease total listings count by 1" do
+ expect do
+ delete "/listings/#{org_listing_draft.id}"
+ end.to change(ClassifiedListing, :count).by(-1)
+ end
+ end
+
+ context "when deleting org listing" do
+ it "redirect to dashboard" do
+ delete "/listings/#{org_listing.id}"
+ expect(response).to redirect_to("/listings/dashboard")
+ end
+
+ it "have status 302 found" do
+ delete "/listings/#{org_listing.id}"
+ expect(response).to have_http_status(:found)
+ end
+
+ it "decrease total listings count by 1" do
+ expect do
+ delete "/listings/#{org_listing.id}"
+ end.to change(ClassifiedListing, :count).by(-1)
+ end
+ end
end
end