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) diff --git a/README.md b/README.md index 097b74a..4d1a6ba 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 diff --git a/app/controllers/job_applications_controller.rb b/app/controllers/job_applications_controller.rb index 2139285..c56b68f 100644 --- a/app/controllers/job_applications_controller.rb +++ b/app/controllers/job_applications_controller.rb @@ -1,5 +1,6 @@ class JobApplicationsController < ApplicationController - before_action :set_job_application, only: [:create, :update, :destroy] + include ActionView::RecordIdentifier + before_action :set_job_application, only: [:edit, :update, :destroy] def index @job_applications = filter_and_sort_job_applications @@ -22,6 +23,17 @@ 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 + 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 def create @@ -29,18 +41,19 @@ def create respond_to do |format| if @job_application.save - format.html { redirect_to job_applications_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}) - ] - } + format.html { redirect_to root_path, success: "Job application was successfully created." } + format.turbo_stream do + render turbo_stream: turbo_stream.redirect_advanced(root_path) + flash[:success] = "Job application was successfully created." + end 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 do + 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 end @@ -48,17 +61,13 @@ 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.turbo_stream { - flash.now[:notice] = "Job application was successfully updated." - render turbo_stream: [ - turbo_stream.replace(@job_application, partial: "job_application", locals: {job_application: @job_application}), - turbo_stream.update("flash_messages", partial: "flash_messages") - ] - } + format.html { redirect_to root_path, success: "Job application was successfully updated." } + format.turbo_stream do + render turbo_stream: turbo_stream.redirect_advanced(root_path) + flash[:success] = "Job application was successfully updated." + end 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}) } end end end @@ -66,9 +75,9 @@ 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, success: "Job application was successfully deleted." } format.turbo_stream { - flash.now[:notice] = "Job application was successfully deleted." + flash.now[:success] = "Job application was successfully deleted." render turbo_stream: [ turbo_stream.remove(@job_application), turbo_stream.update("job_application_count", JobApplication.count), @@ -82,6 +91,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 @@ -97,11 +108,18 @@ def filter_and_sort_job_applications sort_column = sort_column(params[:sort]) sort_direction = sort_direction(params[:direction]) - job_applications.order(sort_column => sort_direction) + + if sort_column == "created_at" || params[:sort].blank? + # If sorting by created_at or no sorting specified, always use desc order + job_applications.order(created_at: :desc) + else + # For other columns, use the specified sort direction + job_applications.order(sort_column => sort_direction) + end end def sort_column(column) - %w[date_applied company_name position_title].include?(column) ? column : "date_applied" + %w[date_applied company_name position_title created_at].include?(column) ? column : "created_at" end def sort_direction(direction) diff --git a/app/frontend/controllers/flash_message_controller.js b/app/frontend/controllers/flash_message_controller.js index fced9ca..6786f55 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); + } + + fadeOut() { + const flashMessage = this.element.querySelector('.flash-message'); + if (flashMessage) { + flashMessage.classList.add('fade-out'); + setTimeout(() => { + this.element.classList.add('hidden'); + }, 500); + } } } diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index 644acc4..a8e14c6 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -1,5 +1,5 @@ import "~/controllers"; -import "@hotwired/turbo-rails"; +import { Turbo } from "@hotwired/turbo-rails" // To see this message, add the following to the `` section in your // views/layouts/application.html.erb // @@ -20,7 +20,7 @@ console.log('Visit the guide for more information: ', 'https://vite-ruby.netlify Turbo.start(); -document.addEventListener("turbo:load", function () { +document.addEventListener("turbo:load", () => { console.log("turbo:load"); }); // @@ -32,3 +32,9 @@ document.addEventListener("turbo:load", function () { // Example: Import a stylesheet in app/frontend/index.css // import '~/index.css' + +Turbo.StreamActions.redirect_advanced = function () { + const url = this.getAttribute('url') || '/' + // Turbo.visit(url, { frame: '_top', action: 'advance' }) + Turbo.visit(url) +} 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/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/helpers/turbo_stream_actions_helper.rb b/app/helpers/turbo_stream_actions_helper.rb new file mode 100644 index 0000000..dace92d --- /dev/null +++ b/app/helpers/turbo_stream_actions_helper.rb @@ -0,0 +1,8 @@ +module TurboStreamActionsHelper + # render turbo_stream: turbo_stream.redirect_advanced(projects_path) + def redirect_advanced(url) + turbo_stream_action_tag :redirect_advanced, url: url + end +end + +Turbo::Streams::TagBuilder.prepend(TurboStreamActionsHelper) 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 @@ -