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
92 changes: 41 additions & 51 deletions app/controllers/projects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,25 @@ def new

@lecturer_options = Enrolment.where(course: @course, role: :lecturer).includes(:user)

@field_values = {}

# Optionally preselect topic or own proposal
topic_id = params[:topic_id].presence || params[:based_on_topic]
# Choose Supervisor
@lecturers = @course.lecturers
@lecturer_capacity_info = {}
@lecturers.each do |lecturer|
@lecturer_capacity_info[lecturer.id] = @course.lecturer_capacity(lecturer)
end

return unless topic_id.present?
@field_values = {}

if topic_id.to_s.start_with?('own_proposal_')
@selected_topic_id = topic_id
elsif @course.topics.exists?(id: topic_id)
@selected_topic_id = topic_id
if params[:topic_id].present?
@selected_topic = @course.topics.find_by(id: params[:topic_id])
if @selected_topic
latest_instance = @selected_topic.current_instance
@field_values = latest_instance.project_instance_fields.each_with_object({}) do |f, h|
h[f.project_template_field_id.to_i] = f.value
end
end
elsif params[:lecturer_id].present?
@selected_lecturer = @course.lecturers.find_by(id: params[:lecturer_id])
end
end

Expand Down Expand Up @@ -135,29 +143,20 @@ def create
raise StandardError, 'You already have a project' if has_project
end

raise StandardError, 'Please choose a lecturer and topic' if params[:based_on_topic].blank?

if params[:based_on_topic].start_with?('own_proposal_')
# Extract lecturer ID from value
lecturer_id = params[:based_on_topic].split('_').last.to_i

# Find lecturer enrolment for course
supervisor_enrolment = Enrolment.find_by(user_id: lecturer_id, course_id: @course.id, role: :lecturer)

raise StandardError unless supervisor_enrolment
else
# Treat as topic_id
topic = Topic.find_by(id: params[:based_on_topic], course: @course)
topic = Topic.find_by(id: params[:based_on_topic], course: @course) if params[:based_on_topic].present?
lecturer_id = params[:lecturer_id].presence

raise StandardError unless topic
raise StandardError, 'Please choose a lecturer or topic' if topic.nil? && lecturer_id.nil?

# Set supervisor enrolment to the owner of the topic (assuming you want this)
topic_owner = topic&.owner
raise StandardError unless topic_owner.is_a?(User)
if topic
topic_owner = topic.owner
raise StandardError, 'Topic has no valid owner' unless topic_owner.is_a?(User)

supervisor_enrolment = Enrolment.find_by(user_id: topic_owner.id, course_id: @course.id, role: :lecturer)

raise StandardError unless supervisor_enrolment
raise StandardError, 'Could not find supervisor enrolment' unless supervisor_enrolment
else
supervisor_enrolment = Enrolment.find_by(user_id: lecturer_id, course_id: @course.id, role: :lecturer)
raise StandardError, 'Could not find supervisor enrolment' unless supervisor_enrolment
end

@project = Project.create!(
Expand Down Expand Up @@ -256,34 +255,25 @@ def update
end
end

@instance.title = params[:fields].values.first if !is_approved && params[:fields].present? && @instance.title.blank?

@instance.last_edit_time = Time.current
@instance.last_edit_by = current_user.id
@instance.save!
topic = Topic.find_by(id: params[:based_on_topic], course: @course) if params[:based_on_topic].present?
lecturer_id = params[:lecturer_id].presence

unless is_approved
raise StandardError, 'Please choose a lecturer and topic' if params[:based_on_topic].blank?

if params[:based_on_topic].start_with?('own_proposal_')
lecturer_id = params[:based_on_topic].split('_').last.to_i
supervisor_enrolment = Enrolment.find_by(id: lecturer_id, course_id: @course.id, role: :lecturer)
raise StandardError, 'Lecturer not found' unless supervisor_enrolment

@instance.update!(source_topic_id: nil)
else
topic = Topic.find_by(id: params[:based_on_topic], course: @course)
raise StandardError, 'Topic not found' unless topic && topic.owner.is_a?(User)
raise StandardError, 'Please choose a lecturer or topic' if topic.nil? && lecturer_id.nil?

supervisor_enrolment = Enrolment.find_by(user_id: topic.owner.id, course_id: @course.id, role: :lecturer)
raise StandardError, 'Supervisor enrolment missing' unless supervisor_enrolment

@instance.update!(source_topic: topic)
end
if topic
raise StandardError, 'Topic has no valid owner' unless topic.owner.is_a?(User)
supervisor_enrolment = Enrolment.find_by(user_id: topic.owner.id, course_id: @course.id, role: :lecturer)

raise StandardError, 'Could not find supervisor enrolment' unless supervisor_enrolment
@instance.update!(source_topic: topic)
else
supervisor_enrolment = Enrolment.find_by(user_id: lecturer_id, course_id: @course.id, role: :lecturer)
raise StandardError, 'Could not find supervisor enrolment' unless supervisor_enrolment
@instance.update!(source_topic_id: nil)
end

@project.update!(enrolment: supervisor_enrolment)
end
end
rescue StandardError => e
redirect_to course_project_path(@course, @project), alert: "Project update failed: #{e.message}"
return
Expand Down
4 changes: 4 additions & 0 deletions app/javascript/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ import "htmx.org";
document.addEventListener("turbo:load", function () {
window.htmx.process(document.body);
});

Turbo.config.drive.confirmationMethod = (message, element, submitter) => {
return Promise.resolve(window.confirm(message));
};
2 changes: 1 addition & 1 deletion app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Import and register all your controllers from the importmap via controllers/**/*_controller
import { application } from "controllers/application";
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading";
eagerLoadControllersFrom("controllers", application);
eagerLoadControllersFrom("controllers", application);
80 changes: 80 additions & 0 deletions app/javascript/controllers/proposal_selection_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["lecturerPanel", "topicPanel", "hiddenLecturerId", "hiddenTopicId"]

connect() {
// Restore initial state from server-rendered hidden fields on page load
const lecturerId = this.hiddenLecturerIdTarget.value
const topicId = this.hiddenTopicIdTarget.value

if (lecturerId) {
this.#disablePanel(this.topicPanelTarget)
} else if (topicId) {
this.#disablePanel(this.lecturerPanelTarget)
}
}

selectLecturer(event) {
const button = event.currentTarget
const lecturerId = button.dataset.lecturerId
const isAlreadySelected = this.hiddenLecturerIdTarget.value === lecturerId

if (isAlreadySelected) {
// Deselect
this.hiddenLecturerIdTarget.value = ""
this.#markSelected(button, false)
this.#enablePanel(this.topicPanelTarget)
} else {
// Warn if topic is already selected
if (this.hiddenTopicIdTarget.value) {
if (!confirm("You'll lose your selected topic. Continue?")) return
this.hiddenTopicIdTarget.value = ""
}

this.hiddenLecturerIdTarget.value = lecturerId
this.#clearAllLecturerSelections()
this.#markSelected(button, true)
this.#disablePanel(this.topicPanelTarget)
this.#enablePanel(this.lecturerPanelTarget)
}
}

// Called by the "Clear" link next to the lecturer heading
clearLecturer(event) {
event.preventDefault()
this.hiddenLecturerIdTarget.value = ""
this.#clearAllLecturerSelections()
this.#enablePanel(this.topicPanelTarget)
}

// Private

#clearAllLecturerSelections() {
this.lecturerPanelTarget
.querySelectorAll("button[data-lecturer-id]")
.forEach(btn => this.#markSelected(btn, false))
}

#markSelected(button, selected) {
const check = button.querySelector("[data-selected-check]")

if (selected) {
button.classList.add("border-blue-400", "ring-2", "ring-blue-100")
button.classList.remove("border-gray-200", "hover:border-gray-400")
if (check) check.classList.remove("hidden")
} else {
button.classList.remove("border-blue-400", "ring-2", "ring-blue-100")
button.classList.add("border-gray-200", "hover:border-gray-400")
if (check) check.classList.add("hidden")
}
}

#disablePanel(panel) {
panel.classList.add("opacity-50", "pointer-events-none")
}

#enablePanel(panel) {
panel.classList.remove("opacity-50", "pointer-events-none")
}
}
6 changes: 5 additions & 1 deletion app/policies/project_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def show?
end

def create?
student || coordinator
student && !has_existing_project?
end

def update?
Expand Down Expand Up @@ -110,4 +110,8 @@ def any_free_edit_fields?
&.project_template_fields
&.exists?(free_edit: true) || false
end

def has_existing_project?
course.projects.owned_by_user(user).exists?
end
end
2 changes: 1 addition & 1 deletion app/views/courses/_topic_card.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
if local_assigns[:lecturer].present?
course_lecturer_topic_path(course, lecturer, topic)
else
course_topic_path(@course, topic)
course_topic_path(@course, topic, from_new_project: params[:from_new_project])
end

card_classes =
Expand Down
Loading
Loading