diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index fe6904d..bc4ad8d 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -17,6 +17,7 @@ //= require bootstrap-datepicker //= require_tree . +// JQuery to choose the format of datepicker $(document).on("focus", "[data-behaviour~='datepicker']", function(e){ $(this).datepicker({"format": "dd-mm-yyyy", "weekStart": 1, diff --git a/app/assets/javascripts/pagination.js b/app/assets/javascripts/pagination.js index 5ebe995..40feaab 100644 --- a/app/assets/javascripts/pagination.js +++ b/app/assets/javascripts/pagination.js @@ -1,3 +1,5 @@ +// Used for ajax feature of pagination, used when user wants to navigate between pages +// pagination $(function() { $(document).on("click", ".pagination a", function() { $.get(this.href, null, null, "script"); diff --git a/app/assets/javascripts/todo_posts.js b/app/assets/javascripts/todo_posts.js index 7baffff..4532725 100644 --- a/app/assets/javascripts/todo_posts.js +++ b/app/assets/javascripts/todo_posts.js @@ -1,16 +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/ - -//$function() { - //$('a#complete_tick').click(function() { - //alert('clicked!'); - //}); -//} - -//$(function() { -// $(document).on("click", "a#complete_tick", function() { -// $.get(this.href, null, null, "script"); -// return false; -// }); -//}); diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss index af440a1..8ebf81f 100644 --- a/app/assets/stylesheets/custom.scss +++ b/app/assets/stylesheets/custom.scss @@ -309,6 +309,7 @@ aside { } } +// date picker div#datepicker.input-daterange.input-group { margin-bottom: 15px; } diff --git a/app/controllers/account_activations_controller.rb b/app/controllers/account_activations_controller.rb index 198d435..dfa6eb6 100644 --- a/app/controllers/account_activations_controller.rb +++ b/app/controllers/account_activations_controller.rb @@ -1,6 +1,10 @@ class AccountActivationsController < ApplicationController + # this action will be activated when user clicked on their account activate link that + # was being sent to their email. def edit user = User.find_by(email: params[:email]) + # if user exist and user not activated and user's credentials(meaning the activation link) + # are correct, then go ahead and activate the user's account. if user && !user.activated? && user.authenticated?(:activation, params[:id]) user.activate log_in user diff --git a/app/controllers/advanced_searches_controller.rb b/app/controllers/advanced_searches_controller.rb index a7c904b..4d31209 100644 --- a/app/controllers/advanced_searches_controller.rb +++ b/app/controllers/advanced_searches_controller.rb @@ -7,6 +7,7 @@ def new def create @user = current_user + # create a new entry of advanced_searches that is linked to the current_user @advanced_search = @user.advanced_searches.create!(search_params) redirect_to @advanced_search end @@ -14,13 +15,24 @@ def create def show @user = current_user @todo_post = @user.todo_posts.build + # @advanced_search represents the advanced_search queries that user entered @advanced_search = @user.advanced_searches.find(params[:id]) @header = "Advanced search" - @todo_posts_to_be_due = filter_tasks_to_be_due(@advanced_search.todo_posts).paginate(page: params[:to_be_due_page], per_page: 5) - @todo_posts_overdue = filter_tasks_overdue(@advanced_search.todo_posts).paginate(page: params[:overdue_page], per_page: 5) - @todo_posts_deferred = filter_tasks_deferred(@advanced_search.todo_posts).paginate(page: params[:deferred_page], per_page: 5) - @todo_posts_completed = filter_tasks_completed(@advanced_search.todo_posts).paginate(page: params[:completed_page], per_page: 5) + # 2 rounds of filtering are taking place here + # + # First is the @advanced_search.todo_posts, which uses the AdvancedSearch's + # method called "todo_posts" to filter out the todo_posts according the queries made + # by the user + # + # Next filter_task_* methods are methods by Application controller to filter out + # todo_posts according to their categories and they are subsequently displayed. + @todo_posts = @advanced_search.todo_posts + + @todo_posts_to_be_due = filter_tasks_to_be_due(@todo_posts).paginate(page: params[:to_be_due_page], per_page: 5) + @todo_posts_overdue = filter_tasks_overdue(@todo_posts).paginate(page: params[:overdue_page], per_page: 5) + @todo_posts_deferred = filter_tasks_deferred(@todo_posts).paginate(page: params[:deferred_page], per_page: 5) + @todo_posts_completed = filter_tasks_completed(@todo_posts).paginate(page: params[:completed_page], per_page: 5) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1f79f36..5af3782 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -18,6 +18,7 @@ def user_time_zone(&block) Time.use_zone(current_user.time_zone, &block) end + # The methods below are used to filter out todo_posts according to their categories def filter_tasks_to_be_due(todo_posts) todo_posts_to_be_due = todo_posts.where.not(due_date: nil) todo_posts_to_be_due = todo_posts_to_be_due.where(completed_at: nil) diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb index 3368db0..66a1156 100644 --- a/app/controllers/password_resets_controller.rb +++ b/app/controllers/password_resets_controller.rb @@ -1,3 +1,4 @@ +# This controller will be activated when user clicks on "Forgot password link" class PasswordResetsController < ApplicationController before_action :get_user, only: [:edit, :update] before_action :valid_user, only: [:edit, :update] diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index e54b198..aa78715 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,3 +1,4 @@ +# This controller is when user wants to log into his account. class SessionsController < ApplicationController # action for login page def new @@ -12,7 +13,7 @@ def create if user.activated? log_in user params[:session][:remember_me] == '1' ? remember(user) : forget(user) - redirect_back_or user + redirect_back_or root_url else message = "Account not activated." message += " Check your email for the activation link." diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index d50ca75..82f07b6 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -5,29 +5,44 @@ def home # for /share/todo_post_form @todo_post = @user.todo_posts.build + # 2 rounds of filtering are taking place in each case where user can choose to view + # the post in either "Filtered by tag" or "Filtered by simple search" or "Default + # view" which has no filter. + # + # The first round of filtering occurs according to the type of view the user choose + # + # Next filter_task_* methods are methods by Application controller to filter out + # todo_posts according to their categories and they are subsequently displayed. + if params[:tag] - @todo_posts_to_be_due = filter_tasks_to_be_due(TodoPost.tagged_with(params[:tag], owned_by: @user)).paginate(page: params[:to_be_due_page], per_page: 5) - @todo_posts_overdue = filter_tasks_overdue(TodoPost.tagged_with(params[:tag], owned_by: @user)).paginate(page: params[:overdue_page], per_page: 5) - @todo_posts_deferred = filter_tasks_deferred(TodoPost.tagged_with(params[:tag], owned_by: @user)).paginate(page: params[:deferred_page], per_page: 5) - @todo_posts_completed = filter_tasks_completed(TodoPost.tagged_with(params[:tag], owned_by: @user)).paginate(page: params[:completed_page], per_page: 5) + # Filter the todo_posts according to the choose tag which are owned by @user + @todo_posts = TodoPost.tagged_with(params[:tag], owned_by: @user); + + @todo_posts_to_be_due = filter_tasks_to_be_due(@todo_posts).paginate(page: params[:to_be_due_page], per_page: 5) + @todo_posts_overdue = filter_tasks_overdue(@todo_posts).paginate(page: params[:overdue_page], per_page: 5) + @todo_posts_deferred = filter_tasks_deferred(@todo_posts).paginate(page: params[:deferred_page], per_page: 5) + @todo_posts_completed = filter_tasks_completed(@todo_posts).paginate(page: params[:completed_page], per_page: 5) @header = "Category: " + params[:tag] elsif params[:search] - @todo_posts_to_be_due = filter_tasks_to_be_due(@user.search_task(params[:search])).paginate(page: params[:to_be_due_page], per_page: 5) - @todo_posts_overdue = filter_tasks_overdue(@user.search_task(params[:search])).paginate(page: params[:overdue_page], per_page: 5) - @todo_posts_deferred = filter_tasks_deferred(@user.search_task(params[:search])).paginate(page: params[:deferred_page], per_page: 5) - @todo_posts_completed = filter_tasks_completed(@user.search_task(params[:search])).paginate(page: params[:completed_page], per_page: 5) + # Filter the todo_posts according to the simple search params + @todo_posts = @user.search_task(params[:search]); + + @todo_posts_to_be_due = filter_tasks_to_be_due(@todo_posts).paginate(page: params[:to_be_due_page], per_page: 5) + @todo_posts_overdue = filter_tasks_overdue(@todo_posts).paginate(page: params[:overdue_page], per_page: 5) + @todo_posts_deferred = filter_tasks_deferred(@todo_posts).paginate(page: params[:deferred_page], per_page: 5) + @todo_posts_completed = filter_tasks_completed(@todo_posts).paginate(page: params[:completed_page], per_page: 5) @header = "Search: " + params[:search] else - @todo_posts_to_be_due = filter_tasks_to_be_due(@user.todo_posts).paginate(page: params[:to_be_due_page], per_page: 5) - @todo_posts_overdue = filter_tasks_overdue(@user.todo_posts).paginate(page: params[:overdue_page], per_page: 5) - @todo_posts_deferred = filter_tasks_deferred(@user.todo_posts).paginate(page: params[:deferred_page], per_page: 5) - @todo_posts_completed = filter_tasks_completed(@user.todo_posts).paginate(page: params[:completed_page], per_page: 5) + # No filter is being applied, will obtain all the posts by the user + @todo_posts = @user.todo_posts; + + @todo_posts_to_be_due = filter_tasks_to_be_due(@todo_posts).paginate(page: params[:to_be_due_page], per_page: 5) + @todo_posts_overdue = filter_tasks_overdue(@todo_posts).paginate(page: params[:overdue_page], per_page: 5) + @todo_posts_deferred = filter_tasks_deferred(@todo_posts).paginate(page: params[:deferred_page], per_page: 5) + @todo_posts_completed = filter_tasks_completed(@todo_posts).paginate(page: params[:completed_page], per_page: 5) @header = "TODO list(" + @user.todo_posts.count.to_s + ")" end end end - def contact - end - end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 905b4a4..2c20ce6 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,37 +3,42 @@ class UsersController < ApplicationController before_action :correct_user, only: [:show, :edit, :update] before_action :admin_user, only: [:index, :destroy] + # Action that is only used by admin, do view a list of users def index @users = User.where(activated: true).paginate(page: params[:page]) end + # To view the profile of individual user def show @user = User.find(params[:id]) redirect_to root_url and return if (!@user.activated?) @todo_posts = @user.todo_posts.paginate(page: params[:page]) end + # Used for signup page def new @user = User.new - @path_if_signup_fail = signup_path end + # Action triggered when user click on submit button on signup page. def create @user = User.new(user_params) + # If the information keyed in are valid, send activation link to user's email if @user.save @user.send_activation_email flash[:info] = "Please check your email to activate your account." redirect_to root_url else - @path_if_signup_fail = signup_path - render 'new' + render 'new' # else render the signup page, displaying the errors detected end end + # Action triggered when user wants to edit its own profile def edit @user = User.find(params[:id]) end + # Action will trigger when user click on the submit button on edit profile page def update @user = User.find(params[:id]) if @user.update_attributes(user_params) @@ -44,6 +49,7 @@ def update end end + # To destroy user account and all its related data, used by admin. def destroy User.find(params[:id]).destroy flash[:success] = "User deleted" diff --git a/app/models/advanced_search.rb b/app/models/advanced_search.rb index 820edec..30a6314 100644 --- a/app/models/advanced_search.rb +++ b/app/models/advanced_search.rb @@ -1,8 +1,9 @@ class AdvancedSearch < ApplicationRecord belongs_to :user, required: false + # method to call the private method to filter out the posts based on the queries. def todo_posts - @todo_posts ||= find_todo_posts + @todo_posts = find_todo_posts end def completed_true? @@ -21,6 +22,8 @@ def find_todo_posts filtered_posts = filtered_posts.where("due_date <= ?", due_date_end) if due_date_end.present? + # these 2 operations below will convert the filtered_posts from ActiveRecords to + # Array. if category.present? filtered_posts = filtered_posts.reject { |todo_post| !(todo_post.category.map(&:name).include? category) @@ -33,6 +36,7 @@ def find_todo_posts } end + # Will return as an ActiveRecord object return TodoPost.where(id: filtered_posts.map(&:id)) end diff --git a/app/models/user.rb b/app/models/user.rb index 9c9e0d9..4d73eeb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -14,18 +14,23 @@ class User < ApplicationRecord validates :email, presence: true, length: {maximum: 255}, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false} + has_secure_password # enable the user to use the authenticate method validates :password, presence: true, length: { minimum: 6 }, allow_nil: true - validates :time_zone, presence: true - validates :time_zone, inclusion: { in: ActiveSupport::TimeZone.all.map(&:name) } + # Ensure each user has an associated timezone, and the timezone exists in the list + # of timezones provided by rails + validates :time_zone, presence: true, + inclusion: { in: ActiveSupport::TimeZone.all.map(&:name) } + # To encrypt the "string" using BCrypt def User.digest(string) cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost BCrypt::Password.create(string, cost: cost) end + # produce a random string def User.new_token SecureRandom.urlsafe_base64 end @@ -55,6 +60,8 @@ def send_activation_email UserMailer.account_activation(self).deliver_now end + # To create an encrypted token to store in database when user opts to reset their password + # from the "forget password" link. def create_reset_digest self.reset_token = User.new_token self.update_columns(reset_digest: User.digest(reset_token), @@ -69,6 +76,7 @@ def password_reset_expired? reset_sent_at < 2.hours.ago end + # method for simple search def search_task(search_param) if search_param todo_posts.where("description LIKE ?", "%#{search_param}%") diff --git a/app/views/static_pages/_welcome_page.html.erb b/app/views/static_pages/_welcome_page.html.erb index e148270..3fc2e2d 100644 --- a/app/views/static_pages/_welcome_page.html.erb +++ b/app/views/static_pages/_welcome_page.html.erb @@ -1,10 +1,7 @@
-

Welcome to the TODO Manager App

+

Welcome

-

- This is the github page for the - TODO Manager App! -

+

A Todo task manager application.

<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
diff --git a/app/views/static_pages/contact.html.erb b/app/views/static_pages/contact.html.erb deleted file mode 100644 index e075879..0000000 --- a/app/views/static_pages/contact.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= provide(:title, "Contact") %> -

Contact

-

You cannot contact me

diff --git a/app/views/static_pages/home.js.erb b/app/views/static_pages/home.js.erb index 9dd8e16..5860cda 100644 --- a/app/views/static_pages/home.js.erb +++ b/app/views/static_pages/home.js.erb @@ -1 +1,2 @@ +// ajax for pagination $('#todo_posts').html('<%= escape_javascript(render("todo_posts/todo_posts")) %>'); diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index a26db8a..e53b07d 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -1,4 +1,4 @@ -<%= form_for(@user, url: @path_if_signup_fail || user_path(@user)) do |f| %> +<%= form_for(@user) do |f| %> <%= render 'shared/error_messages', object: f.object %> <%= f.label :name %> diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index e2cd9b9..a512289 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -4,6 +4,6 @@
- <%= render 'form', object: @path_to_light%> + <%= render 'form' %>
diff --git a/test/controllers/static_pages_controller_test.rb b/test/controllers/static_pages_controller_test.rb index 8a4495a..4378db2 100644 --- a/test/controllers/static_pages_controller_test.rb +++ b/test/controllers/static_pages_controller_test.rb @@ -10,11 +10,4 @@ def setup assert_response :success assert_select "title", "#{@base_title}" end - - test "should get contact" do - get contact_path - assert_response :success - assert_select "title", "Contact | #{@base_title}" - end - end diff --git a/test/integration/site_layout_test.rb b/test/integration/site_layout_test.rb index a256734..61fcd77 100644 --- a/test/integration/site_layout_test.rb +++ b/test/integration/site_layout_test.rb @@ -5,6 +5,5 @@ class SiteLayoutTest < ActionDispatch::IntegrationTest get root_path assert_template 'static_pages/home' assert_select "a[href=?]", root_path, count = 2 - assert_select "a[href=?]", contact_path end end diff --git a/test/integration/users_login_test.rb b/test/integration/users_login_test.rb index ae83407..c2334db 100644 --- a/test/integration/users_login_test.rb +++ b/test/integration/users_login_test.rb @@ -21,9 +21,9 @@ def setup post login_path, params: { session: { email: @user.email, password: 'password' } } assert is_logged_in? - assert_redirected_to @user + assert_redirected_to root_url follow_redirect! - assert_template 'users/show' + assert_template 'static_pages/home' assert_select "a[href=?]", login_path, count: 0 assert_select "a[href=?]", logout_path assert_select "a[href=?]", user_path(@user)