From 1c95cbc31b54e8f78034c38988dd70bb52596ac9 Mon Sep 17 00:00:00 2001 From: northeastprince Date: Sun, 6 Aug 2023 14:08:43 -0400 Subject: [PATCH] Hackathon applications --- Gemfile | 2 + Gemfile.lock | 14 ++++++ app/assets/stylesheets/animations.scss | 9 ++++ app/assets/stylesheets/application.css | 15 ------- app/assets/stylesheets/application.scss | 6 +++ app/assets/stylesheets/components/flash.scss | 18 ++++++++ app/assets/stylesheets/text.scss | 7 +++ app/controllers/concerns/hackathon_scoped.rb | 13 ++++++ .../hackathons/submissions_controller.rb | 39 +++++++++++++++++ app/controllers/hackathons_controller.rb | 1 + app/javascript/application.js | 3 ++ ...oller.js => element_removal_controller.js} | 4 +- app/models/hackathon/regional.rb | 2 + app/models/hackathon/scheduled.rb | 27 ++++++++++-- app/views/hackathons/index.html.erb | 0 .../hackathons/submissions/index.html.erb | 8 ++++ app/views/hackathons/submissions/new.html.erb | 43 +++++++++++++++++++ .../hackathons/submissions/show.html.erb | 3 ++ app/views/layouts/application.html.erb | 1 + app/views/shared/_flash.html.erb | 7 +++ config/importmap.rb | 1 + config/initializers/field_with_errors.rb | 13 ++++++ config/routes.rb | 4 ++ test/fixtures/hackathons.yml | 3 -- 24 files changed, 220 insertions(+), 23 deletions(-) create mode 100644 app/assets/stylesheets/animations.scss delete mode 100644 app/assets/stylesheets/application.css create mode 100644 app/assets/stylesheets/application.scss create mode 100644 app/assets/stylesheets/components/flash.scss create mode 100644 app/assets/stylesheets/text.scss create mode 100644 app/controllers/concerns/hackathon_scoped.rb create mode 100644 app/controllers/hackathons/submissions_controller.rb rename app/javascript/controllers/{hello_controller.js => element_removal_controller.js} (61%) delete mode 100644 app/views/hackathons/index.html.erb create mode 100644 app/views/hackathons/submissions/index.html.erb create mode 100644 app/views/hackathons/submissions/new.html.erb create mode 100644 app/views/hackathons/submissions/show.html.erb create mode 100644 app/views/shared/_flash.html.erb create mode 100644 config/initializers/field_with_errors.rb diff --git a/Gemfile b/Gemfile index ccad9794..3c59906f 100644 --- a/Gemfile +++ b/Gemfile @@ -16,9 +16,11 @@ gem "airrecord" # Airtable client # Assets gem "sprockets-rails" +gem "sass-rails" gem "importmap-rails" gem "turbo-rails" gem "stimulus-rails" +gem "local_time" # Active Storage gem "aws-sdk-s3", require: false diff --git a/Gemfile.lock b/Gemfile.lock index d69194ac..957e49b2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -163,6 +163,7 @@ GEM railties (>= 5.2) rexml lint_roller (1.1.0) + local_time (2.1.0) loofah (2.21.3) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -269,6 +270,16 @@ GEM ffi (~> 1.12) ruby2_keywords (0.0.5) rubyzip (2.3.2) + sass-rails (6.0.0) + sassc-rails (~> 2.1, >= 2.1.1) + sassc (2.4.0) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt selenium-webdriver (4.10.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) @@ -301,6 +312,7 @@ GEM stimulus-rails (1.2.1) railties (>= 6.0.0) thor (1.2.2) + tilt (2.2.0) timeout (0.4.0) turbo-rails (1.4.0) actionpack (>= 6.0.0) @@ -348,12 +360,14 @@ DEPENDENCIES importmap-rails jbuilder letter_opener_web + local_time pagy pg puma rack-mini-profiler rails (~> 7.0.6) redis + sass-rails selenium-webdriver shoulda spring diff --git a/app/assets/stylesheets/animations.scss b/app/assets/stylesheets/animations.scss new file mode 100644 index 00000000..a35bf988 --- /dev/null +++ b/app/assets/stylesheets/animations.scss @@ -0,0 +1,9 @@ +@keyframes appear-then-fade { + 0%,100% { + opacity: 0 + } + + 6%,66% { + opacity: 1 + } +} diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css deleted file mode 100644 index 288b9ab7..00000000 --- a/app/assets/stylesheets/application.css +++ /dev/null @@ -1,15 +0,0 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's - * vendor/assets/stylesheets directory can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the bottom of the - * compiled file so the styles you add here take precedence over styles defined in any other CSS - * files in this directory. Styles in this file should be added after the last require_* statement. - * It is generally better to create a new file per style scope. - * - *= require_tree . - *= require_self - */ diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss new file mode 100644 index 00000000..c9808f8c --- /dev/null +++ b/app/assets/stylesheets/application.scss @@ -0,0 +1,6 @@ +@import "*"; +@import "components/*"; + +.field_with_errors { + border: darkred 1px solid; +} diff --git a/app/assets/stylesheets/components/flash.scss b/app/assets/stylesheets/components/flash.scss new file mode 100644 index 00000000..d9706860 --- /dev/null +++ b/app/assets/stylesheets/components/flash.scss @@ -0,0 +1,18 @@ +.flash-notice { + position: fixed; + top: 5rem; + width: 100%; + display: flex; + justify-content: center; +} + +.flash-notice__content { + background-color: rgba(35, 28, 51, 0.75); + animation: appear-then-fade 6s 250ms both; + border-radius: 5rem; + padding: .5rem 1.5rem; + display: flex; + align-items: center; + color: gainsboro; + backdrop-filter: blur(3px) contrast(0.5) +} diff --git a/app/assets/stylesheets/text.scss b/app/assets/stylesheets/text.scss new file mode 100644 index 00000000..ae5095bf --- /dev/null +++ b/app/assets/stylesheets/text.scss @@ -0,0 +1,7 @@ +.heading--medium { + font-size: 2rem; +} + +.text--small { + font-size: 1rem; +} diff --git a/app/controllers/concerns/hackathon_scoped.rb b/app/controllers/concerns/hackathon_scoped.rb new file mode 100644 index 00000000..7314d7f7 --- /dev/null +++ b/app/controllers/concerns/hackathon_scoped.rb @@ -0,0 +1,13 @@ +module HackathonScoped + extend ActiveSupport::Concern + + included do + before_action :set_hackathon, except: [:index, :new, :create] + end + + private + + def set_hackathon + @hackathon = Hackathon.find(params[:hackathon_id]) + end +end diff --git a/app/controllers/hackathons/submissions_controller.rb b/app/controllers/hackathons/submissions_controller.rb new file mode 100644 index 00000000..4d612d5c --- /dev/null +++ b/app/controllers/hackathons/submissions_controller.rb @@ -0,0 +1,39 @@ +class Hackathons::SubmissionsController < ApplicationController + def index + @hackathons = Hackathon.not_approved.where applicant: Current.user + + redirect_to new_hackathons_request_path if @hackathons.none? + end + + def new + @hackathon = Hackathon.new + end + + def create + @hackathon = Hackathon.new(hackathon_params) + if @hackathon.save context: :submit + redirect_to hackathons_requests_path, notice: "Your hackathon has been submitted for approval!" + else + render :new, status: :unprocessable_entity + end + end + + def show + @hackathon = Hackathon.not_approved.where(applicant: Current.user).find(params[:id]) + end + + private + + def hackathon_params + params.require(:hackathon).permit( + :name, + :website, + :logo, + :banner, + :starts_at, + :ends_at, + :address, + :expected_attendees + ) + end +end diff --git a/app/controllers/hackathons_controller.rb b/app/controllers/hackathons_controller.rb index 8cfa1fd8..4298e552 100644 --- a/app/controllers/hackathons_controller.rb +++ b/app/controllers/hackathons_controller.rb @@ -2,5 +2,6 @@ class HackathonsController < ApplicationController skip_before_action :redirect_if_unauthenticated def index + redirect_to "https://hackathons.hackclub.com", allow_other_host: true end end diff --git a/app/javascript/application.js b/app/javascript/application.js index 0d7b4940..35815167 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,3 +1,6 @@ // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails import "@hotwired/turbo-rails" import "controllers" + +import LocalTime from "local-time" +LocalTime.start() diff --git a/app/javascript/controllers/hello_controller.js b/app/javascript/controllers/element_removal_controller.js similarity index 61% rename from app/javascript/controllers/hello_controller.js rename to app/javascript/controllers/element_removal_controller.js index 5975c078..9d4172df 100644 --- a/app/javascript/controllers/hello_controller.js +++ b/app/javascript/controllers/element_removal_controller.js @@ -1,7 +1,7 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { - connect() { - this.element.textContent = "Hello World!" + remove() { + this.element.remove() } } diff --git a/app/models/hackathon/regional.rb b/app/models/hackathon/regional.rb index a1555b75..19bd4bf8 100644 --- a/app/models/hackathon/regional.rb +++ b/app/models/hackathon/regional.rb @@ -2,6 +2,8 @@ module Hackathon::Regional extend ActiveSupport::Concern included do + validates :address, presence: true, on: :submit + geocoded_by :address reverse_geocoded_by :latitude, :longitude do |hackathon, results| # essentially formats the location if (result = results.first) diff --git a/app/models/hackathon/scheduled.rb b/app/models/hackathon/scheduled.rb index db13196a..a8155faa 100644 --- a/app/models/hackathon/scheduled.rb +++ b/app/models/hackathon/scheduled.rb @@ -2,12 +2,33 @@ module Hackathon::Scheduled extend ActiveSupport::Concern included do + scope :upcoming, -> { where("starts_at > ?", Time.now) } + validates :starts_at, presence: true validates :ends_at, presence: true - validate do - errors.add(:ends_at, :before_start_date) if errors.none? && ends_at < starts_at + + validate :dates_are_chronological, + unless: -> { errors.include?(:starts_at) || errors.include?(:ends_at) } + + validate :dates_are_in_the_future, on: :submit, + unless: -> { errors.include?(:starts_at) || errors.include?(:ends_at) } + end + + private + + def dates_are_chronological + if ends_at < starts_at + errors.add(:ends_at, :before_the_start) end + end - scope :upcoming, -> { where("starts_at > ?", Time.now) } + def dates_are_in_the_future + if starts_at < Time.now + errors.add(:starts_at, :in_the_past) + end + + if ends_at < Time.now + errors.add(:ends_at, :in_the_past) + end end end diff --git a/app/views/hackathons/index.html.erb b/app/views/hackathons/index.html.erb deleted file mode 100644 index e69de29b..00000000 diff --git a/app/views/hackathons/submissions/index.html.erb b/app/views/hackathons/submissions/index.html.erb new file mode 100644 index 00000000..9ec17b20 --- /dev/null +++ b/app/views/hackathons/submissions/index.html.erb @@ -0,0 +1,8 @@ +

Your Hackathon Submissions

+<% @hackathons.each do |hackathon| %> +
+ <%= link_to hackathon.name, hackathons_request_path(hackathon) %> + + <%= hackathon.events.timelined.last %> +
+<% end %> diff --git a/app/views/hackathons/submissions/new.html.erb b/app/views/hackathons/submissions/new.html.erb new file mode 100644 index 00000000..7e9ea833 --- /dev/null +++ b/app/views/hackathons/submissions/new.html.erb @@ -0,0 +1,43 @@ +<%= form_with model: @hackathon, url: hackathons_requests_path do |form| %> +
+ <%= form.label :name, "Hackathon Name" %> + <%= form.text_field :name, required: true, placeholder: "Epoch" %> +
+ +
+ <%= form.label :website %> + <%= form.text_field :website, required: true, placeholder: "epoch.hackclub.com" %> +
+ +
+ <%= form.label :logo %> + <%= form.file_field :logo %> +
+ +
+ <%= form.label :banner %> + <%= form.file_field :banner %> +
+ +
+ <%= form.label :start_date %> + <%= form.date_field :starts_at, required: true %> +
+ +
+ <%= form.label :end_date %> + <%= form.date_field :ends_at, required: true %> +
+ +
+ <%= form.label :location %> + <%= form.text_field :address, required: true, placeholder: "15 Falls Rd, Shelburne VT" %> +
+ +
+ <%= form.label :expected_attendees %> + <%= form.number_field :expected_attendees, required: true %> +
+ + <%= form.button "Submit for Review" %> +<% end %> diff --git a/app/views/hackathons/submissions/show.html.erb b/app/views/hackathons/submissions/show.html.erb new file mode 100644 index 00000000..c267d233 --- /dev/null +++ b/app/views/hackathons/submissions/show.html.erb @@ -0,0 +1,3 @@ +

+ <%= @hackathon.name %> +

diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 9175c836..28ccf5e5 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -12,6 +12,7 @@ + <%= render "shared/flash" %> <%= yield %> diff --git a/app/views/shared/_flash.html.erb b/app/views/shared/_flash.html.erb new file mode 100644 index 00000000..c7470b23 --- /dev/null +++ b/app/views/shared/_flash.html.erb @@ -0,0 +1,7 @@ +<% if flash.notice %> +
+
+ <%= flash.notice %> +
+
+<% end %> diff --git a/config/importmap.rb b/config/importmap.rb index 8dce42d4..f81a5015 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -5,3 +5,4 @@ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers" +pin "local-time", to: "https://ga.jspm.io/npm:local-time@2.1.0/app/assets/javascripts/local-time.js" diff --git a/config/initializers/field_with_errors.rb b/config/initializers/field_with_errors.rb new file mode 100644 index 00000000..b34d5200 --- /dev/null +++ b/config/initializers/field_with_errors.rb @@ -0,0 +1,13 @@ +Rails.application.configure do + config.action_view.field_error_proc = proc do |html_tag, instance| + if html_tag !~ /^input/ + html_tag + elsif (class_attribute_index = html_tag.index('class="')) + html_tag.insert(class_attribute_index + 7, "field_with_errors ") + elsif html_tag.index("/>") + html_tag.insert(html_tag.index("/>"), " class=field_with_errors ") + else + html_tag.insert(html_tag.index(">"), " class=field_with_errors ") + end + end +end diff --git a/config/routes.rb b/config/routes.rb index eeb11d66..25ff0a5d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,5 +16,9 @@ end end + namespace :hackathons do + resources :submissions, only: [:index, :new, :create, :show] + end + mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development? end diff --git a/test/fixtures/hackathons.yml b/test/fixtures/hackathons.yml index e7bc9433..0e312ebf 100644 --- a/test/fixtures/hackathons.yml +++ b/test/fixtures/hackathons.yml @@ -17,7 +17,6 @@ zephyr: high_school_led: true expected_attendees: 50 modality: <%= Hackathon.modalities[:in_person] %> - financial_assistance: true applicant: gary seattle_hacks: name: SeattleHacks @@ -28,7 +27,6 @@ seattle_hacks: high_school_led: true expected_attendees: 50 modality: <%= Hackathon.modalities[:in_person] %> - financial_assistance: true applicant: gary city: Seattle province: Washington @@ -42,7 +40,6 @@ bellevue_hacks: high_school_led: true expected_attendees: 50 modality: <%= Hackathon.modalities[:in_person] %> - financial_assistance: true applicant: gary city: Bellevue province: Washington