diff --git a/app/controllers/waiting_lists_controller.rb b/app/controllers/waiting_lists_controller.rb new file mode 100644 index 0000000..d71d6f1 --- /dev/null +++ b/app/controllers/waiting_lists_controller.rb @@ -0,0 +1,20 @@ +# app/controllers/waiting_lists_controller.rb +class WaitingListsController < ApplicationController + def create + @waiting_list = WaitingList.new(waiting_list_params) + + if @waiting_list.save + flash[:notice] = "Thanks for joining our waiting list!" + else + error_message = @waiting_list.errors.full_messages.to_sentence + flash[:alert] = "There was an error: #{error_message}" + end + redirect_to root_path + end + + private + + def waiting_list_params + params.require(:waiting_list).permit(:email) + end +end \ No newline at end of file diff --git a/app/helpers/waiting_lists_helper.rb b/app/helpers/waiting_lists_helper.rb new file mode 100644 index 0000000..2aa034e --- /dev/null +++ b/app/helpers/waiting_lists_helper.rb @@ -0,0 +1,2 @@ +module WaitingListsHelper +end diff --git a/app/jobs/send_waiting_list_report_job.rb b/app/jobs/send_waiting_list_report_job.rb new file mode 100644 index 0000000..3d987ee --- /dev/null +++ b/app/jobs/send_waiting_list_report_job.rb @@ -0,0 +1,8 @@ +# app/jobs/send_waiting_list_report_job.rb +class SendWaitingListReportJob < ApplicationJob + queue_as :default + + def perform + WaitingListMailer.daily_report('dean@deanlofts.xyz').deliver_now + end +end \ No newline at end of file diff --git a/app/mailers/waiting_list_mailer.rb b/app/mailers/waiting_list_mailer.rb new file mode 100644 index 0000000..02fde62 --- /dev/null +++ b/app/mailers/waiting_list_mailer.rb @@ -0,0 +1,13 @@ +# app/mailers/waiting_list_mailer.rb +class WaitingListMailer < ApplicationMailer + default from: 'loftwah@linkarooie.com' + + def daily_report(email) + @waiting_list = WaitingList.all + @total_count = @waiting_list.count + @today_count = @waiting_list.where('created_at >= ?', Time.zone.now.beginning_of_day).count + @url = 'https://linkarooie.com' + + mail(to: email, subject: 'Daily Waiting List Report - Linkarooie') + end +end \ No newline at end of file diff --git a/app/models/waiting_list.rb b/app/models/waiting_list.rb new file mode 100644 index 0000000..aa8eaf0 --- /dev/null +++ b/app/models/waiting_list.rb @@ -0,0 +1,6 @@ +# app/models/waiting_list.rb +class WaitingList < ApplicationRecord + validates :email, presence: true, + uniqueness: { case_sensitive: false }, + format: { with: URI::MailTo::EMAIL_REGEXP } +end \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 8a57041..9ec68d7 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -54,7 +54,7 @@
-
+
<% if notice %> + +
+
+

Join Our Waiting List

+

+ Be the first to know when Linkarooie is available. Enter your email to join our waiting list! +

+ <%= form_with model: WaitingList.new, url: waiting_lists_path, local: true, class: "flex flex-col items-center w-full max-w-md mx-auto" do |form| %> + <%= form.email_field :email, placeholder: "Enter your email", class: "p-2 w-full rounded bg-gray-700 text-white focus:outline-none focus:ring-2 focus:ring-lime-400 mb-4" %> + <%= form.submit "Join Now", class: "w-full bg-lime-500 hover:bg-lime-600 text-white font-bold py-2 px-4 rounded cursor-pointer transition duration-300" %> + <% end %> +
+
+
diff --git a/app/views/waiting_list_mailer/daily_report.html.erb b/app/views/waiting_list_mailer/daily_report.html.erb new file mode 100644 index 0000000..609523b --- /dev/null +++ b/app/views/waiting_list_mailer/daily_report.html.erb @@ -0,0 +1,63 @@ + + + + + + + + +
+

Daily Waiting List Report - Linkarooie

+ +
+

Total subscribers: <%= @total_count %>

+

New subscribers today: <%= @today_count %>

+
+ +

All Subscribers:

+
    + <% @waiting_list.each do |subscriber| %> +
  • <%= subscriber.email %> (joined: <%= subscriber.created_at.strftime('%B %d, %Y') %>)
  • + <% end %> +
+ + +
+ + \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 911a877..f5ccce9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,6 +14,8 @@ # Health check route get 'up' => 'rails/health#show', as: :rails_health_check + resources :waiting_lists, only: [:create] + # Root route root to: 'pages#home' diff --git a/config/sidekiq_scheduler.yml b/config/sidekiq_scheduler.yml index ad94670..ecb05a6 100644 --- a/config/sidekiq_scheduler.yml +++ b/config/sidekiq_scheduler.yml @@ -5,3 +5,7 @@ aggregate_metrics: backup_database: cron: '0 2 * * *' # Runs daily at 2 AM class: BackupDatabaseJob + +send_waiting_list_report: + cron: '0 3 * * *' # Runs daily at 3 AM + class: SendWaitingListReportJob \ No newline at end of file diff --git a/db/migrate/20240904121238_create_waiting_lists.rb b/db/migrate/20240904121238_create_waiting_lists.rb new file mode 100644 index 0000000..be3e691 --- /dev/null +++ b/db/migrate/20240904121238_create_waiting_lists.rb @@ -0,0 +1,9 @@ +class CreateWaitingLists < ActiveRecord::Migration[7.1] + def change + create_table :waiting_lists do |t| + t.string :email + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 02c5548..b6a6039 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_09_02_002819) do +ActiveRecord::Schema[7.1].define(version: 2024_09_04_121238) do create_table "achievement_views", force: :cascade do |t| t.integer "achievement_id", null: false t.integer "user_id", null: false @@ -141,6 +141,12 @@ t.index ["username"], name: "index_users_on_username", unique: true end + create_table "waiting_lists", force: :cascade do |t| + t.string "email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + add_foreign_key "achievement_views", "achievements" add_foreign_key "achievement_views", "users" add_foreign_key "achievements", "users" diff --git a/spec/controllers/waiting_lists_controller_spec.rb b/spec/controllers/waiting_lists_controller_spec.rb new file mode 100644 index 0000000..8bf6be9 --- /dev/null +++ b/spec/controllers/waiting_lists_controller_spec.rb @@ -0,0 +1,50 @@ +# spec/controllers/waiting_lists_controller_spec.rb +require 'rails_helper' + +RSpec.describe WaitingListsController, type: :controller do + describe "POST #create" do + context "with valid params" do + it "creates a new WaitingList" do + expect { + post :create, params: { waiting_list: attributes_for(:waiting_list) } + }.to change(WaitingList, :count).by(1) + end + + it "redirects to the root path with a success notice" do + post :create, params: { waiting_list: attributes_for(:waiting_list) } + expect(response).to redirect_to(root_path) + expect(flash[:notice]).to eq("Thanks for joining our waiting list!") + end + end + + context "with invalid params" do + it "does not create a new WaitingList" do + expect { + post :create, params: { waiting_list: { email: "invalid_email" } } + }.to_not change(WaitingList, :count) + end + + it "redirects to the root path with an error alert" do + post :create, params: { waiting_list: { email: "invalid_email" } } + expect(response).to redirect_to(root_path) + expect(flash[:alert]).to include("There was an error") + end + end + + context "with duplicate email" do + before { create(:waiting_list, email: "test@example.com") } + + it "does not create a new WaitingList" do + expect { + post :create, params: { waiting_list: { email: "test@example.com" } } + }.to_not change(WaitingList, :count) + end + + it "redirects to the root path with an error alert" do + post :create, params: { waiting_list: { email: "test@example.com" } } + expect(response).to redirect_to(root_path) + expect(flash[:alert]).to include("Email has already been taken") + end + end + end +end \ No newline at end of file diff --git a/spec/factories/waiting_lists.rb b/spec/factories/waiting_lists.rb new file mode 100644 index 0000000..ed302e2 --- /dev/null +++ b/spec/factories/waiting_lists.rb @@ -0,0 +1,6 @@ +# spec/factories/waiting_lists.rb +FactoryBot.define do + factory :waiting_list do + sequence(:email) { |n| "user#{n}@example.com" } + end +end \ No newline at end of file diff --git a/spec/helpers/waiting_lists_helper_spec.rb b/spec/helpers/waiting_lists_helper_spec.rb new file mode 100644 index 0000000..4abd8d2 --- /dev/null +++ b/spec/helpers/waiting_lists_helper_spec.rb @@ -0,0 +1,6 @@ +# spec/helpers/waiting_lists_helper_spec.rb +require 'rails_helper' + +RSpec.describe WaitingListsHelper, type: :helper do + # Add helper specs here if you create any helper methods in the future +end \ No newline at end of file diff --git a/spec/models/waiting_list_spec.rb b/spec/models/waiting_list_spec.rb new file mode 100644 index 0000000..567942e --- /dev/null +++ b/spec/models/waiting_list_spec.rb @@ -0,0 +1,26 @@ +# spec/models/waiting_list_spec.rb +require 'rails_helper' + +RSpec.describe WaitingList, type: :model do + it "has a valid factory" do + expect(build(:waiting_list)).to be_valid + end + + describe "validations" do + it { should validate_presence_of(:email) } + it { should validate_uniqueness_of(:email).case_insensitive } + + it "validates email format" do + valid_emails = ["user@example.com", "USER@foo.COM", "A_US-ER@foo.bar.org"] + invalid_emails = ["user@example,com", "user_at_foo.org", "user.name@example.", "foo@bar_baz.com", "foo@bar+baz.com"] + + valid_emails.each do |email| + expect(build(:waiting_list, email: email)).to be_valid + end + + invalid_emails.each do |email| + expect(build(:waiting_list, email: email)).to be_invalid + end + end + end +end \ No newline at end of file diff --git a/spec/requests/waiting_lists_spec.rb b/spec/requests/waiting_lists_spec.rb new file mode 100644 index 0000000..29cd65a --- /dev/null +++ b/spec/requests/waiting_lists_spec.rb @@ -0,0 +1,44 @@ +# spec/requests/waiting_lists_spec.rb +require 'rails_helper' + +RSpec.describe "WaitingLists", type: :request do + describe "POST /waiting_lists" do + context "with valid parameters" do + it "creates a new waiting list entry" do + expect { + post waiting_lists_path, params: { waiting_list: { email: "test@example.com" } } + }.to change(WaitingList, :count).by(1) + + expect(response).to redirect_to(root_path) + follow_redirect! + expect(response.body).to include("Thanks for joining our waiting list!") + end + end + + context "with invalid parameters" do + it "does not create a new waiting list entry" do + expect { + post waiting_lists_path, params: { waiting_list: { email: "invalid_email" } } + }.not_to change(WaitingList, :count) + + expect(response).to redirect_to(root_path) + follow_redirect! + expect(response.body).to include("There was an error") + end + end + + context "with duplicate email" do + before { WaitingList.create(email: "test@example.com") } + + it "does not create a new waiting list entry" do + expect { + post waiting_lists_path, params: { waiting_list: { email: "test@example.com" } } + }.not_to change(WaitingList, :count) + + expect(response).to redirect_to(root_path) + follow_redirect! + expect(response.body).to include("Email has already been taken") + end + end + end +end \ No newline at end of file