From b637c6e7f261a991907750e311eb9c04e36d1f99 Mon Sep 17 00:00:00 2001 From: christianloring Date: Fri, 12 Sep 2025 11:22:48 -0400 Subject: [PATCH 1/6] Events - CRUD Setup --- app/controllers/events_controller.rb | 76 +++++++ app/models/event.rb | 3 + app/views/events/_event.json.jbuilder | 2 + app/views/events/_form.html.erb | 22 ++ app/views/events/edit.html.erb | 6 + app/views/events/index.html.erb | 26 +++ app/views/events/index.json.jbuilder | 1 + app/views/events/new.html.erb | 5 + app/views/events/show.html.erb | 31 +++ app/views/events/show.json.jbuilder | 1 + config/routes.rb | 1 + db/migrate/20250912133056_create_events.rb | 12 + db/schema.rb | 248 +++++++++++---------- spec/factories/events.rb | 5 + spec/models/event_spec.rb | 5 + spec/requests/events_spec.rb | 131 +++++++++++ spec/views/events/edit.html.erb_spec.rb | 14 ++ spec/views/events/index.html.erb_spec.rb | 14 ++ spec/views/events/new.html.erb_spec.rb | 14 ++ spec/views/events/show.html.erb_spec.rb | 11 + 20 files changed, 513 insertions(+), 115 deletions(-) create mode 100644 app/controllers/events_controller.rb create mode 100644 app/models/event.rb create mode 100644 app/views/events/_event.json.jbuilder create mode 100644 app/views/events/_form.html.erb create mode 100644 app/views/events/edit.html.erb create mode 100644 app/views/events/index.html.erb create mode 100644 app/views/events/index.json.jbuilder create mode 100644 app/views/events/new.html.erb create mode 100644 app/views/events/show.html.erb create mode 100644 app/views/events/show.json.jbuilder create mode 100644 db/migrate/20250912133056_create_events.rb create mode 100644 spec/factories/events.rb create mode 100644 spec/models/event_spec.rb create mode 100644 spec/requests/events_spec.rb create mode 100644 spec/views/events/edit.html.erb_spec.rb create mode 100644 spec/views/events/index.html.erb_spec.rb create mode 100644 spec/views/events/new.html.erb_spec.rb create mode 100644 spec/views/events/show.html.erb_spec.rb diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb new file mode 100644 index 000000000..8784b67e3 --- /dev/null +++ b/app/controllers/events_controller.rb @@ -0,0 +1,76 @@ +class EventsController < ApplicationController + before_action :set_event, only: %i[ show edit update destroy ] + before_action :authorize_admin!, only: %i[ edit update destroy ] + + # GET /events or /events.json + def index + @events = Event.all + end + + # GET /events/1 or /events/1.json + def show + end + + # GET /events/new + def new + @event = Event.new + end + + # GET /events/1/edit + def edit + end + + # POST /events or /events.json + def create + @event = Event.new(event_params) + + respond_to do |format| + if @event.save + format.html { redirect_to @event, notice: "Event was successfully created." } + format.json { render :show, status: :created, location: @event } + else + format.html { render :new, status: :unprocessable_entity } + format.json { render json: @event.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /events/1 or /events/1.json + def update + respond_to do |format| + if @event.update(event_params) + format.html { redirect_to @event, notice: "Event was successfully updated." } + format.json { render :show, status: :ok, location: @event } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @event.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /events/1 or /events/1.json + def destroy + @event.destroy + + respond_to do |format| + format.html { redirect_to events_path, status: :see_other, notice: "Event was successfully destroyed." } + format.json { head :no_content } + end + end + + private + + # Use callbacks to share common setup or constraints between actions. + def set_event + @event = Event.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def event_params + params.require(:event).permit(:title, :description, :start_date, :end_date, :registration_close_date) + end + + def authorize_admin! + redirect_to events_path, alert: "You are not authorized to perform this action." unless current_admin + end +end diff --git a/app/models/event.rb b/app/models/event.rb new file mode 100644 index 000000000..72093f23b --- /dev/null +++ b/app/models/event.rb @@ -0,0 +1,3 @@ +class Event < ApplicationRecord + +end diff --git a/app/views/events/_event.json.jbuilder b/app/views/events/_event.json.jbuilder new file mode 100644 index 000000000..86d106bb1 --- /dev/null +++ b/app/views/events/_event.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! event, :id, :created_at, :updated_at +json.url event_url(event, format: :json) diff --git a/app/views/events/_form.html.erb b/app/views/events/_form.html.erb new file mode 100644 index 000000000..fad789a3b --- /dev/null +++ b/app/views/events/_form.html.erb @@ -0,0 +1,22 @@ +<%= form_for(@event) do |f| %> +
+ <%= f.label :title, class: 'bold' %> + <%= f.text_field :title, class: 'form-control' %> + + <%= f.label :description, class: 'bold' %> + <%= f.text_area :description, class: 'form-control' %> + + <%= f.label :start_date, class: 'bold' %> + <%= f.text_field :start_date, type: 'datetime-local', class: 'form-control' %> + + <%= f.label :end_date, class: 'bold' %> + <%= f.text_field :end_date, type: 'datetime-local', class: 'form-control' %> + + <%= f.label :registration_close_date, class: 'bold' %> + <%= f.text_field :registration_close_date, type: 'datetime-local', class: 'form-control' %> +
+ +
+ <%= f.button :submit, class: 'btn btn-primary' %> +
+<% end %> diff --git a/app/views/events/edit.html.erb b/app/views/events/edit.html.erb new file mode 100644 index 000000000..6578b30d6 --- /dev/null +++ b/app/views/events/edit.html.erb @@ -0,0 +1,6 @@ +

Editing Event

+ +<%= render 'form', event: @event %> + +<%= link_to 'Show', @event %> | +<%= link_to 'Back', events_path %> diff --git a/app/views/events/index.html.erb b/app/views/events/index.html.erb new file mode 100644 index 000000000..722085b60 --- /dev/null +++ b/app/views/events/index.html.erb @@ -0,0 +1,26 @@ +

<%= notice %>

+ +

Events

+ + + + + + + + + + <% @events.each do |event| %> + + + + + + + <% end %> + +
<%= event.title %><%= link_to 'Show', event %><%= link_to 'Edit', edit_event_path(event) %><%= link_to 'Destroy', event, method: :delete, data: { confirm: 'Are you sure?' } %>
+ +
+ +<%= link_to 'New Event', new_event_path %> diff --git a/app/views/events/index.json.jbuilder b/app/views/events/index.json.jbuilder new file mode 100644 index 000000000..034fd83a2 --- /dev/null +++ b/app/views/events/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @events, partial: "events/event", as: :event diff --git a/app/views/events/new.html.erb b/app/views/events/new.html.erb new file mode 100644 index 000000000..1feff5f26 --- /dev/null +++ b/app/views/events/new.html.erb @@ -0,0 +1,5 @@ +

New Event

+ +<%= render 'form', event: @event %> + +<%= link_to 'Back', events_path %> diff --git a/app/views/events/show.html.erb b/app/views/events/show.html.erb new file mode 100644 index 000000000..334710dea --- /dev/null +++ b/app/views/events/show.html.erb @@ -0,0 +1,31 @@ +

<%= notice %>

+ +

Event Details

+ +

+ Title: + <%= @event.title %> +

+ +

+ Description: + <%= @event.description %> +

+ +

+ Start Date: + <%= @event.start_date.strftime("%B %d, %Y %I:%M %p") if @event.start_date.present? %> +

+ +

+ End Date: + <%= @event.end_date.strftime("%B %d, %Y %I:%M %p") if @event.end_date.present? %> +

+ +

+ Registration Close Date: + <%= @event.registration_close_date.strftime("%B %d, %Y %I:%M %p") if @event.registration_close_date.present? %> +

+ +<%= link_to 'Edit', edit_event_path(@event) %> | +<%= link_to 'Back', events_path %> diff --git a/app/views/events/show.json.jbuilder b/app/views/events/show.json.jbuilder new file mode 100644 index 000000000..1b1c36edf --- /dev/null +++ b/app/views/events/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "events/event", event: @event diff --git a/config/routes.rb b/config/routes.rb index 298a2b0a2..7d66a022b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,6 +45,7 @@ resources :workshop_log_creation_wizard resources :workshop_logs, only: [:show, :edit, :new, :create, :update] + resources :events resources :resources get 'stories', to: 'resources#stories' diff --git a/db/migrate/20250912133056_create_events.rb b/db/migrate/20250912133056_create_events.rb new file mode 100644 index 000000000..c7abfb42e --- /dev/null +++ b/db/migrate/20250912133056_create_events.rb @@ -0,0 +1,12 @@ +class CreateEvents < ActiveRecord::Migration[6.1] + def change + create_table :events do |t| + t.string "title" + t.text "description" + t.datetime "start_date" + t.datetime "end_date" + t.datetime "registration_close_date" + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f591eaf1e..bec893501 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2,17 +2,17 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema.define(version: 2025_09_12_144532) do - create_table "admins", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "admins", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "first_name", default: "", null: false @@ -31,7 +31,7 @@ t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true end - create_table "age_ranges", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "age_ranges", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -39,14 +39,14 @@ t.index ["windows_type_id"], name: "index_age_ranges_on_windows_type_id" end - create_table "answer_options", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "answer_options", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.integer "order" t.datetime "created_at", null: false t.datetime "updated_at", null: false end - create_table "attachments", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "attachments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "owner_id" t.string "owner_type" t.datetime "created_at", null: false @@ -57,22 +57,22 @@ t.datetime "file_updated_at" end - create_table "banners", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "banners", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.text "content" t.boolean "show" t.datetime "created_at", null: false t.datetime "updated_at", null: false end - create_table "bookmark_annotations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "bookmark_annotations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "bookmark_id" - t.text "annotation", limit: 16777215 + t.text "annotation", size: :medium t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["bookmark_id"], name: "index_bookmark_annotations_on_bookmark_id" end - create_table "bookmarks", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "bookmarks", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "user_id" t.string "bookmarkable_type" t.integer "bookmarkable_id" @@ -81,7 +81,7 @@ t.index ["user_id"], name: "index_bookmarks_on_user_id" end - create_table "categories", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "categories", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "metadatum_id" t.string "name" t.integer "legacy_id" @@ -91,7 +91,7 @@ t.index ["metadatum_id"], name: "index_categories_on_metadatum_id" end - create_table "categorizable_items", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "categorizable_items", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "categorizable_id" t.string "categorizable_type" t.integer "category_id" @@ -102,7 +102,7 @@ t.index ["category_id"], name: "index_categorizable_items_on_category_id" end - create_table "ckeditor_assets", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "ckeditor_assets", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "data_file_name", null: false t.string "data_content_type" t.integer "data_file_size" @@ -126,16 +126,34 @@ t.datetime "updated_at", precision: 6, null: false end - create_table "faqs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "facilitators", force: :cascade do |t| + t.string "first_name", null: false + t.string "last_name", null: false + t.string "email", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + + create_table "events", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.string "title" + t.text "description" + t.datetime "start_date" + t.datetime "end_date" + t.datetime "registration_close_date" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + + create_table "faqs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "question" - t.text "answer", limit: 16777215 + t.text "answer", size: :medium t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "inactive" t.integer "ordering" end - create_table "footers", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "footers", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "phone" t.string "children_program" t.string "adult_program" @@ -144,17 +162,17 @@ t.datetime "updated_at", null: false end - create_table "form_builders", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "form_builders", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.integer "owner_type" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.text "description", limit: 16777215 + t.text "description", size: :medium t.integer "windows_type_id" t.index ["windows_type_id"], name: "index_form_builders_on_windows_type_id" end - create_table "form_field_answer_options", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "form_field_answer_options", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "form_field_id" t.integer "answer_option_id" t.datetime "created_at", null: false @@ -163,7 +181,7 @@ t.index ["form_field_id"], name: "index_form_field_answer_options_on_form_field_id" end - create_table "form_fields", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "form_fields", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "form_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -178,7 +196,7 @@ t.index ["form_id"], name: "index_form_fields_on_form_id" end - create_table "forms", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "forms", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "owner_type" t.integer "owner_id" t.datetime "created_at", null: false @@ -187,7 +205,7 @@ t.index ["form_builder_id"], name: "index_forms_on_form_builder_id" end - create_table "images", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "images", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "owner_id" t.string "owner_type" t.datetime "created_at", null: false @@ -200,7 +218,7 @@ t.index ["owner_id"], name: "index_images_on_owner_id" end - create_table "locations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "locations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "city" t.string "state" t.string "country" @@ -208,7 +226,7 @@ t.datetime "updated_at", null: false end - create_table "media_files", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "media_files", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "file_file_name" t.string "file_content_type" t.integer "file_file_size" @@ -217,7 +235,7 @@ t.integer "workshop_log_id" end - create_table "metadata", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "metadata", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.string "legacy_id" t.datetime "created_at", null: false @@ -225,7 +243,7 @@ t.boolean "published", default: false end - create_table "monthly_reports", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "monthly_reports", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "month" t.integer "project_id" t.integer "project_user_id" @@ -234,11 +252,11 @@ t.boolean "mail_evaluations" t.string "num_ongoing_participants" t.string "num_new_participants" - t.text "most_effective", limit: 16777215 - t.text "most_challenging", limit: 16777215 - t.text "goals_reached", limit: 16777215 - t.text "goals", limit: 16777215 - t.text "comments", limit: 16777215 + t.text "most_effective", size: :medium + t.text "most_challenging", size: :medium + t.text "goals_reached", size: :medium + t.text "goals", size: :medium + t.text "comments", size: :medium t.boolean "call_requested" t.string "best_call_time" t.string "phone" @@ -248,7 +266,7 @@ t.index ["project_user_id"], name: "index_monthly_reports_on_project_user_id" end - create_table "notifications", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "notifications", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "notification_type" @@ -256,26 +274,26 @@ t.integer "noticeable_id" end - create_table "permissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "permissions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "security_cat" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "legacy_id" end - create_table "project_obligations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "project_obligations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false end - create_table "project_statuses", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "project_statuses", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false end - create_table "project_users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "project_users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "agency_id" t.integer "user_id" t.integer "position" @@ -288,7 +306,7 @@ t.index ["user_id"], name: "index_project_users_on_user_id" end - create_table "projects", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "projects", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.integer "location_id" t.datetime "created_at", null: false @@ -298,8 +316,8 @@ t.date "start_date" t.date "end_date" t.string "locality" - t.text "description", limit: 16777215 - t.text "notes", limit: 16777215 + t.text "description", size: :medium + t.text "notes", size: :medium t.string "filemaker_code" t.boolean "inactive", default: false t.integer "legacy_id" @@ -310,7 +328,7 @@ t.index ["windows_type_id"], name: "index_projects_on_windows_type_id" end - create_table "quotable_item_quotes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "quotable_item_quotes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "quotable_type" t.integer "quotable_id" t.integer "legacy_id" @@ -320,8 +338,8 @@ t.index ["quote_id"], name: "index_quotable_item_quotes_on_quote_id" end - create_table "quotes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| - t.text "quote", limit: 16777215 + create_table "quotes", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.text "quote", size: :medium t.boolean "inactive", default: true t.integer "legacy_id" t.boolean "legacy", default: false @@ -334,10 +352,10 @@ t.index ["workshop_id"], name: "index_quotes_on_workshop_id" end - create_table "report_form_field_answers", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "report_form_field_answers", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "report_id" t.integer "form_field_id" - t.text "answer", limit: 16777215 + t.text "answer", size: :medium t.datetime "created_at" t.datetime "updated_at" t.integer "answer_option_id" @@ -346,7 +364,7 @@ t.index ["report_id"], name: "index_report_form_field_answers_on_report_id" end - create_table "reports", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "reports", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "type" t.integer "owner_id" t.string "owner_type" @@ -376,11 +394,11 @@ t.index ["windows_type_id"], name: "index_reports_on_windows_type_id" end - create_table "resources", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "resources", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "title" t.string "author" t.integer "user_id" - t.text "text", limit: 16777215 + t.text "text", size: :medium t.boolean "featured", default: false t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -401,7 +419,7 @@ t.index ["workshop_id"], name: "index_resources_on_workshop_id" end - create_table "sectorable_items", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "sectorable_items", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "sectorable_id" t.string "sectorable_type" t.integer "sector_id" @@ -411,24 +429,24 @@ t.index ["sector_id"], name: "index_sectorable_items_on_sector_id" end - create_table "sectors", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "sectors", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "published", default: false end - create_table "user_form_form_fields", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "user_form_form_fields", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "form_field_id" t.integer "user_form_id" - t.text "text", limit: 16777215 + t.text "text", size: :medium t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["form_field_id"], name: "index_user_form_form_fields_on_form_field_id" t.index ["user_form_id"], name: "index_user_form_form_fields_on_user_form_id" end - create_table "user_forms", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "user_forms", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "user_id" t.integer "form_id" t.datetime "created_at", null: false @@ -437,7 +455,7 @@ t.index ["user_id"], name: "index_user_forms_on_user_id" end - create_table "user_permissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "user_permissions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "user_id" t.integer "permission_id" t.datetime "created_at", null: false @@ -446,7 +464,7 @@ t.index ["user_id"], name: "index_user_permissions_on_user_id" end - create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "users", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "first_name", default: "" @@ -469,8 +487,8 @@ t.string "zip" t.date "birthday" t.string "subscribecode" - t.text "comment", limit: 16777215 - t.text "notes", limit: 16777215 + t.text "comment", size: :medium + t.text "notes", size: :medium t.boolean "legacy", default: false t.boolean "inactive", default: false t.boolean "confirmed", default: true @@ -495,7 +513,7 @@ t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end - create_table "windows_types", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "windows_types", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -503,7 +521,7 @@ t.string "short_name" end - create_table "workshop_age_ranges", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "workshop_age_ranges", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "workshop_id" t.integer "age_range_id" t.datetime "created_at", null: false @@ -512,20 +530,20 @@ t.index ["workshop_id"], name: "index_workshop_age_ranges_on_workshop_id" end - create_table "workshop_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "workshop_logs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "workshop_id" t.integer "user_id" t.date "date" t.integer "rating", default: 0 - t.text "reaction", limit: 16777215 - t.text "successes", limit: 16777215 - t.text "challenges", limit: 16777215 - t.text "suggestions", limit: 16777215 - t.text "questions", limit: 16777215 + t.text "reaction", size: :medium + t.text "successes", size: :medium + t.text "challenges", size: :medium + t.text "suggestions", size: :medium + t.text "questions", size: :medium t.boolean "lead_similar" - t.text "similarities", limit: 16777215 - t.text "differences", limit: 16777215 - t.text "comments", limit: 16777215 + t.text "similarities", size: :medium + t.text "differences", size: :medium + t.text "comments", size: :medium t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "project_id" @@ -537,7 +555,7 @@ t.index ["workshop_id"], name: "index_workshop_logs_on_workshop_id" end - create_table "workshop_resources", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "workshop_resources", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "workshop_id" t.integer "resource_id" t.datetime "created_at", null: false @@ -546,11 +564,11 @@ t.index ["workshop_id"], name: "index_workshop_resources_on_workshop_id" end - create_table "workshop_variations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "workshop_variations", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.integer "workshop_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.text "code", limit: 16777215 + t.text "code", size: :medium t.boolean "inactive", default: true t.integer "ordering" t.string "name" @@ -559,27 +577,27 @@ t.index ["workshop_id"], name: "index_workshop_variations_on_workshop_id" end - create_table "workshops", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb3", force: :cascade do |t| + create_table "workshops", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "title" t.string "full_name" t.string "author_location" t.integer "month" t.integer "year" - t.text "objective", limit: 16777215 - t.text "materials", limit: 16777215 - t.text "timeframe", limit: 16777215 - t.text "age_range", limit: 16777215 - t.text "setup", limit: 16777215 - t.text "instructions", limit: 16777215 - t.text "warm_up", limit: 16777215 - t.text "creation", limit: 16777215 - t.text "closing", limit: 16777215 - t.text "misc_instructions", limit: 16777215 - t.text "project", limit: 16777215 - t.text "description", limit: 16777215 - t.text "notes", limit: 16777215 - t.text "timestamps", limit: 16777215 - t.text "tips", limit: 16777215 + t.text "objective", size: :medium + t.text "materials", size: :medium + t.text "timeframe", size: :medium + t.text "age_range", size: :medium + t.text "setup", size: :medium + t.text "instructions", size: :medium + t.text "warm_up", size: :medium + t.text "creation", size: :medium + t.text "closing", size: :medium + t.text "misc_instructions", size: :medium + t.text "project", size: :medium + t.text "description", size: :medium + t.text "notes", size: :medium + t.text "timestamps", size: :medium + t.text "tips", size: :medium t.string "pub_issue" t.string "misc1" t.string "misc2" @@ -595,36 +613,36 @@ t.integer "windows_type_id" t.integer "user_id" t.integer "led_count", default: 0 - t.text "objective_spanish", limit: 16777215 - t.text "materials_spanish", limit: 16777215 - t.text "timeframe_spanish", limit: 16777215 - t.text "age_range_spanish", limit: 16777215 - t.text "setup_spanish", limit: 16777215 - t.text "instructions_spanish", limit: 16777215 - t.text "project_spanish", limit: 16777215 - t.text "warm_up_spanish", limit: 16777215 - t.text "creation_spanish", limit: 16777215 - t.text "closing_spanish", limit: 16777215 - t.text "misc_instructions_spanish", limit: 16777215 - t.text "description_spanish", limit: 16777215 - t.text "notes_spanish", limit: 16777215 - t.text "tips_spanish", limit: 16777215 + t.text "objective_spanish", size: :medium + t.text "materials_spanish", size: :medium + t.text "timeframe_spanish", size: :medium + t.text "age_range_spanish", size: :medium + t.text "setup_spanish", size: :medium + t.text "instructions_spanish", size: :medium + t.text "project_spanish", size: :medium + t.text "warm_up_spanish", size: :medium + t.text "creation_spanish", size: :medium + t.text "closing_spanish", size: :medium + t.text "misc_instructions_spanish", size: :medium + t.text "description_spanish", size: :medium + t.text "notes_spanish", size: :medium + t.text "tips_spanish", size: :medium t.string "thumbnail_file_name" t.string "thumbnail_content_type" t.integer "thumbnail_file_size" t.datetime "thumbnail_updated_at" - t.text "optional_materials", limit: 16777215 - t.text "optional_materials_spanish", limit: 16777215 - t.text "introduction", limit: 16777215 - t.text "introduction_spanish", limit: 16777215 - t.text "demonstration", limit: 16777215 - t.text "demonstration_spanish", limit: 16777215 - t.text "opening_circle", limit: 16777215 - t.text "opening_circle_spanish", limit: 16777215 - t.text "visualization", limit: 16777215 - t.text "visualization_spanish", limit: 16777215 - t.text "misc1_spanish", limit: 16777215 - t.text "misc2_spanish", limit: 16777215 + t.text "optional_materials", size: :medium + t.text "optional_materials_spanish", size: :medium + t.text "introduction", size: :medium + t.text "introduction_spanish", size: :medium + t.text "demonstration", size: :medium + t.text "demonstration_spanish", size: :medium + t.text "opening_circle", size: :medium + t.text "opening_circle_spanish", size: :medium + t.text "visualization", size: :medium + t.text "visualization_spanish", size: :medium + t.text "misc1_spanish", size: :medium + t.text "misc2_spanish", size: :medium t.integer "time_intro" t.integer "time_demonstration" t.integer "time_warm_up" diff --git a/spec/factories/events.rb b/spec/factories/events.rb new file mode 100644 index 000000000..3c24ee103 --- /dev/null +++ b/spec/factories/events.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :event do + + end +end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb new file mode 100644 index 000000000..68e8f83d5 --- /dev/null +++ b/spec/models/event_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Event, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/requests/events_spec.rb b/spec/requests/events_spec.rb new file mode 100644 index 000000000..7f7cd6dc3 --- /dev/null +++ b/spec/requests/events_spec.rb @@ -0,0 +1,131 @@ +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to test the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. + +RSpec.describe "/events", type: :request do + + # This should return the minimal set of attributes required to create a valid + # Event. As you add validations to Event, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + describe "GET /index" do + it "renders a successful response" do + Event.create! valid_attributes + get events_url + expect(response).to be_successful + end + end + + describe "GET /show" do + it "renders a successful response" do + event = Event.create! valid_attributes + get event_url(event) + expect(response).to be_successful + end + end + + describe "GET /new" do + it "renders a successful response" do + get new_event_url + expect(response).to be_successful + end + end + + describe "GET /edit" do + it "renders a successful response" do + event = Event.create! valid_attributes + get edit_event_url(event) + expect(response).to be_successful + end + end + + describe "POST /create" do + context "with valid parameters" do + it "creates a new Event" do + expect { + post events_url, params: { event: valid_attributes } + }.to change(Event, :count).by(1) + end + + it "redirects to the created event" do + post events_url, params: { event: valid_attributes } + expect(response).to redirect_to(event_url(Event.last)) + end + end + + context "with invalid parameters" do + it "does not create a new Event" do + expect { + post events_url, params: { event: invalid_attributes } + }.to change(Event, :count).by(0) + end + + it "renders a successful response (i.e. to display the 'new' template)" do + post events_url, params: { event: invalid_attributes } + expect(response).to be_successful + end + end + end + + describe "PATCH /update" do + context "with valid parameters" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested event" do + event = Event.create! valid_attributes + patch event_url(event), params: { event: new_attributes } + event.reload + skip("Add assertions for updated state") + end + + it "redirects to the event" do + event = Event.create! valid_attributes + patch event_url(event), params: { event: new_attributes } + event.reload + expect(response).to redirect_to(event_url(event)) + end + end + + context "with invalid parameters" do + it "renders a successful response (i.e. to display the 'edit' template)" do + event = Event.create! valid_attributes + patch event_url(event), params: { event: invalid_attributes } + expect(response).to be_successful + end + end + end + + describe "DELETE /destroy" do + it "destroys the requested event" do + event = Event.create! valid_attributes + expect { + delete event_url(event) + }.to change(Event, :count).by(-1) + end + + it "redirects to the events list" do + event = Event.create! valid_attributes + delete event_url(event) + expect(response).to redirect_to(events_url) + end + end +end diff --git a/spec/views/events/edit.html.erb_spec.rb b/spec/views/events/edit.html.erb_spec.rb new file mode 100644 index 000000000..5eba13a59 --- /dev/null +++ b/spec/views/events/edit.html.erb_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "events/edit", type: :view do + before(:each) do + @event = assign(:event, Event.create!()) + end + + it "renders the edit event form" do + render + + assert_select "form[action=?][method=?]", event_path(@event), "post" do + end + end +end diff --git a/spec/views/events/index.html.erb_spec.rb b/spec/views/events/index.html.erb_spec.rb new file mode 100644 index 000000000..2638a6198 --- /dev/null +++ b/spec/views/events/index.html.erb_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "events/index", type: :view do + before(:each) do + assign(:events, [ + Event.create!(), + Event.create!() + ]) + end + + it "renders a list of events" do + render + end +end diff --git a/spec/views/events/new.html.erb_spec.rb b/spec/views/events/new.html.erb_spec.rb new file mode 100644 index 000000000..36b20731b --- /dev/null +++ b/spec/views/events/new.html.erb_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "events/new", type: :view do + before(:each) do + assign(:event, Event.new()) + end + + it "renders new event form" do + render + + assert_select "form[action=?][method=?]", events_path, "post" do + end + end +end diff --git a/spec/views/events/show.html.erb_spec.rb b/spec/views/events/show.html.erb_spec.rb new file mode 100644 index 000000000..12809f06c --- /dev/null +++ b/spec/views/events/show.html.erb_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +RSpec.describe "events/show", type: :view do + before(:each) do + @event = assign(:event, Event.create!()) + end + + it "renders attributes in

" do + render + end +end From 5f017d8a1172399b6d26f4e520551cb5fe691fb6 Mon Sep 17 00:00:00 2001 From: christianloring Date: Fri, 12 Sep 2025 14:26:09 -0400 Subject: [PATCH 2/6] Event Registration & add publicly visible to events --- .../event_registrations_controller.rb | 16 ++++ app/models/event.rb | 2 +- app/models/event_registration.rb | 3 + app/views/dashboard/index.html.erb | 4 + app/views/events/_form.html.erb | 5 ++ ...250912155207_create_event_registrations.rb | 12 +++ ...12173152_add_publicly_visible_to_events.rb | 5 ++ db/schema.rb | 12 +++ spec/factories/event_registrations.rb | 8 ++ spec/models/event_registration_spec.rb | 5 ++ spec/models/event_spec.rb | 7 +- spec/requests/events_spec.rb | 75 +++++++++++++++---- 12 files changed, 136 insertions(+), 18 deletions(-) create mode 100644 app/controllers/event_registrations_controller.rb create mode 100644 app/models/event_registration.rb create mode 100644 db/migrate/20250912155207_create_event_registrations.rb create mode 100644 db/migrate/20250912173152_add_publicly_visible_to_events.rb create mode 100644 spec/factories/event_registrations.rb create mode 100644 spec/models/event_registration_spec.rb diff --git a/app/controllers/event_registrations_controller.rb b/app/controllers/event_registrations_controller.rb new file mode 100644 index 000000000..2b9d99a25 --- /dev/null +++ b/app/controllers/event_registrations_controller.rb @@ -0,0 +1,16 @@ +class EventRegistrationsController < ApplicationController + def create + @event_registration = EventRegistration.new(event_registration_params) + if @event_registration.save + redirect_to @event_registration.event, notice: 'Successfully registered for the event.' + else + render :new + end + end + + private + + def event_registration_params + params.require(:event_registration).permit(:event_id, :user_id) + end +end \ No newline at end of file diff --git a/app/models/event.rb b/app/models/event.rb index 72093f23b..0067e2768 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,3 +1,3 @@ class Event < ApplicationRecord - + validates_presence_of :title, :start_date, :end_date, :publicly_visible end diff --git a/app/models/event_registration.rb b/app/models/event_registration.rb new file mode 100644 index 000000000..99424a88e --- /dev/null +++ b/app/models/event_registration.rb @@ -0,0 +1,3 @@ +class EventRegistration < ApplicationRecord + belongs_to :event +end diff --git a/app/views/dashboard/index.html.erb b/app/views/dashboard/index.html.erb index e661d618f..043b8cdb2 100644 --- a/app/views/dashboard/index.html.erb +++ b/app/views/dashboard/index.html.erb @@ -135,6 +135,10 @@ +

+ <%= link_to 'View Events', events_path, class: 'btn btn-primary' %> +
+