Skip to content
Draft
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
81 changes: 81 additions & 0 deletions app/controllers/workshop_variation_ideas_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
class WorkshopVariationIdeasController < ApplicationController
before_action :set_workshop_variation_idea, only: [ :show, :edit, :update, :destroy ]

def index
per_page = params[:number_of_items_per_page].presence || 25
workshop_variation_ideas = WorkshopVariationIdea.includes(:workshop, :created_by, :updated_by)
@workshop_variation_ideas_count = workshop_variation_ideas.size
@workshop_variation_ideas = workshop_variation_ideas.order(created_at: :desc)
.paginate(page: params[:page], per_page: per_page)
.decorate
end

def show
end

def new
@workshop_variation_idea = WorkshopVariationIdea.new
set_form_variables
end

def edit
set_form_variables
end

def create
@workshop_variation_idea = WorkshopVariationIdea.new(workshop_variation_idea_params)

if @workshop_variation_idea.save
NotificationServices::CreateNotification.call(
noticeable: @workshop_variation_idea,
kind: :idea_submitted_fyi,
recipient_role: :admin,
recipient_email: ENV.fetch("REPLY_TO_EMAIL", "programs@awbw.org"),
notification_type: 0)
redirect_to workshop_variation_ideas_path, notice: "Workshop variation idea was successfully created."
else
set_form_variables
render :new, status: :unprocessable_content
end
end

def update
if @workshop_variation_idea.update(workshop_variation_idea_params)
redirect_to workshop_variation_ideas_path, notice: "Workshop variation idea was successfully updated.", status: :see_other
else
set_form_variables
render :edit, status: :unprocessable_content
end
end

def destroy
@workshop_variation_idea.destroy!
redirect_to workshop_variation_ideas_path, notice: "Workshop variation idea was successfully destroyed."
end

private

def set_workshop_variation_idea
@workshop_variation_idea = WorkshopVariationIdea.find(params[:id])
end

def set_form_variables
@workshop_variation_idea.build_primary_asset if @workshop_variation_idea.primary_asset.blank?
@workshop_variation_idea.gallery_assets.build

@workshops = Workshop.published.order(:title)
@users = User.active.or(User.where(id: @workshop_variation_idea.created_by_id))
.order(:first_name, :last_name)
end

def workshop_variation_idea_params
params.require(:workshop_variation_idea).permit(
:name, :description, :youtube_url,
:inactive, :position,
:workshop_id,
:created_by_id, :updated_by_id,
primary_asset_attributes: [ :id, :file, :_destroy ],
gallery_assets_attributes: [ :id, :file, :_destroy ]
)
end
end
7 changes: 6 additions & 1 deletion app/controllers/workshop_variations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ def index
end

def new
@workshop_variation = WorkshopVariation.new
if params[:workshop_variation_idea_id].present?
@workshop_variation_idea = WorkshopVariationIdea.find(params[:workshop_variation_idea_id])
@workshop_variation = WorkshopVariationFromIdeaService.new(@workshop_variation_idea, user: current_user).call
else
@workshop_variation = WorkshopVariation.new
end
workshops = current_user.super_user? ? Workshop.all : Workshop.published
@workshops = workshops.order(:title)
@workshop = @workshop_variation.workshop || params[:workshop_id].present? &&
Expand Down
9 changes: 9 additions & 0 deletions app/decorators/workshop_variation_idea_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class WorkshopVariationIdeaDecorator < ApplicationDecorator
def detail(length: nil)
length ? description&.truncate(length) : description
end

def default_display_image
"workshop_default.jpg"
end
end
1 change: 1 addition & 0 deletions app/models/workshop_variation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class WorkshopVariation < ApplicationRecord

belongs_to :workshop
belongs_to :created_by, class_name: "User", optional: true
belongs_to :workshop_variation_idea, optional: true
has_many :bookmarks, as: :bookmarkable, dependent: :destroy
has_many :notifications, as: :noticeable, dependent: :destroy
# Asset associations
Expand Down
32 changes: 32 additions & 0 deletions app/models/workshop_variation_idea.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class WorkshopVariationIdea < ApplicationRecord
belongs_to :created_by, class_name: "User"
belongs_to :updated_by, class_name: "User"
belongs_to :workshop
has_many :bookmarks, as: :bookmarkable, dependent: :destroy
has_many :notifications, as: :noticeable, dependent: :destroy
has_many :workshop_variations

# Asset associations
has_one :primary_asset, -> { where(type: "PrimaryAsset") },
as: :owner, class_name: "PrimaryAsset", dependent: :destroy
has_many :gallery_assets, -> { where(type: "GalleryAsset") },
as: :owner, class_name: "GalleryAsset", dependent: :destroy
has_many :assets, as: :owner, dependent: :destroy

# Validations
validates :name, presence: true
validates :created_by_id, presence: true
validates :updated_by_id, presence: true
validates :workshop_id, presence: true

# Nested attributes
accepts_nested_attributes_for :primary_asset, allow_destroy: true, reject_if: :all_blank
accepts_nested_attributes_for :gallery_assets, allow_destroy: true, reject_if: :all_blank

# Scopes
scope :workshop_id, ->(workshop_id) { where(workshop_id: workshop_id) if workshop_id.present? }

def title
name
end
end
35 changes: 35 additions & 0 deletions app/services/workshop_variation_from_idea_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

class WorkshopVariationFromIdeaService
def initialize(workshop_variation_idea, user:)
@workshop_variation_idea = workshop_variation_idea
@user = user
end

def call
WorkshopVariation.new(attributes_from_idea).tap do |workshop_variation|
duplicate_assets(workshop_variation)
end
end

private

attr_reader :workshop_variation_idea, :user

def attributes_from_idea
workshop_variation_idea.attributes.slice(
"name", "description", "youtube_url",
"position", "workshop_id"
).merge(
created_by_id: user.id,
workshop_variation_idea_id: workshop_variation_idea.id,
inactive: true
)
end

def duplicate_assets(workshop_variation)
workshop_variation_idea.assets.each do |asset|
workshop_variation.assets.build(file: asset.file.blob)
end
end
end
92 changes: 92 additions & 0 deletions app/views/workshop_variation_ideas/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<div class="rounded-xl bg-white p-6">
<%= simple_form_for(workshop_variation_idea, html: { multipart: true }) do |f| %>
<%= f.hidden_field :created_by_id, value: f.object.created_by_id || current_user&.id %>
<%= f.hidden_field :updated_by_id, value: current_user&.id %>

<% promoted_to_variation = f.object.workshop_variations.any? %>
<% if promoted_to_variation %>
<div class="admin-only bg-yellow-100 m-3 p-3">
This workshop variation idea has been promoted to a
<% f.object.workshop_variations.each do |workshop_variation| %>
<%= link_to "Workshop Variation ##{workshop_variation.id}",
workshop_variation_path(workshop_variation), class: "btn btn-secondary-outline" %>
<% end %>
and should not be edited here.
</div>
<% end %>

<%= render 'shared/errors', resource: workshop_variation_idea if workshop_variation_idea.errors.any? %>

<!-- Row 1 -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div>
<%= f.input :name,
label: "Variation name",
required: true,
disabled: promoted_to_variation,
input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 #{('readonly' if promoted_to_variation)}" } %>
</div>

<div>
<%= f.input :workshop_id,
label: "Workshop",
required: true,
collection: @workshops,
label_method: :title,
value_method: :id,
disabled: promoted_to_variation,
input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm #{('readonly' if promoted_to_variation)}" } %>
</div>
</div>

<!-- Row 2 -->
<div class="mb-6">
<%= f.input :description,
as: :text,
label: "Description",
hint: "Describe how this variation differs from the base workshop",
disabled: promoted_to_variation,
input_html: { rows: 5, class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm #{('readonly' if promoted_to_variation)}" } %>
</div>

<!-- Row 3 -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div>
<%= f.input :youtube_url,
label: "YouTube URL",
disabled: promoted_to_variation,
input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 #{('readonly' if promoted_to_variation)}" } %>
</div>

<div>
<%= f.input :position,
label: "Position (order)",
disabled: promoted_to_variation,
input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 #{('readonly' if promoted_to_variation)}" } %>
</div>

<% if current_user.super_user? %>
<div class="admin-only bg-blue-100 p-3">
<%= f.input :inactive,
as: :boolean,
disabled: promoted_to_variation,
wrapper_html: { class: "flex items-center" } %>
</div>
<% end %>
</div>

<%= render "shared/form_image_fields", f: f, include_primary_asset: true unless promoted_to_variation %>

<!-- Actions -->
<div class="flex gap-x-3 mt-6">
<% if current_user.super_user? && f.object.persisted? && !promoted_to_variation %>
<%= link_to "Promote to Workshop Variation",
new_workshop_variation_path(workshop_variation_idea_id: f.object.id),
class: "btn btn-secondary-outline" %>
<%= link_to "Delete", @workshop_variation_idea, class: "btn btn-danger-outline", method: :delete, data: { confirm: "Are you sure?" } %>
<% end %>
<%= link_to "Cancel", workshop_variation_ideas_path, class: "btn btn-secondary-outline" %>
<%= f.submit "Submit", class: "btn btn-primary", disabled: promoted_to_variation %>
</div>
<% end %>
</div>
31 changes: 31 additions & 0 deletions app/views/workshop_variation_ideas/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<div class="min-h-screen py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="max-w-5xl mx-auto bg-white border border-gray-200 rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-200 p-6">
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-semibold text-gray-900">Edit Workshop Variation Idea</h1>
<div class="text-end text-right items-end items-right">
<% if @workshop_variation_idea.workshop_variations.any? %>
<% @workshop_variation_idea.workshop_variations.each do |workshop_variation| %>
<%= link_to "View Variation", workshop_variation_path(workshop_variation),
class: "btn btn-secondary-outline" %>
<% end %>
<% else %>
<% if current_user.super_user && @workshop_variation_idea.persisted? %>
<%= link_to "Promote to Workshop Variation",
new_workshop_variation_path(workshop_variation_idea_id: @workshop_variation_idea.id),
class: "admin-only bg-blue-100 btn btn-secondary-outline ms-2" %>
<% end %>
<% end %>
<%= link_to "View", workshop_variation_idea_path(@workshop_variation_idea),
class: "btn btn-secondary-outline" %>
</div>
</div>

<div class="space-y-6">
<div class="mt-4">
<%= render "form", workshop_variation_idea: @workshop_variation_idea %>
</div>
</div>
</div>
</div>
</div>
85 changes: 85 additions & 0 deletions app/views/workshop_variation_ideas/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<div class="min-h-screen py-8">
<div class="max-w-full mx-auto px-4 sm:px-6 lg:px-8">
<div class="admin-only bg-blue-100 rounded p-6">
<div class="max-w-7xl mx-auto bg-white border border-gray-200
rounded-xl shadow-lg hover:shadow-xl transition-shadow duration-200 p-6">
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-semibold text-gray-900">
Workshop Variation Ideas (<%= @workshop_variation_ideas_count %>)
</h1>
<div class="flex gap-2">
<%= link_to "New workshop variation idea",
new_workshop_variation_idea_path,
class: "btn btn-primary" %>
</div>
</div>

<div class="rounded-xl bg-white p-6">
<div class="overflow-x-auto">
<table class="w-full border-collapse border border-gray-200">
<thead class="bg-gray-100">
<tr>
<th class="px-4 py-2 text-center text-sm font-semibold text-gray-700 w-[60px]">Main Image</th>
<th class="px-4 py-2 text-left text-sm font-semibold text-gray-700 w-1/6">Name</th>
<th class="px-4 py-2 text-left text-sm font-semibold text-gray-700 w-1/4">Workshop</th>
<th class="px-4 py-2 text-left text-sm font-semibold text-gray-700 w-1/6">Author</th>
<th class="px-4 py-2 text-center text-sm font-semibold text-gray-700 w-[60px]">Promoted?</th>
<th class="px-4 py-2 text-center text-sm font-semibold text-gray-700 w-[80px]">Actions</th>
</tr>
</thead>

<tbody class="divide-y divide-gray-200">
<% @workshop_variation_ideas.each do |workshop_variation_idea| %>
<tr class="hover:bg-gray-50 transition-colors duration-150">
<td>
<div class="p-3">
<%= render "assets/display_image",
resource: workshop_variation_idea,
width: 18, height: 14,
variant: :index,
link_to_object: true,
file: workshop_variation_idea.display_image %>
</div>
</td>
<td class="px-4 py-2 text-sm text-gray-800"><%= workshop_variation_idea.name %></td>
<td class="px-4 py-2 text-sm text-gray-800"><%= workshop_variation_idea.workshop.title %></td>
<td class="px-4 py-2 text-sm text-gray-800"><%= workshop_variation_idea.created_by.full_name %></td>
<td class="px-4 py-2 text-sm text-gray-800 text-center">
<% if workshop_variation_idea.workshop_variations.any? %>
<span class='fa fa-check-circle text-green-500'></span>
<% else %>
<span>--</span>
<% end %>
</td>

<!-- Actions -->
<td class="px-4 py-2 flex justify-center gap-2">
<%= link_to 'Edit', edit_workshop_variation_idea_path(workshop_variation_idea), class: "btn btn-secondary-outline" %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>

<!-- Empty state -->
<% unless @workshop_variation_ideas.any? %>
<p class="text-gray-500 text-center py-6">
No workshop variation ideas found.
</p>
<% end %>

<!-- Pagination -->
<div class="mt-6 flex justify-center">
<div class="pagination">
<%= tailwind_paginate @workshop_variation_ideas %>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Loading