diff --git a/app/controllers/course_management/curriculums_controller.rb b/app/controllers/course_management/curriculums_controller.rb index ef79810c2..4553a6c94 100644 --- a/app/controllers/course_management/curriculums_controller.rb +++ b/app/controllers/course_management/curriculums_controller.rb @@ -3,7 +3,7 @@ module CourseManagement class CurriculumsController < ApplicationController include PagyBackendWithHelpers - before_action :set_curriculum, only: %i[show edit update destroy courses] + before_action :set_curriculum, only: %i[show edit update destroy openable_courses] def index curriculums = Curriculum.includes(:unit) @@ -44,9 +44,9 @@ def destroy redirect_with(message) end - def courses - @courses = @curriculum.courses - render json: @courses + def openable_courses + @curriculum = CurriculumDecorator.new(@curriculum) + render json: @curriculum.openable_courses_for_active_term end private diff --git a/app/decorators/curriculum_decorator.rb b/app/decorators/curriculum_decorator.rb new file mode 100644 index 000000000..840da3112 --- /dev/null +++ b/app/decorators/curriculum_decorator.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CurriculumDecorator < SimpleDelegator + def openable_courses_for_active_term(appends: nil) + term = AcademicTerm.active.last.try(:term) + + return [] unless term + + courses = semesters.where(term: term) + .includes(:courses) + .where.not(courses: { id: available_courses.pluck(:course_id) }) + .order('courses.name') + .map(&:courses) + [*appends, courses].flatten + end +end diff --git a/app/helpers/link_helper.rb b/app/helpers/link_helper.rb index 85af1c2b8..6d63f8ce9 100644 --- a/app/helpers/link_helper.rb +++ b/app/helpers/link_helper.rb @@ -1,62 +1,79 @@ # frozen_string_literal: true module LinkHelper - def link_to_back(path = nil, text = t('action_group.back')) - link_to( - fa_icon('arrow-left', text: text), - path, - class: 'btn btn-secondary btn-sm' - ) - end - - def link_to_destroy(path = nil, text = t('action_group.destroy')) - link_to( - fa_icon('trash', text: text), - path, - method: :delete, - data: { confirm: t('are_you_sure') }, - class: 'btn btn-outline-danger btn-sm' - ) - end - - def link_to_edit(path = nil, text = t('action_group.edit')) - link_to( - fa_icon('pencil', text: text), - path, - class: 'btn btn-outline-success btn-sm' - ) - end + LINKS = { + back: { + icon: 'arrow-left', + text: I18n.t('action_group.back'), + options: { + class: 'btn btn-secondary btn-sm' + } + }, + destroy: { + icon: 'trash', + text: I18n.t('action_group.destroy'), + options: { + class: 'btn btn-outline-danger btn-sm', + method: :delete, + data: { confirm: I18n.t('are_you_sure') } + } + }, + edit: { + icon: 'pencil', + text: I18n.t('action_group.edit'), + options: { + class: 'btn btn-outline-success btn-sm' + } + }, + new: { + icon: 'plus', + text: I18n.t('action_group.add'), + options: { + class: 'btn btn-outline-primary btn-sm', + id: 'add-button' + } + }, + show: { + icon: 'eye', + text: I18n.t('action_group.show'), + options: { + class: 'btn btn-outline-info btn-sm' + } + }, + update: { + icon: 'pencil-square-o', + text: I18n.t('action_group.update'), + options: { + class: 'btn btn-outline-info btn-sm' + } + }, + file: { + icon: 'file-word-o', + text: I18n.t('action_group.file'), + options: { + class: 'btn btn-secondary btn-sm' + } + } + }.freeze - def link_to_new(path = nil, text = t('action_group.add')) - link_to( - fa_icon('plus', text: text), - path, - class: 'btn btn-outline-primary btn-sm', - id: 'add-button' - ) + LINKS.each do |action, configuration| + define_method("link_to_#{action}") do |*args| + link_builder(args, configuration) + end end - def link_to_show(path = nil, text = t('action_group.show')) - link_to( - fa_icon('eye', text: text), - path, - class: 'btn btn-outline-info btn-sm' - ) - end + private - def link_to_update(path = nil, text = t('action_group.update')) + def link_builder(args, configuration) + text, path = split_args_for_link_to(args) link_to( - fa_icon('pencil-square-o', text: text), + fa_icon(configuration[:icon], text: text || configuration[:text]), path, - class: 'btn btn-outline-info btn-sm' + configuration.fetch(:options, {}) ) end - def link_to_file(path = nil, text = t('action_group.file')) - link_to( - fa_icon('file-word-o', text: text), - path, - class: 'btn btn-secondary btn-sm' - ) + def split_args_for_link_to(args) + args.length == 1 ? [nil, *args] : args end end diff --git a/app/models/academic_term.rb b/app/models/academic_term.rb index ea90125f4..dc071378a 100644 --- a/app/models/academic_term.rb +++ b/app/models/academic_term.rb @@ -3,6 +3,9 @@ class AcademicTerm < ApplicationRecord include EnumForTerm + # callbacks + after_save -> { AcademicTerm.where.not(id: id).update(active: false) }, if: :active? + # relations has_many :calendars, dependent: :nullify has_many :registration_documents, dependent: :nullify @@ -12,6 +15,7 @@ class AcademicTerm < ApplicationRecord validates :start_of_term, presence: true validates :end_of_term, presence: true validates :active, inclusion: { in: [true, false] } + validates_with AcademicTermValidator # scopes scope :active, -> { where(active: true) } diff --git a/app/validators/academic_term_validator.rb b/app/validators/academic_term_validator.rb new file mode 100644 index 000000000..09aab524d --- /dev/null +++ b/app/validators/academic_term_validator.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class AcademicTermValidator < ActiveModel::Validator + def validate(record) + return if record.active? + return if AcademicTerm.where.not(id: record.id).exists?(active: true) + + record.errors[:active] << I18n.t('active_check', scope: %i[validators academic_term]) + end +end diff --git a/app/views/account/addresses/index.html.erb b/app/views/account/addresses/index.html.erb index 1bd97a7e1..c36a21af6 100644 --- a/app/views/account/addresses/index.html.erb +++ b/app/views/account/addresses/index.html.erb @@ -1,6 +1,6 @@
<%= link_to_back user_path(@user) %> - <%= link_to_new(new_user_address_path(@user), t('.new_address')) unless @user.addresses.informal.present? %> + <%= link_to_new(t('.new_address'), new_user_address_path(@user)) unless @user.addresses.informal.present? %> <%= link_to (@addresses.formal.present? ? t('.update_from_mernis') : t('.create_from_mernis') ), save_from_mernis_user_addresses_path, class: "btn btn-outline-primary btn-sm" %>
diff --git a/app/views/account/identities/index.html.erb b/app/views/account/identities/index.html.erb index 5ce0b2d62..5b3edefbc 100644 --- a/app/views/account/identities/index.html.erb +++ b/app/views/account/identities/index.html.erb @@ -1,6 +1,6 @@
<%= link_to_back user_path(@user) %> - <%= link_to_new(new_user_identity_path(@user), t('.new_identity')) unless @user.identities.informal.present? %> + <%= link_to_new(t('.new_identity'), new_user_identity_path(@user)) unless @user.identities.informal.present? %> <%= link_to (@identities.formal.present? ? t('.update_from_mernis') : t('.create_from_mernis') ), save_from_mernis_user_identities_path, class: "btn btn-outline-primary btn-sm" %>
diff --git a/app/views/admin/cities/show.html.erb b/app/views/admin/cities/show.html.erb index 42113db6e..13f83c1d7 100644 --- a/app/views/admin/cities/show.html.erb +++ b/app/views/admin/cities/show.html.erb @@ -29,7 +29,7 @@
<%= fa_icon 'street-view', text: t('.card_header') %>
- <%= link_to_new [:new, :admin, @country, @city, :district], t('.new_district_link') %> + <%= link_to_new t('.new_district_link'), [:new, :admin, @country, @city, :district] %>
diff --git a/app/views/admin/countries/show.html.erb b/app/views/admin/countries/show.html.erb index 9d8a5326c..3c3d7b5ab 100644 --- a/app/views/admin/countries/show.html.erb +++ b/app/views/admin/countries/show.html.erb @@ -45,7 +45,7 @@
<%= fa_icon 'street-view', text: t('.card_header') %>
- <%= link_to_new [:new, :admin, @country, 'city'], t('.new_city_link') %> + <%= link_to_new t('.new_city_link'), [:new, :admin, @country, 'city'] %>
diff --git a/app/views/calendar_management/calendars/index.html.erb b/app/views/calendar_management/calendars/index.html.erb index c46242193..14252cfd7 100644 --- a/app/views/calendar_management/calendars/index.html.erb +++ b/app/views/calendar_management/calendars/index.html.erb @@ -4,7 +4,7 @@
<%= fa_icon 'street-view', text: t('.card_header') %>
- <%= link_to_new [:new, :calendar_management, :calendar], t('.new_calendar_link') %> + <%= link_to_new t('.new_calendar_link'), [:new, :calendar_management, :calendar] %>
diff --git a/app/views/committee/agenda_types/index.html.erb b/app/views/committee/agenda_types/index.html.erb index 645f70abc..45b0f16c5 100644 --- a/app/views/committee/agenda_types/index.html.erb +++ b/app/views/committee/agenda_types/index.html.erb @@ -1,5 +1,5 @@
- <%= link_to_new new_agenda_type_path, t('.new_agenda_type_link') %> + <%= link_to_new t('.new_agenda_type_link'), new_agenda_type_path %>
diff --git a/app/views/committee/agendas/index.html.erb b/app/views/committee/agendas/index.html.erb index e14ab6f94..ec1fbe45b 100644 --- a/app/views/committee/agendas/index.html.erb +++ b/app/views/committee/agendas/index.html.erb @@ -1,5 +1,5 @@
- <%= link_to_new new_committee_agenda_path(@committee), t('.new_agenda_link') %> + <%= link_to_new t('.new_agenda_link'), new_committee_agenda_path(@committee) %> <%= link_to(fa_icon('archive', text: t('.meetings')), committee_meetings_path(@committee), diff --git a/app/views/committee/decisions/show.html.erb b/app/views/committee/decisions/show.html.erb index 25fb5c3cd..bd50ce0e2 100644 --- a/app/views/committee/decisions/show.html.erb +++ b/app/views/committee/decisions/show.html.erb @@ -39,7 +39,7 @@
diff --git a/app/views/committee/meetings/index.html.erb b/app/views/committee/meetings/index.html.erb index ce8ac4ac6..86f5298cc 100644 --- a/app/views/committee/meetings/index.html.erb +++ b/app/views/committee/meetings/index.html.erb @@ -1,5 +1,5 @@
- <%= link_to_new new_committee_meeting_path(@committee), t('.new_committee_meeting_link') %> + <%= link_to_new t('.new_committee_meeting_link'), new_committee_meeting_path(@committee) %> <%= link_to(fa_icon('tasks', text: t('.agendas')), committee_agendas_path(@committee), class: 'btn btn-dark btn-sm') %> diff --git a/app/views/committee/meetings/show.html.erb b/app/views/committee/meetings/show.html.erb index 8fdea1c26..7ccb215d1 100644 --- a/app/views/committee/meetings/show.html.erb +++ b/app/views/committee/meetings/show.html.erb @@ -56,10 +56,10 @@ <%= agenda.decision.try(:decision_no) %> <% if agenda.decision.present? %> - <%= link_to_show(committee_meeting_agenda_decision_path(@committee, agenda, agenda.decision), t('.show_decision')) %> - <%= link_to_edit(edit_committee_meeting_agenda_decision_path(@committee, agenda), t('.update_decision')) %> + <%= link_to_show(t('.show_decision'), committee_meeting_agenda_decision_path(@committee, agenda, agenda.decision)) %> + <%= link_to_edit(t('.update_decision'), edit_committee_meeting_agenda_decision_path(@committee, agenda)) %> <% else %> - <%= link_to_new(new_committee_meeting_agenda_decision_path(@committee, agenda), t('.create_decision')) %> + <%= link_to_new(t('.create_decision'), new_committee_meeting_agenda_decision_path(@committee, agenda)) %> <% end %> diff --git a/app/views/course_management/available_courses/_evaluation_types.html.erb b/app/views/course_management/available_courses/_evaluation_types.html.erb index 4ecbf5dff..53281ebc1 100644 --- a/app/views/course_management/available_courses/_evaluation_types.html.erb +++ b/app/views/course_management/available_courses/_evaluation_types.html.erb @@ -3,7 +3,7 @@
<%= fa_icon 'list-alt' %><%= t('.card_header') %> - <%= link_to_new(new_available_course_evaluation_type_path(@available_course), t('.create_evaluation_type')) %> + <%= link_to_new(t('.create_evaluation_type'), new_available_course_evaluation_type_path(@available_course)) %>
diff --git a/app/views/course_management/available_courses/_form.html.erb b/app/views/course_management/available_courses/_form.html.erb index 73982b4f5..f5292f1b9 100644 --- a/app/views/course_management/available_courses/_form.html.erb +++ b/app/views/course_management/available_courses/_form.html.erb @@ -20,16 +20,26 @@ <% end %>
- <%= f.association :unit, collection: Unit.active.coursable.order(:name), label_method: :names_depth_cache %> + <%= f.association :unit, + collection: Unit.active.coursable.order(:name), + label_method: :names_depth_cache %>
- <%= f.association :curriculum, collection: f.object.unit.present? ? f.object.unit.managed_curriculums : [] %> + <%= f.association :curriculum, + collection: [*f.object.unit.try(:managed_curriculums)] %>
- <%= f.association :coordinator, collection: f.object.unit.present? ? f.object.unit.subtree_employees : [], label_method: lambda { |c| full_name(c) } %> + <%= f.association :coordinator, + collection: [*f.object.unit.try(:subtree_employees)], + label_method: lambda { |c| full_name(c) } %>
- <%= f.association :course, collection: f.object.curriculum.present? ? f.object.curriculum.courses : [] %> + <% if f.object.curriculum %> + <% courses = CurriculumDecorator.new(f.object.curriculum) + .openable_courses_for_active_term(appends: f.object.course) %> + <% end %> + <%= f.association :course, + collection: [*courses] %>
<%= f.button :submit, class: 'btn btn-outline-success btn-sm' %> diff --git a/app/views/course_management/available_courses/_groups.html.erb b/app/views/course_management/available_courses/_groups.html.erb index 4538a65eb..fce2a95e4 100644 --- a/app/views/course_management/available_courses/_groups.html.erb +++ b/app/views/course_management/available_courses/_groups.html.erb @@ -3,7 +3,7 @@
<%= fa_icon 'cubes' %><%= t('.available_course_groups') %> - <%= link_to_new(new_available_course_available_course_group_path(@available_course), t('.create_available_course_group')) %> + <%= link_to_new(t('.create_available_course_group'), new_available_course_available_course_group_path(@available_course)) %>
diff --git a/app/views/course_management/available_courses/_js.html.erb b/app/views/course_management/available_courses/_js.html.erb index 6713876d3..72df1546f 100644 --- a/app/views/course_management/available_courses/_js.html.erb +++ b/app/views/course_management/available_courses/_js.html.erb @@ -1,7 +1,13 @@