From b20288618072b8b5a0ec72a779ee3afe78f90054 Mon Sep 17 00:00:00 2001 From: Tony Gaeta Date: Tue, 16 Jul 2024 15:59:38 -0400 Subject: [PATCH 1/9] save progress --- .../job_applications_controller.rb | 38 +++++-- app/views/job_applications/_form.html.erb | 104 ++++++++++-------- app/views/job_applications/edit.html.erb | 9 +- app/views/job_applications/index.html.erb | 32 +++--- app/views/job_applications/new.html.erb | 12 +- 5 files changed, 108 insertions(+), 87 deletions(-) diff --git a/app/controllers/job_applications_controller.rb b/app/controllers/job_applications_controller.rb index 2139285..1477ca5 100644 --- a/app/controllers/job_applications_controller.rb +++ b/app/controllers/job_applications_controller.rb @@ -1,5 +1,5 @@ class JobApplicationsController < ApplicationController - before_action :set_job_application, only: [:create, :update, :destroy] + before_action :set_job_application, only: [:edit, :update, :destroy] def index @job_applications = filter_and_sort_job_applications @@ -22,6 +22,18 @@ def index def new @job_application = JobApplication.new + respond_to do |format| + format.html + format.turbo_stream { render turbo_stream: turbo_stream.replace("new_job_application", partial: "form", locals: {job_application: @job_application, title: "New"}) } + end + end + + def edit + @job_application = JobApplication.find_by(id: params[:id]) + if @job_application.nil? + Rails.logger.error "Job Application with id #{params[:id]} not found" + redirect_to root_path, alert: "Job Application not found" + end end def create @@ -29,18 +41,23 @@ def create respond_to do |format| if @job_application.save - format.html { redirect_to job_applications_path, notice: "Job application was successfully created." } + format.html { redirect_to root_path, notice: "Job application was successfully created." } format.turbo_stream { flash.now[:notice] = "Job application was successfully created." render turbo_stream: [ turbo_stream.prepend("job_applications", partial: "job_application", locals: {job_application: @job_application}), - turbo_stream.update("flash_messages", partial: "flash_messages"), - turbo_stream.replace("new_job_application", partial: "form", locals: {job_application: JobApplication.new}) + turbo_stream.update("job_application_count", JobApplication.count), + turbo_stream.update("flash_messages", partial: "flash_messages") ] } else format.html { render :new, status: :unprocessable_entity } - format.turbo_stream { render turbo_stream: turbo_stream.replace(@job_application, partial: "form", locals: {job_application: @job_application}) } + format.turbo_stream { + render turbo_stream: [ + turbo_stream.replace("new_job_application", partial: "form", locals: {job_application: @job_application, title: "New"}), + turbo_stream.update("flash_messages", partial: "flash_messages") + ] + } end end end @@ -48,7 +65,7 @@ def create def update respond_to do |format| if @job_application.update(job_application_params) - format.html { redirect_to job_applications_path, notice: "Job application was successfully updated." } + format.html { redirect_to root_path, notice: "Job application was successfully updated." } format.turbo_stream { flash.now[:notice] = "Job application was successfully updated." render turbo_stream: [ @@ -58,7 +75,12 @@ def update } else format.html { render :edit, status: :unprocessable_entity } - format.turbo_stream { render turbo_stream: turbo_stream.replace(@job_application, partial: "form", locals: {job_application: @job_application}) } + format.turbo_stream { + render turbo_stream: [ + turbo_stream.replace(dom_id(@job_application), partial: "form", locals: {job_application: @job_application, title: "Edit"}), + turbo_stream.update("flash_messages", partial: "flash_messages") + ] + } end end end @@ -66,7 +88,7 @@ def update def destroy @job_application.destroy respond_to do |format| - format.html { redirect_to job_applications_url, notice: "Job application was successfully deleted." } + format.html { redirect_to root_path, notice: "Job application was successfully deleted." } format.turbo_stream { flash.now[:notice] = "Job application was successfully deleted." render turbo_stream: [ diff --git a/app/views/job_applications/_form.html.erb b/app/views/job_applications/_form.html.erb index a7909aa..fe1777b 100644 --- a/app/views/job_applications/_form.html.erb +++ b/app/views/job_applications/_form.html.erb @@ -1,51 +1,63 @@ <%= turbo_frame_tag dom_id(job_application) do %> - <%= form_with(model: job_application, local: false) do |form| %> - <% if job_application.errors.any? %> - - <% end %> -
- <%= form.label :date_applied, class: 'block text-gray-700 text-sm font-bold mb-2' %> - <%= form.date_field :date_applied, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %> -
-
- <%= form.label :company_name, class: 'block text-gray-700 text-sm font-bold mb-2' %> - <%= form.text_field :company_name, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %> -
-
- <%= form.label :method_of_contact, class: 'block text-gray-700 text-sm font-bold mb-2' %> - <%= form.select :method_of_contact, JobApplication.method_of_contacts.keys.map { |k| [k.humanize, k] }, { include_blank: 'Select a method' }, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %> -
-
- <%= form.label :email_address, class: 'block text-gray-700 text-sm font-bold mb-2' %> - <%= form.email_field :email_address, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %> -
-
- <%= form.label :point_of_contact, class: 'block text-gray-700 text-sm font-bold mb-2' %> - <%= form.text_field :point_of_contact, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %> -
-
- <%= form.label :website_link, class: 'block text-gray-700 text-sm font-bold mb-2' %> - <%= form.url_field :website_link, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %> -
-
- <%= form.label :position_type, class: 'block text-gray-700 text-sm font-bold mb-2' %> - <%= form.select :position_type, JobApplication.position_types.keys.map { |k| [k.humanize, k] }, { include_blank: 'Select a type' }, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %> -
-
- <%= form.label :position_title, class: 'block text-gray-700 text-sm font-bold mb-2' %> - <%= form.text_field :position_title, class: 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline' %> -
-
- <%= form.submit class: 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline cursor-pointer' %> - <%= link_to 'Back', root_path, class: 'inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800' %>
- <% end %> + <% end %> diff --git a/app/views/job_applications/edit.html.erb b/app/views/job_applications/edit.html.erb index 565366b..839e7c2 100644 --- a/app/views/job_applications/edit.html.erb +++ b/app/views/job_applications/edit.html.erb @@ -1,10 +1,3 @@ <%= turbo_frame_tag dom_id(@job_application) do %> -
-
-

Edit Job Application

-
- <%= render 'form', job_application: @job_application %> - <%= link_to 'Back', root_path, class: 'inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800' %> - <%= link_to 'Back to Job Applications', job_applications_path, class: 'mt-4 inline-block text-blue-500 hover:text-blue-700' %> -
+ <%= render 'form', job_application: @job_application, title: "Edit" %> <% end %> diff --git a/app/views/job_applications/index.html.erb b/app/views/job_applications/index.html.erb index dcd8ba9..e229abb 100644 --- a/app/views/job_applications/index.html.erb +++ b/app/views/job_applications/index.html.erb @@ -1,36 +1,36 @@ -
- <%= turbo_frame_tag "flash_messages" do %> - <%= render 'flash_messages' %> - <% end %> +
- <%= link_to 'Add New Job Application', new_job_application_path, class: 'text-sm bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded cursor-pointer', data: { turbo_frame: "_top" } %> + <%= link_to 'New Job Application', new_job_application_path, class: 'text-sm bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded cursor-pointer', data: { turbo_frame: "_top" } %>
<%= form_with url: job_applications_path, method: :get, data: { controller: "job-filter", turbo_frame: "job_applications_table" } do |form| %>
+ <%= form.label :search, "Search", class: "sr-only" %> <%= form.text_field :search, value: params[:search], class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline", placeholder: "Search by company or position", data: { action: "input->job-filter#submit" } %>
+ <%= form.label :method_of_contact, "Contact Method", class: "sr-only" %> <%= form.select :method_of_contact, - options_for_select([["All Contact Methods", ""]] + JobApplication.method_of_contacts.map { |k, v| [k.humanize, v] }, params[:method_of_contact]), - {}, - class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline", - data: { action: "change->job-filter#submit" } - %> + options_for_select([["All Contact Methods", ""]] + JobApplication.method_of_contacts.map { |k, v| [k.humanize, v] }, params[:method_of_contact]), + {}, + class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline", + data: { action: "change->job-filter#submit" } + %>
+ <%= form.label :position_type, "Position Type", class: "sr-only" %> <%= form.select :position_type, - options_for_select([["All Position Types", ""]] + JobApplication.position_types.map { |k, v| [k.humanize, v] }, params[:position_type]), - {}, - class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline", - data: { action: "change->job-filter#submit" } - %> + options_for_select([["All Position Types", ""]] + JobApplication.position_types.map { |k, v| [k.humanize, v] }, params[:position_type]), + {}, + class: "shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline", + data: { action: "change->job-filter#submit" } + %>
- <%= form.button "Reset Filters", type: "button", class: "w-full bg-gray-400 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded cursor-pointer", data: { action: "click->job-filter#reset" } %> + <%= form.button "Reset Filters", type: "button", class: "w-full bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded cursor-pointer", data: { action: "click->job-filter#reset" } %>
<% end %> diff --git a/app/views/job_applications/new.html.erb b/app/views/job_applications/new.html.erb index f667adf..f992cab 100644 --- a/app/views/job_applications/new.html.erb +++ b/app/views/job_applications/new.html.erb @@ -1,9 +1,3 @@ -
-
-

New Job Application

-
- <%= turbo_frame_tag "new_job_application" do %> - <%= render 'form', job_application: @job_application %> - <% end %> - <%= link_to 'Back to Job Applications', job_applications_path, class: 'mt-4 inline-block text-blue-500 hover:text-blue-700' %> -
+<%= turbo_frame_tag "new_job_application" do %> + <%= render 'form', job_application: @job_application, title: "New" %> +<% end %> From a34d4b72b2c68b7915bd047a8c6dd81f065f65f4 Mon Sep 17 00:00:00 2001 From: Tony Gaeta Date: Tue, 16 Jul 2024 18:15:14 -0400 Subject: [PATCH 2/9] fit the whole table in the viewport --- app/frontend/stylesheets/index.css | 8 +++++ .../_job_application.html.erb | 6 ++-- .../_job_applications_table.html.erb | 24 ++++++------- .../job_applications/_pagination.html.erb | 35 ++++++++++--------- app/views/layouts/application.html.erb | 2 +- 5 files changed, 42 insertions(+), 33 deletions(-) diff --git a/app/frontend/stylesheets/index.css b/app/frontend/stylesheets/index.css index 4e88c4c..4343340 100644 --- a/app/frontend/stylesheets/index.css +++ b/app/frontend/stylesheets/index.css @@ -24,3 +24,11 @@ body { background-position: center; background-attachment: fixed; } + +td { + font-size: 12px !important; +} + +th { + font-size: 12px !important; +} diff --git a/app/views/job_applications/_job_application.html.erb b/app/views/job_applications/_job_application.html.erb index 5483e63..b6c5de1 100644 --- a/app/views/job_applications/_job_application.html.erb +++ b/app/views/job_applications/_job_application.html.erb @@ -26,14 +26,14 @@ safe_url(job_application.website_link), target: "_blank", rel: "noopener noreferrer", - class: "px-3 py-1 rounded-md text-sm font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50", + class: "px-3 py-1 rounded-md text-xs text-gray-700 bg-white border border-gray-300 hover:bg-gray-50", title: "Visit Website" %> <% end %>
- <%= link_to 'Edit', edit_job_application_path(job_application), class: 'px-3 py-1 rounded-md text-sm font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 mr-2', data: { turbo_frame: "_top" }, title: "Edit Application" %> - <%= button_to 'Delete', job_application_path(job_application), method: :delete, form: { data: { turbo_confirm: 'Are you sure?' } }, class: 'px-3 py-1 rounded-md text-sm font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50', data: { turbo_frame: "_top" }, title: "Delete Application" %> + <%= link_to 'Edit', edit_job_application_path(job_application), class: 'px-3 py-1 rounded-md text-xs text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 mr-2', data: { turbo_frame: "_top" }, title: "Edit Application" %> + <%= button_to 'Delete', job_application_path(job_application), method: :delete, form: { data: { turbo_confirm: 'Are you sure?' } }, class: 'px-3 py-1 rounded-md text-xs text-gray-700 bg-white border border-gray-300 hover:bg-gray-50', data: { turbo_frame: "_top" }, title: "Delete Application" %>
diff --git a/app/views/job_applications/_job_applications_table.html.erb b/app/views/job_applications/_job_applications_table.html.erb index 2bfa83c..3ce9c0c 100644 --- a/app/views/job_applications/_job_applications_table.html.erb +++ b/app/views/job_applications/_job_applications_table.html.erb @@ -2,34 +2,34 @@
<% if @job_applications.any? %> - + - - - - - - - - - diff --git a/app/views/job_applications/_pagination.html.erb b/app/views/job_applications/_pagination.html.erb index dadee7a..bc65968 100644 --- a/app/views/job_applications/_pagination.html.erb +++ b/app/views/job_applications/_pagination.html.erb @@ -1,18 +1,19 @@ -<% if @pagination_info[:total_pages] > 1 %> -
-
- Showing <%= @pagination_info[:offset] + 1 %> to <%= [@pagination_info[:offset] + @pagination_info[:length], @job_application_count].min %> of <%= @job_application_count %> results +
+ <% if @pagination_info[:total_pages] > 1 %> +
+
+
+
+ <%= will_paginate @job_applications, + renderer: CustomPaginationRenderer, + previous_label: 'Previous', + next_label: 'Next', + inner_window: 0, + outer_window: 0, + class: 'pagination', + previous_page_class: 'px-3 py-1 rounded-md text-sm font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50', + next_page_class: 'px-3 py-1 rounded-md text-sm font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50' %> +
-
- <%= will_paginate @job_applications, - renderer: CustomPaginationRenderer, - previous_label: 'Previous', - next_label: 'Next', - inner_window: 0, - outer_window: 0, - class: 'pagination', - previous_page_class: 'px-3 py-1 rounded-md text-sm font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50', - next_page_class: 'px-3 py-1 rounded-md text-sm font-medium text-gray-700 bg-white border border-gray-300 hover:bg-gray-50' %> -
-
-<% end %> + <% end %> +
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index a697828..a124c50 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,7 +11,7 @@ <%= vite_stylesheet_tag "application", data: {"turbo-track": "reload"} %> -
+
<%= render 'flash_messages' %> <%= yield %>
From 4ba8023f2491f38730b9dc0cf0538cc4d7c4f9b4 Mon Sep 17 00:00:00 2001 From: Tony Gaeta Date: Tue, 16 Jul 2024 18:17:00 -0400 Subject: [PATCH 3/9] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 097b74a..7741f72 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Job Tracker is a simple, powerful, and user-friendly web application designed to help job seekers efficiently manage their job search process. Built with Ruby on Rails and enhanced with modern web technologies, this tool streamlines the often overwhelming task of tracking multiple job applications. -UI Screenshot +UI Screenshot ## Key Features From 1ee2363b364114e2a4876058a386639b7f44f89c Mon Sep 17 00:00:00 2001 From: Tony Gaeta Date: Tue, 16 Jul 2024 18:40:08 -0400 Subject: [PATCH 4/9] add rexml to clear vulnerability --- Gemfile | 1 + Gemfile.lock | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 845ae90..0b71dab 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,7 @@ gem "phlex-rails", "~> 1.2" gem "devise", "~> 4.9" gem "will_paginate", "~> 4.0" gem "validate_url", "~> 1.0" +gem "rexml", ">= 3.3.2" group :development, :test do gem "debug", "~> 1.9", ">= 1.9.2", platforms: %i[mri windows] diff --git a/Gemfile.lock b/Gemfile.lock index cae456f..dcfca61 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -261,8 +261,8 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.3.2) + strscan rubocop (1.64.0) json (~> 2.3) language_server-protocol (>= 3.17.0) @@ -366,6 +366,7 @@ DEPENDENCIES phlex-rails (~> 1.2) puma (~> 6.4, >= 6.4.2) rails (~> 7.1, >= 7.1.3.4) + rexml (>= 3.3.2) rubocop (~> 1.63, >= 1.63.5) rubocop-factory_bot (~> 2.25, >= 2.25.1) rubocop-minitest (~> 0.35.0) From fc067dcea4fb5d951e29f314d1cfacc259dd4c0f Mon Sep 17 00:00:00 2001 From: Tony Gaeta Date: Tue, 16 Jul 2024 19:44:50 -0400 Subject: [PATCH 5/9] save --- .../job_applications_controller.rb | 11 +++--- .../controllers/flash_message_controller.js | 14 ++++++-- app/helpers/application_helper.rb | 34 +++++++++++++++---- .../application/_flash_messages.html.erb | 34 +++++++++++++++++-- app/views/job_applications/_form.html.erb | 5 ++- app/views/job_applications/index.html.erb | 3 ++ app/views/layouts/application.html.erb | 1 - 7 files changed, 83 insertions(+), 19 deletions(-) diff --git a/app/controllers/job_applications_controller.rb b/app/controllers/job_applications_controller.rb index 1477ca5..40b9cc3 100644 --- a/app/controllers/job_applications_controller.rb +++ b/app/controllers/job_applications_controller.rb @@ -4,7 +4,7 @@ class JobApplicationsController < ApplicationController def index @job_applications = filter_and_sort_job_applications @job_application_count = @job_applications.count - @job_applications = @job_applications.paginate(page: params[:page], per_page: 10) + @job_applications = @job_applications.paginate(page: params[:page], per_page: 10).order(created_at: :desc) @pagination_info = { current_page: @job_applications.current_page, @@ -29,10 +29,9 @@ def new end def edit - @job_application = JobApplication.find_by(id: params[:id]) - if @job_application.nil? - Rails.logger.error "Job Application with id #{params[:id]} not found" - redirect_to root_path, alert: "Job Application not found" + respond_to do |format| + format.html + format.turbo_stream { render turbo_stream: turbo_stream.replace(dom_id(@job_application), partial: "form", locals: {job_application: @job_application, title: "Edit"}) } end end @@ -104,6 +103,8 @@ def destroy def set_job_application @job_application = JobApplication.find(params[:id]) + rescue ActiveRecord::RecordNotFound + redirect_to root_path, alert: "Job application not found." end def job_application_params diff --git a/app/frontend/controllers/flash_message_controller.js b/app/frontend/controllers/flash_message_controller.js index fced9ca..7c0fe77 100644 --- a/app/frontend/controllers/flash_message_controller.js +++ b/app/frontend/controllers/flash_message_controller.js @@ -10,7 +10,17 @@ export default class extends Controller { show() { this.element.classList.remove('hidden'); setTimeout(() => { - this.element.classList.add('hidden'); - }, 5000); + this.fadeOut(); + }, 2000); // Start fading out after 4 seconds + } + + fadeOut() { + const flashMessage = this.element.querySelector('.flash-message'); + if (flashMessage) { + flashMessage.classList.add('fade-out'); + setTimeout(() => { + this.element.classList.add('hidden'); + }, 500); // Duration of fade-out animation + } } } diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f578809..27a777f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,12 +1,34 @@ module ApplicationHelper def flash_class(type) + base_class = "border-l-4 p-4 mb-4 rounded-md" case type.to_sym - when :notice then "bg-blue-100 border-blue-500 text-blue-700" - when :success then "bg-green-100 border-green-500 text-green-700" - when :error then "bg-red-100 border-red-500 text-red-700" - when :alert then "bg-yellow-100 border-yellow-500 text-yellow-700" - else "bg-gray-100 border-gray-500 text-gray-700" - end + " border-l-4 p-4 mb-4" + when :notice + "bg-blue-100 border-blue-500 text-blue-700 #{base_class}" + when :success + "bg-green-100 border-green-500 text-green-700 #{base_class}" + when :error + "bg-red-100 border-red-500 text-red-700 #{base_class}" + when :alert + "bg-yellow-100 border-yellow-500 text-yellow-700 #{base_class}" + else + "bg-gray-100 border-gray-500 text-gray-700 #{base_class}" + end + end + + def progress_bar_class(type) + base_class = "rounded-md" + case type.to_sym + when :notice + "bg-blue-500 #{base_class}" + when :success + "bg-green-500 #{base_class}" + when :error + "bg-red-500 #{base_class}" + when :alert + "bg-yellow-500 #{base_class}" + else + "bg-gray-500 #{base_class}" + end end def sort_link_to(name, column) diff --git a/app/views/application/_flash_messages.html.erb b/app/views/application/_flash_messages.html.erb index 8a365e4..f189164 100644 --- a/app/views/application/_flash_messages.html.erb +++ b/app/views/application/_flash_messages.html.erb @@ -1,6 +1,36 @@ -
- <%= sort_link_to 'Applied', 'date_applied' %> + + <%= sort_link_to 'Date Applied', 'date_applied' %> - <%= sort_link_to 'Company', 'company_name' %> + + <%= sort_link_to 'Company Name', 'company_name' %> + <%= sort_link_to 'Position', 'position_title' %> + Type + Contact Method + P.o.C. + Email + Website + Actions