diff --git a/.github/workflows/rubyonrails.yml b/.github/workflows/rubyonrails.yml index 67d8a841..244200ef 100644 --- a/.github/workflows/rubyonrails.yml +++ b/.github/workflows/rubyonrails.yml @@ -59,3 +59,22 @@ jobs: - name: Lint Ruby files run: bundle exec rubocop --parallel + + brakeman: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Install Ruby and gems + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + with: + bundler-cache: true + - name: Install dependencies + run: bundle install + - name: Run Brakeman + run: bundle exec brakeman -f sarif -o output.sarif.json + continue-on-error: true + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: output.sarif.json diff --git a/.slugignore b/.slugignore index ef9d84b4..3ee05081 100644 --- a/.slugignore +++ b/.slugignore @@ -34,6 +34,9 @@ spec/** .gitignore .dockerignore +# Ignore contributing files +CONTRIBUTING.md + # Ignore yml files generated by VCR. spec/fixtures/cassettes/ApplicantTrackingSystem/*.yml diff --git a/Gemfile b/Gemfile index d45eb469..21705ade 100644 --- a/Gemfile +++ b/Gemfile @@ -77,8 +77,9 @@ gem "ruby-openai" gem 'meta-tags' # gem 'sitemap_generator' # TODO: install this gem for sitemap generation -# Email -# gem 'sendgrid-ruby' # TODO: install this gem for sending emails +# Email & CRM +# gem 'hubspot-api-client' +gem 'sendgrid-ruby' # Importing, Parsing & APIs gem 'csv' # can probably use ruby standard library @@ -88,7 +89,7 @@ gem 'rails-html-sanitizer' gem "flipper-active_record", "~> 1.3" # Monitoring -# gem 'newrelic_rpm' # TODO: install this gem for monitoring +gem 'newrelic_rpm' # Analytics # gem 'analytics-ruby', '~> 2.4.0', :require => 'segment/analytics' # TODO: install this gem for analytics @@ -103,8 +104,7 @@ gem "flipper-active_record", "~> 1.3" # gem 'sentry-raven' # TODO: install this gem for error tracking # Front-end -gem 'will_paginate', '~> 4.0' # TODO: Update to kaminari now index page is fixed (better maintained / looks nicer) -# gem 'kaminari' +gem 'kaminari' gem "high_voltage", "~> 3.1" gem 'tinymce-rails' # TODO: now have trix and action text (check whether we need to replace) @@ -124,6 +124,9 @@ group :development, :test do gem 'rubocop-rails' gem 'rubocop-rspec' + # Security + gem 'brakeman', require: false + # Email # gem 'letter_opener' # TODO: install this gem for email testing end @@ -135,9 +138,6 @@ group :development do gem "better_errors" gem "binding_of_caller" - # Security - # gem 'brakeman', require: false # TODO: install this gem for security checks - # Performance # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] # gem "rack-mini-profiler" # TODO: install this gem for performance monitoring diff --git a/Gemfile.lock b/Gemfile.lock index 6a1402a5..34644f68 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,35 +1,35 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.1.3.2) - actionpack (= 7.1.3.2) - activesupport (= 7.1.3.2) + actioncable (7.1.3.3) + actionpack (= 7.1.3.3) + activesupport (= 7.1.3.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.3.2) - actionpack (= 7.1.3.2) - activejob (= 7.1.3.2) - activerecord (= 7.1.3.2) - activestorage (= 7.1.3.2) - activesupport (= 7.1.3.2) + actionmailbox (7.1.3.3) + actionpack (= 7.1.3.3) + activejob (= 7.1.3.3) + activerecord (= 7.1.3.3) + activestorage (= 7.1.3.3) + activesupport (= 7.1.3.3) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.1.3.2) - actionpack (= 7.1.3.2) - actionview (= 7.1.3.2) - activejob (= 7.1.3.2) - activesupport (= 7.1.3.2) + actionmailer (7.1.3.3) + actionpack (= 7.1.3.3) + actionview (= 7.1.3.3) + activejob (= 7.1.3.3) + activesupport (= 7.1.3.3) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.2) - actionpack (7.1.3.2) - actionview (= 7.1.3.2) - activesupport (= 7.1.3.2) + actionpack (7.1.3.3) + actionview (= 7.1.3.3) + activesupport (= 7.1.3.3) nokogiri (>= 1.8.5) racc rack (>= 2.2.4) @@ -37,15 +37,15 @@ GEM rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.3.2) - actionpack (= 7.1.3.2) - activerecord (= 7.1.3.2) - activestorage (= 7.1.3.2) - activesupport (= 7.1.3.2) + actiontext (7.1.3.3) + actionpack (= 7.1.3.3) + activerecord (= 7.1.3.3) + activestorage (= 7.1.3.3) + activesupport (= 7.1.3.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.3.2) - activesupport (= 7.1.3.2) + actionview (7.1.3.3) + activesupport (= 7.1.3.3) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -53,22 +53,22 @@ GEM active_link_to (1.0.5) actionpack addressable - activejob (7.1.3.2) - activesupport (= 7.1.3.2) + activejob (7.1.3.3) + activesupport (= 7.1.3.3) globalid (>= 0.3.6) - activemodel (7.1.3.2) - activesupport (= 7.1.3.2) - activerecord (7.1.3.2) - activemodel (= 7.1.3.2) - activesupport (= 7.1.3.2) + activemodel (7.1.3.3) + activesupport (= 7.1.3.3) + activerecord (7.1.3.3) + activemodel (= 7.1.3.3) + activesupport (= 7.1.3.3) timeout (>= 0.4.0) - activestorage (7.1.3.2) - actionpack (= 7.1.3.2) - activejob (= 7.1.3.2) - activerecord (= 7.1.3.2) - activesupport (= 7.1.3.2) + activestorage (7.1.3.3) + actionpack (= 7.1.3.3) + activejob (= 7.1.3.3) + activerecord (= 7.1.3.3) + activesupport (= 7.1.3.3) marcel (~> 1.0) - activesupport (7.1.3.2) + activesupport (7.1.3.3) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -83,7 +83,7 @@ GEM ast (2.4.2) autoprefixer-rails (10.4.16.0) execjs (~> 2) - avo (3.6.1) + avo (3.7.4) actionview (>= 6.1) active_link_to activerecord (>= 6.1) @@ -105,15 +105,17 @@ GEM erubi (>= 1.0.0) rack (>= 0.9.0) rouge (>= 1.0.0) - bigdecimal (3.1.7) + bigdecimal (3.1.8) bindex (0.8.1) binding_of_caller (1.0.1) debug_inspector (>= 1.2.0) bootsnap (1.18.3) msgpack (~> 1.2) - bootstrap (5.3.2) + bootstrap (5.3.3) autoprefixer-rails (>= 9.1.0) popper_js (>= 2.11.8, < 3) + brakeman (6.1.2) + racc builder (3.2.4) bullet (7.1.6) activesupport (>= 3.0.0) @@ -151,9 +153,9 @@ GEM warden (~> 1.2.3) diff-lcs (1.5.1) docile (1.4.0) - dotenv (3.1.0) - dotenv-rails (3.1.0) - dotenv (= 3.1.0) + dotenv (3.1.2) + dotenv-rails (3.1.2) + dotenv (= 3.1.2) railties (>= 6.1) drb (2.2.1) dry-initializer (3.1.1) @@ -185,10 +187,12 @@ GEM flipper (~> 1.3.0) font-awesome-sass (6.5.2) sassc (~> 2.0) - fugit (1.10.1) - et-orbi (~> 1, >= 1.2.7) + fugit (1.11.0) + et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) - geocoder (1.8.2) + geocoder (1.8.3) + base64 (>= 0.1.0) + csv (>= 3.0.0) globalid (1.2.1) activesupport (>= 6.1) hashdiff (1.1.0) @@ -197,10 +201,11 @@ GEM actionpack nokogiri rubyzip (>= 1.0) - httparty (0.21.0) + httparty (0.22.0) + csv mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) - i18n (1.14.4) + i18n (1.14.5) concurrent-ruby (~> 1.0) importmap-rails (2.0.1) actionpack (>= 6.0.0) @@ -210,10 +215,22 @@ GEM activesupport (>= 3.0) nokogiri (>= 1.6) io-console (0.7.2) - irb (1.12.0) - rdoc + irb (1.13.1) + rdoc (>= 4.0.0) reline (>= 0.4.2) json (2.7.2) + kaminari (1.2.2) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) + actionview + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) + activerecord + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) language_server-protocol (3.17.0.3) loofah (2.22.0) crass (~> 1.0.2) @@ -230,14 +247,15 @@ GEM method_source (1.1.0) mime-types (1.25.1) mini_mime (1.1.5) - minitest (5.22.3) + minitest (5.23.0) msgpack (1.7.2) - multi_xml (0.6.0) - multipart-post (2.4.0) + multi_xml (0.7.1) + bigdecimal (~> 3.1) + multipart-post (2.4.1) mutex_m (0.2.0) net-http (0.4.1) uri - net-imap (0.4.10) + net-imap (0.4.11) date net-protocol net-pop (0.1.2) @@ -246,7 +264,8 @@ GEM timeout net-smtp (0.5.0) net-protocol - nio4r (2.7.1) + newrelic_rpm (9.9.0) + nio4r (2.7.3) nokogiri (1.16.5-aarch64-linux) racc (~> 1.4) nokogiri (1.16.5-arm-linux) @@ -260,9 +279,9 @@ GEM nokogiri (1.16.5-x86_64-linux) racc (~> 1.4) orm_adapter (0.5.0) - pagy (8.2.0) + pagy (8.4.0) parallel (1.24.0) - parser (3.3.0.5) + parser (3.3.1.0) ast (~> 2.4.1) racc pg (1.5.6) @@ -285,20 +304,20 @@ GEM rackup (1.0.0) rack (< 3) webrick - rails (7.1.3.2) - actioncable (= 7.1.3.2) - actionmailbox (= 7.1.3.2) - actionmailer (= 7.1.3.2) - actionpack (= 7.1.3.2) - actiontext (= 7.1.3.2) - actionview (= 7.1.3.2) - activejob (= 7.1.3.2) - activemodel (= 7.1.3.2) - activerecord (= 7.1.3.2) - activestorage (= 7.1.3.2) - activesupport (= 7.1.3.2) + rails (7.1.3.3) + actioncable (= 7.1.3.3) + actionmailbox (= 7.1.3.3) + actionmailer (= 7.1.3.3) + actionpack (= 7.1.3.3) + actiontext (= 7.1.3.3) + actionview (= 7.1.3.3) + activejob (= 7.1.3.3) + activemodel (= 7.1.3.3) + activerecord (= 7.1.3.3) + activestorage (= 7.1.3.3) + activesupport (= 7.1.3.3) bundler (>= 1.15.0) - railties (= 7.1.3.2) + railties (= 7.1.3.3) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -306,9 +325,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.1.3.2) - actionpack (= 7.1.3.2) - activesupport (= 7.1.3.2) + railties (7.1.3.3) + actionpack (= 7.1.3.3) + activesupport (= 7.1.3.3) irb rackup (>= 1.0.0) rake (>= 12.2) @@ -319,8 +338,8 @@ GEM rdoc (6.6.3.1) psych (>= 4.0.0) redis (4.8.1) - regexp_parser (2.9.0) - reline (0.5.2) + regexp_parser (2.9.2) + reline (0.5.7) io-console (~> 0.5) responders (3.1.1) actionpack (>= 5.2) @@ -333,7 +352,7 @@ GEM rspec-expectations (3.13.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.0) + rspec-mocks (3.13.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-rails (6.1.2) @@ -345,7 +364,7 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.1) - rubocop (1.63.2) + rubocop (1.63.5) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -356,8 +375,8 @@ GEM rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.2) - parser (>= 3.3.0.4) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) rubocop-capybara (2.20.0) rubocop (~> 1.41) rubocop-factory_bot (2.25.1) @@ -365,23 +384,24 @@ GEM rubocop-performance (1.21.0) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.24.1) + rubocop-rails (2.25.0) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rspec (2.29.1) + rubocop-rspec (2.29.2) rubocop (~> 1.40) rubocop-capybara (~> 2.17) rubocop-factory_bot (~> 2.22) rubocop-rspec_rails (~> 2.28) rubocop-rspec_rails (2.28.3) rubocop (~> 1.40) - ruby-openai (6.5.0) + ruby-openai (7.0.1) event_stream_parser (>= 0.3.0, < 2.0.0) faraday (>= 1) faraday-multipart (>= 1) ruby-progressbar (1.13.0) + ruby_http_client (3.5.5) rubyzip (2.3.2) rufus-scheduler (3.9.1) fugit (~> 1.1, >= 1.1.6) @@ -398,6 +418,8 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) + sendgrid-ruby (6.7.0) + ruby_http_client (~> 3.4) shoulda-matchers (6.1.0) activesupport (>= 5.2.0) sidekiq (6.5.12) @@ -427,7 +449,7 @@ GEM thor (1.3.1) tilt (2.3.0) timeout (0.4.1) - tinymce-rails (7.0.1) + tinymce-rails (7.1.0) railties (>= 3.1.1) turbo-rails (2.0.5) actionpack (>= 6.0.0) @@ -441,7 +463,7 @@ GEM uniform_notifier (1.16.0) uri (0.13.0) vcr (6.2.0) - view_component (3.12.0) + view_component (3.12.1) activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) @@ -464,12 +486,11 @@ GEM websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - will_paginate (4.0.0) xpath (3.2.0) nokogiri (~> 1.8) yomu (0.1.10) mime-types (~> 1.23) - zeitwerk (2.6.13) + zeitwerk (2.6.14) PLATFORMS aarch64-linux @@ -486,6 +507,7 @@ DEPENDENCIES binding_of_caller bootsnap bootstrap + brakeman bullet capybara cloudinary @@ -503,7 +525,9 @@ DEPENDENCIES htmltoword importmap-rails json (~> 2.6, >= 2.6.3) + kaminari meta-tags + newrelic_rpm nokogiri pg (~> 1.1) pg_search @@ -518,6 +542,7 @@ DEPENDENCIES ruby-openai sassc-rails selenium-webdriver (~> 4.18.1) + sendgrid-ruby shoulda-matchers (= 6.1.0) sidekiq (~> 6.5.5) sidekiq-failures (~> 1.0) @@ -532,7 +557,6 @@ DEPENDENCIES watir web-console webmock - will_paginate (~> 4.0) yomu RUBY VERSION diff --git a/app/assets/stylesheets/components/_button.scss b/app/assets/stylesheets/components/_button.scss index 020b7173..7f1cb81e 100644 --- a/app/assets/stylesheets/components/_button.scss +++ b/app/assets/stylesheets/components/_button.scss @@ -19,23 +19,6 @@ text-decoration: none; } -#search-button { - background-color: $main-color; - color: white; - // font-weight: 500; - text-decoration: none; - border: none; - border-radius: 8px; - padding: 10px 20px; - // margin-right: 6px; - margin-left: 6px; -} - -#search-button:hover { - opacity: 85%; - color: white; -} - .btn-add-info, #btn-add-job { background-color: $main-color; opacity: 0.7; diff --git a/app/assets/stylesheets/components/_footer.scss b/app/assets/stylesheets/components/_footer.scss index 691fb117..0345a0ee 100644 --- a/app/assets/stylesheets/components/_footer.scss +++ b/app/assets/stylesheets/components/_footer.scss @@ -9,49 +9,8 @@ } .footer { - background: #F4F4F4; - display: flex; - align-items: center; - justify-content: space-between; - height: 160px; - padding: 0px 50px; - color: rgba(0,0,0,0.3); - flex-shrink: 0; + border-top: 1px solid rgba(0,0,0,0.1); + height: 20vh; width: 100%; - margin-top: 24px; -} - -.footer-links { - display: flex; - align-items: center; + color: rgba(0, 0, 0, 0.5); } - -.footer-links a { - color: black; - opacity: 0.3; - text-decoration: none; - font-size: 24px; - padding: 0px 10px; -} - -.footer-links a:hover { - opacity: 1; -} - -.footer .fa-heart { - color: #D23333; -} - -.page-links a { - color: black; - opacity: 0.3; - text-decoration: none; - font-size: 16px; - padding: 0px 10px; -} - -// .footer-copyright { -// display: flex; -// justify-content: right; -// align-items: right; -// } diff --git a/app/assets/stylesheets/components/_search_bar.scss b/app/assets/stylesheets/components/_search_bar.scss index 74dc894f..f5b757f3 100644 --- a/app/assets/stylesheets/components/_search_bar.scss +++ b/app/assets/stylesheets/components/_search_bar.scss @@ -9,7 +9,8 @@ #search-jobs { width: 600px; - border-radius: 10px; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; } .clear-search-icon { @@ -19,7 +20,17 @@ top: 10px; } -.sticky-top { - top: 0; - z-index: 1020; /* High enough to stay above other content */ +#search-button { + border-top-right-radius: 10px; + border-bottom-right-radius: 10px; + background-color: $main-color; + color: white; + text-decoration: none; + border: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +#search-button:hover { + opacity: 85%; + color: white; } diff --git a/app/assets/stylesheets/components/_utility_classes.scss b/app/assets/stylesheets/components/_utility_classes.scss index fc003879..9a1258dc 100644 --- a/app/assets/stylesheets/components/_utility_classes.scss +++ b/app/assets/stylesheets/components/_utility_classes.scss @@ -17,3 +17,11 @@ .small-text { font-size: 0.8rem; } + +.bg-main { + background-color: $main-color; +} + +.tc-main { + color: $main-color; +} diff --git a/app/assets/stylesheets/pages/_about.scss b/app/assets/stylesheets/pages/_about.scss index 35c8588c..e69de29b 100644 --- a/app/assets/stylesheets/pages/_about.scss +++ b/app/assets/stylesheets/pages/_about.scss @@ -1,134 +0,0 @@ -#about-page { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - margin-top: 24px; - color: $main-color; - text-align: center; - width: 100%; - - h1 { - font-weight: bold; - margin: 16px 0; - - } - - h3 { - font-weight: normal; - margin: 16px 0; - width: 60%; - font-size: 1.4rem; - } - - #about-text-logo { - width: 15%; - margin-bottom: 16px; - } - - #about-logo { - width: 10%; - } - - .about-team-row { - display: flex; - align-items: flex-start; - justify-content: space-around; - width: 60%; - } - - .about-team-card { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; - width: 25%; - - h3 { - width: 100%; - } - - #charlie-avatar, #ilya-avatar, #dan-avatar, #alejandro-avatar, #chris-avatar, #jamie-avatar { - height: 200px; - width: 200px; - background-size: cover; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - - &:hover { - .team-icons { - display: flex; - width: 60%; - justify-content: space-around; - align-items: center; - filter: brightness(1) !important; - } - } - } - - #charlie-avatar { - background-image: image-url("team/charlie_avatar.png"); - - &:hover { - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), image-url("team/charlie_avatar.png"); - } - } - - #ilya-avatar { - background-image: image-url("team/ilya_avatar.png"); - - &:hover { - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), image-url("team/ilya_avatar.png"); - } - } - - #dan-avatar { - background-image: image-url("team/dan_avatar.png"); - - &:hover { - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), image-url("team/dan_avatar.png"); - } - } - - #alejandro-avatar { - background-image: image-url("team/alejandro_avatar.png"); - - &:hover { - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), image-url("team/alejandro_avatar.png"); - } - } - - #chris-avatar { - background-image: image-url("team/chris_avatar.png"); - - &:hover { - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), image-url("team/chris_avatar.png"); - } - } - - #jamie-avatar { - background-image: image-url("team/jamie_avatar.png"); - - &:hover { - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), image-url("team/jamie_avatar.png"); - } - } - - .team-icons { - display: none; - - i { - color: white; - font-size: 2rem; - - &:hover { - color: #ff9433; - } - } - } - } - -} diff --git a/app/assets/stylesheets/pages/_apply.scss b/app/assets/stylesheets/pages/_apply.scss index 560baca5..f6d46915 100644 --- a/app/assets/stylesheets/pages/_apply.scss +++ b/app/assets/stylesheets/pages/_apply.scss @@ -1,4 +1,10 @@ // Application Summary + +.sticky-top { + top: 0; + z-index: 1020; +} + .company-apply-summary { width: 600px; border-radius: 8px; diff --git a/app/assets/stylesheets/pages/_faqs.scss b/app/assets/stylesheets/pages/_faqs.scss index faa2ca4f..7861a6aa 100644 --- a/app/assets/stylesheets/pages/_faqs.scss +++ b/app/assets/stylesheets/pages/_faqs.scss @@ -1,114 +1,70 @@ -.faq-section { - background: #fdfdfd; - min-height: 100vh; - padding: 10vh 0 0; -} -.faq-title h2 { +/*=========================== + about-06 css +===========================*/ +.about-six { + padding-top: 90px; + padding-bottom: 120px; position: relative; - margin-bottom: 45px; - display: inline-block; - font-weight: 600; - line-height: 1; } -.faq-title h2::before { - content: ""; - position: absolute; - left: 50%; - width: 60px; - height: 2px; - background-color: #6007C6; - bottom: -25px; - margin-left: -30px; +.about-six .faq-title .sub-title { + color: var(--primary); + text-transform: uppercase; } -.faq-title p { - padding: 0 190px; - margin-bottom: 10px; +.about-six .faq-title .title { + padding-top: 10px; } - -.faq { - background: #fdfdfd; - box-shadow: 0 2px 48px 0 rgba(0, 0, 0, 0.06); - border-radius: 4px; +.about-six .faq-accordion { + margin-top: 45px; } - -.faq .card { - border: none; +.about-six .faq-accordion .accordion .card { + border: 0; background: none; - border-bottom: 1px dashed #CEE1F8; } - -.faq .card .card-header { - padding: 0px; - border: none; +.about-six .faq-accordion .accordion .card .card-header { + padding: 0; + border: 0; background: none; - -webkit-transition: all 0.3s ease 0s; - -moz-transition: all 0.3s ease 0s; - -o-transition: all 0.3s ease 0s; - transition: all 0.3s ease 0s; + margin-top: 40px; +} +.about-six .faq-accordion .accordion .card .card-header a { + font-size: 18px; + font-weight: 500; + color: var(--black); + display: block; + position: relative; + padding-right: 20px; } - -.faq .card .card-header:hover { - background: rgba(136, 66, 216, 0.8); - padding-left: 10px; +@media only screen and (min-width: 992px) and (max-width: 1199px) { + .about-six .faq-accordion .accordion .card .card-header a { + font-size: 16px; + } } -.faq .card .card-header .faq-title { - width: 100%; - text-align: left; - padding: 0px; - padding-left: 30px; - padding-right: 30px; - font-weight: 400; - font-size: 15px; - letter-spacing: 1px; - color: #3B566E; - text-decoration: none !important; - -webkit-transition: all 0.3s ease 0s; - -moz-transition: all 0.3s ease 0s; - -o-transition: all 0.3s ease 0s; - transition: all 0.3s ease 0s; - cursor: pointer; - padding-top: 20px; - padding-bottom: 20px; +@media (max-width: 767px) { + .about-six .faq-accordion .accordion .card .card-header a { + font-size: 16px; + } } - -.faq .card .card-header .faq-title .badge { - display: inline-block; - width: 20px; - height: 20px; - line-height: 14px; - float: left; - -webkit-border-radius: 100px; - -moz-border-radius: 100px; - border-radius: 100px; - text-align: center; - background: #FFEC00; - color: #fff; - font-size: 12px; - margin-right: 20px; +.about-six .faq-accordion .accordion .card .card-header a::before { + content: "\eb30"; + font-family: "LineIcons"; + position: absolute; + top: 50%; + right: 0; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); } - -.faq .card .card-body { - padding: 30px; - padding-left: 35px; - padding-bottom: 16px; - font-weight: 400; - font-size: 16px; - color: #6F8BA4; - line-height: 28px; - letter-spacing: 1px; - border-top: 1px solid #F3F8FF; +.about-six .faq-accordion .accordion .card .card-header .collapsed::before { + content: "\eb5c"; } - -.faq .card .card-body p { - margin-bottom: 14px; +.about-six .faq-accordion .accordion .card .card-body { + padding: 20px 20px 0; } - -@media (max-width: 991px) { - .faq { - margin-bottom: 30px; - } - .faq .card .card-header .faq-title { - line-height: 26px; - margin-top: 10px; - } +.about-six .faq-image { + margin-top: 50px; +} +.about-six .faq-image img { + width: 100%; } diff --git a/app/assets/stylesheets/pages/_jobs_index.scss b/app/assets/stylesheets/pages/_jobs_index.scss index 6034e769..937a214c 100644 --- a/app/assets/stylesheets/pages/_jobs_index.scss +++ b/app/assets/stylesheets/pages/_jobs_index.scss @@ -1,6 +1,9 @@ .category-select { border-top: 1px solid #e3e6f0; - padding: 16px 8px; + padding-top: 8px; + padding-bottom: 16px; + padding-left: 4px; + padding-right: 4px; } .category-label { @@ -34,3 +37,9 @@ padding-right: 6px; margin-right: 12px; } + +.pagination { + span { + margin: 0 4px; + } +} diff --git a/app/controllers/emails_controller.rb b/app/controllers/emails_controller.rb new file mode 100644 index 00000000..1b44120c --- /dev/null +++ b/app/controllers/emails_controller.rb @@ -0,0 +1,19 @@ +class EmailsController < ApplicationController + def create + email = params[:email] + + # TODO: Integrate Hubspot via OAuth - at the moment we're using Sendgrid and this doesn't work + # hubspot_service = Email::Hubspot.new + sendgrid_service = Email::Sendgrid.new + + # hubspot_response = hubspot_service.add_contact(email) + sendgrid_response = sendgrid_service.add_contact(email) + + if sendgrid_response.status_code.to_i == 202 + sendgrid_service.send_confirmation_email(email) + redirect_to root_path, notice: 'Subscription successful. Please check your email for confirmation.' + else + redirect_to root_path, alert: 'Subscription failed. Please try again later.' + end + end +end diff --git a/app/controllers/jobs_controller.rb b/app/controllers/jobs_controller.rb index 6d3d1ad1..f18e8704 100644 --- a/app/controllers/jobs_controller.rb +++ b/app/controllers/jobs_controller.rb @@ -7,7 +7,7 @@ class JobsController < ApplicationController before_action :job_show_page_status, only: [:show] def index - @jobs = jobs_and_associated_tables.filter_and_sort(params).paginate(page: params[:page], per_page: 20) + @jobs = jobs_and_associated_tables.filter_and_sort(params).order(sort_order(params[:sort])).page(params[:page]).per(20) @resources = CategorySidebar.build_with(params) @saved_jobs = SavedJob.all @@ -29,6 +29,25 @@ def add_job private + def sort_order(sort_param) + case sort_param + when 'title' + 'jobs.title ASC' + when 'title_desc' + 'jobs.title DESC' + when 'company' + 'companies.name ASC' + when 'company_desc' + 'companies.name DESC' + when 'created_at' + 'jobs.created_at DESC' + when 'created_at_asc' + 'jobs.created_at ASC' + else # rubocop:disable Lint/DuplicateBranch + 'jobs.created_at DESC' + end + end + def job_show_page_status redirect_to jobs_path, notice: 'Job show page coming soon!' unless Flipper.enabled?(:job_show_page) end diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 25e98787..a64d5da2 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -1,10 +1,10 @@ class PagesController < ApplicationController - skip_before_action :authenticate_user!, only: %i[home test faqs how_it_works about jobs landing privacy ts_and_cs] + skip_before_action :authenticate_user!, only: %i[home faqs how_it_works about landing privacy ts_and_cs] def home end - def test + def about end def faqs @@ -13,21 +13,12 @@ def faqs def how_it_works end - def jobs - end - - def temp - end - def climate end def landing end - def about - end - def privacy end diff --git a/app/javascript/controllers/select_by_job_type_controller.js b/app/javascript/controllers/select_by_job_type_controller.js index 39e3db2b..5302ebc3 100644 --- a/app/javascript/controllers/select_by_job_type_controller.js +++ b/app/javascript/controllers/select_by_job_type_controller.js @@ -5,6 +5,7 @@ export default class extends Controller { static targets = [ 'jobRow', 'search', 'cheddar', 'posted', 'role', 'company', 'location', 'seniority', 'employment' ] connect() { + this.searchTarget.addEventListener('input', this.checkEmptySearch.bind(this)); } combinedSearch(event) { @@ -41,6 +42,12 @@ export default class extends Controller { window.location.href = `/jobs${filterQueryString}`; } + checkEmptySearch() { + if (this.searchTarget.value.trim() === '') { + window.location.href = `/jobs`; + } + } + buildQueryString(searchTerms) { const queryStringParams = [] diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 00000000..74ecdb37 --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,8 @@ +class UserMailer < ApplicationMailer + default from: 'charlie@cheddar.jobs' + + def confirmation_email(email) + @email = email + mail(to: @email, subject: 'Subscription Confirmation') + end +end diff --git a/app/models/job.rb b/app/models/job.rb index e89d20b9..eace028f 100644 --- a/app/models/job.rb +++ b/app/models/job.rb @@ -55,15 +55,6 @@ def application_criteria read_attribute(:application_criteria).with_indifferent_access end - # def update_application_criteria - # if posting_url.include?('greenhouse') - # extra_fields = GetFormFieldsJob.perform_later(posting_url) - # # p extra_fields - # else - # p "No additional fields to add" - # end - # end - CONVERT_TO_DAYS = { 'today' => 0, '3-days' => 3, diff --git a/app/services/category_sidebar.rb b/app/services/category_sidebar.rb index 67c02b48..59f9f7c3 100644 --- a/app/services/category_sidebar.rb +++ b/app/services/category_sidebar.rb @@ -73,9 +73,7 @@ class CategorySidebar def self.build_with(params) @params = params @jobs = Job.includes(:roles, :locations).select("jobs.id, jobs.date_posted, jobs.seniority, jobs.remote, jobs.employment_type") - Rails.cache.fetch("category_sidebar #{@params.except(:page, :controller, :action)}", expires_in: 2.hours, race_condition_ttl: 10.seconds) do - fetch_sidebar_data - end + fetch_sidebar_data end private diff --git a/app/services/email/hubspot.rb b/app/services/email/hubspot.rb new file mode 100644 index 00000000..4df4f336 --- /dev/null +++ b/app/services/email/hubspot.rb @@ -0,0 +1,28 @@ +module Email + class Hubspot # rubocop:disable Lint/EmptyClass + # require 'hubspot-api-client' + + # def initialize + # @client = Hubspot::Client.new(access_token: ENV.fetch('HUBSPOT_API_KEY')) + # end + + # def add_contact(email) + # contact_properties = { + # properties: { + # email: + # } + # } + + # begin + # response = @client.crm.contacts.basic_api.create(contact_properties) + # p "HubSpot contact created: #{response.to_json}" + # Rails.logger.info("HubSpot contact created: #{response.to_json}") + # true + # rescue Hubspot::ApiError => e + # p "Error creating HubSpot contact: #{e.response_body}" + # Rails.logger.error("Error creating HubSpot contact: #{e.response_body}") + # false + # end + # end + end +end diff --git a/app/services/email/sendgrid.rb b/app/services/email/sendgrid.rb new file mode 100644 index 00000000..5973f069 --- /dev/null +++ b/app/services/email/sendgrid.rb @@ -0,0 +1,45 @@ +module Email + class Sendgrid + require 'sendgrid-ruby' + include SendGrid + + def initialize + @sg = SendGrid::API.new(api_key: ENV.fetch('SENDGRID_API_KEY')) + end + + def add_contact(email) + data = { + contacts: [ + { + email: + } + ] + } + + response = @sg.client.marketing.contacts.put(request_body: data.to_json) + log_response(response) + response + end + + def send_confirmation_email(email) + from = SendGrid::Email.new(email: 'charlie@cheddar.jobs') + to = SendGrid::Email.new(email:) + subject = 'Cheddar sign up confirmation' + content = SendGrid::Content.new(type: 'text/plain', value: 'Thank you for subscribing!') + + mail = SendGrid::Mail.new(from, subject, to, content) + + response = @sg.client.mail._('send').post(request_body: mail.to_json) + log_response(response) + response + end + + private + + def log_response(response) + puts response.status_code + puts response.body + puts response.headers + end + end +end diff --git a/app/views/job_applications/new.html.erb b/app/views/job_applications/new.html.erb index ee1817b4..67bc2c56 100644 --- a/app/views/job_applications/new.html.erb +++ b/app/views/job_applications/new.html.erb @@ -1,5 +1,6 @@ <%= provide(:title, 'New job applications') %> + + + + <%= javascript_importmap_tags %> + + + <%= render "shared/google_analytics" %> + <%= render "shared/hotjar" %> + <%= render "shared/hubspot" %> diff --git a/app/views/pages/_footer.html.erb b/app/views/pages/_footer.html.erb index 979bb7e2..9d92c3d0 100644 --- a/app/views/pages/_footer.html.erb +++ b/app/views/pages/_footer.html.erb @@ -1,39 +1,36 @@ -