Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions app/controllers/event_registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ def bulk_create
end
end

def destroy
@event_registration = EventRegistration.find(params[:id])
if @event_registration
@event_registration.destroy
flash[:notice] = 'You are no longer registered.'
redirect_to events_path
else
flash[:alert] = 'Unable to find that registration.'
end
end

private

def event_registration_params
Expand Down
18 changes: 11 additions & 7 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
class EventsController < ApplicationController
before_action :set_event, only: %i[ show edit update destroy ]
before_action :authorize_admin!, only: %i[ new edit update destroy ]
before_action :authorize_admin!, only: %i[ edit update destroy ]

layout "tailwind", only: [:index, :edit, :new, :show]

def index
@events = Event.all
@events = Event.order(start_date: :desc)
@events = @events.publicly_visible unless current_user.super_user?
end

def show
end

def new
def new # all logged in users can create events
@event = Event.new
end

Expand All @@ -21,7 +24,7 @@ def create

respond_to do |format|
if @event.save
format.html { redirect_to @event, notice: "Event was successfully created." }
format.html { redirect_to events_path, notice: "Event was successfully created." }
format.json { render :show, status: :created, location: @event }
else
format.html { render :new, status: :unprocessable_entity }
Expand All @@ -33,7 +36,7 @@ def create
def update
respond_to do |format|
if @event.update(event_params)
format.html { redirect_to @event, notice: "Event was successfully updated." }
format.html { redirect_to events_path, notice: "Event was successfully updated." }
format.json { render :show, status: :ok, location: @event }
else
format.html { render :edit, status: :unprocessable_entity }
Expand All @@ -58,10 +61,11 @@ def set_event
end

def event_params
params.require(:event).permit(:title, :description, :start_date, :end_date, :registration_close_date, :publicly_visible)
params.require(:event).permit(:title, :description, :start_date, :end_date,
:registration_close_date, :publicly_visible)
end

def authorize_admin!
redirect_to events_path, alert: "You are not authorized to perform this action." unless current_admin
redirect_to events_path, alert: "You are not authorized to perform this action." unless current_user.super_user?
end
end
7 changes: 7 additions & 0 deletions app/models/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,11 @@ class Event < ApplicationRecord

validates_presence_of :title, :start_date, :end_date
validates_inclusion_of :publicly_visible, in: [true, false]

scope :publicly_visible, -> { where(publicly_visible: true) }

def registerable?
publicly_visible &&
(registration_close_date.nil? || registration_close_date >= Time.current)
end
end
59 changes: 39 additions & 20 deletions app/views/events/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,29 +1,48 @@
<%= form_for(@event) do |f| %>
<%= render 'shared/errors', resource: @event if @event.errors.any? %>
<%= simple_form_for(@event) do |f| %>
<% if @event.errors.any? %>
<%= render 'shared/errors', resource: @event %>
<% end %>

<div class="form-group">
<%= f.label :title, class: 'bold' %>
<%= f.text_field :title, class: 'form-control' %>

<%= f.label :description, class: 'bold' %>
<%= f.text_area :description, class: 'form-control' %>

<%= f.label :start_date, class: 'bold' %>
<%= f.text_field :start_date, type: 'datetime-local', class: 'form-control', value: @event.start_date&.strftime('%Y-%m-%dT%H:%M') %>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div>
<%= f.input :title, label: "Event title", input_html: { class: "w-full rounded border-gray-300 shadow-sm px-3 py-2 focus:ring-blue-500 focus:border-blue-500" } %>
</div>
<div>
<%= f.input :description, input_html: { class: "w-full rounded border-gray-300 shadow-sm px-3 py-2 focus:ring-blue-500 focus:border-blue-500" } %>
</div>
<div class="flex items-center mt-6 md:mt-0">
<%= f.input :publicly_visible, as: :boolean, label: "Publicly visible?", wrapper_html: { class: "ml-2" } %>
</div>
</div>

<%= f.label :end_date, class: 'bold' %>
<%= f.text_field :end_date, type: 'datetime-local', class: 'form-control', value: @event.end_date&.strftime('%Y-%m-%dT%H:%M') %>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div>
<%= f.label :start_date, "Start time", class: "block font-medium mb-1" %>
<%= f.text_field :start_date, type: 'datetime-local',
class: "w-full rounded border-gray-300 shadow-sm px-3 py-2 focus:ring-blue-500 focus:border-blue-500",
value: @event.start_date&.strftime('%Y-%m-%dT%H:%M') %>
</div>

<%= f.label :registration_close_date, class: 'bold' %>
<%= f.text_field :registration_close_date, type: 'datetime-local', class: 'form-control', value: @event.registration_close_date&.strftime('%Y-%m-%dT%H:%M') %>
<div>
<%= f.label :end_date, "End time", class: "block font-medium mb-1" %>
<%= f.text_field :end_date, type: 'datetime-local',
class: "w-full rounded border-gray-300 shadow-sm px-3 py-2 focus:ring-blue-500 focus:border-blue-500",
value: @event.end_date&.strftime('%Y-%m-%dT%H:%M') %>
</div>

<div style="display: flex; align-items: center; margin-top: 10px; column-gap: 10px;">
<%= f.label :publicly_visible, class: 'bold' %>
<%= f.check_box :publicly_visible, { class: 'bold mt-0' }, '1', '0' %>
<div>
<%= f.label :registration_close_date, "Registration close time", class: "block font-medium mb-1" %>
<%= f.text_field :registration_close_date, type: 'datetime-local',
class: "w-full rounded border-gray-300 shadow-sm px-3 py-2 focus:ring-blue-500 focus:border-blue-500",
value: @event.registration_close_date&.strftime('%Y-%m-%dT%H:%M') %>
</div>
</div>

<div class="form-actions">
<%= f.button :submit, class: 'btn btn-primary' %>
<div class="flex justify-end gap-4">
<%= link_to_button "Delete", @event, variant: :danger_outline, method: :delete, data: { confirm: "Are you sure?" } %>
<%= link_to_button 'Cancel', events_path, variant: :secondary_outline %>
<%= f.button :submit, class: 'inline-flex items-center gap-2 px-4 py-2 rounded-lg
transition-colors duration-200 font-medium shadow-sm text-sm
border border-blue-600 text-grey-600 hover:bg-blue-600 hover:text-white' %>
</div>
<% end %>
18 changes: 12 additions & 6 deletions app/views/events/edit.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<h1>Editing Event</h1>

<%= render 'form', event: @event %>

<%= link_to 'Show', @event %> |
<%= link_to 'Back', events_path %>
<div class="w-full">
<div class="flex items-start justify-between mb-6">
<div class="pr-6">
<h1 class="text-2xl font-semibold text-gray-900 mb-2">Edit Event</h1>
<div class="border-b border-gray-300 mb-6"></div>
<div class="mt-4 pt-4">
<%= render 'form', event: @event %>
</div>
</div>
<%= link_to_button 'View', event_path(@event), variant: :secondary_outline %>
</div>
</div>
164 changes: 116 additions & 48 deletions app/views/events/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,60 +1,128 @@
<p id="notice"><%= notice %></p>
<div class="w-full">
<div class="flex items-start justify-between mb-6">
<div class="pr-6">
<h1 class="text-2xl font-semibold mb-2">Events</h1>
Click 'Register' and then 'Register for Selected Events' to sign up for multiple events at once.
</div>
<%= link_to_button 'New Event', new_event_path, variant: :secondary_outline %>
</div>
</div>

<h1>Events</h1>
<!-- Divider -->
<div class="w-full mt-6">
<hr class="border-gray-300 mb-6">
</div>

<%= form_with url: bulk_create_event_registrations_path, method: :post, local: true, id: 'bulk-registration-form' do |form| %>
<table>
<thead>
<tr>
<th></th>
<th>Title</th>
<th>Start Date</th>
<th>End Date</th>
<th>Actions</th>
</tr>
</thead>

<tbody>
<% if @events.any? %>
<% if @events.length > 10 %>
<div class="flex justify-center mb-6">
<%= form.submit "Register for Selected Events",
class: "inline-flex items-center gap-2 px-4 py-2 rounded-lg
transition-colors duration-200 font-medium shadow-sm text-sm
border border-blue-600 bg-blue-600 text-white
enabled:hover:bg-white enabled:hover:text-blue-600
disabled:opacity-50 disabled:cursor-not-allowed",
id: "register-button",
disabled: true %>
</div>
<% end %>

<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<% @events.each do |event| %>
<tr>
<td style="padding-right: 10px;">
<%= check_box_tag "event_ids[]", event.id, false, class: "event-checkbox" %>
</td>
<td style="padding-right: 10px;"><%= event.title %></td>
<td style="padding-right: 10px;"><%= event.start_date.strftime("%B %d, %Y") if event.start_date %></td>
<td style="padding-right: 10px;"><%= event.end_date.strftime("%B %d, %Y") if event.end_date %></td>
<td style="padding-right: 10px;">
<%= link_to 'Show', event %> |
<%= link_to 'Edit', edit_event_path(event) %> |
<%= link_to 'Destroy', event, method: :delete, data: { confirm: 'Are you sure?' } %>
</td>
</tr>
<div class="relative bg-white border border-gray-200 rounded-xl shadow hover:shadow-md transition p-4 flex flex-col">
<!-- Event Title -->
<h3 class="text-lg font-semibold text-gray-900 mb-2">
<%= link_to "#{ event.title}#{ " [HIDDEN]" if current_user.super_user? && !event.publicly_visible }",
event_path(event),
class: "text-gray-900 hover:underline font-medium block" %>

</h3>
<% if event.event_registrations.where(email: current_user.email).exists? %>
<span class="absolute top-2 right-2 text-xs bg-green-100 text-green-700 px-2 py-0.5 rounded-full">
Registered
</span>
<% end %>

<!-- Dates -->
<p class="text-sm text-gray-600 mb-1">
<span class="font-medium">Start:</span>
<%= event.start_date&.strftime("%B %d, %Y") %>
</p>
<p class="text-sm text-gray-600 mb-3">
<span class="font-medium">End:</span>
<%= event.end_date&.strftime("%B %d, %Y") %>
</p>

<div class="mt-3 flex gap-2">
<!-- Admin Edit -->
<% if current_user.super_user %>
<%= link_to_button 'Edit', edit_event_path(event), variant: :secondary_outline %>
<% end %>

<% if event.event_registrations.where(email: current_user.email).exists? %>
<% registration = event.event_registrations.where(email: current_user.email).last %>
<div class="mt-2 ml-auto">
<%= link_to_button 'De-register',
event_registration_path(registration),
variant: :secondary_outline,
method: :delete,
data: { confirm: "Are you sure you want to de-register?" } %>
</div>
<% elsif event.registerable? %>
<div class="mt-2 ml-auto">
<label class="flex items-center gap-2 cursor-pointer">
<%= check_box_tag "event_ids[]",
event.id,
false,
id: "event_ids_#{event.id}",
class: "event-checkbox h-5 w-5 text-blue-600 rounded border-gray-300 focus:ring-blue-500" %>
<span class="text-gray-700">Register</span>
</label>
</div>
<% else %>
<div class="mt-2 ml-auto">
<span class="text-sm text-gray-500 italic">Registration closed</span>
</div>
<% end %>
</div>
</div>
<% end %>
</tbody>
</table>
</div>

<br>

<div>
<%= form.submit "Register for Selected Events", class: "btn btn-primary", id: "register-button", disabled: true %>
<%= link_to 'New Event', new_event_path, class: "btn btn-primary" %>
</div>
<div class="flex justify-start m-6">
<%= form.submit "Register for Selected Events",
class: "inline-flex items-center gap-2 px-4 py-2 rounded-lg
transition-colors duration-200 font-medium shadow-sm text-sm
border border-blue-600 bg-blue-600 text-white
enabled:hover:bg-white enabled:hover:text-blue-600
disabled:opacity-50 disabled:cursor-not-allowed",
id: "register-button",
disabled: true %>
</div>
<% else %>
<!-- Empty State -->
<div class="text-center py-12 text-gray-500">
No events available.
</div>
<% end %>
<% end %>

<script>
document.addEventListener('DOMContentLoaded', function() {
const checkboxes = document.querySelectorAll('.event-checkbox');
const registerButton = document.getElementById('register-button');

function updateRegisterButton() {
const checkedBoxes = document.querySelectorAll('.event-checkbox:checked');
registerButton.disabled = checkedBoxes.length === 0;
}

checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', updateRegisterButton);
document.addEventListener('DOMContentLoaded', function() {
const checkboxes = document.querySelectorAll('.event-checkbox');
const registerButton = document.getElementById('register-button');

function updateRegisterButton() {
const checkedBoxes = document.querySelectorAll('.event-checkbox:checked');
registerButton.disabled = checkedBoxes.length === 0;
}

checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', updateRegisterButton);
});

updateRegisterButton();
});

updateRegisterButton();
});
</script>
11 changes: 7 additions & 4 deletions app/views/events/new.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<h1>New Event</h1>
<div class="max-w-7xl mx-auto px-4 py-8">
<h1 class="text-3xl font-semibold text-gray-900 mb-4">New Event</h1>
<div class="border-b border-gray-300 mb-6"></div>
<div class="mt-4 pt-4">
<%= render 'form', event: @event %>
</div>
</div>

<%= render 'form', event: @event %>

<%= link_to 'Back', events_path %>
Loading