From 9210f0850d08f2b5b44ecb948a67b52e32b635f9 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Sun, 17 Mar 2024 11:18:34 -0700
Subject: [PATCH 01/36] Add starter code for pd frontend
---
app/controllers/workshops_controller.rb | 52 +++++++++++++++++++++++++
app/helpers/application_helper.rb | 3 +-
app/helpers/workshops_helper.rb | 2 +
app/views/workshops/create.html.erb | 2 +
app/views/workshops/destroy.html.erb | 2 +
app/views/workshops/edit.html.erb | 2 +
app/views/workshops/index.html.erb | 34 ++++++++++++++++
app/views/workshops/new.html.erb | 2 +
app/views/workshops/show.html.erb | 2 +
app/views/workshops/update.html.erb | 2 +
config/routes.rb | 8 ++++
11 files changed, 110 insertions(+), 1 deletion(-)
create mode 100644 app/controllers/workshops_controller.rb
create mode 100644 app/helpers/workshops_helper.rb
create mode 100644 app/views/workshops/create.html.erb
create mode 100644 app/views/workshops/destroy.html.erb
create mode 100644 app/views/workshops/edit.html.erb
create mode 100644 app/views/workshops/index.html.erb
create mode 100644 app/views/workshops/new.html.erb
create mode 100644 app/views/workshops/show.html.erb
create mode 100644 app/views/workshops/update.html.erb
diff --git a/app/controllers/workshops_controller.rb b/app/controllers/workshops_controller.rb
new file mode 100644
index 00000000..49278b1b
--- /dev/null
+++ b/app/controllers/workshops_controller.rb
@@ -0,0 +1,52 @@
+class WorkshopsController < ApplicationController
+ def index
+ # TODO: here is mocked data for workshops. Fix them after the model is created and the database is seeded.
+ @workshops = [
+ OpenStruct.new(
+ id: 1,
+ name: "Web Development Basics",
+ location: "San Francisco",
+ start_date: "2024-04-01",
+ end_date: "2024-04-30",
+ grade_level: "Beginner",
+ registration_open: true
+ ),
+ OpenStruct.new(
+ id: 2,
+ name: "Advanced Pottery",
+ location: "New York",
+ start_date: "2024-05-15",
+ end_date: "2024-06-15",
+ grade_level: "Advanced",
+ registration_open: false
+ ),
+ OpenStruct.new(
+ id: 3,
+ name: "Digital Photography",
+ location: "London",
+ start_date: "2024-07-01",
+ end_date: "2024-07-31",
+ grade_level: "Intermediate",
+ registration_open: true
+ )
+ ]
+ end
+
+ def show
+ end
+
+ def new
+ end
+
+ def edit
+ end
+
+ def create
+ end
+
+ def update
+ end
+
+ def destroy
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 1423a740..db597ddc 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -19,7 +19,8 @@ def admin_nav_links
"Dashboard": dashboard_path,
"Schools": schools_path,
"Teachers": teachers_path,
- "Email Templates": email_templates_path
+ "Email Templates": email_templates_path,
+ "Workshops": workshops_path,
}
end
diff --git a/app/helpers/workshops_helper.rb b/app/helpers/workshops_helper.rb
new file mode 100644
index 00000000..215662bd
--- /dev/null
+++ b/app/helpers/workshops_helper.rb
@@ -0,0 +1,2 @@
+module WorkshopsHelper
+end
diff --git a/app/views/workshops/create.html.erb b/app/views/workshops/create.html.erb
new file mode 100644
index 00000000..6010fb78
--- /dev/null
+++ b/app/views/workshops/create.html.erb
@@ -0,0 +1,2 @@
+
Workshops#create
+Find me in app/views/workshops/create.html.erb
diff --git a/app/views/workshops/destroy.html.erb b/app/views/workshops/destroy.html.erb
new file mode 100644
index 00000000..d5661876
--- /dev/null
+++ b/app/views/workshops/destroy.html.erb
@@ -0,0 +1,2 @@
+Workshops#destroy
+Find me in app/views/workshops/destroy.html.erb
diff --git a/app/views/workshops/edit.html.erb b/app/views/workshops/edit.html.erb
new file mode 100644
index 00000000..bed91225
--- /dev/null
+++ b/app/views/workshops/edit.html.erb
@@ -0,0 +1,2 @@
+Workshops#edit
+Find me in app/views/workshops/edit.html.erb
diff --git a/app/views/workshops/index.html.erb b/app/views/workshops/index.html.erb
new file mode 100644
index 00000000..3424aeea
--- /dev/null
+++ b/app/views/workshops/index.html.erb
@@ -0,0 +1,34 @@
+<%= provide(:title, "BJC Schools") %>
+<%= provide(:header_button, "New Workshop") %>
+
+
+
+
+ Name |
+ Location |
+ Start Date |
+ End Date |
+ Grade Level |
+ Registration Open |
+ Actions |
+
+
+
+ <% @workshops.each_with_index do |workshop, index| %>
+
+ <%= link_to(workshop.name, workshop_path(workshop)) %> |
+ <%= workshop.location %> |
+ <%= workshop.start_date %> |
+ <%= workshop.end_date %> |
+ <%= workshop.grade_level %> |
+ <%= workshop.registration_open ? 'Yes' : 'No' %> |
+
+
+ <%= link_to("Edit", edit_workshop_path(workshop), class: "btn btn-info") %>
+ <%= link_to("❌", workshop_path(workshop), method: "delete", class: "btn btn-outline-danger", data: {confirm: "Are you sure?"}) %>
+
+ |
+
+ <% end %>
+
+
diff --git a/app/views/workshops/new.html.erb b/app/views/workshops/new.html.erb
new file mode 100644
index 00000000..571fde1c
--- /dev/null
+++ b/app/views/workshops/new.html.erb
@@ -0,0 +1,2 @@
+Workshops#new
+Find me in app/views/workshops/new.html.erb
diff --git a/app/views/workshops/show.html.erb b/app/views/workshops/show.html.erb
new file mode 100644
index 00000000..4986c74e
--- /dev/null
+++ b/app/views/workshops/show.html.erb
@@ -0,0 +1,2 @@
+Workshops#show
+Find me in app/views/workshops/show.html.erb
diff --git a/app/views/workshops/update.html.erb b/app/views/workshops/update.html.erb
new file mode 100644
index 00000000..1716c5a6
--- /dev/null
+++ b/app/views/workshops/update.html.erb
@@ -0,0 +1,2 @@
+Workshops#update
+Find me in app/views/workshops/update.html.erb
diff --git a/config/routes.rb b/config/routes.rb
index beab79d8..ce067803 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,13 @@
# frozen_string_literal: true
Rails.application.routes.draw do
+ get 'workshops/index'
+ get 'workshops/show'
+ get 'workshops/new'
+ get 'workshops/edit'
+ get 'workshops/create'
+ get 'workshops/update'
+ get 'workshops/destroy'
mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
# The priority is based upon order of creation: first created -> highest priority.
@@ -19,6 +26,7 @@
resources :schools
resources :pages, param: :url_slug
resources :email_templates, except: [:show]
+ resources :workshops
get "/login", to: "sessions#new", as: "login"
delete "/logout", to: "sessions#destroy", as: "logout"
From a194ed69ba18fd553a6ee55aedd3599671055305 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Sun, 17 Mar 2024 20:27:46 -0700
Subject: [PATCH 02/36] Backup work for frontend
---
app/controllers/workshops_controller.rb | 65 +++++++++++--------
app/models/workshop.rb | 53 +++++++++++++++
app/views/workshops/_form.html.erb | 85 +++++++++++++++++++++++++
app/views/workshops/edit.html.erb | 15 ++++-
4 files changed, 191 insertions(+), 27 deletions(-)
create mode 100644 app/models/workshop.rb
create mode 100644 app/views/workshops/_form.html.erb
diff --git a/app/controllers/workshops_controller.rb b/app/controllers/workshops_controller.rb
index 49278b1b..6bf4a32f 100644
--- a/app/controllers/workshops_controller.rb
+++ b/app/controllers/workshops_controller.rb
@@ -1,29 +1,62 @@
class WorkshopsController < ApplicationController
+
+ # TODO: revise any method using `set_workshops` to use `MockWorkshop.all` instead. It's currently used for mocking data.
+ before_action :set_workshops, only: [:show, :edit]
+
def index
- # TODO: here is mocked data for workshops. Fix them after the model is created and the database is seeded.
+ set_workshops
+ end
+
+ def show
+ @workshop = @workshops.find { |workshop| workshop.id == params[:id].to_i }
+ end
+
+ def new
+ end
+
+ def edit
+ @workshop = @workshops.find { |workshop| workshop.id == params[:id].to_i }
+ end
+
+ def create
+ end
+
+ def update
+ end
+
+ def destroy
+ end
+
+ def set_workshops
@workshops = [
- OpenStruct.new(
+ Workshop.new(
id: 1,
name: "Web Development Basics",
- location: "San Francisco",
+ city: "San Francisco",
+ state: "CA",
+ country: "USA",
start_date: "2024-04-01",
end_date: "2024-04-30",
grade_level: "Beginner",
registration_open: true
),
- OpenStruct.new(
+ Workshop.new(
id: 2,
name: "Advanced Pottery",
- location: "New York",
+ city: "New York",
+ state: "NY",
+ country: "USA",
start_date: "2024-05-15",
end_date: "2024-06-15",
grade_level: "Advanced",
registration_open: false
),
- OpenStruct.new(
+ Workshop.new(
id: 3,
name: "Digital Photography",
- location: "London",
+ city: "London",
+ state: "",
+ country: "UK",
start_date: "2024-07-01",
end_date: "2024-07-31",
grade_level: "Intermediate",
@@ -31,22 +64,4 @@ def index
)
]
end
-
- def show
- end
-
- def new
- end
-
- def edit
- end
-
- def create
- end
-
- def update
- end
-
- def destroy
- end
end
diff --git a/app/models/workshop.rb b/app/models/workshop.rb
new file mode 100644
index 00000000..2b23ece6
--- /dev/null
+++ b/app/models/workshop.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+# == Schema Information
+#
+# Table name: workshops
+#
+# id :integer not null, primary key
+# city :string
+# country :string
+# grade_level :integer
+# lat :float
+# lng :float
+# name :string
+# state :string
+# tags :text default([]), is an Array
+# teachers_count :integer default(0)
+# website :string TODO: Confirm is it necessary field
+# created_at :datetime
+# updated_at :datetime
+#
+# Indexes
+#
+# TODO: Define indexes
+
+
+class Workshop
+ include ActiveModel::Model
+ include ActiveModel::Attributes # Make sure this is included
+
+ # Define attributes
+ attribute :id, :integer
+ attribute :name, :string
+ attribute :city, :string
+ attribute :state, :string
+ attribute :country, :string
+ attribute :start_date, :date
+ attribute :end_date, :date
+ attribute :grade_level, :string
+ attribute :registration_open, :boolean
+
+ def initialize(attributes = {})
+ super(attributes)
+ # Now ActiveModel handles attributes, no need to manually set defaults for attributes defined above
+ end
+
+ def persisted?
+ id.present?
+ end
+
+ def location
+ "#{city}, #{state}, #{country}"
+ end
+end
diff --git a/app/views/workshops/_form.html.erb b/app/views/workshops/_form.html.erb
new file mode 100644
index 00000000..e024a559
--- /dev/null
+++ b/app/views/workshops/_form.html.erb
@@ -0,0 +1,85 @@
+Create a new Workshop
+
+
+
+
+
+ <%= f.label :grade_level, "Grade Level", for: "workshop_grade_level" %>
+ <%= f.select(
+ :grade_level,
+ options_for_select(School.grade_level_options, workshop.grade_level),
+ { include_blank: "Select a grade level" },
+ { class: 'form-control', required: true, id: 'workshop_grade_level' }
+ ) %>
+
+
+
diff --git a/app/views/workshops/edit.html.erb b/app/views/workshops/edit.html.erb
index bed91225..82ee5a36 100644
--- a/app/views/workshops/edit.html.erb
+++ b/app/views/workshops/edit.html.erb
@@ -1,2 +1,13 @@
-Workshops#edit
-Find me in app/views/workshops/edit.html.erb
+<%= provide(:h1, "Update #{@workshop.name}") %>
+
+<% if @workshop.nil? %>
+ Workshop not found.
+<% else %>
+ <%= form_for @workshop do |f| %>
+ <%= render 'workshops/form', f: f, workshop: @workshop %>
+
+
+ <%= f.submit 'Submit', {class: 'btn btn-primary', id: 'submit_button'} %>
+
+ <% end %>
+<% end %>
From 292e245a1d70313ef018c2aec6a8107430d1b544 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Mon, 18 Mar 2024 12:29:15 -0700
Subject: [PATCH 03/36] Backup frontend work
---
app/controllers/workshops_controller.rb | 6 +++---
app/models/workshop.rb | 18 +++++++++++++++++-
app/views/workshops/_form.html.erb | 7 +++----
app/views/workshops/index.html.erb | 2 +-
4 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/app/controllers/workshops_controller.rb b/app/controllers/workshops_controller.rb
index 6bf4a32f..0693d38e 100644
--- a/app/controllers/workshops_controller.rb
+++ b/app/controllers/workshops_controller.rb
@@ -37,7 +37,7 @@ def set_workshops
country: "USA",
start_date: "2024-04-01",
end_date: "2024-04-30",
- grade_level: "Beginner",
+ # grade_level: "University",
registration_open: true
),
Workshop.new(
@@ -48,7 +48,7 @@ def set_workshops
country: "USA",
start_date: "2024-05-15",
end_date: "2024-06-15",
- grade_level: "Advanced",
+ grade_level: 1,
registration_open: false
),
Workshop.new(
@@ -59,7 +59,7 @@ def set_workshops
country: "UK",
start_date: "2024-07-01",
end_date: "2024-07-31",
- grade_level: "Intermediate",
+ grade_level: 0,
registration_open: true
)
]
diff --git a/app/models/workshop.rb b/app/models/workshop.rb
index 2b23ece6..00ab48f5 100644
--- a/app/models/workshop.rb
+++ b/app/models/workshop.rb
@@ -35,9 +35,17 @@ class Workshop
attribute :country, :string
attribute :start_date, :date
attribute :end_date, :date
- attribute :grade_level, :string
+ attribute :grade_level, :integer, default: -1
attribute :registration_open, :boolean
+ GRADE_LEVELS = {
+ elementary: 0,
+ middle_school: 1,
+ high_school: 2,
+ community_college: 3,
+ university: 4
+ }.freeze
+
def initialize(attributes = {})
super(attributes)
# Now ActiveModel handles attributes, no need to manually set defaults for attributes defined above
@@ -50,4 +58,12 @@ def persisted?
def location
"#{city}, #{state}, #{country}"
end
+
+ def display_grade_level
+ # Directly access the grade_level attribute
+ grade_level_value = self.grade_level
+ return "Unknown" if grade_level_value == -1
+
+ GRADE_LEVELS.key(grade_level_value).to_s.titlecase
+ end
end
diff --git a/app/views/workshops/_form.html.erb b/app/views/workshops/_form.html.erb
index e024a559..9907509a 100644
--- a/app/views/workshops/_form.html.erb
+++ b/app/views/workshops/_form.html.erb
@@ -7,12 +7,10 @@
@@ -52,7 +51,7 @@
:grade_level,
options_for_select(School.grade_level_options, workshop.grade_level),
{ include_blank: "Select a grade level" },
- { class: 'form-control', required: true, id: 'workshop_grade_level' }
+ { class: 'form-control', required: false, id: 'workshop_grade_level' }
) %>
diff --git a/app/views/workshops/index.html.erb b/app/views/workshops/index.html.erb
index 3424aeea..d092f9dc 100644
--- a/app/views/workshops/index.html.erb
+++ b/app/views/workshops/index.html.erb
@@ -20,7 +20,7 @@
<%= workshop.location %> |
<%= workshop.start_date %> |
<%= workshop.end_date %> |
- <%= workshop.grade_level %> |
+ <%= workshop.display_grade_level %> |
<%= workshop.registration_open ? 'Yes' : 'No' %> |
From 4ee54b6e256040ccde874fe6b4bab132e9464563 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Mon, 18 Mar 2024 14:37:14 -0700
Subject: [PATCH 04/36] Back temporary work for frontend
---
app/controllers/workshops_controller.rb | 43 +++++++++++++----
app/helpers/workshops_helper.rb | 2 -
app/models/pd_registration.rb | 16 +++++++
app/models/workshop.rb | 7 ++-
app/views/workshops/create.html.erb | 2 -
app/views/workshops/destroy.html.erb | 2 -
app/views/workshops/new.html.erb | 11 ++++-
app/views/workshops/show.html.erb | 64 ++++++++++++++++++++++++-
app/views/workshops/update.html.erb | 2 -
config/routes.rb | 7 ---
10 files changed, 127 insertions(+), 29 deletions(-)
delete mode 100644 app/helpers/workshops_helper.rb
create mode 100644 app/models/pd_registration.rb
delete mode 100644 app/views/workshops/create.html.erb
delete mode 100644 app/views/workshops/destroy.html.erb
delete mode 100644 app/views/workshops/update.html.erb
diff --git a/app/controllers/workshops_controller.rb b/app/controllers/workshops_controller.rb
index 0693d38e..689ada70 100644
--- a/app/controllers/workshops_controller.rb
+++ b/app/controllers/workshops_controller.rb
@@ -1,7 +1,10 @@
-class WorkshopsController < ApplicationController
+# frozen_string_literal: true
+
+require "ostruct"
+class WorkshopsController < ApplicationController
# TODO: revise any method using `set_workshops` to use `MockWorkshop.all` instead. It's currently used for mocking data.
- before_action :set_workshops, only: [:show, :edit]
+ before_action :set_workshops, only: [:show, :edit, :update, :destroy]
def index
set_workshops
@@ -12,6 +15,8 @@ def show
end
def new
+ @workshop = Workshop.new
+ load_ordered_workshops
end
def edit
@@ -19,12 +24,18 @@ def edit
end
def create
+ flash[:danger] = "This feature is not yet implemented."
+ redirect_to schools_path
end
def update
+ flash[:danger] = "This feature is not yet implemented."
+ redirect_to schools_path
end
def destroy
+ flash[:danger] = "This feature is not yet implemented."
+ redirect_to schools_path
end
def set_workshops
@@ -37,8 +48,11 @@ def set_workshops
country: "USA",
start_date: "2024-04-01",
end_date: "2024-04-30",
- # grade_level: "University",
- registration_open: true
+ registration_open: true,
+ pd_registrations: [
+ PdRegistration.new(teacher_id: 1, pd_id: 1, attended: true, role: "leader", teacher_name: "Alex Johnson"),
+ PdRegistration.new(teacher_id: 2, pd_id: 1, attended: false, role: "attendee", teacher_name: "Jamie Smith")
+ ]
),
Workshop.new(
id: 2,
@@ -48,8 +62,12 @@ def set_workshops
country: "USA",
start_date: "2024-05-15",
end_date: "2024-06-15",
- grade_level: 1,
- registration_open: false
+ grade_level: "High School",
+ registration_open: false,
+ pd_registrations: [
+ PdRegistration.new(teacher_id: 3, pd_id: 2, attended: true, role: "attendee", teacher_name: "Sam Lee"),
+ PdRegistration.new(teacher_id: 4, pd_id: 2, attended: true, role: "leader", teacher_name: "Chris Doe")
+ ]
),
Workshop.new(
id: 3,
@@ -59,9 +77,18 @@ def set_workshops
country: "UK",
start_date: "2024-07-01",
end_date: "2024-07-31",
- grade_level: 0,
- registration_open: true
+ grade_level: "College",
+ registration_open: true,
+ pd_registrations: [
+ PdRegistration.new(teacher_id: 5, pd_id: 3, attended: false, role: "attendee", teacher_name: "Morgan Bailey"),
+ PdRegistration.new(teacher_id: 6, pd_id: 3, attended: true, role: "leader", teacher_name: "Casey Jordan"),
+ PdRegistration.new(teacher_id: 7, pd_id: 3, attended: true, role: "attendee", teacher_name: "Jordan Casey") # Added an extra registration for variety
+ ]
)
]
end
+
+ def load_ordered_workshops
+ @ordered_schools = School.all.order(:name)
+ end
end
diff --git a/app/helpers/workshops_helper.rb b/app/helpers/workshops_helper.rb
deleted file mode 100644
index 215662bd..00000000
--- a/app/helpers/workshops_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module WorkshopsHelper
-end
diff --git a/app/models/pd_registration.rb b/app/models/pd_registration.rb
new file mode 100644
index 00000000..054c393e
--- /dev/null
+++ b/app/models/pd_registration.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class PdRegistration
+ include ActiveModel::Model
+ include ActiveModel::Attributes
+
+ attribute :teacher_id, :integer
+ attribute :pd_id, :integer
+ attribute :attended, :boolean
+ attribute :role, :string
+ attribute :teacher_name, :string # Adding this for convenience in mocking
+
+ def initialize(attributes = {})
+ super(attributes)
+ end
+end
diff --git a/app/models/workshop.rb b/app/models/workshop.rb
index 00ab48f5..6ed6b53a 100644
--- a/app/models/workshop.rb
+++ b/app/models/workshop.rb
@@ -12,7 +12,6 @@
# lng :float
# name :string
# state :string
-# tags :text default([]), is an Array
# teachers_count :integer default(0)
# website :string TODO: Confirm is it necessary field
# created_at :datetime
@@ -22,7 +21,9 @@
#
# TODO: Define indexes
-
+# This class is a mock representation of the Workshop model.
+# In the final application, Workshops and Teachers are associated through PdRegistrations.
+# This mock setup uses arrays of mock PdRegistration objects to simulate many-to-many relationships.
class Workshop
include ActiveModel::Model
include ActiveModel::Attributes # Make sure this is included
@@ -36,7 +37,9 @@ class Workshop
attribute :start_date, :date
attribute :end_date, :date
attribute :grade_level, :integer, default: -1
+ attribute :teachers_count, :integer, default: 0
attribute :registration_open, :boolean
+ attribute :pd_registrations, default: []
GRADE_LEVELS = {
elementary: 0,
diff --git a/app/views/workshops/create.html.erb b/app/views/workshops/create.html.erb
deleted file mode 100644
index 6010fb78..00000000
--- a/app/views/workshops/create.html.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-Workshops#create
-Find me in app/views/workshops/create.html.erb
diff --git a/app/views/workshops/destroy.html.erb b/app/views/workshops/destroy.html.erb
deleted file mode 100644
index d5661876..00000000
--- a/app/views/workshops/destroy.html.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-Workshops#destroy
-Find me in app/views/workshops/destroy.html.erb
diff --git a/app/views/workshops/new.html.erb b/app/views/workshops/new.html.erb
index 571fde1c..23582b80 100644
--- a/app/views/workshops/new.html.erb
+++ b/app/views/workshops/new.html.erb
@@ -1,2 +1,9 @@
-Workshops#new
-Find me in app/views/workshops/new.html.erb
+<%= provide(:h1, "Add a School") %>
+
+<%= form_for @workshop do |f| %>
+ <%= render 'workshops/form', f: f, workshop: @workshop %>
+
+
+ <%= f.submit 'Submit', {class: 'btn btn-primary', id: 'submit_button'} %>
+
+<% end %>
diff --git a/app/views/workshops/show.html.erb b/app/views/workshops/show.html.erb
index 4986c74e..83a21340 100644
--- a/app/views/workshops/show.html.erb
+++ b/app/views/workshops/show.html.erb
@@ -1,2 +1,62 @@
-Workshops#show
-Find me in app/views/workshops/show.html.erb
+<%= provide(:h1, @workshop.name) %>
+
+
+
+
+
+
+ Location
+ <%= "#{@workshop.location}, #{@workshop.country}" %>
+
+
+
+ Dates
+ <%= @workshop.start_date %> to <%= @workshop.end_date %>
+
+
+
+ GradeLevel
+ <%= @workshop.display_grade_level %>
+
+
+
+
+
+ PD Registrations
+
+
+
+
+ Teacher Name |
+ PD Session |
+ Attended |
+ Role |
+
+
+
+ <% @workshop.pd_registrations.each do |registration| %>
+
+ <%= link_to(registration.teacher_name, teacher_path(registration.teacher_id)) %> |
+ <%= @workshop.name %> |
+ <%= registration.attended ? 'Yes' : 'No' %> |
+ <%= registration.role %> |
+
+ <% end %>
+
+
+
+
+
+
diff --git a/app/views/workshops/update.html.erb b/app/views/workshops/update.html.erb
deleted file mode 100644
index 1716c5a6..00000000
--- a/app/views/workshops/update.html.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-Workshops#update
-Find me in app/views/workshops/update.html.erb
diff --git a/config/routes.rb b/config/routes.rb
index ce067803..ba682f22 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,13 +1,6 @@
# frozen_string_literal: true
Rails.application.routes.draw do
- get 'workshops/index'
- get 'workshops/show'
- get 'workshops/new'
- get 'workshops/edit'
- get 'workshops/create'
- get 'workshops/update'
- get 'workshops/destroy'
mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
# The priority is based upon order of creation: first created -> highest priority.
From c0e563482b6d1100fedd5f253c97327c543b4a2e Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Wed, 20 Mar 2024 09:53:25 -0700
Subject: [PATCH 05/36] Rename workshop to professional developments
---
...> professional_developments_controller.rb} | 36 +++++++++---------
app/helpers/application_helper.rb | 2 +-
...orkshop.rb => professional_development.rb} | 8 ++--
.../_form.html.erb | 38 +++++++++----------
.../professional_developments/edit.html.erb | 13 +++++++
.../professional_developments/index.html.erb | 34 +++++++++++++++++
.../professional_developments/new.html.erb | 9 +++++
.../show.html.erb | 18 ++++-----
app/views/workshops/edit.html.erb | 13 -------
app/views/workshops/index.html.erb | 34 -----------------
app/views/workshops/new.html.erb | 9 -----
config/routes.rb | 2 +-
12 files changed, 108 insertions(+), 108 deletions(-)
rename app/controllers/{workshops_controller.rb => professional_developments_controller.rb} (70%)
rename app/models/{workshop.rb => professional_development.rb} (87%)
rename app/views/{workshops => professional_developments}/_form.html.erb (60%)
create mode 100644 app/views/professional_developments/edit.html.erb
create mode 100644 app/views/professional_developments/index.html.erb
create mode 100644 app/views/professional_developments/new.html.erb
rename app/views/{workshops => professional_developments}/show.html.erb (61%)
delete mode 100644 app/views/workshops/edit.html.erb
delete mode 100644 app/views/workshops/index.html.erb
delete mode 100644 app/views/workshops/new.html.erb
diff --git a/app/controllers/workshops_controller.rb b/app/controllers/professional_developments_controller.rb
similarity index 70%
rename from app/controllers/workshops_controller.rb
rename to app/controllers/professional_developments_controller.rb
index 689ada70..fb11619b 100644
--- a/app/controllers/workshops_controller.rb
+++ b/app/controllers/professional_developments_controller.rb
@@ -2,45 +2,45 @@
require "ostruct"
-class WorkshopsController < ApplicationController
- # TODO: revise any method using `set_workshops` to use `MockWorkshop.all` instead. It's currently used for mocking data.
- before_action :set_workshops, only: [:show, :edit, :update, :destroy]
+class ProfessionalDevelopmentsController < ApplicationController
+ # TODO: revise any method using `set_pds` to use `MockProfessionalDevelopments.all` instead. It's currently used for mocking data.
+ before_action :set_pds, only: [:show, :edit, :update, :destroy]
def index
- set_workshops
+ set_pds
end
def show
- @workshop = @workshops.find { |workshop| workshop.id == params[:id].to_i }
+ @professional_development = @professional_developments.find { |pd| pd.id == params[:id].to_i }
end
def new
- @workshop = Workshop.new
- load_ordered_workshops
+ @professional_development = ProfessionalDevelopment.new
+ load_ordered_pds
end
def edit
- @workshop = @workshops.find { |workshop| workshop.id == params[:id].to_i }
+ @professional_development = @professional_developments.find { |pd| pd.id == params[:id].to_i }
end
def create
flash[:danger] = "This feature is not yet implemented."
- redirect_to schools_path
+ redirect_to new_professional_development_path
end
def update
flash[:danger] = "This feature is not yet implemented."
- redirect_to schools_path
+ redirect_to edit_professional_development_path
end
def destroy
flash[:danger] = "This feature is not yet implemented."
- redirect_to schools_path
+ redirect_to professional_developments_path
end
- def set_workshops
- @workshops = [
- Workshop.new(
+ def set_pds
+ @professional_developments = [
+ ProfessionalDevelopment.new(
id: 1,
name: "Web Development Basics",
city: "San Francisco",
@@ -54,7 +54,7 @@ def set_workshops
PdRegistration.new(teacher_id: 2, pd_id: 1, attended: false, role: "attendee", teacher_name: "Jamie Smith")
]
),
- Workshop.new(
+ ProfessionalDevelopment.new(
id: 2,
name: "Advanced Pottery",
city: "New York",
@@ -69,7 +69,7 @@ def set_workshops
PdRegistration.new(teacher_id: 4, pd_id: 2, attended: true, role: "leader", teacher_name: "Chris Doe")
]
),
- Workshop.new(
+ ProfessionalDevelopment.new(
id: 3,
name: "Digital Photography",
city: "London",
@@ -88,7 +88,7 @@ def set_workshops
]
end
- def load_ordered_workshops
- @ordered_schools = School.all.order(:name)
+ def load_ordered_pds
+ # not yet implemented
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index db597ddc..060ab96d 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -20,7 +20,7 @@ def admin_nav_links
"Schools": schools_path,
"Teachers": teachers_path,
"Email Templates": email_templates_path,
- "Workshops": workshops_path,
+ "Professional Developments": professional_developments_path,
}
end
diff --git a/app/models/workshop.rb b/app/models/professional_development.rb
similarity index 87%
rename from app/models/workshop.rb
rename to app/models/professional_development.rb
index 6ed6b53a..2954844c 100644
--- a/app/models/workshop.rb
+++ b/app/models/professional_development.rb
@@ -2,7 +2,7 @@
# == Schema Information
#
-# Table name: workshops
+# Table name: professional_developments
#
# id :integer not null, primary key
# city :string
@@ -21,10 +21,10 @@
#
# TODO: Define indexes
-# This class is a mock representation of the Workshop model.
-# In the final application, Workshops and Teachers are associated through PdRegistrations.
+# This class is a mock representation of the ProfessionalDevelopment model.
+# In the final application, Professional Developments and Teachers are associated through PdRegistrations.
# This mock setup uses arrays of mock PdRegistration objects to simulate many-to-many relationships.
-class Workshop
+class ProfessionalDevelopment
include ActiveModel::Model
include ActiveModel::Attributes # Make sure this is included
diff --git a/app/views/workshops/_form.html.erb b/app/views/professional_developments/_form.html.erb
similarity index 60%
rename from app/views/workshops/_form.html.erb
rename to app/views/professional_developments/_form.html.erb
index 9907509a..1d4244f6 100644
--- a/app/views/workshops/_form.html.erb
+++ b/app/views/professional_developments/_form.html.erb
@@ -1,44 +1,44 @@
-Create a new Workshop
+Create a new Professional Development
- <%= f.label :city, class: "label-required", for: "workshop_city" %>
+ <%= f.label :city, class: "label-required", for: "professional_development_city" %>
<%= f.text_field :city, placeholder: 'Berkeley', class: 'form-control',
- required: true, id: 'workshop_city' %>
+ required: true, id: 'professional_development_city' %>
- <%= f.label :state, class: "label-required", for: "workshop_state" %>
+ <%= f.label :state, class: "label-required", for: "professional_development_state" %>
<%= f.select :state, School::VALID_STATES, { include_blank: "State" }, { id: "state_select", class: 'form-control' } %>
- <%= f.label :state, for: "workshop_state" %>
+ <%= f.label :state, for: "professional_development_state" %>
<%= f.text_field :state, placeholder: "State", class: 'form-control', id: "state_textfield" %>
- <%= f.label :country, "Country", class: "label-required", for: "workshop_country" %>
+ <%= f.label :country, "Country", class: "label-required", for: "professional_development_country" %>
<%= f.country_select(
:country,
{ priority_countries: ['United States'] },
- { class: 'form-control', required: false, id: 'workshop_country', format: :with_full_country_name, selected: 'United States'}
+ { class: 'form-control', required: false, id: 'professional_development_country', format: :with_full_country_name, selected: 'United States'}
) %>
@@ -46,18 +46,18 @@
- <%= f.label :grade_level, "Grade Level", for: "workshop_grade_level" %>
+ <%= f.label :grade_level, "Grade Level", for: "professional_development_grade_level" %>
<%= f.select(
:grade_level,
- options_for_select(School.grade_level_options, workshop.grade_level),
+ options_for_select(School.grade_level_options, professional_development.grade_level),
{ include_blank: "Select a grade level" },
- { class: 'form-control', required: false, id: 'workshop_grade_level' }
+ { class: 'form-control', required: false, id: 'professional_development_grade_level' }
) %>
diff --git a/config/routes.rb b/config/routes.rb
index bef95fa9..56dbea88 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -19,7 +19,9 @@
resources :schools
resources :pages, param: :url_slug
resources :email_templates, except: [:show]
- resources :professional_developments
+ resources :professional_developments do
+ resources :pd_registrations, except: [:show]
+ end
get "/login", to: "sessions#new", as: "login"
delete "/logout", to: "sessions#destroy", as: "logout"
From 669e1a2e29a01e3b499002b46e7dbea0fea7b674 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Wed, 20 Mar 2024 12:12:23 -0700
Subject: [PATCH 08/36] Refract and clean up current frontend code
---
.../pd_registrations_controller.rb | 2 +
.../professional_developments_controller.rb | 8 +-
app/models/pd_registration.rb | 2 +
.../professional_developments/edit.html.erb | 1 -
.../professional_developments/show.html.erb | 117 +++++++++---------
5 files changed, 64 insertions(+), 66 deletions(-)
diff --git a/app/controllers/pd_registrations_controller.rb b/app/controllers/pd_registrations_controller.rb
index 8b2bc110..e92ea50f 100644
--- a/app/controllers/pd_registrations_controller.rb
+++ b/app/controllers/pd_registrations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PdRegistrationsController < ApplicationController
def create
flash[:danger] = "Create feature is not yet implemented."
diff --git a/app/controllers/professional_developments_controller.rb b/app/controllers/professional_developments_controller.rb
index bd7d6aa4..bc91219a 100644
--- a/app/controllers/professional_developments_controller.rb
+++ b/app/controllers/professional_developments_controller.rb
@@ -22,17 +22,17 @@ def edit
end
def create
- flash[:danger] = "This feature is not yet implemented."
+ flash[:danger] = "Create is not yet implemented."
redirect_to new_professional_development_path
end
def update
- flash[:danger] = "This feature is not yet implemented."
+ flash[:danger] = "Update feature is not yet implemented."
redirect_to edit_professional_development_path
end
def destroy
- flash[:danger] = "This feature is not yet implemented."
+ flash[:danger] = "Destroy feature is not yet implemented."
redirect_to professional_developments_path
end
@@ -87,6 +87,6 @@ def set_pds
end
def load_ordered_pds
- # not yet implemented
+ # not yet implemented
end
end
diff --git a/app/models/pd_registration.rb b/app/models/pd_registration.rb
index 9981b93b..c86744c8 100644
--- a/app/models/pd_registration.rb
+++ b/app/models/pd_registration.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+# This class is a mock representation of the PdRegistration model.
+# In the final application, Professional Developments and Teachers are associated through PdRegistrations.
class PdRegistration
include ActiveModel::Model
include ActiveModel::Attributes
diff --git a/app/views/professional_developments/edit.html.erb b/app/views/professional_developments/edit.html.erb
index 3bc42087..64f4f467 100644
--- a/app/views/professional_developments/edit.html.erb
+++ b/app/views/professional_developments/edit.html.erb
@@ -5,7 +5,6 @@
<% else %>
<%= form_for @professional_development do |f| %>
<%= render 'professional_developments/form', f: f, professional_development: @professional_development %>
-
<%= f.submit 'Submit', {class: 'btn btn-primary', id: 'submit_button'} %>
diff --git a/app/views/professional_developments/show.html.erb b/app/views/professional_developments/show.html.erb
index d4fa1899..63e8c064 100644
--- a/app/views/professional_developments/show.html.erb
+++ b/app/views/professional_developments/show.html.erb
@@ -30,7 +30,6 @@
PD Registrations
-
<%= button_tag "Add Teacher", type: 'button', class: "btn btn-success", data: { toggle: "modal", target: "#addTeacherModal" } %>
@@ -79,23 +78,19 @@
<%= form_for :pd_registration, url: professional_development_pd_registrations_path(@professional_development), method: :post do |f| %>
<%= f.label :teacher_name, "Teacher Name" %>
-
<%= f.text_field :teacher_name, class: "form-control", placeholder: "Enter teacher name", required: true %>
<%= f.label :role, "Role" %>
-
<%= f.select :role, [['Leader', 'Leader'], ['Attendee', 'Attendee']], { prompt: "Select your role" }, { class: "form-control", required: true } %>
<%= f.label :attended, "Attended" %>
-
<%= f.select :attended, [['Yes', true], ['No', false]], { prompt: "Did the teacher attend?" }, { class: "form-control", required: true } %>
-
<%= f.hidden_field :professional_development_id, value: @professional_development.id %>
<%= f.submit "Add", class: "btn btn-primary" %>
@@ -106,75 +101,75 @@
From edfedf57ad778adae8b275884ebc9e593b75f317 Mon Sep 17 00:00:00 2001
From: JacksonXu33
Date: Fri, 22 Mar 2024 18:28:56 -0700
Subject: [PATCH 09/36] crud, but no schema yet
---
.../pd_registrations_controller.rb | 41 ++++++++++++++++---
.../professional_developments_controller.rb | 41 +++++++++++++------
app/models/pd_registration.rb | 6 +++
app/models/professional_development.rb | 5 +++
app/models/teacher.rb | 2 +
5 files changed, 76 insertions(+), 19 deletions(-)
diff --git a/app/controllers/pd_registrations_controller.rb b/app/controllers/pd_registrations_controller.rb
index e92ea50f..e51c8ccf 100644
--- a/app/controllers/pd_registrations_controller.rb
+++ b/app/controllers/pd_registrations_controller.rb
@@ -1,18 +1,47 @@
# frozen_string_literal: true
+# Not sure how the ids are going to work with professional development.
+# With a belongs to relationship, depending on customer implementation,
+# might have 2 ids in params that we need to distinguish between.
class PdRegistrationsController < ApplicationController
+ def index
+ set_pds
+ end
+
+ def show
+ @pd_registration = PdRegistration.find(params[:id])
+ end
+
+ def new
+ @pd_registration = PdRegistration.new
+ end
+
+ def edit
+ @pd_registration = PdRegistration.find(params[:id])
+ end
+
def create
- flash[:danger] = "Create feature is not yet implemented."
- redirect_to professional_development_path(params[:professional_development_id])
+ @pd_registration = PdRegistration.new(pd_registration_params)
+ if !@pd_registration.save
+ flash.now[:alert] = "Failed to save registration: #{@pd_registration.errors.full_messages.join(", ")}"
+ render :new
+ end
+ redirect_to pd_registrations_path, success: "PD registration created successfully."
end
def update
- flash[:danger] = "Update feature is not yet implemented."
- redirect_to professional_development_path(params[:professional_development_id])
+ @pd_registration = PdRegistration.find(params[:id])
+ if !@pd_registration.update(pd_registration_params)
+ flash.now[:alert] = "Failed to save registration: #{@pd_registration.errors.full_messages.join(", ")}"
+ render "edit"
+ end
+ redirect_to pd_registrations_path, success: "Saved the PD registration."
end
def destroy
- flash[:danger] = "Destroy feature is not yet implemented."
- redirect_to professional_development_path(params[:professional_development_id])
+ if !@pd_registration.destroy
+ redirect_back(fallback_location: pd_registrations_path, alert: "Failed to delete PD registration.")
+ end
+ redirect_to pd_registrations_path, success: "Deleted PD registration successfully."
end
end
diff --git a/app/controllers/professional_developments_controller.rb b/app/controllers/professional_developments_controller.rb
index bc91219a..3a195a7f 100644
--- a/app/controllers/professional_developments_controller.rb
+++ b/app/controllers/professional_developments_controller.rb
@@ -3,37 +3,56 @@
class ProfessionalDevelopmentsController < ApplicationController
# TODO: revise any method using `set_pds` to use `MockProfessionalDevelopments.all` instead. It's currently used for mocking data.
before_action :set_pds, only: [:show, :edit, :update, :destroy]
+ before_action :require_login
+ before_action :require_admin
def index
set_pds
end
def show
- @professional_development = @professional_developments.find { |pd| pd.id == params[:id].to_i }
+ @professional_development = ProfessionalDevelopment.find(params[:id])
+ @pd_registrations = @professional_development.pd_registrations
end
def new
@professional_development = ProfessionalDevelopment.new
- load_ordered_pds
end
def edit
- @professional_development = @professional_developments.find { |pd| pd.id == params[:id].to_i }
+ @professional_development = ProfessionalDevelopment.find(params[:id])
+ @pd_registrations = @professional_development.pd_registrations
end
def create
- flash[:danger] = "Create is not yet implemented."
- redirect_to new_professional_development_path
+ @professional_development = ProfessionalDevelopment.find_by(name: professional_development_params[:name])
+ if @professional_development
+ flash.now[:alert] = "A professional development with the name #{@professional_development.name} already exists."
+ render :new
+ end
+ @professional_development = ProfessionalDevelopment.new(professional_development_params)
+ if !@professional_development.save
+ flash.now[:alert] = "Failed to save #{@professional_development.name}: #{@professional_development.errors.full_messages.join(", ")}"
+ render :new
+ end
+ redirect_to professional_developments_path, success: "Professional development created successfully."
end
def update
- flash[:danger] = "Update feature is not yet implemented."
- redirect_to edit_professional_development_path
+ @professional_development = ProfessionalDevelopment.find(params[:id])
+ @pd_registrations = @professional_development.pd_registrations
+ if !@professional_development.update(professional_development_params)
+ flash.now[:alert] = "Failed to save #{@professional_development.name}: #{@professional_development.errors.full_messages.join(", ")}"
+ render "edit"
+ end
+ redirect_to professional_developments_path, success: "Saved #{@professional_development.name}"
end
def destroy
- flash[:danger] = "Destroy feature is not yet implemented."
- redirect_to professional_developments_path
+ if !@professional_development.destroy
+ redirect_back(fallback_location: professional_developments_path, alert: "Failed to delete #{@professional_development.name}")
+ end
+ redirect_to professional_developments_path, success: "Deleted #{@professional_development.name} successfully."
end
def set_pds
@@ -85,8 +104,4 @@ def set_pds
)
]
end
-
- def load_ordered_pds
- # not yet implemented
- end
end
diff --git a/app/models/pd_registration.rb b/app/models/pd_registration.rb
index c86744c8..4585f327 100644
--- a/app/models/pd_registration.rb
+++ b/app/models/pd_registration.rb
@@ -13,6 +13,12 @@ class PdRegistration
attribute :role, :string
attribute :teacher_name, :string # Adding this for convenience in mocking
+ # probably don't need the 3 id attributes (id, teacherid, pdid) if adding this relationship status into model
+ belongs_to :professional_development
+ belongs_to :teacher
+
+ validates :professional_development_id, uniqueness: { scope: :teacher_id, message: "Teacher already has a registration for this PD" }
+
def initialize(attributes = {})
super(attributes)
end
diff --git a/app/models/professional_development.rb b/app/models/professional_development.rb
index 2954844c..ce2b835a 100644
--- a/app/models/professional_development.rb
+++ b/app/models/professional_development.rb
@@ -41,6 +41,8 @@ class ProfessionalDevelopment
attribute :registration_open, :boolean
attribute :pd_registrations, default: []
+ validates :name, presence: { message: "can't be blank" }, uniqueness: { message: "must be unique" }
+
GRADE_LEVELS = {
elementary: 0,
middle_school: 1,
@@ -49,6 +51,9 @@ class ProfessionalDevelopment
university: 4
}.freeze
+ has_many :pd_registrations
+ has_many :teachers, through: :pd_registrations
+
def initialize(attributes = {})
super(attributes)
# Now ActiveModel handles attributes, no need to manually set defaults for attributes defined above
diff --git a/app/models/teacher.rb b/app/models/teacher.rb
index 76fa7b34..a6ab386d 100644
--- a/app/models/teacher.rb
+++ b/app/models/teacher.rb
@@ -51,6 +51,8 @@ class Teacher < ApplicationRecord
validates_inclusion_of :application_status, in: application_statuses.keys
belongs_to :school, counter_cache: true
+ has_many :professional_development_registrations
+ has_many :professional_developments, through: :professional_development_registrations
# Non-admin teachers whose application has neither been accepted nor denied
# It might or might not have been reviewed.
From 1a6c703c406ffa36b0286f1e694b3a3cf2b2f0d6 Mon Sep 17 00:00:00 2001
From: JacksonXu33
Date: Sat, 30 Mar 2024 16:20:01 -0700
Subject: [PATCH 10/36] add comments
---
.../pd_registrations_controller.rb | 32 ++++++++++++++++++-
app/models/pd_registration.rb | 3 +-
app/models/professional_development.rb | 3 +-
3 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/app/controllers/pd_registrations_controller.rb b/app/controllers/pd_registrations_controller.rb
index e51c8ccf..7b63d770 100644
--- a/app/controllers/pd_registrations_controller.rb
+++ b/app/controllers/pd_registrations_controller.rb
@@ -1,9 +1,11 @@
# frozen_string_literal: true
# Not sure how the ids are going to work with professional development.
-# With a belongs to relationship, depending on customer implementation,
+# With a belongs to relationship, depending on the implementation,
# might have 2 ids in params that we need to distinguish between.
class PdRegistrationsController < ApplicationController
+ before_action :require_login
+
def index
set_pds
end
@@ -20,8 +22,16 @@ def edit
@pd_registration = PdRegistration.find(params[:id])
end
+ # Create and update do NOT support admin overriding user functionality yet, only normal user functionality
def create
+ @professional_development = pd_name_to_pd()
+ if (!@professional_development)
+ flash.now[:alert] = "Failed to save registration: No PD with that name exists.}"
+ render :new
+ end
@pd_registration = PdRegistration.new(pd_registration_params)
+ @pd_registration.professional_development = @professional_development
+ @pd_registration.teacher = current_user
if !@pd_registration.save
flash.now[:alert] = "Failed to save registration: #{@pd_registration.errors.full_messages.join(", ")}"
render :new
@@ -30,7 +40,14 @@ def create
end
def update
+ @professional_development = pd_name_to_pd()
+ if (!@professional_development)
+ flash.now[:alert] = "Failed to save registration: No PD with that name exists.}"
+ render :new
+ end
@pd_registration = PdRegistration.find(params[:id])
+ @pd_registration.professional_development = @professional_development
+ @pd_registration.teacher = current_user
if !@pd_registration.update(pd_registration_params)
flash.now[:alert] = "Failed to save registration: #{@pd_registration.errors.full_messages.join(", ")}"
render "edit"
@@ -44,4 +61,17 @@ def destroy
end
redirect_to pd_registrations_path, success: "Deleted PD registration successfully."
end
+
+ private
+ # This method will take in a that will be the professional_development name,
+ # and return the pd; nil if doesn't exist or pd is not open
+ # It assumes the pd_registrations form will have a place for the user to input the name of a pd to register for
+ # But feel free to edit this if this is not the planned implementation
+ def pd_name_to_pd
+ professional_development = ProfessionalDevelopment.find(pd_registration_params[:name])
+ if professional_development.nil? || !professional_development.registration_open
+ return nil
+ end
+ return professional_development
+ end
end
diff --git a/app/models/pd_registration.rb b/app/models/pd_registration.rb
index 4585f327..4cb6b641 100644
--- a/app/models/pd_registration.rb
+++ b/app/models/pd_registration.rb
@@ -2,7 +2,7 @@
# This class is a mock representation of the PdRegistration model.
# In the final application, Professional Developments and Teachers are associated through PdRegistrations.
-class PdRegistration
+class PdRegistration < ApplicationRecord
include ActiveModel::Model
include ActiveModel::Attributes
@@ -17,6 +17,7 @@ class PdRegistration
belongs_to :professional_development
belongs_to :teacher
+ # if teacher should only have one registration per pd
validates :professional_development_id, uniqueness: { scope: :teacher_id, message: "Teacher already has a registration for this PD" }
def initialize(attributes = {})
diff --git a/app/models/professional_development.rb b/app/models/professional_development.rb
index ce2b835a..de718379 100644
--- a/app/models/professional_development.rb
+++ b/app/models/professional_development.rb
@@ -24,7 +24,7 @@
# This class is a mock representation of the ProfessionalDevelopment model.
# In the final application, Professional Developments and Teachers are associated through PdRegistrations.
# This mock setup uses arrays of mock PdRegistration objects to simulate many-to-many relationships.
-class ProfessionalDevelopment
+class ProfessionalDevelopment < ApplicationRecord
include ActiveModel::Model
include ActiveModel::Attributes # Make sure this is included
@@ -41,6 +41,7 @@ class ProfessionalDevelopment
attribute :registration_open, :boolean
attribute :pd_registrations, default: []
+ # Assuming that the names will have to be unique, to make it possible for teachers to link registration to pd
validates :name, presence: { message: "can't be blank" }, uniqueness: { message: "must be unique" }
GRADE_LEVELS = {
From 08273f2e97ba6f5636309beb3e3ca070bf7c7119 Mon Sep 17 00:00:00 2001
From: JacksonXu33
Date: Sat, 30 Mar 2024 16:22:19 -0700
Subject: [PATCH 11/36] link registrations and pd and teacher
---
app/controllers/pd_registrations_controller.rb | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/controllers/pd_registrations_controller.rb b/app/controllers/pd_registrations_controller.rb
index 7b63d770..695fa82b 100644
--- a/app/controllers/pd_registrations_controller.rb
+++ b/app/controllers/pd_registrations_controller.rb
@@ -25,7 +25,7 @@ def edit
# Create and update do NOT support admin overriding user functionality yet, only normal user functionality
def create
@professional_development = pd_name_to_pd()
- if (!@professional_development)
+ if !@professional_development
flash.now[:alert] = "Failed to save registration: No PD with that name exists.}"
render :new
end
@@ -41,7 +41,7 @@ def create
def update
@professional_development = pd_name_to_pd()
- if (!@professional_development)
+ if !@professional_development
flash.now[:alert] = "Failed to save registration: No PD with that name exists.}"
render :new
end
@@ -72,6 +72,6 @@ def pd_name_to_pd
if professional_development.nil? || !professional_development.registration_open
return nil
end
- return professional_development
+ professional_development
end
end
From 3b1625fd6cae99c65825eee9fea73ea34092c740 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Thu, 4 Apr 2024 14:54:28 -0700
Subject: [PATCH 12/36] Implement backend model & migration
---
app/models/pd_registration.rb | 57 +++++++++----
app/models/professional_development.rb | 84 ++++++++-----------
...190834_create_professional_developments.rb | 17 ++++
.../20240404191433_create_pd_registrations.rb | 17 ++++
db/schema.rb | 27 +++++-
5 files changed, 135 insertions(+), 67 deletions(-)
create mode 100644 db/migrate/20240404190834_create_professional_developments.rb
create mode 100644 db/migrate/20240404191433_create_pd_registrations.rb
diff --git a/app/models/pd_registration.rb b/app/models/pd_registration.rb
index 4cb6b641..e786ce25 100644
--- a/app/models/pd_registration.rb
+++ b/app/models/pd_registration.rb
@@ -1,26 +1,49 @@
# frozen_string_literal: true
-# This class is a mock representation of the PdRegistration model.
-# In the final application, Professional Developments and Teachers are associated through PdRegistrations.
+# == Schema Information
+#
+# Table name: pd_registrations
+#
+# id :bigint not null, primary key
+# attended :boolean default(FALSE)
+# role :string not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# professional_development_id :integer not null
+# teacher_id :integer not null
+#
+# Indexes
+#
+# index_pd_reg_on_teacher_id_and_pd_id (teacher_id,professional_development_id) UNIQUE
+#
+# Foreign Keys
+#
+# fk_rails_... (professional_development_id => professional_developments.id)
+# fk_rails_... (teacher_id => teachers.id)
+#
class PdRegistration < ApplicationRecord
- include ActiveModel::Model
- include ActiveModel::Attributes
+ belongs_to :teacher
+ belongs_to :professional_development
- attribute :id, :integer
- attribute :teacher_id, :integer
- attribute :pd_id, :integer
- attribute :attended, :boolean
- attribute :role, :string
- attribute :teacher_name, :string # Adding this for convenience in mocking
+ validates :teacher_id, uniqueness: { scope: :professional_development_id, message: "already has a registration for this PD" }
+ validates :role, inclusion: { in: %w[leader attendee], message: "%{value} is not a valid role" }
+ validates :attended, inclusion: { in: [true, false] }
- # probably don't need the 3 id attributes (id, teacherid, pdid) if adding this relationship status into model
- belongs_to :professional_development
- belongs_to :teacher
+ validate :professional_development_dates_passed, on: :create
+
+ def teacher_name
+ teacher = Teacher.find_by(id: teacher_id)
+ if teacher.present?
+ "#{teacher.first_name} #{teacher.last_name}"
+ else
+ "Teacher not found"
+ end
+ end
- # if teacher should only have one registration per pd
- validates :professional_development_id, uniqueness: { scope: :teacher_id, message: "Teacher already has a registration for this PD" }
+ private
+ def professional_development_dates_passed
+ return unless professional_development&.end_date&.past?
- def initialize(attributes = {})
- super(attributes)
+ errors.add(:professional_development_id, "can't register for past events")
end
end
diff --git a/app/models/professional_development.rb b/app/models/professional_development.rb
index de718379..8962e17f 100644
--- a/app/models/professional_development.rb
+++ b/app/models/professional_development.rb
@@ -4,75 +4,61 @@
#
# Table name: professional_developments
#
-# id :integer not null, primary key
-# city :string
-# country :string
-# grade_level :integer
-# lat :float
-# lng :float
-# name :string
-# state :string
-# teachers_count :integer default(0)
-# website :string TODO: Confirm is it necessary field
-# created_at :datetime
-# updated_at :datetime
+# id :bigint not null, primary key
+# city :string not null
+# country :string not null
+# end_date :date not null
+# grade_level :integer not null
+# name :string not null
+# start_date :date not null
+# state :string
+# created_at :datetime not null
+# updated_at :datetime not null
#
# Indexes
#
-# TODO: Define indexes
-
-# This class is a mock representation of the ProfessionalDevelopment model.
-# In the final application, Professional Developments and Teachers are associated through PdRegistrations.
-# This mock setup uses arrays of mock PdRegistration objects to simulate many-to-many relationships.
+# index_professional_developments_on_name_and_start_date (name,start_date) UNIQUE
+#
class ProfessionalDevelopment < ApplicationRecord
- include ActiveModel::Model
- include ActiveModel::Attributes # Make sure this is included
-
- # Define attributes
- attribute :id, :integer
- attribute :name, :string
- attribute :city, :string
- attribute :state, :string
- attribute :country, :string
- attribute :start_date, :date
- attribute :end_date, :date
- attribute :grade_level, :integer, default: -1
- attribute :teachers_count, :integer, default: 0
- attribute :registration_open, :boolean
- attribute :pd_registrations, default: []
+ VALID_STATES = %w[AL AK AS AZ AR CA CO CT DE DC FM FL GA GU HI ID IL IN IA KS KY LA ME MH MD MA MI MN MS MO MT NE NV
+ NH NJ NM NY NC ND MP OH OK OR PW PA PR RI SC SD TN TX UT VT VI VA WA WV WI WY].freeze
- # Assuming that the names will have to be unique, to make it possible for teachers to link registration to pd
- validates :name, presence: { message: "can't be blank" }, uniqueness: { message: "must be unique" }
+ validates :name, :city, :country, :start_date, :end_date, presence: true
+ validates :name, uniqueness: { scope: :start_date, message: "should be unique per start date" }
+ validates :state, presence: true, if: -> { country == "US" }
+ validates :state, inclusion: { in: VALID_STATES, message: "%{value} is not a valid state" },
+ if: -> { country == "US" }
+ validate :end_date_after_start_date
- GRADE_LEVELS = {
+ enum grade_level: {
elementary: 0,
middle_school: 1,
high_school: 2,
community_college: 3,
university: 4
- }.freeze
+ }
- has_many :pd_registrations
+ has_many :pd_registrations, dependent: :destroy
has_many :teachers, through: :pd_registrations
- def initialize(attributes = {})
- super(attributes)
- # Now ActiveModel handles attributes, no need to manually set defaults for attributes defined above
+ def location
+ "#{city}, #{state}, #{country}"
end
- def persisted?
- id.present?
+ def display_grade_level
+ return "Unknown" if grade_level_before_type_cast.to_i == -1
+
+ grade_level.to_s.titlecase
end
- def location
- "#{city}, #{state}, #{country}"
+ def registration_open
+ @professional_development.registration_open ? "Yes" : "No"
end
- def display_grade_level
- # Directly access the grade_level attribute
- grade_level_value = self.grade_level
- return "Unknown" if grade_level_value == -1
+ private
+ def end_date_after_start_date
+ return if end_date.blank? || start_date.blank?
- GRADE_LEVELS.key(grade_level_value).to_s.titlecase
+ errors.add(:end_date, "must be after the start date") if end_date < start_date
end
end
diff --git a/db/migrate/20240404190834_create_professional_developments.rb b/db/migrate/20240404190834_create_professional_developments.rb
new file mode 100644
index 00000000..61ba5f50
--- /dev/null
+++ b/db/migrate/20240404190834_create_professional_developments.rb
@@ -0,0 +1,17 @@
+class CreateProfessionalDevelopments < ActiveRecord::Migration[6.1]
+ def change
+ create_table :professional_developments do |t|
+ t.string :name, null: false
+ t.string :city, null: false
+ t.string :state
+ t.string :country, null: false
+ t.date :start_date, null: false
+ t.date :end_date, null: false
+ t.integer :grade_level, null: false
+
+ t.timestamps
+ end
+
+ add_index :professional_developments, [:name, :start_date], unique: true
+ end
+end
diff --git a/db/migrate/20240404191433_create_pd_registrations.rb b/db/migrate/20240404191433_create_pd_registrations.rb
new file mode 100644
index 00000000..e81ddd0e
--- /dev/null
+++ b/db/migrate/20240404191433_create_pd_registrations.rb
@@ -0,0 +1,17 @@
+class CreatePdRegistrations < ActiveRecord::Migration[6.1]
+ def change
+ create_table :pd_registrations do |t|
+ t.integer :teacher_id, null: false
+ t.integer :professional_development_id, null: false
+ t.boolean :attended, default: false
+ t.string :role, null: false
+
+ t.timestamps
+ end
+
+ add_index :pd_registrations, [:teacher_id, :professional_development_id], unique: true,
+ name: 'index_pd_reg_on_teacher_id_and_pd_id'
+ add_foreign_key :pd_registrations, :teachers
+ add_foreign_key :pd_registrations, :professional_developments
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a9927666..44f6d7f9 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2024_03_07_225738) do
+ActiveRecord::Schema.define(version: 2024_04_04_191433) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -82,6 +82,29 @@
t.index ["url_slug"], name: "index_pages_on_url_slug", unique: true
end
+ create_table "pd_registrations", force: :cascade do |t|
+ t.integer "teacher_id", null: false
+ t.integer "professional_development_id", null: false
+ t.boolean "attended", default: false
+ t.string "role", null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["teacher_id", "professional_development_id"], name: "index_pd_reg_on_teacher_id_and_pd_id", unique: true
+ end
+
+ create_table "professional_developments", force: :cascade do |t|
+ t.string "name", null: false
+ t.string "city", null: false
+ t.string "state"
+ t.string "country", null: false
+ t.date "start_date", null: false
+ t.date "end_date", null: false
+ t.integer "grade_level", null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["name", "start_date"], name: "index_professional_developments_on_name_and_start_date", unique: true
+ end
+
create_table "schools", id: :serial, force: :cascade do |t|
t.string "name"
t.string "city"
@@ -130,5 +153,7 @@
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "pages", "teachers", column: "creator_id"
add_foreign_key "pages", "teachers", column: "last_editor_id"
+ add_foreign_key "pd_registrations", "professional_developments"
+ add_foreign_key "pd_registrations", "teachers"
add_foreign_key "teachers", "schools"
end
From 67dfaf5f9ca0e2ea005a0bc2bf92c2739e3a5a03 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Thu, 4 Apr 2024 14:54:56 -0700
Subject: [PATCH 13/36] Adjust frontend view to align with backend
implementation
---
.../professional_developments/_form.html.erb | 6 ++--
.../professional_developments/index.html.erb | 4 +--
.../professional_developments/show.html.erb | 30 +++++++++++--------
3 files changed, 23 insertions(+), 17 deletions(-)
diff --git a/app/views/professional_developments/_form.html.erb b/app/views/professional_developments/_form.html.erb
index 1d4244f6..f9748bea 100644
--- a/app/views/professional_developments/_form.html.erb
+++ b/app/views/professional_developments/_form.html.erb
@@ -25,7 +25,7 @@
<%= f.label :state, class: "label-required", for: "professional_development_state" %>
- <%= f.select :state, School::VALID_STATES, { include_blank: "State" }, { id: "state_select", class: 'form-control' } %>
+ <%= f.select :state, ProfessionalDevelopment::VALID_STATES, { include_blank: "State" }, { id: "state_select", class: 'form-control' } %>
@@ -69,13 +69,13 @@
stateTextfieldContainer.hide();
stateTextfield.removeAttr('name');
- stateSelect.attr('name', 'mock_professional_development[state]');
+ stateSelect.attr('name', 'professional_development[state]');
} else {
stateTextfieldContainer.show().attr('required', false);
stateSelectContainer.hide();
stateSelect.removeAttr('name');
- stateTextfield.attr('name', 'mock_professional_development[state]');
+ stateTextfield.attr('name', 'professional_development[state]');
}
}
countrySelected.change(handleCountryChange);
diff --git a/app/views/professional_developments/index.html.erb b/app/views/professional_developments/index.html.erb
index d452b0a8..ed1bb5b4 100644
--- a/app/views/professional_developments/index.html.erb
+++ b/app/views/professional_developments/index.html.erb
@@ -9,7 +9,8 @@
Start Date |
End Date |
Grade Level |
- Registration Open |
+
+
Actions |
@@ -21,7 +22,6 @@
<%= pd.start_date %> |
<%= pd.end_date %> |
<%= pd.display_grade_level %> |
- <%= pd.registration_open ? 'Yes' : 'No' %> |
<%= link_to("Edit", edit_professional_development_path(pd), class: "btn btn-info") %>
diff --git a/app/views/professional_developments/show.html.erb b/app/views/professional_developments/show.html.erb
index 63e8c064..b92dde35 100644
--- a/app/views/professional_developments/show.html.erb
+++ b/app/views/professional_developments/show.html.erb
@@ -18,7 +18,7 @@
<% [['Location', @professional_development.location],
['Dates', "#{@professional_development.start_date.to_s} to #{@professional_development.end_date.to_s}"],
- ['GradeLevel', @professional_development.display_grade_level]].each do |label, value| %>
+ ['Grade Level', @professional_development.display_grade_level]].each do |label, value| %>
<%= label %>
<%= value %>
@@ -28,7 +28,10 @@
- PD Registrations
+
+ PD Registrations
+ Total Registered Teachers: <%= @professional_development.pd_registrations.count %>
+
<%= button_tag "Add Teacher", type: 'button', class: "btn btn-success", data: { toggle: "modal", target: "#addTeacherModal" } %>
@@ -36,6 +39,7 @@
+ Teacher ID |
Teacher Name |
PD Session |
Attended |
@@ -46,6 +50,7 @@
<% @professional_development.pd_registrations.each do |registration| %>
+ <%= registration.teacher_id %> |
<%= link_to(registration.teacher_name, teacher_path(registration.teacher_id)) %> |
<%= @professional_development.name %> |
<%= registration.attended ? 'Yes' : 'No' %> |
@@ -77,13 +82,13 @@
<%= form_for :pd_registration, url: professional_development_pd_registrations_path(@professional_development), method: :post do |f| %>
- <%= f.label :teacher_name, "Teacher Name" %>
- <%= f.text_field :teacher_name, class: "form-control", placeholder: "Enter teacher name", required: true %>
+ <%= f.label :teacher_id, "Teacher ID" %>
+ <%= f.text_field :teacher_id, class: "form-control", placeholder: "Enter teacher id", required: true %>
<%= f.label :role, "Role" %>
- <%= f.select :role, [['Leader', 'Leader'], ['Attendee', 'Attendee']], { prompt: "Select your role" }, { class: "form-control", required: true } %>
+ <%= f.select :role, [['Leader', 'leader'], ['Attendee', 'attendee']], { prompt: "Select your role" }, { class: "form-control", required: true } %>
@@ -117,9 +122,9 @@
form.reset();
}
- function populateModalForm({ teacher_name, role, attended }) {
- console.log("Populating form with data:", { teacher_name, role, attended });
- document.querySelector('[name="pd_registration[teacher_name]"]').value = teacher_name;
+ // Fill in the old values in the form for easier editing
+ function populateModalForm({ teacher_id, role, attended }) {
+ document.querySelector('[name="pd_registration[teacher_id]"]').value = teacher_id;
setDropdownValue('[name="pd_registration[role]"]', role);
setDropdownValue('[name="pd_registration[attended]"]', String(attended));
}
@@ -139,17 +144,17 @@
}
function setupModalForUpdate(button, pdId) {
- const {teacher_name, role, attended} = extractTeacherData(button);
- populateModalForm({ teacher_name, role, attended });
+ const {teacher_id, role, attended} = extractTeacherData(button);
+ populateModalForm({ teacher_id, role, attended });
modalLabel.textContent = 'Edit Teacher in PD Session';
- const registrationId = button.getAttribute("data-registration_id");
+ const registrationId = button.getAttribute("data-registration-id");
setFormActionForUpdate(pdId, registrationId);
}
function extractTeacherData(button) {
const row = button.closest('tr');
return {
- teacher_name: row.querySelector('[data-teacher-name]').textContent,
+ teacher_id: row.querySelector('[data-teacher-id]').textContent,
role: row.querySelector('[data-role]').textContent,
attended: row.querySelector('[data-attended]').textContent === 'Yes'
};
@@ -157,6 +162,7 @@
function setFormActionForUpdate(pdId, registrationId) {
form.action = `/professional_developments/${pdId}/pd_registrations/${registrationId}`;
+ console.debug("Setting form action to:", form.action);
form.method = 'post';
ensureMethodInput('patch');
}
From abd5e9fc359095d5d26333ee15c9b931ecb3390f Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Thu, 4 Apr 2024 14:55:29 -0700
Subject: [PATCH 14/36] Implement backend controller
---
.../pd_registrations_controller.rb | 82 ++++++++--------
.../professional_developments_controller.rb | 98 +++++--------------
2 files changed, 65 insertions(+), 115 deletions(-)
diff --git a/app/controllers/pd_registrations_controller.rb b/app/controllers/pd_registrations_controller.rb
index 695fa82b..009d0b33 100644
--- a/app/controllers/pd_registrations_controller.rb
+++ b/app/controllers/pd_registrations_controller.rb
@@ -1,17 +1,15 @@
# frozen_string_literal: true
-# Not sure how the ids are going to work with professional development.
-# With a belongs to relationship, depending on the implementation,
-# might have 2 ids in params that we need to distinguish between.
class PdRegistrationsController < ApplicationController
before_action :require_login
+ before_action :set_pd_registration, only: [:show, :edit, :update, :destroy]
+ before_action :set_professional_development, only: [:new, :create, :edit, :update, :destroy]
def index
- set_pds
+ @pd_registrations = PdRegistration.where(professional_development_id: @professional_development.id)
end
def show
- @pd_registration = PdRegistration.find(params[:id])
end
def new
@@ -19,59 +17,57 @@ def new
end
def edit
- @pd_registration = PdRegistration.find(params[:id])
end
- # Create and update do NOT support admin overriding user functionality yet, only normal user functionality
def create
- @professional_development = pd_name_to_pd()
- if !@professional_development
- flash.now[:alert] = "Failed to save registration: No PD with that name exists.}"
- render :new
- end
- @pd_registration = PdRegistration.new(pd_registration_params)
- @pd_registration.professional_development = @professional_development
- @pd_registration.teacher = current_user
- if !@pd_registration.save
- flash.now[:alert] = "Failed to save registration: #{@pd_registration.errors.full_messages.join(", ")}"
- render :new
+ @pd_registration = PdRegistration.new(pd_registration_params.merge(
+ professional_development_id: @professional_development.id))
+
+ if @pd_registration.save
+ redirect_to professional_development_path(@professional_development),
+ notice: "Registration for professional development was successfully created."
+ else
+ flash.now[:alert] = @pd_registration.errors.full_messages.to_sentence
+ render "professional_developments/show"
end
- redirect_to pd_registrations_path, success: "PD registration created successfully."
end
def update
- @professional_development = pd_name_to_pd()
- if !@professional_development
- flash.now[:alert] = "Failed to save registration: No PD with that name exists.}"
- render :new
- end
- @pd_registration = PdRegistration.find(params[:id])
- @pd_registration.professional_development = @professional_development
- @pd_registration.teacher = current_user
- if !@pd_registration.update(pd_registration_params)
- flash.now[:alert] = "Failed to save registration: #{@pd_registration.errors.full_messages.join(", ")}"
- render "edit"
+ if @pd_registration.update(pd_registration_params)
+ redirect_to professional_development_path(@professional_development),
+ notice: "Registration was successfully updated."
+ else
+ flash.now[:alert] = @pd_registration.errors.full_messages.to_sentence
+ render "professional_developments/show"
end
- redirect_to pd_registrations_path, success: "Saved the PD registration."
end
def destroy
- if !@pd_registration.destroy
- redirect_back(fallback_location: pd_registrations_path, alert: "Failed to delete PD registration.")
+ if @pd_registration.destroy
+ redirect_to professional_development_path(@professional_development),
+ notice: "Registration was successfully cancelled."
+ else
+ flash.now[:alert] = @pd_registration.errors.full_messages.to_sentence
+ render "professional_developments/show"
end
- redirect_to pd_registrations_path, success: "Deleted PD registration successfully."
end
private
- # This method will take in a that will be the professional_development name,
- # and return the pd; nil if doesn't exist or pd is not open
- # It assumes the pd_registrations form will have a place for the user to input the name of a pd to register for
- # But feel free to edit this if this is not the planned implementation
- def pd_name_to_pd
- professional_development = ProfessionalDevelopment.find(pd_registration_params[:name])
- if professional_development.nil? || !professional_development.registration_open
- return nil
+
+ def set_pd_registration
+ @pd_registration = PdRegistration.find(params[:id])
+ rescue ActiveRecord::RecordNotFound
+ redirect_to professional_development_path, alert: "Registration not found."
+ end
+
+ def set_professional_development
+ @professional_development = ProfessionalDevelopment.find_by(id: params[:professional_development_id])
+ unless @professional_development
+ redirect_to professional_developments_path, alert: "Professional Development not found. test"
end
- professional_development
+ end
+
+ def pd_registration_params
+ params.require(:pd_registration).permit(:teacher_id, :attended, :role, :professional_development_id)
end
end
diff --git a/app/controllers/professional_developments_controller.rb b/app/controllers/professional_developments_controller.rb
index 3a195a7f..67b6808f 100644
--- a/app/controllers/professional_developments_controller.rb
+++ b/app/controllers/professional_developments_controller.rb
@@ -1,18 +1,15 @@
# frozen_string_literal: true
class ProfessionalDevelopmentsController < ApplicationController
- # TODO: revise any method using `set_pds` to use `MockProfessionalDevelopments.all` instead. It's currently used for mocking data.
- before_action :set_pds, only: [:show, :edit, :update, :destroy]
+ before_action :set_professional_development, only: [:show, :edit, :update, :destroy]
before_action :require_login
before_action :require_admin
def index
- set_pds
+ @professional_developments = ProfessionalDevelopment.all
end
def show
- @professional_development = ProfessionalDevelopment.find(params[:id])
- @pd_registrations = @professional_development.pd_registrations
end
def new
@@ -20,88 +17,45 @@ def new
end
def edit
- @professional_development = ProfessionalDevelopment.find(params[:id])
- @pd_registrations = @professional_development.pd_registrations
end
def create
- @professional_development = ProfessionalDevelopment.find_by(name: professional_development_params[:name])
- if @professional_development
- flash.now[:alert] = "A professional development with the name #{@professional_development.name} already exists."
- render :new
- end
@professional_development = ProfessionalDevelopment.new(professional_development_params)
- if !@professional_development.save
- flash.now[:alert] = "Failed to save #{@professional_development.name}: #{@professional_development.errors.full_messages.join(", ")}"
+
+ if @professional_development.save
+ redirect_to @professional_development, notice: "Professional development created successfully."
+ else
+ flash.now[:alert] = @professional_development.errors.full_messages.to_sentence
render :new
end
- redirect_to professional_developments_path, success: "Professional development created successfully."
end
def update
- @professional_development = ProfessionalDevelopment.find(params[:id])
- @pd_registrations = @professional_development.pd_registrations
- if !@professional_development.update(professional_development_params)
- flash.now[:alert] = "Failed to save #{@professional_development.name}: #{@professional_development.errors.full_messages.join(", ")}"
- render "edit"
+ if @professional_development.update(professional_development_params)
+ redirect_to @professional_development, notice: "Professional development updated successfully."
+ else
+ flash.now[:alert] = @professional_development.errors.full_messages.to_sentence
+ render :edit
end
- redirect_to professional_developments_path, success: "Saved #{@professional_development.name}"
end
def destroy
- if !@professional_development.destroy
- redirect_back(fallback_location: professional_developments_path, alert: "Failed to delete #{@professional_development.name}")
+ if @professional_development.destroy
+ redirect_to professional_developments_url, notice: "Professional development deleted successfully."
+ else
+ redirect_to professional_developments_url, alert: "Failed to delete professional development."
end
- redirect_to professional_developments_path, success: "Deleted #{@professional_development.name} successfully."
end
- def set_pds
- @professional_developments = [
- ProfessionalDevelopment.new(
- id: 1,
- name: "Web Development Basics",
- city: "San Francisco",
- state: "CA",
- country: "USA",
- start_date: "2024-04-01",
- end_date: "2024-04-30",
- registration_open: true,
- pd_registrations: [
- PdRegistration.new(id: 1, teacher_id: 1, pd_id: 1, attended: true, role: "leader", teacher_name: "Alex Johnson"),
- PdRegistration.new(id: 2, teacher_id: 2, pd_id: 1, attended: false, role: "attendee", teacher_name: "Jamie Smith")
- ]
- ),
- ProfessionalDevelopment.new(
- id: 2,
- name: "Advanced Pottery",
- city: "New York",
- state: "NY",
- country: "USA",
- start_date: "2024-05-15",
- end_date: "2024-06-15",
- grade_level: "High School",
- registration_open: false,
- pd_registrations: [
- PdRegistration.new(id: 3, teacher_id: 3, pd_id: 2, attended: true, role: "attendee", teacher_name: "Sam Lee"),
- PdRegistration.new(id: 4, teacher_id: 4, pd_id: 2, attended: true, role: "leader", teacher_name: "Chris Doe")
- ]
- ),
- ProfessionalDevelopment.new(
- id: 3,
- name: "Digital Photography",
- city: "London",
- state: "",
- country: "UK",
- start_date: "2024-07-01",
- end_date: "2024-07-31",
- grade_level: "College",
- registration_open: true,
- pd_registrations: [
- PdRegistration.new(id: 5, teacher_id: 5, pd_id: 3, attended: false, role: "attendee", teacher_name: "Morgan Bailey"),
- PdRegistration.new(id: 6, teacher_id: 6, pd_id: 3, attended: true, role: "leader", teacher_name: "Casey Jordan"),
- PdRegistration.new(id: 7, teacher_id: 7, pd_id: 3, attended: true, role: "attendee", teacher_name: "Jordan Casey") # Added an extra registration for variety
- ]
- )
- ]
+ private
+ def set_professional_development
+ @professional_development = ProfessionalDevelopment.find(params[:id])
+ rescue ActiveRecord::RecordNotFound
+ redirect_to professional_developments_url, alert: "Professional development not found. test2"
+ end
+
+ def professional_development_params
+ params.require(:professional_development).permit(:name, :city, :state, :country, :start_date, :end_date,
+ :grade_level)
end
end
From fe605bad72e7f02915aa884788dfb9247ca1c11c Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Thu, 4 Apr 2024 15:27:07 -0700
Subject: [PATCH 15/36] Fix rubocop check
---
app/controllers/pd_registrations_controller.rb | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/controllers/pd_registrations_controller.rb b/app/controllers/pd_registrations_controller.rb
index 009d0b33..e1bfe1d7 100644
--- a/app/controllers/pd_registrations_controller.rb
+++ b/app/controllers/pd_registrations_controller.rb
@@ -53,7 +53,6 @@ def destroy
end
private
-
def set_pd_registration
@pd_registration = PdRegistration.find(params[:id])
rescue ActiveRecord::RecordNotFound
From 12479b7ea9a09d2e809f32e9b2e2db8180e32cb3 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Thu, 4 Apr 2024 17:35:26 -0700
Subject: [PATCH 16/36] Fix title issue
---
app/views/professional_developments/new.html.erb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/views/professional_developments/new.html.erb b/app/views/professional_developments/new.html.erb
index e799dfc3..3203e7a0 100644
--- a/app/views/professional_developments/new.html.erb
+++ b/app/views/professional_developments/new.html.erb
@@ -1,4 +1,4 @@
-<%= provide(:h1, "Add a School") %>
+<%= provide(:h1, "Add a Professional Development Workshop") %>
<%= form_for @professional_development do |f| %>
<%= render 'professional_developments/form', f: f, professional_development: @professional_development %>
From 66ac823631f3f3342304256e1ab1bea37fc2798f Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Fri, 5 Apr 2024 16:52:45 -0700
Subject: [PATCH 17/36] Shorthand PD name on navbar
---
app/helpers/application_helper.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 060ab96d..d1c86c2a 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -20,7 +20,7 @@ def admin_nav_links
"Schools": schools_path,
"Teachers": teachers_path,
"Email Templates": email_templates_path,
- "Professional Developments": professional_developments_path,
+ "PD": professional_developments_path,
}
end
From d26123d5fedd3eeac5545413f4c218e472280225 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Fri, 5 Apr 2024 16:53:21 -0700
Subject: [PATCH 18/36] Remove past event validation
---
app/models/pd_registration.rb | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/app/models/pd_registration.rb b/app/models/pd_registration.rb
index e786ce25..fdd0d292 100644
--- a/app/models/pd_registration.rb
+++ b/app/models/pd_registration.rb
@@ -29,8 +29,6 @@ class PdRegistration < ApplicationRecord
validates :role, inclusion: { in: %w[leader attendee], message: "%{value} is not a valid role" }
validates :attended, inclusion: { in: [true, false] }
- validate :professional_development_dates_passed, on: :create
-
def teacher_name
teacher = Teacher.find_by(id: teacher_id)
if teacher.present?
@@ -39,11 +37,4 @@ def teacher_name
"Teacher not found"
end
end
-
- private
- def professional_development_dates_passed
- return unless professional_development&.end_date&.past?
-
- errors.add(:professional_development_id, "can't register for past events")
- end
end
From 788c1f5adbd62255d9bc66b1db64d90b9860688a Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Fri, 5 Apr 2024 16:54:22 -0700
Subject: [PATCH 19/36] Fix minor details on views
---
app/views/professional_developments/_form.html.erb | 2 +-
app/views/professional_developments/show.html.erb | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/views/professional_developments/_form.html.erb b/app/views/professional_developments/_form.html.erb
index f9748bea..efcedd0c 100644
--- a/app/views/professional_developments/_form.html.erb
+++ b/app/views/professional_developments/_form.html.erb
@@ -3,7 +3,7 @@
<%= f.label :name, "Professional Development Name", class: "label-required" %>
<%= f.text_field :name, placeholder: 'BJC Teacher Training', class: 'form-control',
- required: false, id: 'professional_development_name' %>
+ required: true, id: 'professional_development_name' %>
diff --git a/app/views/professional_developments/show.html.erb b/app/views/professional_developments/show.html.erb
index b92dde35..2ab41ba4 100644
--- a/app/views/professional_developments/show.html.erb
+++ b/app/views/professional_developments/show.html.erb
@@ -33,7 +33,7 @@
Total Registered Teachers: <%= @professional_development.pd_registrations.count %>
- <%= button_tag "Add Teacher", type: 'button', class: "btn btn-success", data: { toggle: "modal", target: "#addTeacherModal" } %>
+ <%= button_tag "Add Registration", type: 'button', class: "btn btn-success", data: { toggle: "modal", target: "#addTeacherModal" } %>
From 27351f135162d2587d5effce692f3fc5c96156e2 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Fri, 5 Apr 2024 16:55:10 -0700
Subject: [PATCH 20/36] Add cucumber tests
---
features/professional_development.feature | 190 ++++++++++++++++++
features/step_definitions/page_steps.rb | 13 ++
.../professional_development_steps.rb | 28 +++
3 files changed, 231 insertions(+)
create mode 100644 features/professional_development.feature
create mode 100644 features/step_definitions/professional_development_steps.rb
diff --git a/features/professional_development.feature b/features/professional_development.feature
new file mode 100644
index 00000000..3dbc6558
--- /dev/null
+++ b/features/professional_development.feature
@@ -0,0 +1,190 @@
+Feature: Professional Development and Registration Management
+
+ As an admin or a teacher
+ I want to manage professional development events and registrations
+ So that I can organize and track participation in PD activities
+
+ Background: Admin and Teacher exist
+ Given the following teachers exist:
+ | first_name | last_name | admin | email | id |
+ | Perry | Zhong | true | testadminuser@berkeley.edu | 100 |
+ | Joseph | Mamoa | false | testteacher@berkeley.edu | 101 |
+ And the following professional developments exist:
+ | id | name | city | state | country | start_date | end_date | grade_level | created_at | updated_at |
+ | 10 | Classroom Management | San Francisco | CA | United States| 2023-02-01 | 2023-02-05 | university | 2023-02-01 00:00:00 | 2023-02-01 00:00:00 |
+ | 11 | Teaching Strategies | San Francisco | CA | United States| 2023-01-01 | 2023-01-05 | university | 2023-01-01 00:00:00 | 2023-01-01 00:00:00 |
+
+# Basic CRUD operations for Professional Developments
+ Scenario: Admin creates a new Professional Development
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ Then I should see "New Requests"
+ Then I go to the new professional development page
+ Then I should see "Create a new Professional Development"
+ And I fill in "Professional Development Name" with "Innovative Teaching Techniques"
+ And I fill in "City" with "San Francisco"
+ And I select "CA" from "State" dropdown
+ And I select "United States" from "Country"
+ And I fill in "Start Date" with "2023-12-01"
+ And I fill in "End Date" with "2023-12-05"
+ And I select "University" from "Grade Level"
+ And I press "Submit"
+ Then I should see a "info" flash message "Professional development created successfully."
+ And I should arrive at the professional development show page titled "Innovative Teaching Techniques"
+
+ Scenario: Admin updates a Professional Development
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ When I go to the professional developments page
+# first "Edit" link is for the Classroom Management, due to alphabetical order in the table
+ And I follow the first "Edit" link
+ And I fill in "Professional Development Name" with "Classroom Management 2.0"
+ And I press "Submit"
+ Then I should see a "info" flash message "Professional development updated successfully."
+ And I should arrive at the professional development show page titled "Classroom Management 2.0"
+
+ Scenario: Admin deletes a Professional Development with confirmation
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ When I go to the professional developments page
+ And I should see "Classroom Management"
+ Then I follow the first "❌" link
+ And I confirm the action
+ Then I should see a "info" flash message "Professional development deleted successfully."
+ And I should be on the professional developments page
+ And I should not see "Classroom Management"
+
+ Scenario: Admin deletes a Professional Development without confirmation
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ When I go to the professional developments page
+ And I should see "Classroom Management"
+ Then I follow the first "❌" link
+ And I dismiss the action
+ And I should be on the professional developments page
+ And I should see "Classroom Management"
+
+ Scenario: Admin registers a teacher for a Professional Development
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ Then I go to the professional developments page
+ And I should see "Classroom Management"
+ And I follow the first "Classroom Management" link
+ Then I should see "Classroom Management"
+ Then I press "Add Registration"
+ Then I should see "Add Teacher to PD Session"
+ And I fill in "Teacher ID" with "100"
+ And I select "Attendee" from "Role"
+ And I select "Yes" from "Attended"
+ And I press "Add"
+ Then I should see a "info" flash message "Registration for professional development was successfully created."
+ And I should see "Total Registered Teachers: 1"
+ And I should see "Perry Zhong"
+
+# Basic CRUD operations for Professional Development Registrations
+ Scenario: Admin updates a teacher's registration for a Professional Development
+ Given the following professional developments registrations exist
+ | id | professional_development_id | teacher_id | role | attended | created_at | updated_at |
+ | 1 | 10 | 100 | attendee | yes | 2023-02-01 00:00:00 | 2023-02-01 00:00:00 |
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ Then I go to the professional developments page
+ And I should see "Classroom Management"
+ And I follow the first "Classroom Management" link
+ Then I should see "Classroom Management"
+ And I should see "Total Registered Teachers: 1"
+ And I should see "Perry Zhong"
+ And I should see "Yes"
+ And I follow "Update"
+ Then I should see "Edit Teacher in PD Session"
+ Then I select "No" from "Attended"
+ And I press "Add"
+ Then I should see a "info" flash message "Registration was successfully updated."
+ And I should see "Perry Zhong"
+ And I should see "No"
+ And I should not see "Yes"
+
+ Scenario: Admin cancels a teacher's registration for a Professional Development
+ Given the following professional developments registrations exist
+ | id | professional_development_id | teacher_id | role | attended | created_at | updated_at |
+ | 1 | 10 | 100 | attendee | yes | 2023-02-01 00:00:00 | 2023-02-01 00:00:00 |
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ Then I go to the professional developments page
+ And I should see "Classroom Management"
+ And I follow the first "Classroom Management" link
+ Then I should see "Classroom Management"
+ And I should see "Total Registered Teachers: 1"
+ And I should see "Perry Zhong"
+ And I should see "Yes"
+ And I follow "❌"
+ And I confirm the action
+ Then I should see a "info" flash message "Registration was successfully cancelled."
+ And I should see "Total Registered Teachers: 0"
+ And I should not see "Perry Zhong"
+ And I should not see "Yes"
+
+# Advanced scenarios for Professional Development and Registration Management
+ Scenario: Admin creates a duplicate Professional Development registration should fail
+ Given the following professional developments registrations exist
+ | id | professional_development_id | teacher_id | role | attended | created_at | updated_at |
+ | 1 | 10 | 100 | attendee | yes | 2023-02-01 00:00:00 | 2023-02-01 00:00:00 |
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ When I go to the professional developments page
+ And I follow the first "Classroom Management" link
+ And I should see "Total Registered Teachers: 1"
+ And I press "Add Registration"
+ And I fill in "Teacher ID" with "100"
+ And I select "Attendee" from "Role"
+ And I select "Yes" from "Attended"
+ And I press "Add"
+ Then I should see a "danger" flash message "Teacher already has a registration for this PD"
+ And I should see "Total Registered Teachers: 1"
+
+ Scenario: Admin attempts to create a Professional Development with an end date earlier than the start date
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ Then I should see "New Requests"
+ Then I go to the new professional development page
+ Then I should see "Create a new Professional Development"
+ And I fill in "Professional Development Name" with "Future Educational Strategies"
+ And I fill in "City" with "Los Angeles"
+ And I select "CA" from "State" dropdown
+ And I select "United States" from "Country"
+ And I fill in "Start Date" with "2023-10-01"
+ And I fill in "End Date" with "2023-09-30"
+ And I select "High School" from "Grade Level"
+ And I press "Submit"
+ Then I should see a "danger" flash message "End date must be after the start date"
+ And I should see "Add a Professional Development Workshop"
+
+ Scenario: Admin attempts to create a Professional Development without mandatory fields
+ Given I am on the BJC home page
+ And I have an admin email
+ And I follow "Log In"
+ Then I can log in with Google
+ Then I should see "New Requests"
+ Then I go to the new professional development page
+ Then I should see "Add a Professional Development Workshop"
+ And I press "Submit"
+ Then I should be on the new professional development page
+ And I should see "Add a Professional Development Workshop"
diff --git a/features/step_definitions/page_steps.rb b/features/step_definitions/page_steps.rb
index 90c8796e..ca791de5 100644
--- a/features/step_definitions/page_steps.rb
+++ b/features/step_definitions/page_steps.rb
@@ -70,3 +70,16 @@
page_slug = Page.find_by(title: link_text).url_slug
page.execute_script("document.getElementById('pagelink_#{page_slug}').click();")
end
+
+
+When(/^I confirm the action$/) do
+ # Confirm the alert that appears after triggering a confirmation dialog
+ page.accept_alert do
+ end
+end
+
+When(/^I dismiss the action$/) do
+ # Dismiss the alert that appears after triggering a confirmation dialog
+ page.dismiss_confirm do
+ end
+end
diff --git a/features/step_definitions/professional_development_steps.rb b/features/step_definitions/professional_development_steps.rb
new file mode 100644
index 00000000..13ab317a
--- /dev/null
+++ b/features/step_definitions/professional_development_steps.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require "cucumber/rspec/doubles"
+
+Given(/the following professional developments exist/) do |professional_developments_table|
+ professional_developments_table.symbolic_hashes.each do |development|
+ ProfessionalDevelopment.create!(development)
+ end
+end
+
+Given(/the following professional developments registrations exist/) do |pd_registrations_table|
+ pd_registrations_table.symbolic_hashes.each do |registration_hash|
+ development = ProfessionalDevelopment.find_by(id: registration_hash[:professional_development_id])
+ raise "ProfessionalDevelopment not found: #{registration_hash[:professional_development_name]}" unless development
+ registration_details = registration_hash.merge(professional_development: development)
+ PdRegistration.create!(registration_details)
+ end
+end
+
+Then(/^I should arrive at the professional development show page titled "([^"]*)"$/) do |title|
+ development = ProfessionalDevelopment.find_by(name: title)
+ expect(current_path).to eq(professional_development_path(development))
+end
+
+Then(/^I visit the professional development show page titled "([^"]*)"$/) do |title|
+ development = ProfessionalDevelopment.find_by(name: title)
+ visit(professional_development_path(development))
+end
From a6a1c01b433d183c4f422f309ff2c0401d229413 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Fri, 5 Apr 2024 17:02:43 -0700
Subject: [PATCH 21/36] Remove debugging msg
---
app/controllers/pd_registrations_controller.rb | 2 +-
app/controllers/professional_developments_controller.rb | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/controllers/pd_registrations_controller.rb b/app/controllers/pd_registrations_controller.rb
index e1bfe1d7..aad1afd5 100644
--- a/app/controllers/pd_registrations_controller.rb
+++ b/app/controllers/pd_registrations_controller.rb
@@ -62,7 +62,7 @@ def set_pd_registration
def set_professional_development
@professional_development = ProfessionalDevelopment.find_by(id: params[:professional_development_id])
unless @professional_development
- redirect_to professional_developments_path, alert: "Professional Development not found. test"
+ redirect_to professional_developments_path, alert: "Professional Development not found."
end
end
diff --git a/app/controllers/professional_developments_controller.rb b/app/controllers/professional_developments_controller.rb
index 67b6808f..673ec330 100644
--- a/app/controllers/professional_developments_controller.rb
+++ b/app/controllers/professional_developments_controller.rb
@@ -51,7 +51,7 @@ def destroy
def set_professional_development
@professional_development = ProfessionalDevelopment.find(params[:id])
rescue ActiveRecord::RecordNotFound
- redirect_to professional_developments_url, alert: "Professional development not found. test2"
+ redirect_to professional_developments_url, alert: "Professional development not found."
end
def professional_development_params
From 9e122edf2c0ab6882f0004e76e64415fbb276ea4 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Fri, 5 Apr 2024 18:38:34 -0700
Subject: [PATCH 22/36] Fix indexing & uniqueness check for pd
---
app/models/professional_development.rb | 5 -----
.../20240404190834_create_professional_developments.rb | 2 --
db/schema.rb | 1 -
3 files changed, 8 deletions(-)
diff --git a/app/models/professional_development.rb b/app/models/professional_development.rb
index 8962e17f..9467b217 100644
--- a/app/models/professional_development.rb
+++ b/app/models/professional_development.rb
@@ -15,16 +15,11 @@
# created_at :datetime not null
# updated_at :datetime not null
#
-# Indexes
-#
-# index_professional_developments_on_name_and_start_date (name,start_date) UNIQUE
-#
class ProfessionalDevelopment < ApplicationRecord
VALID_STATES = %w[AL AK AS AZ AR CA CO CT DE DC FM FL GA GU HI ID IL IN IA KS KY LA ME MH MD MA MI MN MS MO MT NE NV
NH NJ NM NY NC ND MP OH OK OR PW PA PR RI SC SD TN TX UT VT VI VA WA WV WI WY].freeze
validates :name, :city, :country, :start_date, :end_date, presence: true
- validates :name, uniqueness: { scope: :start_date, message: "should be unique per start date" }
validates :state, presence: true, if: -> { country == "US" }
validates :state, inclusion: { in: VALID_STATES, message: "%{value} is not a valid state" },
if: -> { country == "US" }
diff --git a/db/migrate/20240404190834_create_professional_developments.rb b/db/migrate/20240404190834_create_professional_developments.rb
index 61ba5f50..f551d470 100644
--- a/db/migrate/20240404190834_create_professional_developments.rb
+++ b/db/migrate/20240404190834_create_professional_developments.rb
@@ -11,7 +11,5 @@ def change
t.timestamps
end
-
- add_index :professional_developments, [:name, :start_date], unique: true
end
end
diff --git a/db/schema.rb b/db/schema.rb
index 44f6d7f9..0f32ca27 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -102,7 +102,6 @@
t.integer "grade_level", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
- t.index ["name", "start_date"], name: "index_professional_developments_on_name_and_start_date", unique: true
end
create_table "schools", id: :serial, force: :cascade do |t|
From 700c87ea4bcc6a08668b50a3d9d76ebdf318f0db Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Fri, 5 Apr 2024 18:39:31 -0700
Subject: [PATCH 23/36] Add rspec tests
---
Gemfile | 3 +
Gemfile.lock | 5 +
.../pd_registrations_controller_spec.rb | 120 ++++++++++++++++++
...ofessional_developments_controller_spec.rb | 119 +++++++++++++++++
4 files changed, 247 insertions(+)
create mode 100644 spec/controllers/pd_registrations_controller_spec.rb
create mode 100644 spec/controllers/professional_developments_controller_spec.rb
diff --git a/Gemfile b/Gemfile
index 0b73d21e..e1dac083 100644
--- a/Gemfile
+++ b/Gemfile
@@ -96,4 +96,7 @@ group :test do
# Accessibility Testing
gem "axe-core-rspec"
gem "axe-core-cucumber"
+
+ # Test suite speedup
+ gem "rails-controller-testing"
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 24ef2246..9d16489e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -399,6 +399,10 @@ GEM
bundler (>= 1.15.0)
railties (= 6.1.7.6)
sprockets-rails (>= 2.0.0)
+ rails-controller-testing (1.0.5)
+ actionpack (>= 5.0.1.rc1)
+ actionview (>= 5.0.1.rc1)
+ activesupport (>= 5.0.1.rc1)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
@@ -595,6 +599,7 @@ DEPENDENCIES
puma (~> 5)
rack-mini-profiler (~> 2.0)
rails (= 6.1.7.6)
+ rails-controller-testing
rspec-rails
rubocop
rubocop-faker
diff --git a/spec/controllers/pd_registrations_controller_spec.rb b/spec/controllers/pd_registrations_controller_spec.rb
new file mode 100644
index 00000000..1a91a95f
--- /dev/null
+++ b/spec/controllers/pd_registrations_controller_spec.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+RSpec.describe "PdRegistrations", type: :request do
+ fixtures :all
+
+ let(:admin_user) { teachers(:admin) }
+ let(:regular_user) { teachers(:validated_teacher) }
+ let(:professional_development) { ProfessionalDevelopment.create!(name: "Effective Teaching Strategies", city: "City", state: "State", country: "Country", start_date: Date.today, end_date: Date.tomorrow, grade_level: "university") }
+ let(:teacher1) { teachers(:bob) }
+ let(:teacher2) { teachers(:long) }
+ let(:valid_registration_attributes) {
+ {
+ teacher_id: teacher1.id,
+ professional_development_id: professional_development.id,
+ attended: true,
+ role: "attendee"
+ }
+ }
+ let(:valid_registration_attributes2) {
+ {
+ teacher_id: teacher2.id,
+ professional_development_id: professional_development.id,
+ attended: true,
+ role: "attendee"
+ }
+ }
+ let(:invalid_registration_attributes) {
+ {
+ teacher_id: nil,
+ professional_development_id: nil,
+ attended: false,
+ role: ""
+ }
+ }
+ let!(:pd_registration) { PdRegistration.create!(valid_registration_attributes) }
+
+ shared_examples "admin access" do
+ it "allows operation" do
+ action
+ expect(flash[:notice]).to match(/successfully/)
+ end
+ end
+
+ shared_examples "regular user denied" do
+ it "denies operation" do
+ action
+ expect(flash[:danger]).to be_present
+ end
+ end
+
+ describe "CRUD operations for PD Registrations" do
+ describe "CREATE" do
+ let(:action) { post professional_development_pd_registrations_path(
+ professional_development_id: professional_development.id),
+ params: { pd_registration: valid_registration_attributes2 } }
+
+ context "as an admin" do
+ before { log_in(admin_user) }
+ include_examples "admin access"
+ end
+
+ context "as a regular user" do
+ before { log_in(regular_user) }
+ include_examples "regular user denied"
+ end
+ end
+
+ describe "UPDATE" do
+ context "with valid attributes" do
+ let(:action) { patch professional_development_pd_registration_path(
+ professional_development_id: professional_development.id, id: pd_registration.id),
+ params: { pd_registration: { role: "leader" } } }
+
+ context "as an admin" do
+ before { log_in(admin_user) }
+ include_examples "admin access"
+ end
+
+ context "as a regular user" do
+ before { log_in(regular_user) }
+ include_examples "regular user denied"
+ end
+ end
+
+ context "with invalid attributes" do
+ let(:action) { patch professional_development_pd_registration_path(
+ professional_development_id: professional_development.id, id: pd_registration.id),
+ params: { pd_registration: invalid_registration_attributes } }
+
+ it "fails to update and redirects for admin" do
+ log_in(admin_user)
+ action
+ expect(response).to render_template(:show)
+ expect(flash[:alert]).to be_present
+ end
+ end
+ end
+
+ describe "DELETE" do
+ let(:action) { delete professional_development_pd_registration_path(
+ professional_development_id: professional_development.id, id: pd_registration.id) }
+
+ context "as an admin" do
+ before { log_in(admin_user) }
+ it "deletes the PD registration" do
+ expect { action }.to change(PdRegistration, :count).by(-1)
+ expect(response).to redirect_to(professional_development_path(professional_development))
+ expect(flash[:notice]).to match(/successfully cancelled/)
+ end
+ end
+
+ context "as a regular user" do
+ before { log_in(regular_user) }
+ include_examples "regular user denied"
+ end
+ end
+ end
+end
diff --git a/spec/controllers/professional_developments_controller_spec.rb b/spec/controllers/professional_developments_controller_spec.rb
new file mode 100644
index 00000000..a6952181
--- /dev/null
+++ b/spec/controllers/professional_developments_controller_spec.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+require "rails_helper"
+
+RSpec.describe "ProfessionalDevelopments", type: :request do
+ fixtures :all
+
+ let(:admin_user) { teachers(:admin) }
+ let(:regular_user) { teachers(:validated_teacher) }
+ let(:valid_attributes) {
+ {
+ name: "Effective Teaching Strategies",
+ city: "City",
+ state: "State",
+ country: "Country",
+ start_date: Date.today,
+ end_date: Date.tomorrow,
+ grade_level: "university",
+ }
+ }
+ let(:valid_attributes2) {
+ {
+ name: "PD2",
+ city: "City",
+ state: "State",
+ country: "Country",
+ start_date: Date.today,
+ end_date: Date.tomorrow,
+ grade_level: "university",
+ }
+ }
+ let(:invalid_attributes) {
+ {
+ name: "",
+ city: "",
+ state: "",
+ country: "",
+ start_date: Date.tomorrow,
+ end_date: Date.today - 1.day,
+ grade_level: ""
+ }
+ }
+ let!(:professional_development) { ProfessionalDevelopment.create!(valid_attributes) }
+
+ shared_examples "admin access" do
+ it "allows operation" do
+ action
+ expect(flash[:notice]).to match(/successfully/)
+ end
+ end
+
+ shared_examples "regular user denied" do
+ it "denies operation and redirects" do
+ action
+ expect(flash[:danger]).to be_present
+ end
+ end
+
+ describe "CRUD operations" do
+ describe "CREATE" do
+ let(:action) { post professional_developments_path, params: { professional_development: valid_attributes2 } }
+
+ context "as an admin" do
+ before { log_in(admin_user) }
+ include_examples "admin access"
+ end
+
+ context "as a regular user" do
+ before { log_in(regular_user) }
+ include_examples "regular user denied"
+ end
+ end
+
+ describe "UPDATE" do
+ context "with valid attributes" do
+ let(:action) { patch professional_development_path(professional_development), params: { professional_development: { name: "Updated PD Name" } } }
+
+ context "as an admin" do
+ before { log_in(admin_user) }
+ include_examples "admin access"
+ end
+
+ context "as a regular user" do
+ before { log_in(regular_user) }
+ include_examples "regular user denied"
+ end
+ end
+
+ context "with invalid attributes" do
+ let(:action) { patch professional_development_path(professional_development), params: { professional_development: invalid_attributes } }
+
+ it "fails to update and re-renders edit for admin" do
+ log_in(admin_user)
+ action
+ expect(response).to render_template(:edit)
+ expect(flash[:alert]).to be_present
+ end
+ end
+ end
+
+ describe "DELETE" do
+ let(:action) { delete professional_development_path(professional_development) }
+
+ context "as an admin" do
+ before { log_in(admin_user) }
+ it "deletes the professional development" do
+ expect { action }.to change(ProfessionalDevelopment, :count).by(-1)
+ expect(response).to redirect_to(professional_developments_path)
+ expect(flash[:notice]).to match(/deleted successfully/)
+ end
+ end
+
+ context "as a regular user" do
+ before { log_in(regular_user) }
+ include_examples "regular user denied"
+ end
+ end
+ end
+end
From dbeb705b89baf3b759f76b4fc9d39471adb0f5e1 Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Fri, 5 Apr 2024 18:40:00 -0700
Subject: [PATCH 24/36] Add admin check for pd registrations
---
app/controllers/pd_registrations_controller.rb | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/controllers/pd_registrations_controller.rb b/app/controllers/pd_registrations_controller.rb
index aad1afd5..55c171ef 100644
--- a/app/controllers/pd_registrations_controller.rb
+++ b/app/controllers/pd_registrations_controller.rb
@@ -2,6 +2,7 @@
class PdRegistrationsController < ApplicationController
before_action :require_login
+ before_action :require_admin
before_action :set_pd_registration, only: [:show, :edit, :update, :destroy]
before_action :set_professional_development, only: [:new, :create, :edit, :update, :destroy]
From 2c154298816be6ec0bd2b283c3dece298f896c20 Mon Sep 17 00:00:00 2001
From: JacksonXu33
Date: Mon, 8 Apr 2024 21:31:37 -0700
Subject: [PATCH 25/36] fix update path bug for admins, and some refactor
---
app/controllers/teachers_controller.rb | 63 ++++++++++++++------------
1 file changed, 35 insertions(+), 28 deletions(-)
diff --git a/app/controllers/teachers_controller.rb b/app/controllers/teachers_controller.rb
index d64b2430..1b986e08 100644
--- a/app/controllers/teachers_controller.rb
+++ b/app/controllers/teachers_controller.rb
@@ -107,30 +107,22 @@ def update
@teacher.school = @school
end
send_email_if_application_status_changed_and_email_resend_enabled
- if @teacher.denied? && !is_admin?
- redirect_to root_path, alert: "Failed to update your information. You have already been denied. If you have questions, please email contact@bjc.berkeley.edu."
- return
- end
- if (@teacher.email_changed? || @teacher.snap_changed?) && !is_admin?
- redirect_to edit_teacher_path(current_user.id), alert: "Failed to update your information. If you want to change your email or Snap! username, please email contact@bjc.berkeley.edu."
- return
- end
- if !@teacher.save
- redirect_to edit_teacher_path(current_user.id),
- alert: "An error occurred: #{@teacher.errors.full_messages.join(', ')}"
+
+ if fail_to_update
return
end
+
if !@teacher.validated? && !current_user.admin?
TeacherMailer.form_submission(@teacher).deliver_now
TeacherMailer.teacher_form_submission(@teacher).deliver_now
end
+
if is_admin?
- redirect_to edit_teacher_path(current_user.id), notice: "Saved #{@teacher.full_name}"
- return
+ redirect_to teachers_path, notice: "Saved #{@teacher.full_name}"
else
@teacher.try_append_ip(request.remote_ip)
+ redirect_to edit_teacher_path(current_user.id), notice: "Successfully updated your information"
end
- redirect_to edit_teacher_path(current_user.id), notice: "Successfully updated your information"
end
def send_email_if_application_status_changed_and_email_resend_enabled
@@ -201,6 +193,23 @@ def deny_access
redirect_to new_teacher_path, alert: "Email address or Snap username already in use. Please use a different email or Snap username."
end
+ def fail_to_update
+ failed = false
+ if @teacher.denied? && !is_admin?
+ redirect_to root_path, alert: "Failed to update your information. You have already been denied. If you have questions, please email contact@bjc.berkeley.edu."
+ failed = true
+ elsif (@teacher.email_changed? || @teacher.snap_changed?) && !is_admin?
+ flash.now[:alert] = "Failed to update your information. If you want to change your email or Snap! username, please email contact@bjc.berkeley.edu."
+ render "edit"
+ failed = true
+ elsif !@teacher.save
+ flash.now[:alert] = "Failed to update data. #{@teacher.errors.full_messages.to_sentence}"
+ render "edit"
+ failed = true
+ end
+ failed
+ end
+
def load_school
if teacher_params[:school_id].present?
@school ||= School.find(teacher_params[:school_id])
@@ -238,22 +247,20 @@ def ordered_schools
end
def sanitize_params
- if params[:teacher]
- if params[:teacher][:status]
- params[:teacher][:status] = params[:teacher][:status].to_i
- end
- if params[:teacher][:education_level]
- params[:teacher][:education_level] = params[:teacher][:education_level].to_i
- end
+ teacher = params[:teacher]
+ if teacher && teacher[:status]
+ teacher[:status] = teacher[:status].to_i
+ end
+ if teacher && teacher[:education_level]
+ teacher[:education_level] = teacher[:education_level].to_i
end
- if params[:school]
- if params[:school][:grade_level]
- params[:school][:grade_level] = params[:school][:grade_level].to_i
- end
- if params[:school][:school_type]
- params[:school][:school_type] = params[:school][:school_type].to_i
- end
+ school = params[:school]
+ if school && school[:grade_level]
+ school[:grade_level] = school[:grade_level].to_i
+ end
+ if school && school[:school_type]
+ school[:school_type] = school[:school_type].to_i
end
end
From 9099abd2130ebc374ecb86e5dd0effec9a864794 Mon Sep 17 00:00:00 2001
From: JacksonXu33
Date: Thu, 11 Apr 2024 15:49:22 -0700
Subject: [PATCH 26/36] add more helper methods to update and create
---
app/controllers/teachers_controller.rb | 74 +++++++++++++++++---------
1 file changed, 49 insertions(+), 25 deletions(-)
diff --git a/app/controllers/teachers_controller.rb b/app/controllers/teachers_controller.rb
index 1b986e08..8739064e 100644
--- a/app/controllers/teachers_controller.rb
+++ b/app/controllers/teachers_controller.rb
@@ -49,25 +49,13 @@ def new
# TODO: This needs to be re-written.
# If you are logged in and not an admin, this should fail.
def create
- # Find by email, but allow updating other info.
- @teacher = Teacher.find_by(email: teacher_params[:email])
- if @teacher && defined?(current_user.id) && (current_user.id == @teacher.id)
- params[:id] = current_user.id
- update
- return
- elsif @teacher
- redirect_to login_path,
- notice: "You already have signed up with '#{@teacher.email}'. Please log in."
+ if existing_teacher
return
end
- load_school
- if @school.new_record?
- @school = School.new(school_params)
- unless @school.save
- flash[:alert] = "An error occurred! #{@school.errors.full_messages.join(', ')}"
- render "new" && return
- end
+ valid_school = find_or_create_school
+ if !valid_school
+ return
end
@teacher = Teacher.new(teacher_params)
@@ -96,16 +84,12 @@ def update
load_school
ordered_schools
@teacher.assign_attributes(teacher_params)
- if teacher_params[:school_id].present?
- @teacher.school = @school
- else
- @school.update(school_params) if school_params
- unless @school.save
- flash[:alert] = "An error occurred: #{@school.errors.full_messages.join(', ')}"
- render "new" && return
- end
- @teacher.school = @school
+
+ valid_school = update_school_through_teacher
+ if !valid_school
+ return
end
+
send_email_if_application_status_changed_and_email_resend_enabled
if fail_to_update
@@ -193,6 +177,46 @@ def deny_access
redirect_to new_teacher_path, alert: "Email address or Snap username already in use. Please use a different email or Snap username."
end
+ def existing_teacher
+ # Find by email, but allow updating other info.
+ @teacher = Teacher.find_by(email: teacher_params[:email])
+ if @teacher && defined?(current_user.id) && (current_user.id == @teacher.id)
+ params[:id] = current_user.id
+ update
+ return true
+ elsif @teacher
+ redirect_to login_path, notice: "You already have signed up with '#{@teacher.email}'. Please log in."
+ return true
+ end
+ false
+ end
+
+ def find_or_create_school
+ load_school
+ if @school.new_record?
+ @school = School.new(school_params)
+ unless @school.save
+ flash[:alert] = "An error occurred! #{@school.errors.full_messages.join(', ')}"
+ render "new"
+ return false
+ end
+ end
+ true
+ end
+
+ def update_school_through_teacher
+ if !teacher_params[:school_id].present?
+ @school.update(school_params) if school_params
+ unless @school.save
+ flash[:alert] = "An error occurred: #{@school.errors.full_messages.join(', ')}"
+ render "new"
+ return false
+ end
+ end
+ @teacher.school = @school
+ true
+ end
+
def fail_to_update
failed = false
if @teacher.denied? && !is_admin?
From 54eebc6f8fb43c4cd255ecdfeea93cd732e95953 Mon Sep 17 00:00:00 2001
From: JacksonXu33
Date: Thu, 11 Apr 2024 22:17:17 -0700
Subject: [PATCH 27/36] fix school bug in create method
---
app/controllers/teachers_controller.rb | 32 +++++++++-----------------
1 file changed, 11 insertions(+), 21 deletions(-)
diff --git a/app/controllers/teachers_controller.rb b/app/controllers/teachers_controller.rb
index 8739064e..725a3610 100644
--- a/app/controllers/teachers_controller.rb
+++ b/app/controllers/teachers_controller.rb
@@ -53,9 +53,13 @@ def create
return
end
- valid_school = find_or_create_school
- if !valid_school
- return
+ load_school
+ if @school.new_record?
+ @school = School.new(school_params)
+ unless @school.save
+ flash[:alert] = "An error occurred! #{@school.errors.full_messages.join(', ')}"
+ render "new" && return
+ end
end
@teacher = Teacher.new(teacher_params)
@@ -191,19 +195,6 @@ def existing_teacher
false
end
- def find_or_create_school
- load_school
- if @school.new_record?
- @school = School.new(school_params)
- unless @school.save
- flash[:alert] = "An error occurred! #{@school.errors.full_messages.join(', ')}"
- render "new"
- return false
- end
- end
- true
- end
-
def update_school_through_teacher
if !teacher_params[:school_id].present?
@school.update(school_params) if school_params
@@ -218,20 +209,19 @@ def update_school_through_teacher
end
def fail_to_update
- failed = false
if @teacher.denied? && !is_admin?
redirect_to root_path, alert: "Failed to update your information. You have already been denied. If you have questions, please email contact@bjc.berkeley.edu."
- failed = true
+ return true
elsif (@teacher.email_changed? || @teacher.snap_changed?) && !is_admin?
flash.now[:alert] = "Failed to update your information. If you want to change your email or Snap! username, please email contact@bjc.berkeley.edu."
render "edit"
- failed = true
+ return true
elsif !@teacher.save
flash.now[:alert] = "Failed to update data. #{@teacher.errors.full_messages.to_sentence}"
render "edit"
- failed = true
+ return true
end
- failed
+ false
end
def load_school
From 119fb61b5de23d49e74293d9f3c1a53bcf5eb9af Mon Sep 17 00:00:00 2001
From: Jingchao Zhong <92573736+perryzjc@users.noreply.github.com>
Date: Mon, 15 Apr 2024 11:09:45 -0700
Subject: [PATCH 28/36] Backup some frontend change for multi personal emails
---
app/models/teacher.rb | 2 +-
app/views/teachers/_form.html.erb | 32 ++++++++++++++++-------
app/views/teachers/_teacher_info.html.erb | 13 +++++----
3 files changed, 29 insertions(+), 18 deletions(-)
diff --git a/app/models/teacher.rb b/app/models/teacher.rb
index 73661cf7..586d922b 100644
--- a/app/models/teacher.rb
+++ b/app/models/teacher.rb
@@ -303,6 +303,6 @@ def personal_emails
def non_primary_emails
# email_addresses.where(primary: false)&.pluck(:email)
# below code is temporary for current PR, to make sure the frontend same as before (only one personal email)
- email_addresses.where(primary: false)&.pluck(:email)&.first
+ email_addresses.where(primary: false)&.pluck(:email)
end
end
diff --git a/app/views/teachers/_form.html.erb b/app/views/teachers/_form.html.erb
index 0d8e1889..8e228987 100644
--- a/app/views/teachers/_form.html.erb
+++ b/app/views/teachers/_form.html.erb
@@ -82,16 +82,19 @@ status ONLY IF the person viewing this page is an admin. %>
<%# For now... only admins can enter/edit personal emails. %>
- <%- if current_user&.admin? || @teacher.personal_emails.present? %>
- | |