diff --git a/.gitignore b/.gitignore index 9eb51d2b895..e79d6c268d7 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ storage/ .envrc /test/reports + +/memory-bank +.clinerules diff --git a/app/components/sub_tab_component.html.slim b/app/components/sub_tab_component.html.slim new file mode 100644 index 00000000000..07f8001c404 --- /dev/null +++ b/app/components/sub_tab_component.html.slim @@ -0,0 +1,3 @@ +li.tab-nav__item + = link_to link, class: "tab-nav__item-link #{active ? 'is-active' : ''}" do + = name diff --git a/app/components/sub_tab_component.rb b/app/components/sub_tab_component.rb new file mode 100644 index 00000000000..b44a4a2cda8 --- /dev/null +++ b/app/components/sub_tab_component.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class SubTabComponent < ViewComponent::Base + def initialize(name:, link:, active: false) + @name = name + @link = link + @active = active + end + + private + + attr_reader :name, :link, :active +end diff --git a/app/components/sub_tabs_component.html.slim b/app/components/sub_tabs_component.html.slim new file mode 100644 index 00000000000..679a71fd446 --- /dev/null +++ b/app/components/sub_tabs_component.html.slim @@ -0,0 +1,6 @@ +nav.tab-nav + .container + ul.tab-nav__items + - tabs.each do |tab| + - tab[:active] = active_tab == tab[:name] + = render SubTabComponent.new(**tab) diff --git a/app/components/sub_tabs_component.rb b/app/components/sub_tabs_component.rb new file mode 100644 index 00000000000..4ea95b98886 --- /dev/null +++ b/app/components/sub_tabs_component.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class SubTabsComponent < ViewComponent::Base + def initialize(tabs:, active_tab:) + @tabs = tabs + @active_tab = active_tab + end + + private + + attr_reader :tabs, :active_tab +end diff --git a/app/controllers/survey_questions_controller.rb b/app/controllers/mentor/survey_questions_controller.rb similarity index 89% rename from app/controllers/survey_questions_controller.rb rename to app/controllers/mentor/survey_questions_controller.rb index 59638709cc3..01e8d8551fd 100644 --- a/app/controllers/survey_questions_controller.rb +++ b/app/controllers/mentor/survey_questions_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class SurveyQuestionsController < ApplicationController +class Mentor::SurveyQuestionsController < ApplicationController before_action :set_survey_question, only: %i[edit update] before_action :require_admin_or_mentor_login @@ -22,7 +22,7 @@ def create @survey_question.user_id = current_user.id if @survey_question.save - redirect_to survey_questions_path, notice: '質問を保存しました。' + redirect_to mentor_survey_questions_path, notice: '質問を保存しました。' else render action: :new end @@ -32,7 +32,7 @@ def edit; end def update if @survey_question.update(survey_question_params) - redirect_to survey_questions_path, notice: '質問を保存しました。' + redirect_to mentor_survey_questions_path, notice: '質問を保存しました。' else render action: :edit end diff --git a/app/controllers/mentor/surveys/survey_answers_controller.rb b/app/controllers/mentor/surveys/survey_answers_controller.rb new file mode 100644 index 00000000000..302823373bc --- /dev/null +++ b/app/controllers/mentor/surveys/survey_answers_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class Mentor::Surveys::SurveyAnswersController < ApplicationController + before_action :require_admin_or_mentor_login + before_action :set_survey + + def index + @survey_answers = @survey.survey_answers.includes(:user, survey_question_answers: :survey_question) + end + + def show + @survey_answer = @survey.survey_answers.includes(:user, survey_question_answers: :survey_question).find(params[:id]) + end + + private + + def set_survey + @survey = Survey.find(params[:survey_id]) + end +end diff --git a/app/controllers/surveys/survey_question_listings_controller.rb b/app/controllers/mentor/surveys/survey_question_listings_controller.rb similarity index 52% rename from app/controllers/surveys/survey_question_listings_controller.rb rename to app/controllers/mentor/surveys/survey_question_listings_controller.rb index c9e50c92545..bbf5bab5b3c 100644 --- a/app/controllers/surveys/survey_question_listings_controller.rb +++ b/app/controllers/mentor/surveys/survey_question_listings_controller.rb @@ -1,10 +1,16 @@ # frozen_string_literal: true -class Surveys::SurveyQuestionListingsController < ApplicationController +class Mentor::Surveys::SurveyQuestionListingsController < ApplicationController before_action :require_admin_or_mentor_login + before_action :set_survey def index + @survey_question_listings = @survey.survey_question_listings.includes(:survey_question).order(:position) + end + + private + + def set_survey @survey = Survey.find(params[:survey_id]) - @survey_question_listings = @survey.survey_question_listings.order(:position) end end diff --git a/app/controllers/mentor/surveys/survey_result_controller.rb b/app/controllers/mentor/surveys/survey_result_controller.rb new file mode 100644 index 00000000000..c8d9e6c725f --- /dev/null +++ b/app/controllers/mentor/surveys/survey_result_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class Mentor::Surveys::SurveyResultController < ApplicationController + before_action :require_admin_or_mentor_login + before_action :set_survey, only: %i[show] + + def show + @survey_questions = @survey + .survey_questions + .includes( + :linear_scale, + radio_button: :radio_button_choices, + check_box: :check_box_choices + ) + @survey_answers = @survey.survey_answers.includes(survey_question_answers: :survey_question) + end + + private + + def set_survey + @survey = Survey.find(params[:survey_id]) + end +end diff --git a/app/controllers/mentor/surveys_controller.rb b/app/controllers/mentor/surveys_controller.rb new file mode 100644 index 00000000000..0cb3dc86917 --- /dev/null +++ b/app/controllers/mentor/surveys_controller.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +class Mentor::SurveysController < ApplicationController + before_action :require_admin_or_mentor_login + before_action :set_survey, only: %i[show edit update destroy] + + def index + @surveys = Survey.order(end_at: :desc) + end + + def show + @survey_questions = @survey + .survey_questions + .includes( + :linear_scale, + radio_button: :radio_button_choices, + check_box: :check_box_choices + ) + end + + def new + @survey = Survey.new( + start_at: Time.current.beginning_of_day, + end_at: Time.current.end_of_day.strftime('%Y-%m-%dT-%H:%M') + ) + @survey.survey_question_listings.build + end + + def edit; end + + def create + @survey = Survey.new(survey_params) + @survey.user_id = current_user.id + if @survey.save + redirect_to mentor_surveys_path, notice: notice_message(@survey) + else + render action: :new + end + end + + def update + if @survey.update(survey_params) + redirect_to mentor_surveys_path, notice: notice_message(@survey) + else + render :edit + end + end + + def destroy + @survey.destroy + redirect_to mentor_surveys_path, notice: 'アンケートを削除しました。' + end + + private + + def set_survey + @survey = Survey.find(params[:id]) + end + + def survey_params + params.require(:survey).permit( + :title, + :start_at, + :end_at, + :description, + survey_question_listings_attributes: %i[id survey_id survey_question_id _destroy] + ) + end + + def notice_message(survey) + case params[:action] + when 'create' + survey.before_start? ? 'アンケートを受付前として保存しました。' : 'アンケートを作成しました。' + when 'update' + survey.before_start? ? 'アンケートを受付前として保存しました。' : 'アンケートを更新しました。' + end + end +end diff --git a/app/controllers/surveys/survey_answers_controller.rb b/app/controllers/surveys/survey_answers_controller.rb new file mode 100644 index 00000000000..d0e46afe4b3 --- /dev/null +++ b/app/controllers/surveys/survey_answers_controller.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +class Surveys::SurveyAnswersController < ApplicationController + skip_before_action :require_active_user_login + before_action :set_survey + before_action :check_survey_period + before_action :check_already_answered + + def create + @survey_answer = SurveyAnswer.new(survey: @survey, user: current_user) + + if @survey_answer.save + save_question_answers + redirect_to root_path, notice: 'アンケートに回答しました。' + else + @survey_questions = @survey + .survey_questions + .includes( + :linear_scale, + radio_button: :radio_button_choices, + check_box: :check_box_choices + ) + render 'surveys/show' + end + end + + private + + def set_survey + @survey = Survey.find(params[:survey_id]) + end + + def check_survey_period + if @survey.before_start? + redirect_to root_path, alert: 'このアンケートはまだ回答期間ではありません。' + elsif @survey.answer_ended? + redirect_to root_path, alert: 'このアンケートの回答期間は終了しました。' + end + end + + def check_already_answered + return unless SurveyAnswer.exists?(survey: @survey, user: current_user) + + redirect_to root_path, alert: 'このアンケートには既に回答済みです。' + end + + def save_question_answers + params[:survey_question].each do |question_id, answer_params| + survey_question = SurveyQuestion.find(question_id) + + case survey_question.format + when 'radio_button', 'linear_scale' + save_single_answer(survey_question, answer_params) + when 'check_box' + save_multiple_answers(survey_question, answer_params) + when 'text_area', 'text_field' + save_text_answer(survey_question, answer_params) + end + end + end + + def save_single_answer(survey_question, answer_params) + answer = answer_params[:answer] + reason = get_reason(survey_question, answer_params) + + @survey_answer.survey_question_answers.create(survey_question:, answer:, reason:) + end + + def save_multiple_answers(survey_question, answer_params) + answer_params.each do |choice, value| + next if choice == 'title_of_reason_for_checkbox' || value != '1' + + reason = nil + if survey_question.check_box.check_box_choices.exists?(choices: choice, reason_for_choice_required: true) + reason = answer_params[:title_of_reason_for_checkbox] + end + + answer = choice + @survey_answer.survey_question_answers.create(survey_question:, answer:, reason:) + end + end + + def save_text_answer(survey_question, answer_params) + @survey_answer.survey_question_answers.create( + survey_question:, + answer: answer_params + ) + end + + def get_reason(survey_question, answer_params) + case survey_question.format + when 'radio_button' + if survey_question.radio_button.radio_button_choices.exists?(choices: answer_params[:answer], reason_for_choice_required: true) + answer_params[:title_of_reason] + end + when 'linear_scale' + answer_params[:title_of_reason_for_linear_scale] if survey_question.linear_scale.reason_for_choice_required + end + end +end diff --git a/app/controllers/surveys_controller.rb b/app/controllers/surveys_controller.rb index 64f6870947f..cbb75c62ecd 100644 --- a/app/controllers/surveys_controller.rb +++ b/app/controllers/surveys_controller.rb @@ -1,12 +1,10 @@ # frozen_string_literal: true class SurveysController < ApplicationController - before_action :require_admin_or_mentor_login - before_action :set_survey, only: %i[show edit update destroy] - - def index - @surveys = Survey.order(end_at: :desc) - end + skip_before_action :require_active_user_login + before_action :set_survey, only: %i[show] + before_action :check_survey_period, only: %i[show] + before_action :check_already_answered, only: %i[show] def show @survey_questions = @survey @@ -18,58 +16,23 @@ def show ) end - def new - @survey = Survey.new(start_at: Time.current.beginning_of_day, end_at: Time.current.end_of_day.strftime('%Y-%m-%dT-%H:%M')) - @survey.survey_question_listings.build - end - - def edit; end - - def create - @survey = Survey.new(survey_params) - @survey.user_id = current_user.id - if @survey.save - redirect_to surveys_path, notice: notice_message(@survey) - else - render action: :new - end - end - - def update - if @survey.update(survey_params) - redirect_to surveys_path, notice: notice_message(@survey) - else - render :edit - end - end - - def destroy - @survey.destroy - redirect_to surveys_path, notice: 'アンケートを削除しました。' - end - private def set_survey @survey = Survey.find(params[:id]) end - def survey_params - params.require(:survey).permit( - :title, - :start_at, - :end_at, - :description, - survey_question_listings_attributes: %i[id survey_id survey_question_id _destroy] - ) + def check_survey_period + if @survey.before_start? + redirect_to root_path, alert: 'このアンケートはまだ回答期間ではありません。' + elsif @survey.answer_ended? + redirect_to root_path, alert: 'このアンケートの回答期間は終了しました。' + end end - def notice_message(survey) - case params[:action] - when 'create' - survey.before_start? ? 'アンケートを受付前として保存しました。' : 'アンケートを作成しました。' - when 'update' - survey.before_start? ? 'アンケートを受付前として保存しました。' : 'アンケートを更新しました。' - end + def check_already_answered + return unless SurveyAnswer.exists?(survey: @survey, user: current_user) + + redirect_to root_path, alert: 'このアンケートには既に回答済みです。' end end diff --git a/app/helpers/sub_tabs/surveys_helper.rb b/app/helpers/sub_tabs/surveys_helper.rb new file mode 100644 index 00000000000..835aadc34dc --- /dev/null +++ b/app/helpers/sub_tabs/surveys_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module SubTabs + module SurveysHelper + def mentor_surveys_sub_tabs(active_tab:) + tabs = [] + tabs << { name: 'アンケート', link: mentor_surveys_path } + tabs << { name: '質問', link: mentor_survey_questions_path } + render SubTabsComponent.new(tabs:, active_tab:) + end + end +end diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index d9071a33dae..57080a42b8d 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -65,6 +65,7 @@ import '../answer.js' import '../new-answer.js' import '../coding-test.js' import '../copy-url.js' +import '../survey_result_chart.js' import VueMounter from '../VueMounter.js' import Questions from '../components/questions.vue' diff --git a/app/javascript/stylesheets/application.sass b/app/javascript/stylesheets/application.sass index 3ebceed3f92..43802058da3 100644 --- a/app/javascript/stylesheets/application.sass +++ b/app/javascript/stylesheets/application.sass @@ -92,6 +92,9 @@ @import application/blocks/survey/survey-additional-question @import application/blocks/survey/survey-questions-item @import application/blocks/survey/survey-questions +@import application/blocks/survey/survey-answer +@import application/blocks/survey/survey-result +@import application/blocks/survey/survey-answers @import application/blocks/tags/random-tags @import application/blocks/tags/tag-links diff --git a/app/javascript/stylesheets/application/blocks/survey/survey-answer.sass b/app/javascript/stylesheets/application/blocks/survey/survey-answer.sass new file mode 100644 index 00000000000..5d95001ef72 --- /dev/null +++ b/app/javascript/stylesheets/application/blocks/survey/survey-answer.sass @@ -0,0 +1,103 @@ +.survey-answer-summary + margin-bottom: 2rem + background-color: #fff + border-radius: 0.5rem + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1) + padding: 1.5rem + &__header + display: flex + justify-content: space-between + align-items: center + flex-wrap: wrap + gap: 1rem + &__user, &__date + display: flex + align-items: center + gap: 0.5rem + &__icon + color: #6f7b8a + font-size: 1rem + &__name + font-weight: bold + color: #4a90e2 + text-decoration: none + &:hover + text-decoration: underline + +.survey-answer-questions + display: flex + flex-direction: column + gap: 1.5rem + +.survey-answer-question + background-color: #fff + border-radius: 0.5rem + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1) + overflow: hidden + &__header + display: flex + justify-content: space-between + align-items: center + padding: 1rem 1.5rem + background-color: #f8f9fa + border-bottom: 1px solid #e9ecef + flex-wrap: wrap + gap: 0.5rem + &__title + font-size: 1.125rem + font-weight: bold + margin: 0 + flex: 1 + &__badge + display: inline-flex + align-items: center + gap: 0.25rem + font-size: 0.75rem + padding: 0.25rem 0.5rem + border-radius: 1rem + i + font-size: 0.75rem + &.is-text + background-color: #e3f2fd + color: #0d47a1 + &.is-radio + background-color: #e8f5e9 + color: #1b5e20 + &.is-checkbox + background-color: #fff3e0 + color: #e65100 + &.is-scale + background-color: #f3e5f5 + color: #4a148c + &__content + padding: 1.5rem + &__text + line-height: 1.6 + color: #212529 + &__choice, &__reason + margin-bottom: 1rem + display: flex + gap: 0.5rem + flex-wrap: wrap + &__choice-label, &__reason-label + font-weight: bold + min-width: 3rem + &__choice-value + color: #212529 + &__reason-text + color: #495057 + line-height: 1.6 + flex: 1 + width: 100% + margin-top: 0.5rem + +@media (max-width: 767px) + .survey-answer-question + &__header + flex-direction: column + align-items: flex-start + &__badge + margin-top: 0.5rem + &__choice, &__reason + flex-direction: column + gap: 0.25rem diff --git a/app/javascript/stylesheets/application/blocks/survey/survey-answers.sass b/app/javascript/stylesheets/application/blocks/survey/survey-answers.sass new file mode 100644 index 00000000000..8b9a960e5ec --- /dev/null +++ b/app/javascript/stylesheets/application/blocks/survey/survey-answers.sass @@ -0,0 +1,5 @@ +.user-link + color: #4a90e2 + text-decoration: none + &:hover + text-decoration: underline diff --git a/app/javascript/stylesheets/application/blocks/survey/survey-result.sass b/app/javascript/stylesheets/application/blocks/survey/survey-result.sass new file mode 100644 index 00000000000..153ea12b18b --- /dev/null +++ b/app/javascript/stylesheets/application/blocks/survey/survey-result.sass @@ -0,0 +1,127 @@ +.survey-result-summary + margin-bottom: 2rem + background-color: #fff + border-radius: 0.5rem + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1) + padding: 1.5rem + &__header + display: flex + justify-content: space-between + align-items: center + flex-wrap: wrap + gap: 1rem + &__count, &__date + display: flex + align-items: center + gap: 0.5rem + &__icon + color: #6f7b8a + font-size: 1rem + &__value + font-weight: bold + font-size: 1.125rem + +.survey-result-questions + display: flex + flex-direction: column + gap: 1.5rem + +.survey-result-question + background-color: #fff + border-radius: 0.5rem + box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.1) + overflow: hidden + margin-bottom: 2rem + &__header + display: flex + justify-content: space-between + align-items: center + padding: 1rem 1.5rem + background-color: #f8f9fa + border-bottom: 1px solid #e9ecef + flex-wrap: wrap + gap: 0.5rem + &__title + font-size: 1.125rem + font-weight: bold + margin: 0 + flex: 1 + &__badge + display: inline-flex + align-items: center + gap: 0.25rem + font-size: 0.75rem + padding: 0.25rem 0.5rem + border-radius: 1rem + i + font-size: 0.75rem + &.is-text + background-color: #e3f2fd + color: #0d47a1 + &.is-radio + background-color: #e8f5e9 + color: #1b5e20 + &.is-checkbox + background-color: #fff3e0 + color: #e65100 + &.is-scale + background-color: #f3e5f5 + color: #4a148c + &__content + padding: 1.5rem + .row + margin-bottom: 1rem + .table + border-collapse: collapse + width: 100% + margin-bottom: 1rem + th, td + padding: 0.75rem + vertical-align: top + border-top: 1px solid #dee2e6 + thead th + vertical-align: bottom + border-bottom: 2px solid #dee2e6 + background-color: #f8f9fa + tbody + tbody + border-top: 2px solid #dee2e6 + .table-info + background-color: #e3f2fd + .table-responsive + display: block + width: 100% + overflow-x: auto + -webkit-overflow-scrolling: touch + canvas + max-width: 100% + height: auto !important + h4 + font-size: 1rem + font-weight: bold + margin-top: 0 + color: #495057 + .text-break + word-break: break-word + .font-weight-bold + font-weight: bold + a + color: #4a90e2 + text-decoration: none + &:hover + text-decoration: underline + +@media (max-width: 767px) + .survey-result-question + &__header + flex-direction: column + align-items: flex-start + &__badge + margin-top: 0.5rem + &__content + .row + margin-left: 0 + margin-right: 0 + .col-md-6, .col-md-12 + padding-left: 0 + padding-right: 0 + margin-bottom: 1.5rem diff --git a/app/javascript/survey_result_chart.js b/app/javascript/survey_result_chart.js new file mode 100644 index 00000000000..9d04c9af6ff --- /dev/null +++ b/app/javascript/survey_result_chart.js @@ -0,0 +1,329 @@ +import Chart from 'chart.js/auto' +import ChartDataLabels from 'chartjs-plugin-datalabels' +import annotationPlugin from 'chartjs-plugin-annotation' + +Chart.register(ChartDataLabels) +Chart.register(annotationPlugin) + +document.addEventListener('DOMContentLoaded', function () { + initRadioButtonCharts() + initCheckBoxCharts() + initLinearScaleCharts() +}) + +function initRadioButtonCharts() { + document.querySelectorAll('[data-radio-button-chart]').forEach((element) => { + const ctx = element.getContext('2d') + const choices = JSON.parse(element.dataset.choices) + const answers = JSON.parse(element.dataset.answers) + + const counts = choices.map((choice) => { + return answers.filter((answer) => answer === choice).length + }) + + const backgroundColors = [ + 'rgba(255, 99, 132, 0.7)', + 'rgba(54, 162, 235, 0.7)', + 'rgba(255, 206, 86, 0.7)', + 'rgba(75, 192, 192, 0.7)', + 'rgba(153, 102, 255, 0.7)', + 'rgba(255, 159, 64, 0.7)', + 'rgba(199, 199, 199, 0.7)', + 'rgba(83, 102, 255, 0.7)', + 'rgba(40, 159, 64, 0.7)', + 'rgba(210, 199, 199, 0.7)' + ] + + // eslint-disable-next-line no-new + new Chart(ctx, { + type: 'pie', + data: { + labels: choices, + datasets: [ + { + data: counts, + backgroundColor: backgroundColors.slice(0, choices.length), + borderWidth: 1 + } + ] + }, + options: { + responsive: true, + plugins: { + legend: { + position: 'bottom' + }, + title: { + display: true, + text: '回答分布' + }, + datalabels: { + formatter: (value, _ctx) => { + const total = _ctx.dataset.data.reduce( + (acc, data) => acc + data, + 0 + ) + const percentage = + total > 0 ? Math.round((value / total) * 100) : 0 + return percentage > 0 ? `${percentage}%` : '' + }, + color: '#fff', + font: { + weight: 'bold', + size: 14 + } + } + } + } + }) + }) +} + +function initCheckBoxCharts() { + document.querySelectorAll('[data-check-box-chart]').forEach((element) => { + const ctx = element.getContext('2d') + const choices = JSON.parse(element.dataset.choices) + const allSelectedChoices = JSON.parse(element.dataset.allSelectedChoices) + const totalRespondents = parseInt(element.dataset.totalRespondents) + + const counts = choices.map((choice) => { + return allSelectedChoices.filter((selected) => selected === choice).length + }) + + const backgroundColors = [ + 'rgba(255, 99, 132, 0.7)', + 'rgba(54, 162, 235, 0.7)', + 'rgba(255, 206, 86, 0.7)', + 'rgba(75, 192, 192, 0.7)', + 'rgba(153, 102, 255, 0.7)', + 'rgba(255, 159, 64, 0.7)', + 'rgba(199, 199, 199, 0.7)', + 'rgba(83, 102, 255, 0.7)', + 'rgba(40, 159, 64, 0.7)', + 'rgba(210, 199, 199, 0.7)' + ] + + // eslint-disable-next-line no-new + new Chart(ctx, { + type: 'pie', + data: { + labels: choices, + datasets: [ + { + data: counts, + backgroundColor: backgroundColors.slice(0, choices.length), + borderWidth: 1 + } + ] + }, + options: { + responsive: true, + plugins: { + legend: { + position: 'bottom' + }, + title: { + display: true, + text: '回答分布(複数選択可)' + }, + tooltip: { + callbacks: { + label: function (context) { + const label = context.label || '' + const value = context.raw || 0 + const percentage = + totalRespondents > 0 + ? ((value / totalRespondents) * 100).toFixed(1) + : 0 + return `${label}: ${value}件 (${percentage}%)` + } + } + }, + datalabels: { + formatter: (value, _ctx) => { + const percentage = + totalRespondents > 0 + ? Math.round((value / totalRespondents) * 100) + : 0 + return percentage > 0 ? `${percentage}%` : '' + }, + color: '#fff', + font: { + weight: 'bold', + size: 14 + } + } + } + } + }) + }) +} + +function initLinearScaleCharts() { + document.querySelectorAll('[data-linear-scale-chart]').forEach((element) => { + const ctx = element.getContext('2d') + const minValue = parseInt(element.dataset.minValue) + const maxValue = parseInt(element.dataset.maxValue) + const answers = JSON.parse(element.dataset.answers) + + // 平均値と中央値を計算 + let average = null + let median = null + + if ( + element.dataset.average && + !isNaN(parseFloat(element.dataset.average)) + ) { + average = parseFloat(element.dataset.average) + } + + if (element.dataset.median && !isNaN(parseFloat(element.dataset.median))) { + median = parseFloat(element.dataset.median) + } + + const scaleValues = Array.from( + { length: maxValue - minValue + 1 }, + (_, i) => minValue + i + ) + + const counts = scaleValues.map((value) => { + return answers.filter((answer) => parseInt(answer) === value).length + }) + + // アノテーションの設定 + const annotations = {} + + // 平均値のアノテーション + if (average !== null) { + annotations.averageLine = { + type: 'line', + xMin: average, + xMax: average, + borderColor: 'rgba(255, 99, 132, 1)', + borderWidth: 2, + label: { + display: true, + content: `平均値: ${average.toFixed(2)}`, + position: 'start', + backgroundColor: 'rgba(255, 99, 132, 0.8)', + font: { + weight: 'bold' + } + } + } + } + + // 中央値のアノテーション + if (median !== null) { + annotations.medianLine = { + type: 'line', + xMin: median, + xMax: median, + borderColor: 'rgba(75, 192, 192, 1)', + borderWidth: 2, + label: { + display: true, + content: `中央値: ${median.toFixed(1)}`, + position: 'end', + backgroundColor: 'rgba(75, 192, 192, 0.8)', + font: { + weight: 'bold' + } + } + } + } + + // eslint-disable-next-line no-new + new Chart(ctx, { + type: 'bar', + data: { + labels: scaleValues.map((value) => `${value}`), + datasets: [ + { + label: '回答数', + data: counts, + backgroundColor: 'rgba(54, 162, 235, 0.7)', + borderColor: 'rgba(54, 162, 235, 1)', + borderWidth: 1 + } + ] + }, + options: { + responsive: true, + scales: { + y: { + beginAtZero: true, + ticks: { + precision: 0 + } + }, + x: { + type: 'linear', + min: minValue - 0.5, + max: maxValue + 0.5, + offset: false, + grid: { + offset: false + }, + ticks: { + stepSize: 1, + callback: function (value) { + if ( + Number.isInteger(value) && + value >= minValue && + value <= maxValue + ) { + return value.toString() + } + return '' + } + } + } + }, + plugins: { + legend: { + display: true, + position: 'top' + }, + title: { + display: true, + text: '評価分布' + }, + tooltip: { + callbacks: { + title: function (context) { + return `評価: ${context[0].label}` + }, + footer: function () { + let footer = '' + if (average !== null) { + footer += `平均値: ${average.toFixed(2)}` + } + if (median !== null) { + footer += footer + ? `, 中央値: ${median.toFixed(1)}` + : `中央値: ${median.toFixed(1)}` + } + return footer + } + } + }, + datalabels: { + formatter: (value) => { + return value > 0 ? value : '' + }, + anchor: 'end', + align: 'top', + color: '#000', + font: { + weight: 'bold' + } + }, + annotation: { + annotations: annotations + } + } + } + }) + }) +} diff --git a/app/models/survey.rb b/app/models/survey.rb index 13db0e8bfd5..72744e97118 100644 --- a/app/models/survey.rb +++ b/app/models/survey.rb @@ -4,6 +4,7 @@ class Survey < ApplicationRecord belongs_to :user has_many :survey_question_listings, dependent: :destroy has_many :survey_questions, through: :survey_question_listings + has_many :survey_answers, dependent: :destroy accepts_nested_attributes_for :survey_question_listings, allow_destroy: true validates_associated :survey_question_listings @@ -23,4 +24,8 @@ def answer_accepting? def answer_ended? Time.current > end_at end + + def answers? + survey_answers.exists? + end end diff --git a/app/models/survey_answer.rb b/app/models/survey_answer.rb new file mode 100644 index 00000000000..14fff9fae6c --- /dev/null +++ b/app/models/survey_answer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class SurveyAnswer < ApplicationRecord + belongs_to :survey + belongs_to :user + has_many :survey_question_answers, dependent: :destroy + + validates :survey_id, uniqueness: { scope: :user_id, message: 'すでに回答済みです' } +end diff --git a/app/models/survey_question.rb b/app/models/survey_question.rb index 928fb4e1c66..cb876c6cf4b 100644 --- a/app/models/survey_question.rb +++ b/app/models/survey_question.rb @@ -13,6 +13,7 @@ class SurveyQuestion < ApplicationRecord validates_associated :check_box has_many :survey_question_listings, dependent: :destroy has_many :surveys, through: :survey_question_listings + has_many :survey_question_answers, dependent: :destroy enum format: { text_area: 0, diff --git a/app/models/survey_question_answer.rb b/app/models/survey_question_answer.rb new file mode 100644 index 00000000000..e2091d122cd --- /dev/null +++ b/app/models/survey_question_answer.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class SurveyQuestionAnswer < ApplicationRecord + belongs_to :survey_answer + belongs_to :survey_question + + validates :answer, presence: true, if: -> { survey_question.answer_required } + validates :reason, presence: true, if: :reason_required? + + private + + def reason_required? + case survey_question.format + when 'radio_button' + survey_question.radio_button.radio_button_choices.exists?(choices: answer, reason_for_choice_required: true) + when 'check_box' + survey_question.check_box.check_box_choices.exists?(choices: answer, reason_for_choice_required: true) + when 'linear_scale' + survey_question.linear_scale.reason_for_choice_required + else + false + end + end +end diff --git a/app/views/application/_mentor_menu.html.slim b/app/views/application/_mentor_menu.html.slim index 838dade47f4..e2b98bf6122 100644 --- a/app/views/application/_mentor_menu.html.slim +++ b/app/views/application/_mentor_menu.html.slim @@ -30,8 +30,7 @@ = link_to articles_path, class: 'header-dropdown__item-link' do | ブログ - - if Rails.env.development? || Rails.env.test? - li.header-dropdown__item - = link_to survey_questions_path, - class: 'header-dropdown__item-link' do - | アンケート + li.header-dropdown__item + = link_to mentor_surveys_path, + class: 'header-dropdown__item-link' do + | アンケート diff --git a/app/views/mentor/_mentor_page_tabs.html.slim b/app/views/mentor/_mentor_page_tabs.html.slim index 66d6f857aad..59b81073ff9 100644 --- a/app/views/mentor/_mentor_page_tabs.html.slim +++ b/app/views/mentor/_mentor_page_tabs.html.slim @@ -29,3 +29,7 @@ = link_to 'ブログ', articles_path, class: "page-tabs__item-link #{current_link(/^articles/)}" + li.page-tabs__item + = link_to 'アンケート', + mentor_surveys_path, + class: "page-tabs__item-link #{current_link(/^mentor-surveys/)}" diff --git a/app/views/survey_questions/_check_box_choice_fields.html.slim b/app/views/mentor/survey_questions/_check_box_choice_fields.html.slim similarity index 100% rename from app/views/survey_questions/_check_box_choice_fields.html.slim rename to app/views/mentor/survey_questions/_check_box_choice_fields.html.slim diff --git a/app/views/mentor/survey_questions/_errors.html.slim b/app/views/mentor/survey_questions/_errors.html.slim new file mode 100644 index 00000000000..ecb019a1374 --- /dev/null +++ b/app/views/mentor/survey_questions/_errors.html.slim @@ -0,0 +1,9 @@ +- if object.errors.any? + .alert.is-danger + .alert__body + h2.alert__title + = "#{object.errors.count}件のエラーがあります。" + ul.alert__items + - object.errors.full_messages.each do |message| + li.alert__item + = message diff --git a/app/views/survey_questions/_form.html.slim b/app/views/mentor/survey_questions/_form.html.slim similarity index 97% rename from app/views/survey_questions/_form.html.slim rename to app/views/mentor/survey_questions/_form.html.slim index c210b587291..50d8c88d842 100644 --- a/app/views/survey_questions/_form.html.slim +++ b/app/views/mentor/survey_questions/_form.html.slim @@ -1,5 +1,5 @@ = render 'errors', object: survey_question -= form_with model: survey_question, local: true do |f| += form_with model: [:mentor, survey_question], local: true do |f| .form__items .form__items-inner .form-item diff --git a/app/views/survey_questions/_form_check_box.html.slim b/app/views/mentor/survey_questions/_form_check_box.html.slim similarity index 100% rename from app/views/survey_questions/_form_check_box.html.slim rename to app/views/mentor/survey_questions/_form_check_box.html.slim diff --git a/app/views/survey_questions/_form_linear_scale.html.slim b/app/views/mentor/survey_questions/_form_linear_scale.html.slim similarity index 100% rename from app/views/survey_questions/_form_linear_scale.html.slim rename to app/views/mentor/survey_questions/_form_linear_scale.html.slim diff --git a/app/views/survey_questions/_form_radio_button.html.slim b/app/views/mentor/survey_questions/_form_radio_button.html.slim similarity index 100% rename from app/views/survey_questions/_form_radio_button.html.slim rename to app/views/mentor/survey_questions/_form_radio_button.html.slim diff --git a/app/views/survey_questions/_radio_button_choice_fields.html.slim b/app/views/mentor/survey_questions/_radio_button_choice_fields.html.slim similarity index 100% rename from app/views/survey_questions/_radio_button_choice_fields.html.slim rename to app/views/mentor/survey_questions/_radio_button_choice_fields.html.slim diff --git a/app/views/survey_questions/edit.html.slim b/app/views/mentor/survey_questions/edit.html.slim similarity index 75% rename from app/views/survey_questions/edit.html.slim rename to app/views/mentor/survey_questions/edit.html.slim index 4045af44bc6..c9aa14543bd 100644 --- a/app/views/survey_questions/edit.html.slim +++ b/app/views/mentor/survey_questions/edit.html.slim @@ -1,13 +1,14 @@ -- title '質問編集' +- title 'メンターページ' header.page-header .container .page-header__inner .page-header__start .page-header__title - | アンケート + = title -= render 'surveys/tabs' += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: '質問') .page-main header.page-main-header @@ -20,7 +21,7 @@ header.page-header .page-main-header-actions ul.page-main-header-actions__items li.page-main-header-actions__item - = link_to survey_questions_path, class: 'a-button is-md is-secondary is-block is-back' do + = link_to mentor_survey_questions_path, class: 'a-button is-md is-secondary is-block is-back' do | 質問一覧 hr.a-border .page-body diff --git a/app/views/survey_questions/index.html.slim b/app/views/mentor/survey_questions/index.html.slim similarity index 84% rename from app/views/survey_questions/index.html.slim rename to app/views/mentor/survey_questions/index.html.slim index 63f6cb3f450..af4d4b7661e 100644 --- a/app/views/survey_questions/index.html.slim +++ b/app/views/mentor/survey_questions/index.html.slim @@ -1,4 +1,4 @@ -- title 'アンケート' +- title 'メンターページ' header.page-header .container @@ -7,7 +7,8 @@ header.page-header h2.page-header__title = title -= render 'surveys/tabs' += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: '質問') .page-main header.page-main-header @@ -15,12 +16,12 @@ header.page-header .page-main-header__inner .page-main-header__start h1.page-main-header__title - | 質問 + | 質問一覧 .page-main-header__end .page-main-header-actions ul.page-main-header-actions__items li.page-main-header-actions__item - = link_to new_survey_question_path, class: 'a-button is-md is-secondary is-block' do + = link_to new_mentor_survey_question_path, class: 'a-button is-md is-secondary is-block' do i.fa-regular.fa-plus span | 新規作成 @@ -62,5 +63,5 @@ header.page-header td.admin-table__item-value = "更新: #{l survey_question.updated_at, format: :long}" td.admin-table__item-value.is-text-align-center - = link_to edit_survey_question_path(survey_question), class: 'a-button is-sm is-secondary is-icon', id: 'edit_icon' do + = link_to edit_mentor_survey_question_path(survey_question), class: 'a-button is-sm is-secondary is-icon', id: 'edit_icon' do i.fa-solid.fa-pen diff --git a/app/views/survey_questions/new.html.slim b/app/views/mentor/survey_questions/new.html.slim similarity index 75% rename from app/views/survey_questions/new.html.slim rename to app/views/mentor/survey_questions/new.html.slim index f2a930e4c38..1b02db9bc03 100644 --- a/app/views/survey_questions/new.html.slim +++ b/app/views/mentor/survey_questions/new.html.slim @@ -1,13 +1,14 @@ -- title '質問作成' +- title 'メンターページ' header.page-header .container .page-header__inner .page-header__start .page-header__title - | アンケート + = title -= render 'surveys/tabs' += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: '質問') .page-main header.page-main-header @@ -20,7 +21,7 @@ header.page-header .page-main-header-actions ul.page-main-header-actions__items li.page-main-header-actions__item - = link_to survey_questions_path, class: 'a-button is-md is-secondary is-block is-back' do + = link_to mentor_survey_questions_path, class: 'a-button is-md is-secondary is-block is-back' do | 質問一覧 hr.a-border .page-body diff --git a/app/views/mentor/surveys/_errors.html.slim b/app/views/mentor/surveys/_errors.html.slim new file mode 100644 index 00000000000..ecb019a1374 --- /dev/null +++ b/app/views/mentor/surveys/_errors.html.slim @@ -0,0 +1,9 @@ +- if object.errors.any? + .alert.is-danger + .alert__body + h2.alert__title + = "#{object.errors.count}件のエラーがあります。" + ul.alert__items + - object.errors.full_messages.each do |message| + li.alert__item + = message diff --git a/app/views/surveys/_form.html.slim b/app/views/mentor/surveys/_form.html.slim similarity index 79% rename from app/views/surveys/_form.html.slim rename to app/views/mentor/surveys/_form.html.slim index 106f667a567..669e06bdfac 100644 --- a/app/views/surveys/_form.html.slim +++ b/app/views/mentor/surveys/_form.html.slim @@ -1,5 +1,5 @@ = render 'errors', object: survey -= form_with model: survey, local: true do |f| += form_with model: [:mentor, survey], local: true do |f| .form__items .form-item = f.label :title, class: 'a-form-label is-required' @@ -30,10 +30,10 @@ li.form-actions__item.is-sub - case params[:action] - when 'new', 'create' - = link_to 'キャンセル', surveys_path, class: 'a-button is-sm is-text' + = link_to 'キャンセル', mentor_surveys_path, class: 'a-button is-sm is-text' - when 'edit', 'update' - = link_to 'キャンセル', survey_path, class: 'a-button is-sm is-text' + = link_to 'キャンセル', mentor_survey_path, class: 'a-button is-sm is-text' li.form-actions__item.is-sub - case params[:action] - when 'edit', 'update' - = link_to '削除する', survey_path(@survey), class: 'a-button is-sm is-text', data: { confirm: '本当によろしいですか?' }, method: :delete + = link_to '削除する', mentor_survey_path(@survey), class: 'a-button is-sm is-text', data: { confirm: '本当によろしいですか?' }, method: :delete diff --git a/app/views/mentor/surveys/_survey.html.slim b/app/views/mentor/surveys/_survey.html.slim new file mode 100644 index 00000000000..fdf064698bc --- /dev/null +++ b/app/views/mentor/surveys/_survey.html.slim @@ -0,0 +1,43 @@ +tbody.admin-table__items + tr.admin-table__item(class="#{survey.before_start? ? 'is-wip' : ''}") + td.admin-table__item-value.is-text-align-center + - if survey.before_start? + .a-list-item-badge.is-wip + span + | 受付前 + - elsif survey.answer_accepting? + .a-list-item-badge.is-active + span + | 受付中 + - elsif survey.answer_ended? + .a-list-item-badge.is-ended + span + | 受付終了 + td.admin-table__item-value + = l survey.start_at + |〜 + = l survey.end_at + td.admin-table__item-value + = survey.title + td.admin-table__item-value + = link_to '詳細', mentor_survey_path(survey) + = ' | ' + = link_to 'アンケートフォーム', survey_path(survey) + td.admin-table__item-value.is-text-align-center + - if survey.answers? + = link_to mentor_survey_survey_answers_path(survey), class: 'a-button is-sm is-secondary is-block' do + i.fa-regular.fa-list-alt + | 回答一覧 + td.admin-table__item-value.is-text-align-center + - if survey.answers? + = link_to mentor_survey_survey_result_path(survey), class: 'a-button is-sm is-secondary is-block' do + i.fa-regular.fa-chart-pie + | 集計結果 + td.admin-table__item-value.is-text-align-center + ul.is-inline-buttons + li + = link_to edit_mentor_survey_path(survey), class: 'a-button is-sm is-secondary is-icon is-block' do + i.fa.fa-solid.fa-pen + li + = link_to mentor_survey_survey_questions_path(survey), class: 'a-button is-sm is-secondary is-icon is-block' do + i.fa-solid.fa-align-justify diff --git a/app/views/surveys/_survey_question_listing_fields.slim b/app/views/mentor/surveys/_survey_question_listing_fields.slim similarity index 100% rename from app/views/surveys/_survey_question_listing_fields.slim rename to app/views/mentor/surveys/_survey_question_listing_fields.slim diff --git a/app/views/surveys/edit.html.slim b/app/views/mentor/surveys/edit.html.slim similarity index 74% rename from app/views/surveys/edit.html.slim rename to app/views/mentor/surveys/edit.html.slim index 12d7b9f4a18..4804e062c57 100644 --- a/app/views/surveys/edit.html.slim +++ b/app/views/mentor/surveys/edit.html.slim @@ -1,4 +1,4 @@ -- title 'アンケート' +- title 'メンターページ' header.page-header .container @@ -7,7 +7,8 @@ header.page-header h2.page-header__title = title -= render 'tabs' += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: 'アンケート') .page-main header.page-main-header @@ -20,7 +21,7 @@ header.page-header .page-main-header-actions ul.page-main-header-actions__items li.page-main-header-actions__item - = link_to surveys_path, class: 'a-button is-md is-secondary is-block' do + = link_to mentor_surveys_path, class: 'a-button is-md is-secondary is-block' do i.fa-regular | アンケート一覧 hr.a-border diff --git a/app/views/surveys/index.html.slim b/app/views/mentor/surveys/index.html.slim similarity index 60% rename from app/views/surveys/index.html.slim rename to app/views/mentor/surveys/index.html.slim index 6f047eb7988..645030b470b 100644 --- a/app/views/surveys/index.html.slim +++ b/app/views/mentor/surveys/index.html.slim @@ -1,4 +1,4 @@ -- title 'アンケート' +- title 'メンターページ' header.page-header .container @@ -7,7 +7,8 @@ header.page-header h2.page-header__title = title -= render 'tabs' += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: 'アンケート') .page-main header.page-main-header @@ -15,12 +16,12 @@ header.page-header .page-main-header__inner .page-main-header__start h1.page-main-header__title - | アンケート一覧 + | アンケート .page-main-header__end .page-main-header-actions ul.page-main-header-actions__items li.page-main-header-actions__item - = link_to new_survey_path, class: 'a-button is-md is-secondary is-block' do + = link_to new_mentor_survey_path, class: 'a-button is-md is-secondary is-block' do i.fa-regular.fa-plus span | 新規作成 @@ -32,15 +33,14 @@ header.page-header table.admin-table__table thead.admin-table__header tr.admin-table__labels + td.admin-table__label ステータス + td.admin-table__label 回答期間 + td.admin-table__label アンケート名 td.admin-table__label - | ステータス - td.admin-table__label - | 回答期間 - td.admin-table__label - | アンケート名 - td.admin-table__label - | 操作 - = render partial: 'surveys/survey', collection: @surveys, as: :survey + td.admin-table__label 回答一覧 + td.admin-table__label 集計結果 + td.admin-table__label 操作 + = render partial: 'survey', collection: @surveys, as: :survey - else .o-empty-message .o-empty-message__icon diff --git a/app/views/surveys/new.html.slim b/app/views/mentor/surveys/new.html.slim similarity index 73% rename from app/views/surveys/new.html.slim rename to app/views/mentor/surveys/new.html.slim index 85e1321ad58..9b15aa29a89 100644 --- a/app/views/surveys/new.html.slim +++ b/app/views/mentor/surveys/new.html.slim @@ -1,4 +1,4 @@ -- title 'アンケート' +- title 'メンターページ' header.page-header .container @@ -7,7 +7,8 @@ header.page-header h2.page-header__title = title -= render 'tabs' += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: 'アンケート') .page-main header.page-main-header @@ -20,7 +21,7 @@ header.page-header .page-main-header-actions ul.page-main-header-actions__items li.page-main-header-actions__item - = link_to surveys_path, class: 'a-button is-md is-secondary is-block is-back' do + = link_to mentor_surveys_path, class: 'a-button is-md is-secondary is-block is-back' do i.fa-regular | アンケート一覧 hr.a-border diff --git a/app/views/mentor/surveys/show.html.slim b/app/views/mentor/surveys/show.html.slim new file mode 100644 index 00000000000..7e9e626f05d --- /dev/null +++ b/app/views/mentor/surveys/show.html.slim @@ -0,0 +1,128 @@ +- title 'メンターページ' + +header.page-header + .container + .page-header__inner + .page-header__start + h2.page-header__title + = title + += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: 'アンケート') + +.page-main + header.page-main-header + .container + .page-main-header__inner + .page-main-header__start + h1.page-main-header__title + | #{@survey.title} + .page-main-header__end + .page-main-header-actions + ul.page-main-header-actions__items + li.page-main-header-actions__item + = link_to mentor_survey_survey_answers_path(@survey), class: 'a-button is-md is-secondary is-block' do + i.fa-regular.fa-list-alt + | 回答一覧 + li.page-main-header-actions__item + = link_to mentor_surveys_path, class: 'a-button is-md is-secondary is-block is-back' do + i.fa-regular + | アンケート一覧 + hr.a-border + .page-body + .container.is-md + .survey + .survey__description + .a-long-text.is-md + = simple_format(@survey.description) + + .survey-questions + = form_with do |f| + .survey-questions__items + - @survey_questions.each do |survey_question| + .survey-questions-item.a-card + .card-body + .card-body__description + - if survey_question.answer_required + = f.label survey_question.id, survey_question.title, class: 'a-form-label is-lg is-required' + - else + = f.label survey_question.id, survey_question.title, class: 'a-form-label is-lg' + .survey-questions-item__description + .a-long-text.is-sm + = simple_format(survey_question.description) + .survey-questions-item__input + - if survey_question.format == 'text_area' + = f.text_area survey_question.id, class: 'a-text-input js-warning-form' + - if survey_question.format == 'text_field' + = f.text_field survey_question.id, class: 'a-text-input js-warning-form' + - if survey_question.format == 'radio_button' + .survey-questions-item__radios.radios + ul.radios__items + - survey_question.radio_button.radio_button_choices.each do |choice| + li.radios__item + - if choice.reason_for_choice_required + = f.radio_button survey_question.id, choice.choices, id: "radio-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice' + - else + = f.radio_button survey_question.id, choice.choices, id: "radio-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice' + = f.label "#{choice.radio_button_id}_#{choice.choices}", choice.choices, for: "radio-#{choice.id}" + - if survey_question.answer_required_choice_exists?(survey_question.id) + .survey-additional-question.is-hidden name="js-additional_question_#{survey_question.id}" + = f.label survey_question.radio_button.title_of_reason, class: 'a-form-label is-required' + .survey-questions-item__description + .a-long-text.is-sm + = simple_format(survey_question.radio_button.description_of_reason) + .survey-additional-question__input + = f.text_area survey_question.radio_button.title_of_reason, class: 'a-text-input js-warning-form' + - if survey_question.format == 'check_box' + .survey-questions-item__checkboxes.checkboxes + ul.checkboxes__items + - survey_question.check_box.check_box_choices.each do |choice| + li.checkboxes__item + - if choice.reason_for_choice_required + = f.check_box choice.choices, id: "checkbox-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice', name: survey_question.id + - else + = f.check_box choice.choices, id: "checkbox-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice', name: survey_question.id + = f.label choice.choices, for: "checkbox-#{choice.id}" + - if survey_question.answer_required_choice_exists?(survey_question.id) + .survey-additional-question.is-hidden name="js-additional_question_#{survey_question.id}" + = f.label :title_of_reason_for_checkbox, survey_question.check_box.title_of_reason, class: 'a-form-label is-required' + .survey-questions-item__description + .a-long-text.is-sm + = simple_format(survey_question.check_box.description_of_reason) + .survey-additional-question__input + = f.text_area :title_of_reason_for_checkbox, class: 'a-text-input js-warning-form' + + - if survey_question.format == 'linear_scale' + .linear-scale + .linear-scale__label + = survey_question.linear_scale.first + .linear-scale__points + ul.linear-scale__points-items + - 10.times do |i| + li.linear-scale__points-item + - if survey_question.linear_scale.reason_for_choice_required + = f.radio_button survey_question.id, i + 1, id: "linear-scale-#{survey_question.id}-#{i}", + class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice' + - else + = f.radio_button survey_question.id, i + 1, id: "linear-scale-#{survey_question.id}-#{i}", + class: 'a-toggle-checkbox js-questionnaire_choice' + label.linear-scale__point for="linear-scale-#{survey_question.id}-#{i}" + .linear-scale__point-number + = i + 1 + .linear-scale__label + = survey_question.linear_scale.last + - if survey_question.linear_scale.reason_for_choice_required + .survey-additional-question.is-hidden name="js-additional_question_#{survey_question.id}" + = f.label :title_of_reason_for_linear_scale, survey_question.linear_scale.title_of_reason, class: 'a-form-label is-required' + .survey-questions-item__description + .a-long-text.is-sm + = simple_format(survey_question.linear_scale.description_of_reason) + .survey-additional-question__input + = f.text_area :title_of_reason_for_linear_scale, class: 'a-text-input js-warning-form' + + .form-actions + ul.form-actions__items + li.form-actions__item.is-main + = link_to edit_mentor_survey_path(@survey), class: 'a-button is-md is-secondary is-block' do + i.fa-regular + | 編集 diff --git a/app/views/mentor/surveys/survey_answers/index.html.slim b/app/views/mentor/surveys/survey_answers/index.html.slim new file mode 100644 index 00000000000..20c3e7d85b2 --- /dev/null +++ b/app/views/mentor/surveys/survey_answers/index.html.slim @@ -0,0 +1,58 @@ +- title 'メンターページ' + +header.page-header + .container + .page-header__inner + .page-header__start + h2.page-header__title + = title + += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: 'アンケート') + +.page-main + header.page-main-header + .container + .page-main-header__inner + .page-main-header__start + h1.page-main-header__title + | #{@survey.title}の回答一覧 + .page-main-header__end + .page-main-header-actions + ul.page-main-header-actions__items + li.page-main-header-actions__item + = link_to mentor_survey_path(@survey), class: 'a-button is-md is-secondary is-block is-back' do + i.fa-regular + | アンケート詳細 + hr.a-border + .page-body + .container.is-lg + - if @survey_answers.empty? + .o-empty-message + .o-empty-message__icon + i.fa-regular.fa-sad-tear + p.o-empty-message__text + | まだ回答がありません。 + - else + .admin-table + table.admin-table__table + thead.admin-table__header + tr.admin-table__labels + th.admin-table__label 回答者 + th.admin-table__label 回答日時 + th.admin-table__label + tbody.admin-table__items + - @survey_answers.each do |survey_answer| + tr.admin-table__item + td.admin-table__item-value + = link_to user_path(survey_answer.user), class: 'user-link' do + = survey_answer.user.name + td.admin-table__item-value + = l survey_answer.created_at + td.admin-table__item-value + .card-body__actions + .card-body-actions + ul.card-body-actions__items + li.card-body-actions__item + = link_to mentor_survey_survey_answer_path(@survey, survey_answer), class: 'a-button is-sm is-secondary' do + | 詳細を表示 diff --git a/app/views/mentor/surveys/survey_answers/show.html.slim b/app/views/mentor/surveys/survey_answers/show.html.slim new file mode 100644 index 00000000000..25579b42c72 --- /dev/null +++ b/app/views/mentor/surveys/survey_answers/show.html.slim @@ -0,0 +1,77 @@ +- title 'メンターページ' + +header.page-header + .container + .page-header__inner + .page-header__start + h2.page-header__title + = title + += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: 'アンケート') + +.page-main + header.page-main-header + .container + .page-main-header__inner + .page-main-header__start + h1.page-main-header__title + | #{@survey.title}の回答詳細 + .page-main-header__end + .page-main-header-actions + ul.page-main-header-actions__items + li.page-main-header-actions__item + = link_to mentor_survey_survey_answers_path(@survey), class: 'a-button is-md is-secondary is-block is-back' do + i.fa-regular + | 回答一覧に戻る + hr.a-border + .page-body + .container.is-lg + .survey-answer-summary + .survey-answer-summary__header + .survey-answer-summary__user + i.fa-solid.fa-user.survey-answer-summary__icon + = link_to user_path(@survey_answer.user), class: 'survey-answer-summary__name' do + = @survey_answer.user.name + .survey-answer-summary__date + i.fa-regular.fa-calendar.survey-answer-summary__icon + span = l @survey_answer.created_at + + .survey-answer-questions + - @survey_answer.survey_question_answers.includes(:survey_question).find_each do |question_answer| + .survey-answer-question + .survey-answer-question__header + h3.survey-answer-question__title + = question_answer.survey_question.title + .survey-answer-question__type + - case question_answer.survey_question.format + - when 'text_area', 'text_field' + span.survey-answer-question__badge.is-text + i.fa-solid.fa-font + | テキスト + - when 'radio_button' + span.survey-answer-question__badge.is-radio + i.fa-solid.fa-circle-dot + | 単一選択 + - when 'check_box' + span.survey-answer-question__badge.is-checkbox + i.fa-solid.fa-check-square + | 複数選択 + - when 'linear_scale' + span.survey-answer-question__badge.is-scale + i.fa-solid.fa-sliders + | スケール + .survey-answer-question__content + - if question_answer.survey_question.format_text_area? || question_answer.survey_question.format_text_field? + .survey-answer-question__text + = simple_format(question_answer.answer) + - else + .survey-answer-question__choice + .survey-answer-question__choice-label 回答: + .survey-answer-question__choice-value = question_answer.answer + + - if question_answer.reason.present? + .survey-answer-question__reason + .survey-answer-question__reason-label 理由: + .survey-answer-question__reason-text + = simple_format(question_answer.reason) diff --git a/app/views/surveys/survey_question_listings/index.html.slim b/app/views/mentor/surveys/survey_question_listings/index.html.slim similarity index 82% rename from app/views/surveys/survey_question_listings/index.html.slim rename to app/views/mentor/surveys/survey_question_listings/index.html.slim index 15b6ff883f7..3fb18f6a7b7 100644 --- a/app/views/surveys/survey_question_listings/index.html.slim +++ b/app/views/mentor/surveys/survey_question_listings/index.html.slim @@ -1,4 +1,4 @@ -- title 'アンケート質問並び替え' +- title 'メンターページ' header.page-header .container @@ -7,7 +7,8 @@ header.page-header h2.page-header__title = title -= render 'surveys/tabs' += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: 'アンケート') .page-main header.page-main-header @@ -15,12 +16,12 @@ header.page-header .page-main-header__inner .page-main-header__start h1.page-main-header__title - | #{@survey.title} + | アンケート並び替え .page-main-header__end .page-main-header-actions ul.page-main-header-actions__items li.page-main-header-actions__item - = link_to surveys_path, class: 'a-button is-md is-secondary is-block is-back' do + = link_to mentor_surveys_path, class: 'a-button is-md is-secondary is-block is-back' do i.fa-regular | アンケート一覧 hr.a-border diff --git a/app/views/mentor/surveys/survey_result/show.html.slim b/app/views/mentor/surveys/survey_result/show.html.slim new file mode 100644 index 00000000000..7057157e193 --- /dev/null +++ b/app/views/mentor/surveys/survey_result/show.html.slim @@ -0,0 +1,191 @@ +- title 'メンターページ' + +header.page-header + .container + .page-header__inner + .page-header__start + h2.page-header__title + = title + += render 'mentor/mentor_page_tabs' += mentor_surveys_sub_tabs(active_tab: 'アンケート') + +.page-main + header.page-main-header + .container + .page-main-header__inner + .page-main-header__start + h1.page-main-header__title + = "#{@survey.title}の集計結果" + .page-main-header__end + .page-main-header-actions + ul.page-main-header-actions__items + li.page-main-header-actions__item + = link_to mentor_surveys_path, class: 'a-button is-md is-secondary is-block' do + i.fa-regular.fa-angle-left + span + | 一覧に戻る + hr.a-border + .page-body + .container.is-xl + - if @survey_answers.present? + .survey-result-summary + .survey-result-summary__header + .survey-result-summary__count + i.fa-solid.fa-chart-pie.survey-result-summary__icon + span.survey-result-summary__value = "回答数: #{@survey_answers.count}件" + .survey-result-summary__date + i.fa-regular.fa-calendar.survey-result-summary__icon + span = "集計日時: #{l Time.current}" + + .survey-result-questions + - @survey_questions.each do |question| + .survey-result-question + .survey-result-question__header + h3.survey-result-question__title + = question.title + .survey-result-question__type + - case question.format + - when 'text_area', 'text_field' + span.survey-result-question__badge.is-text + i.fa-solid.fa-font + | テキスト + - when 'radio_button' + span.survey-result-question__badge.is-radio + i.fa-solid.fa-circle-dot + | 単一選択 + - when 'check_box' + span.survey-result-question__badge.is-checkbox + i.fa-solid.fa-check-square + | 複数選択 + - when 'linear_scale' + span.survey-result-question__badge.is-scale + i.fa-solid.fa-sliders + | スケール + .survey-result-question__content + - case question.format + - when 'radio_button' + .row + .col-md-6 + ruby: + choices = question.radio_button.radio_button_choices + answers = @survey_answers.map { |sa| sa.survey_question_answers.find_by(survey_question_id: question.id)&.answer }.compact + canvas id="chart-#{question.id}" width="400" height="400" data-radio-button-chart="true" data-choices="#{choices.map(&:choices).to_json}" data-answers="#{answers.to_json}" + .col-md-6 + table.table + thead + tr + th 選択肢 + th 回答数 + th 割合 + tbody + - choices.each do |choice| + - count = answers.count { |answer| answer == choice.choices } + tr + td = choice.choices + td = count + td = "#{(count.to_f / answers.count * 100).round(1)}%" if answers.present? + - when 'check_box' + .row + .col-md-6 + ruby: + choices = question.check_box.check_box_choices + answers = @survey_answers.map { |sa| sa.survey_question_answers.find_by(survey_question_id: question.id)&.answer }.compact + all_selected_choices = answers.flat_map { |answer| answer.split(',') } + total_respondents = answers.count + canvas(id="chart-#{question.id}" + width="400" + height="400" + data-check-box-chart="true" + data-choices="#{choices.map(&:choices).to_json}" + data-all-selected-choices="#{all_selected_choices.to_json}" + data-total-respondents="#{total_respondents}") + .col-md-6 + table.table + thead + tr + th 選択肢 + th 回答数 + th 割合 + tbody + - choices.each do |choice| + - count = all_selected_choices.count { |selected| selected == choice.choices } + tr + td = choice.choices + td = count + td = "#{(count.to_f / total_respondents * 100).round(1)}%" if total_respondents.positive? + - when 'linear_scale' + .row + .col-md-6 + ruby: + min_value = 0 + max_value = 10 + answers = @survey_answers.map { |sa| sa.survey_question_answers.find_by(survey_question_id: question.id)&.answer }.compact + numeric_answers = answers.map(&:to_i) + average = numeric_answers.sum.to_f / numeric_answers.length if numeric_answers.present? + if numeric_answers.present? + sorted = numeric_answers.sort + len = sorted.length + median = len.odd? ? sorted[len / 2] : (sorted[len / 2 - 1] + sorted[len / 2]) / 2.0 + else + median = nil + end + canvas(id="chart-#{question.id}" + width="400" + height="400" + data-linear-scale-chart="true" + data-min-value="#{min_value}" + data-max-value="#{max_value}" + data-answers="#{numeric_answers.to_json}" + data-average="#{average}" + data-median="#{median}") + .col-md-6 + table.table + thead + tr + th 評価 + th 回答数 + th 割合 + tbody + - if numeric_answers.present? + tr.table-info + td.font-weight-bold 平均値 + td.font-weight-bold colspan="2" = average.round(2) + tr.table-info + td.font-weight-bold 中央値 + td.font-weight-bold colspan="2" = median + tr + td colspan="3" + hr + - scale_values = (min_value..max_value).to_a + - scale_values.each do |value| + - count = numeric_answers.count { |answer| answer == value } + tr + td = value + td = count + td = "#{(count.to_f / numeric_answers.count * 100).round(1)}%" if numeric_answers.present? + - when 'text_area', 'text_field' + .row + .col-md-12 + h4.mb-3 テキスト回答一覧 + .table-responsive + table.table + thead + tr + th 回答者 + th 回答内容 + tbody + - @survey_answers.each do |survey_answer| + - answer = survey_answer.survey_question_answers.find_by(survey_question_id: question.id) + - if answer&.answer.present? + tr + td + = link_to user_path(survey_answer.user) do + = survey_answer.user.login_name + td.text-break = simple_format(answer.answer) + - else + .o-empty-message + .o-empty-message__icon + i.fa-regular.fa-sad-tear + .o-empty-message__text + | まだ回答がありません。 diff --git a/app/views/surveys/_survey.html.slim b/app/views/surveys/_survey.html.slim deleted file mode 100644 index 57ba854ecbb..00000000000 --- a/app/views/surveys/_survey.html.slim +++ /dev/null @@ -1,30 +0,0 @@ -tbody.admin-table__items - tr.admin-table__item(class="#{survey.before_start? ? 'is-wip' : ''}") - td.admin-table__item-value.is-text-align-center - - if survey.before_start? - .a-list-item-badge.is-wip - span - | 受付前 - - elsif survey.answer_accepting? - .a-list-item-badge.is-active - span - | 受付中 - - elsif survey.answer_ended? - .a-list-item-badge.is-ended - span - | 受付終了 - td.admin-table__item-value - = l survey.start_at - |〜 - = l survey.end_at - td.admin-table__item-value - = link_to survey do - = survey.title - td.admin-table__item-value.is-text-align-center - ul.is-inline-buttons - li - = link_to edit_survey_path(survey), class: 'a-button is-sm is-secondary is-icon is-block' do - i.fa.fa-solid.fa-pen - li - = link_to survey_survey_questions_path(survey), class: 'a-button is-sm is-secondary is-icon is-block' do - i.fa-solid.fa-align-justify diff --git a/app/views/surveys/_tabs.html.slim b/app/views/surveys/_tabs.html.slim deleted file mode 100644 index 36bbd311e7a..00000000000 --- a/app/views/surveys/_tabs.html.slim +++ /dev/null @@ -1,9 +0,0 @@ -.page-tabs - .container - ul.page-tabs__items - li.page-tabs__item - = link_to 'アンケート', surveys_path, class: "page-tabs__item-link #{current_link(/^surveys/)}" - li.page-tabs__item - = link_to '質問', survey_questions_path, class: "page-tabs__item-link #{current_link(/^survey_questions/)}" - li.page-tabs__item - = link_to '回答', '#', class: 'page-tabs__item-link' diff --git a/app/views/surveys/show.html.slim b/app/views/surveys/show.html.slim index 1b8ee7fbcb5..65bfe41f633 100644 --- a/app/views/surveys/show.html.slim +++ b/app/views/surveys/show.html.slim @@ -7,8 +7,6 @@ header.page-header h2.page-header__title = title -= render 'tabs' - .page-main header.page-main-header .container @@ -20,9 +18,9 @@ header.page-header .page-main-header-actions ul.page-main-header-actions__items li.page-main-header-actions__item - = link_to surveys_path, class: 'a-button is-md is-secondary is-block is-back' do + = link_to root_path, class: 'a-button is-md is-secondary is-block is-back' do i.fa-regular - | アンケート一覧 + | ホームに戻る hr.a-border .page-body .container.is-md @@ -32,92 +30,90 @@ header.page-header = simple_format(@survey.description) .survey-questions - = form_with do |f| + = form_with url: survey_survey_answers_path(@survey), method: :post do |f| .survey-questions__items - @survey_questions.each do |survey_question| .survey-questions-item.a-card .card-body .card-body__description - if survey_question.answer_required - = f.label survey_question.id, survey_question.title, class: 'a-form-label is-lg is-required' + = f.label "survey_question[#{survey_question.id}][answer]", survey_question.title, class: 'a-form-label is-lg is-required' - else - = f.label survey_question.id, survey_question.title, class: 'a-form-label is-lg' + = f.label "survey_question[#{survey_question.id}][answer]", survey_question.title, class: 'a-form-label is-lg' .survey-questions-item__description .a-long-text.is-sm = simple_format(survey_question.description) .survey-questions-item__input - if survey_question.format == 'text_area' - = f.text_area survey_question.id, class: 'a-text-input js-warning-form' + = f.text_area "survey_question[#{survey_question.id}]", class: 'a-text-input js-warning-form' - if survey_question.format == 'text_field' - = f.text_field survey_question.id, class: 'a-text-input js-warning-form' + = f.text_field "survey_question[#{survey_question.id}]", class: 'a-text-input js-warning-form' - if survey_question.format == 'radio_button' .survey-questions-item__radios.radios ul.radios__items - survey_question.radio_button.radio_button_choices.each do |choice| li.radios__item - if choice.reason_for_choice_required - = f.radio_button survey_question.id, choice.choices, id: "radio-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice' + = f.radio_button "survey_question[#{survey_question.id}][answer]", choice.choices, id: "radio-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice' - else - = f.radio_button survey_question.id, choice.choices, id: "radio-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice' - = f.label "#{choice.radio_button_id}_#{choice.choices}", choice.choices, for: "radio-#{choice.id}" + = f.radio_button "survey_question[#{survey_question.id}][answer]", choice.choices, id: "radio-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice' + = f.label "survey_question[#{survey_question.id}][answer]_#{choice.choices}", choice.choices, for: "radio-#{choice.id}" - if survey_question.answer_required_choice_exists?(survey_question.id) .survey-additional-question.is-hidden name="js-additional_question_#{survey_question.id}" - = f.label survey_question.radio_button.title_of_reason, class: 'a-form-label is-required' + = f.label "survey_question[#{survey_question.id}][title_of_reason]", survey_question.radio_button.title_of_reason, class: 'a-form-label is-required' .survey-questions-item__description .a-long-text.is-sm = simple_format(survey_question.radio_button.description_of_reason) .survey-additional-question__input - = f.text_area survey_question.radio_button.title_of_reason, class: 'a-text-input js-warning-form' - - if survey_question.format == 'check_box' - .survey-questions-item__checkboxes.checkboxes - ul.checkboxes__items - - survey_question.check_box.check_box_choices.each do |choice| - li.checkboxes__item - - if choice.reason_for_choice_required - = f.check_box choice.choices, id: "checkbox-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice', name: survey_question.id - - else - = f.check_box choice.choices, id: "checkbox-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice', name: survey_question.id - = f.label choice.choices, for: "checkbox-#{choice.id}" - - if survey_question.answer_required_choice_exists?(survey_question.id) - .survey-additional-question.is-hidden name="js-additional_question_#{survey_question.id}" - = f.label :title_of_reason_for_checkbox, survey_question.check_box.title_of_reason, class: 'a-form-label is-required' - .survey-questions-item__description - .a-long-text.is-sm - = simple_format(survey_question.check_box.description_of_reason) - .survey-additional-question__input - = f.text_area :title_of_reason_for_checkbox, class: 'a-text-input js-warning-form' - - - if survey_question.format == 'linear_scale' - .linear-scale - .linear-scale__label - = survey_question.linear_scale.first - .linear-scale__points - ul.linear-scale__points-items - - 10.times do |i| - li.linear-scale__points-item - - if survey_question.linear_scale.reason_for_choice_required - = f.radio_button survey_question.id, i + 1, id: "linear-scale-#{survey_question.id}-#{i}", - class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice' + = f.text_area "survey_question[#{survey_question.id}][title_of_reason]", class: 'a-text-input js-warning-form' + - if survey_question.format == 'check_box' + .survey-questions-item__checkboxes.checkboxes + ul.checkboxes__items + - survey_question.check_box.check_box_choices.each do |choice| + li.checkboxes__item + - if choice.reason_for_choice_required + = f.check_box "survey_question[#{survey_question.id}][#{choice.choices}]", id: "checkbox-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice' - else - = f.radio_button survey_question.id, i + 1, id: "linear-scale-#{survey_question.id}-#{i}", - class: 'a-toggle-checkbox js-questionnaire_choice' - label.linear-scale__point for="linear-scale-#{survey_question.id}-#{i}" - .linear-scale__point-number - = i + 1 - .linear-scale__label - = survey_question.linear_scale.last - - if survey_question.linear_scale.reason_for_choice_required - .survey-additional-question.is-hidden name="js-additional_question_#{survey_question.id}" - = f.label :title_of_reason_for_linear_scale, survey_question.linear_scale.title_of_reason, class: 'a-form-label is-required' - .survey-questions-item__description - .a-long-text.is-sm - = simple_format(survey_question.linear_scale.description_of_reason) - .survey-additional-question__input - = f.text_area :title_of_reason_for_linear_scale, class: 'a-text-input js-warning-form' + = f.check_box "survey_question[#{survey_question.id}][#{choice.choices}]", id: "checkbox-#{choice.id}", class: 'a-toggle-checkbox js-questionnaire_choice' + = f.label "survey_question[#{survey_question.id}][#{choice.choices}]", choice.choices, for: "checkbox-#{choice.id}" + - if survey_question.answer_required_choice_exists?(survey_question.id) + .survey-additional-question.is-hidden name="js-additional_question_#{survey_question.id}" + = f.label "survey_question[#{survey_question.id}][title_of_reason_for_checkbox]", survey_question.check_box.title_of_reason, class: 'a-form-label is-required' + .survey-questions-item__description + .a-long-text.is-sm + = simple_format(survey_question.check_box.description_of_reason) + .survey-additional-question__input + = f.text_area "survey_question[#{survey_question.id}][title_of_reason_for_checkbox]", class: 'a-text-input js-warning-form' + + - if survey_question.format == 'linear_scale' + .linear-scale + .linear-scale__label + = survey_question.linear_scale.first + .linear-scale__points + ul.linear-scale__points-items + - 10.times do |i| + li.linear-scale__points-item + - if survey_question.linear_scale.reason_for_choice_required + = f.radio_button "survey_question[#{survey_question.id}][answer]", i + 1, id: "linear-scale-#{survey_question.id}-#{i}", + class: 'a-toggle-checkbox js-questionnaire_choice js-answer_required_choice' + - else + = f.radio_button "survey_question[#{survey_question.id}][answer]", i + 1, id: "linear-scale-#{survey_question.id}-#{i}", + class: 'a-toggle-checkbox js-questionnaire_choice' + label.linear-scale__point for="linear-scale-#{survey_question.id}-#{i}" + .linear-scale__point-number + = i + 1 + .linear-scale__label + = survey_question.linear_scale.last + - if survey_question.linear_scale.reason_for_choice_required + .survey-additional-question.is-hidden name="js-additional_question_#{survey_question.id}" + = f.label "survey_question[#{survey_question.id}][title_of_reason_for_linear_scale]", survey_question.linear_scale.title_of_reason, class: 'a-form-label is-required' + .survey-questions-item__description + .a-long-text.is-sm + = simple_format(survey_question.linear_scale.description_of_reason) + .survey-additional-question__input + = f.text_area "survey_question[#{survey_question.id}][title_of_reason_for_linear_scale]", class: 'a-text-input js-warning-form' - .form-actions - ul.form-actions__items - li.form-actions__item.is-main - = link_to edit_survey_path(@survey), class: 'a-button is-md is-secondary is-block' do - i.fa-regular - | 編集 + .form-actions + ul.form-actions__items + li.form-actions__item.is-main + = f.submit '回答する', class: 'a-button is-lg is-primary is-block' diff --git a/bin/lint b/bin/lint index 4ee9a6df08c..be3fa11c69b 100755 --- a/bin/lint +++ b/bin/lint @@ -1,6 +1,3 @@ #!/bin/bash -set -e -bundle exec rubocop -a -bundle exec slim-lint app/views -c config/slim_lint.yml -bin/yarn lint +bundle exec rubocop -a && bundle exec slim-lint app/views -c config/slim_lint.yml && bin/yarn lint diff --git a/config/routes.rb b/config/routes.rb index b9e5927bc18..05a45af28bc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,9 +1,6 @@ # frozen_string_literal: true Rails.application.routes.draw do - resources :surveys do - resources :survey_questions, only: %i(index), controller: "surveys/survey_question_listings" - end root to: "home#index" get "test", to: "home#test", as: "test" @@ -86,7 +83,6 @@ resources :wips, only: %i(index), controller: "wips" end resources :articles - resources :survey_questions, except: %i(show destroy) namespace :events do resources :calendars, only: %i(index) end @@ -108,6 +104,9 @@ resources :generations, only: %i(show index) resource :billing_portal, only: :create, controller: "billing_portal" resources :external_entries, only: %i(index) + resources :surveys, only: %i(show) do + resources :survey_answers, only: %i(create), controller: "surveys/survey_answers" + end get "articles/tags/:tag", to: "articles#index", as: :tag, tag: /.+/ get "pages/tags/:tag", to: "pages#index", as: :pages_tag, tag: /.+/, format: "html" get "questions/tags/:tag", to: "questions#index", as: :questions_tag, tag: /.+/, format: "html" diff --git a/config/routes/mentor.rb b/config/routes/mentor.rb index f3059fc1a1d..71b682ef516 100644 --- a/config/routes/mentor.rb +++ b/config/routes/mentor.rb @@ -13,5 +13,11 @@ resources :courses, only: %i(index new edit create update) do resources :categories, only: %i(index), controller: "courses/categories" end + resources :survey_questions, except: %i(show destroy) + resources :surveys do + resources :survey_questions, only: %i(index), controller: "surveys/survey_question_listings" + resources :survey_answers, only: %i(index show), controller: "surveys/survey_answers" + resource :survey_result, only: %i(show), controller: "surveys/survey_result" + end end end diff --git a/db/fixtures/survey_answers.yml b/db/fixtures/survey_answers.yml new file mode 100644 index 00000000000..e9ec666fd77 --- /dev/null +++ b/db/fixtures/survey_answers.yml @@ -0,0 +1,79 @@ +# 1st survey +survey_answer_first1: + survey: survey1 + user: kimura + created_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + +survey_answer_first2: + survey: survey1 + user: hatsuno + created_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + +survey_answer_first3: + survey: survey1 + user: hajime + created_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + +survey_answer_first4: + survey: survey1 + user: muryou + created_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + +survey_answer_first5: + survey: survey1 + user: kensyu + created_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + +survey_answer_first6: + survey: survey1 + user: senpai + created_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + +survey_answer_first7: + survey: survey1 + user: sumi + created_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + +survey_answer_first8: + survey: survey1 + user: nobu + created_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + +# 2nd survey +survey_answer1: + survey: survey2 + user: kimura + created_at: <%= Time.current - 8.days %> + updated_at: <%= Time.current - 8.days %> + +survey_answer2: + survey: survey2 + user: hatsuno + created_at: <%= Time.current - 7.days %> + updated_at: <%= Time.current - 7.days %> + +survey_answer3: + survey: survey2 + user: hajime + created_at: <%= Time.current - 6.days %> + updated_at: <%= Time.current - 6.days %> + +survey_answer4: + survey: survey2 + user: muryou + created_at: <%= Time.current - 5.days %> + updated_at: <%= Time.current - 5.days %> + +survey_answer5: + survey: survey2 + user: kensyu + created_at: <%= Time.current - 4.days %> + updated_at: <%= Time.current - 4.days %> diff --git a/db/fixtures/survey_question_answers.yml b/db/fixtures/survey_question_answers.yml new file mode 100644 index 00000000000..1a50e6b5cb3 --- /dev/null +++ b/db/fixtures/survey_question_answers.yml @@ -0,0 +1,887 @@ +# komagataの回答(第1回) +survey_question_answer_first1: + survey_answer: survey_answer_first1 + survey_question: survey_question1 + answer: "Rubyの基礎からRailsの実践的な使い方まで、幅広く学ぶことができました。特にテスト駆動開発の考え方が身についたと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + +survey_question_answer_first2: + survey_answer: survey_answer_first1 + survey_question: survey_question2 + answer: "とても満足している" + created_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + +survey_question_answer_first3: + survey_answer: survey_answer_first1 + survey_question: survey_question3 + answer: "とても楽しく学習できている" + created_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + +survey_question_answer_first4: + survey_answer: survey_answer_first1 + survey_question: survey_question4 + answer: "JavaScriptの課題が難しかったです。特にPromiseの概念を理解するのに時間がかかりました。" + created_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + +survey_question_answer_first5: + survey_answer: survey_answer_first1 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + +survey_question_answer_first6: + survey_answer: survey_answer_first1 + survey_question: survey_question6 + answer: "9" + reason: "カリキュラムの質が高く、メンターのサポートも手厚いため、プログラミング学習を真剣に考えている人には最適だと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 10, 0) %> + +# machidaの回答(第1回) +survey_question_answer_first7: + survey_answer: survey_answer_first2 + survey_question: survey_question1 + answer: "デザインとプログラミングの両方の視点から考えることの重要性を学びました。特にユーザー体験を考慮したUI設計について深く理解できました。" + created_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + +survey_question_answer_first8: + survey_answer: survey_answer_first2 + survey_question: survey_question2 + answer: "満足している" + created_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + +survey_question_answer_first9: + survey_answer: survey_answer_first2 + survey_question: survey_question3 + answer: "楽しく学習できている" + created_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + +survey_question_answer_first10: + survey_answer: survey_answer_first2 + survey_question: survey_question4 + answer: "CSSのレイアウト課題が難しかったです。特にFlexboxとGridの使い分けに苦労しました。" + created_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + +survey_question_answer_first11: + survey_answer: survey_answer_first2 + survey_question: survey_question5 + answer: "入りたい企業が見つかるか不安,望んでいる給料がもらえるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + +survey_question_answer_first12: + survey_answer: survey_answer_first2 + survey_question: survey_question6 + answer: "8" + reason: "カリキュラムは素晴らしいですが、デザイン面の内容がもう少し充実するとさらに良くなると思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 11, 0) %> + +# kimuraの回答(第1回) +survey_question_answer_first13: + survey_answer: survey_answer_first3 + survey_question: survey_question1 + answer: "チーム開発の進め方やGitを使った協力作業の方法を学びました。また、コードレビューを通じて品質の高いコードを書く意識が身につきました。" + created_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + +survey_question_answer_first14: + survey_answer: survey_answer_first3 + survey_question: survey_question2 + answer: "満足している" + created_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + +survey_question_answer_first15: + survey_answer: survey_answer_first3 + survey_question: survey_question3 + answer: "楽しく学習できている" + created_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + +survey_question_answer_first16: + survey_answer: survey_answer_first3 + survey_question: survey_question4 + answer: "RSpecでのテスト作成が難しかったです。特にモックやスタブの概念を理解するのに時間がかかりました。" + created_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + +survey_question_answer_first17: + survey_answer: survey_answer_first3 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安,自分でも就職できるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + +survey_question_answer_first18: + survey_answer: survey_answer_first3 + survey_question: survey_question6 + answer: "7" + reason: "学習内容は良いですが、もう少し実践的なプロジェクト経験があるとより就職に役立つと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 12, 0) %> + +# hatsunoの回答(第1回) +survey_question_answer_first19: + survey_answer: survey_answer_first4 + survey_question: survey_question1 + answer: "Webアプリケーション開発の一連の流れを実践的に学ぶことができました。特にデータベース設計とモデル間の関連付けについて理解が深まりました。" + created_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + +survey_question_answer_first20: + survey_answer: survey_answer_first4 + survey_question: survey_question2 + answer: "とても満足している" + created_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + +survey_question_answer_first21: + survey_answer: survey_answer_first4 + survey_question: survey_question3 + answer: "とても楽しく学習できている" + created_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + +survey_question_answer_first22: + survey_answer: survey_answer_first4 + survey_question: survey_question4 + answer: "AWSへのデプロイ課題が難しかったです。特にインフラ周りの設定に苦労しました。" + created_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + +survey_question_answer_first23: + survey_answer: survey_answer_first4 + survey_question: survey_question5 + answer: "入りたい企業が見つかるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + +survey_question_answer_first24: + survey_answer: survey_answer_first4 + survey_question: survey_question6 + answer: "10" + reason: "カリキュラムの質、メンターのサポート、コミュニティの雰囲気すべてが素晴らしく、プログラミング学習に最適な環境だと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 13, 0) %> + +# hajimeの回答(第1回) +survey_question_answer_first25: + survey_answer: survey_answer_first5 + survey_question: survey_question1 + answer: "プログラミングの基礎から応用まで体系的に学ぶことができました。特にオブジェクト指向設計の考え方が身についたと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + +survey_question_answer_first26: + survey_answer: survey_answer_first5 + survey_question: survey_question2 + answer: "どちらでもない" + created_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + +survey_question_answer_first27: + survey_answer: survey_answer_first5 + survey_question: survey_question3 + answer: "楽しく学習できていない" + created_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + +survey_question_answer_first28: + survey_answer: survey_answer_first5 + survey_question: survey_question4 + answer: "Railsの課題が難しかったです。特にアソシエーションの設定に苦労しました。" + created_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + +survey_question_answer_first29: + survey_answer: survey_answer_first5 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安,望んでいる給料がもらえるか不安,自分でも就職できるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + +survey_question_answer_first30: + survey_answer: survey_answer_first5 + survey_question: survey_question6 + answer: "5" + reason: "カリキュラムは良いですが、もう少し初心者向けの説明があると理解しやすいと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 14, 0) %> + +# muryouの回答(第1回) +survey_question_answer_first31: + survey_answer: survey_answer_first6 + survey_question: survey_question1 + answer: "プログラミングの考え方や問題解決のアプローチ方法を学びました。特にデバッグの手法が身についたと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + +survey_question_answer_first32: + survey_answer: survey_answer_first6 + survey_question: survey_question2 + answer: "満足している" + created_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + +survey_question_answer_first33: + survey_answer: survey_answer_first6 + survey_question: survey_question3 + answer: "楽しく学習できている" + created_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + +survey_question_answer_first34: + survey_answer: survey_answer_first6 + survey_question: survey_question4 + answer: "JavaScriptのフレームワーク課題が難しかったです。特にReactの状態管理に苦労しました。" + created_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + +survey_question_answer_first35: + survey_answer: survey_answer_first6 + survey_question: survey_question5 + answer: "その他" + reason: "年齢的に採用されるか不安です。" + created_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + +survey_question_answer_first36: + survey_answer: survey_answer_first6 + survey_question: survey_question6 + answer: "8" + reason: "カリキュラムは充実していますが、もう少し実務に近い内容があるとより良いと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 15, 0) %> + +# kensyuの回答(第1回) +survey_question_answer_first37: + survey_answer: survey_answer_first7 + survey_question: survey_question1 + answer: "実践的なWeb開発の知識とスキルを学びました。特にAPIの設計と実装について理解が深まりました。" + created_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + +survey_question_answer_first38: + survey_answer: survey_answer_first7 + survey_question: survey_question2 + answer: "とても満足している" + created_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + +survey_question_answer_first39: + survey_answer: survey_answer_first7 + survey_question: survey_question3 + answer: "とても楽しく学習できている" + created_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + +survey_question_answer_first40: + survey_answer: survey_answer_first7 + survey_question: survey_question4 + answer: "セキュリティ対策の課題が難しかったです。特にCSRF対策やXSS対策の実装に苦労しました。" + created_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + +survey_question_answer_first41: + survey_answer: survey_answer_first7 + survey_question: survey_question5 + answer: "入りたい企業が見つかるか不安,望んでいる給料がもらえるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + +survey_question_answer_first42: + survey_answer: survey_answer_first7 + survey_question: survey_question6 + answer: "9" + reason: "カリキュラムの質が高く、メンターのサポートも手厚いため、プログラミング学習を真剣に考えている人には最適だと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 16, 0) %> + +# senpaiの回答(第1回) +survey_question_answer_first43: + survey_answer: survey_answer_first8 + survey_question: survey_question1 + answer: "プログラミングの基礎から応用まで体系的に学ぶことができました。特にコードの可読性や保守性を考慮した設計について理解が深まりました。" + created_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + +survey_question_answer_first44: + survey_answer: survey_answer_first8 + survey_question: survey_question2 + answer: "満足している" + created_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + +survey_question_answer_first45: + survey_answer: survey_answer_first8 + survey_question: survey_question3 + answer: "楽しく学習できている" + created_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + +survey_question_answer_first46: + survey_answer: survey_answer_first8 + survey_question: survey_question4 + answer: "パフォーマンス最適化の課題が難しかったです。特にN+1問題の解決に苦労しました。" + created_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + +survey_question_answer_first47: + survey_answer: survey_answer_first8 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + +survey_question_answer_first48: + survey_answer: survey_answer_first8 + survey_question: survey_question6 + answer: "8" + reason: "カリキュラムは充実していますが、もう少し実務に近い内容があるとより良いと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 17, 0) %> + +# sumiの回答(第1回) +survey_question_answer_first49: + survey_answer: survey_answer_first9 + survey_question: survey_question1 + answer: "プログラミングの基礎から応用まで体系的に学ぶことができました。特にテスト駆動開発の考え方が身についたと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + +survey_question_answer_first50: + survey_answer: survey_answer_first9 + survey_question: survey_question2 + answer: "不満がある" + created_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + +survey_question_answer_first51: + survey_answer: survey_answer_first9 + survey_question: survey_question3 + answer: "挫折しそうで辛い" + reason: "学習内容についていくのが難しく、他の受講生との差を感じることがあります。" + created_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + +survey_question_answer_first52: + survey_answer: survey_answer_first9 + survey_question: survey_question4 + answer: "Railsの課題が難しかったです。特にルーティングの設定に苦労しました。" + created_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + +survey_question_answer_first53: + survey_answer: survey_answer_first9 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安,望んでいる給料がもらえるか不安,自分でも就職できるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + +survey_question_answer_first54: + survey_answer: survey_answer_first9 + survey_question: survey_question6 + answer: "4" + reason: "カリキュラムの難易度が高く、初心者には厳しいと感じることがあります。もう少し段階的な学習ステップがあると良いと思います。" + created_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 18, 0) %> + +# nobuの回答(第1回) +survey_question_answer_first55: + survey_answer: survey_answer_first10 + survey_question: survey_question1 + answer: "Webアプリケーション開発の一連の流れを実践的に学ぶことができました。特にデータベース設計とモデル間の関連付けについて理解が深まりました。" + created_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + +survey_question_answer_first56: + survey_answer: survey_answer_first10 + survey_question: survey_question2 + answer: "とても不満がある" + reason: "カリキュラムの内容が古く、最新の技術トレンドに対応していないと感じます。" + created_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + +survey_question_answer_first57: + survey_answer: survey_answer_first10 + survey_question: survey_question3 + answer: "楽しく学習できていない" + created_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + +survey_question_answer_first58: + survey_answer: survey_answer_first10 + survey_question: survey_question4 + answer: "Dockerの課題が難しかったです。特にコンテナ間の通信設定に苦労しました。" + created_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + +survey_question_answer_first59: + survey_answer: survey_answer_first10 + survey_question: survey_question5 + answer: "入りたい企業が見つかるか不安,プログラマーとしてやっていけるか不安,望んでいる給料がもらえるか不安,自分でも就職できるか不安" + created_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + +survey_question_answer_first60: + survey_answer: survey_answer_first10 + survey_question: survey_question6 + answer: "3" + reason: "カリキュラムの内容が古く、メンターの対応も遅いことがあります。もっと最新の技術を取り入れた内容にしてほしいです。" + created_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + updated_at: <%= Time.zone.local(2020, 1, 1, 19, 0) %> + +# 第2回アンケートの回答 + +# komagataの回答(第2回) +survey_question_answer1: + survey_answer: survey_answer1 + survey_question: survey_question1 + answer: "Rubyの基礎からRailsの実践的な使い方まで、幅広く学ぶことができました。特にテスト駆動開発の考え方が身についたと思います。" + created_at: <%= Time.current - 10.days %> + updated_at: <%= Time.current - 10.days %> + +survey_question_answer2: + survey_answer: survey_answer1 + survey_question: survey_question2 + answer: "とても満足している" + created_at: <%= Time.current - 10.days %> + updated_at: <%= Time.current - 10.days %> + +survey_question_answer3: + survey_answer: survey_answer1 + survey_question: survey_question3 + answer: "とても楽しく学習できている" + created_at: <%= Time.current - 10.days %> + updated_at: <%= Time.current - 10.days %> + +survey_question_answer4: + survey_answer: survey_answer1 + survey_question: survey_question4 + answer: "JavaScriptの課題が難しかったです。特にPromiseの概念を理解するのに時間がかかりました。" + created_at: <%= Time.current - 10.days %> + updated_at: <%= Time.current - 10.days %> + +survey_question_answer5: + survey_answer: survey_answer1 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安" + created_at: <%= Time.current - 10.days %> + updated_at: <%= Time.current - 10.days %> + +survey_question_answer6: + survey_answer: survey_answer1 + survey_question: survey_question6 + answer: "9" + reason: "カリキュラムの質が高く、メンターのサポートも手厚いため、プログラミング学習を真剣に考えている人には最適だと思います。" + created_at: <%= Time.current - 10.days %> + updated_at: <%= Time.current - 10.days %> + +# machidaの回答 +survey_question_answer7: + survey_answer: survey_answer2 + survey_question: survey_question1 + answer: "デザインとプログラミングの両方の視点から考えることの重要性を学びました。特にユーザー体験を考慮したUI設計について深く理解できました。" + created_at: <%= Time.current - 9.days %> + updated_at: <%= Time.current - 9.days %> + +survey_question_answer8: + survey_answer: survey_answer2 + survey_question: survey_question2 + answer: "満足している" + created_at: <%= Time.current - 9.days %> + updated_at: <%= Time.current - 9.days %> + +survey_question_answer9: + survey_answer: survey_answer2 + survey_question: survey_question3 + answer: "楽しく学習できている" + created_at: <%= Time.current - 9.days %> + updated_at: <%= Time.current - 9.days %> + +survey_question_answer10: + survey_answer: survey_answer2 + survey_question: survey_question4 + answer: "CSSのレイアウト課題が難しかったです。特にFlexboxとGridの使い分けに苦労しました。" + created_at: <%= Time.current - 9.days %> + updated_at: <%= Time.current - 9.days %> + +survey_question_answer11: + survey_answer: survey_answer2 + survey_question: survey_question5 + answer: "入りたい企業が見つかるか不安,望んでいる給料がもらえるか不安" + created_at: <%= Time.current - 9.days %> + updated_at: <%= Time.current - 9.days %> + +survey_question_answer12: + survey_answer: survey_answer2 + survey_question: survey_question6 + answer: "8" + reason: "カリキュラムは素晴らしいですが、デザイン面の内容がもう少し充実するとさらに良くなると思います。" + created_at: <%= Time.current - 9.days %> + updated_at: <%= Time.current - 9.days %> + +# kimuraの回答 +survey_question_answer13: + survey_answer: survey_answer3 + survey_question: survey_question1 + answer: "チーム開発の進め方やGitを使った協力作業の方法を学びました。また、コードレビューを通じて品質の高いコードを書く意識が身につきました。" + created_at: <%= Time.current - 8.days %> + updated_at: <%= Time.current - 8.days %> + +survey_question_answer14: + survey_answer: survey_answer3 + survey_question: survey_question2 + answer: "満足している" + created_at: <%= Time.current - 8.days %> + updated_at: <%= Time.current - 8.days %> + +survey_question_answer15: + survey_answer: survey_answer3 + survey_question: survey_question3 + answer: "楽しく学習できている" + created_at: <%= Time.current - 8.days %> + updated_at: <%= Time.current - 8.days %> + +survey_question_answer16: + survey_answer: survey_answer3 + survey_question: survey_question4 + answer: "RSpecでのテスト作成が難しかったです。特にモックやスタブの概念を理解するのに時間がかかりました。" + created_at: <%= Time.current - 8.days %> + updated_at: <%= Time.current - 8.days %> + +survey_question_answer17: + survey_answer: survey_answer3 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安,自分でも就職できるか不安" + created_at: <%= Time.current - 8.days %> + updated_at: <%= Time.current - 8.days %> + +survey_question_answer18: + survey_answer: survey_answer3 + survey_question: survey_question6 + answer: "7" + reason: "学習内容は良いですが、もう少し実践的なプロジェクト経験があるとより就職に役立つと思います。" + created_at: <%= Time.current - 8.days %> + updated_at: <%= Time.current - 8.days %> + +# hatsunoの回答 +survey_question_answer19: + survey_answer: survey_answer4 + survey_question: survey_question1 + answer: "Webアプリケーション開発の一連の流れを実践的に学ぶことができました。特にデータベース設計とモデル間の関連付けについて理解が深まりました。" + created_at: <%= Time.current - 7.days %> + updated_at: <%= Time.current - 7.days %> + +survey_question_answer20: + survey_answer: survey_answer4 + survey_question: survey_question2 + answer: "とても満足している" + created_at: <%= Time.current - 7.days %> + updated_at: <%= Time.current - 7.days %> + +survey_question_answer21: + survey_answer: survey_answer4 + survey_question: survey_question3 + answer: "とても楽しく学習できている" + created_at: <%= Time.current - 7.days %> + updated_at: <%= Time.current - 7.days %> + +survey_question_answer22: + survey_answer: survey_answer4 + survey_question: survey_question4 + answer: "AWSへのデプロイ課題が難しかったです。特にインフラ周りの設定に苦労しました。" + created_at: <%= Time.current - 7.days %> + updated_at: <%= Time.current - 7.days %> + +survey_question_answer23: + survey_answer: survey_answer4 + survey_question: survey_question5 + answer: "入りたい企業が見つかるか不安" + created_at: <%= Time.current - 7.days %> + updated_at: <%= Time.current - 7.days %> + +survey_question_answer24: + survey_answer: survey_answer4 + survey_question: survey_question6 + answer: "10" + reason: "カリキュラムの質、メンターのサポート、コミュニティの雰囲気すべてが素晴らしく、プログラミング学習に最適な環境だと思います。" + created_at: <%= Time.current - 7.days %> + updated_at: <%= Time.current - 7.days %> + +# hajimeの回答 +survey_question_answer25: + survey_answer: survey_answer5 + survey_question: survey_question1 + answer: "プログラミングの基礎から応用まで体系的に学ぶことができました。特にオブジェクト指向設計の考え方が身についたと思います。" + created_at: <%= Time.current - 6.days %> + updated_at: <%= Time.current - 6.days %> + +survey_question_answer26: + survey_answer: survey_answer5 + survey_question: survey_question2 + answer: "どちらでもない" + created_at: <%= Time.current - 6.days %> + updated_at: <%= Time.current - 6.days %> + +survey_question_answer27: + survey_answer: survey_answer5 + survey_question: survey_question3 + answer: "楽しく学習できていない" + created_at: <%= Time.current - 6.days %> + updated_at: <%= Time.current - 6.days %> + +survey_question_answer28: + survey_answer: survey_answer5 + survey_question: survey_question4 + answer: "Railsの課題が難しかったです。特にアソシエーションの設定に苦労しました。" + created_at: <%= Time.current - 6.days %> + updated_at: <%= Time.current - 6.days %> + +survey_question_answer29: + survey_answer: survey_answer5 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安,望んでいる給料がもらえるか不安,自分でも就職できるか不安" + created_at: <%= Time.current - 6.days %> + updated_at: <%= Time.current - 6.days %> + +survey_question_answer30: + survey_answer: survey_answer5 + survey_question: survey_question6 + answer: "5" + reason: "カリキュラムは良いですが、もう少し初心者向けの説明があると理解しやすいと思います。" + created_at: <%= Time.current - 6.days %> + updated_at: <%= Time.current - 6.days %> + +# muryouの回答 +survey_question_answer31: + survey_answer: survey_answer6 + survey_question: survey_question1 + answer: "プログラミングの考え方や問題解決のアプローチ方法を学びました。特にデバッグの手法が身についたと思います。" + created_at: <%= Time.current - 5.days %> + updated_at: <%= Time.current - 5.days %> + +survey_question_answer32: + survey_answer: survey_answer6 + survey_question: survey_question2 + answer: "満足している" + created_at: <%= Time.current - 5.days %> + updated_at: <%= Time.current - 5.days %> + +survey_question_answer33: + survey_answer: survey_answer6 + survey_question: survey_question3 + answer: "楽しく学習できている" + created_at: <%= Time.current - 5.days %> + updated_at: <%= Time.current - 5.days %> + +survey_question_answer34: + survey_answer: survey_answer6 + survey_question: survey_question4 + answer: "JavaScriptのフレームワーク課題が難しかったです。特にReactの状態管理に苦労しました。" + created_at: <%= Time.current - 5.days %> + updated_at: <%= Time.current - 5.days %> + +survey_question_answer35: + survey_answer: survey_answer6 + survey_question: survey_question5 + answer: "その他" + reason: "年齢的に採用されるか不安です。" + created_at: <%= Time.current - 5.days %> + updated_at: <%= Time.current - 5.days %> + +survey_question_answer36: + survey_answer: survey_answer6 + survey_question: survey_question6 + answer: "8" + reason: "カリキュラムは充実していますが、もう少し実務に近い内容があるとより良いと思います。" + created_at: <%= Time.current - 5.days %> + updated_at: <%= Time.current - 5.days %> + +# kensyuの回答 +survey_question_answer37: + survey_answer: survey_answer7 + survey_question: survey_question1 + answer: "実践的なWeb開発の知識とスキルを学びました。特にAPIの設計と実装について理解が深まりました。" + created_at: <%= Time.current - 4.days %> + updated_at: <%= Time.current - 4.days %> + +survey_question_answer38: + survey_answer: survey_answer7 + survey_question: survey_question2 + answer: "とても満足している" + created_at: <%= Time.current - 4.days %> + updated_at: <%= Time.current - 4.days %> + +survey_question_answer39: + survey_answer: survey_answer7 + survey_question: survey_question3 + answer: "とても楽しく学習できている" + created_at: <%= Time.current - 4.days %> + updated_at: <%= Time.current - 4.days %> + +survey_question_answer40: + survey_answer: survey_answer7 + survey_question: survey_question4 + answer: "セキュリティ対策の課題が難しかったです。特にCSRF対策やXSS対策の実装に苦労しました。" + created_at: <%= Time.current - 4.days %> + updated_at: <%= Time.current - 4.days %> + +survey_question_answer41: + survey_answer: survey_answer7 + survey_question: survey_question5 + answer: "入りたい企業が見つかるか不安,望んでいる給料がもらえるか不安" + created_at: <%= Time.current - 4.days %> + updated_at: <%= Time.current - 4.days %> + +survey_question_answer42: + survey_answer: survey_answer7 + survey_question: survey_question6 + answer: "9" + reason: "カリキュラムの質が高く、メンターのサポートも手厚いため、プログラミング学習を真剣に考えている人には最適だと思います。" + created_at: <%= Time.current - 4.days %> + updated_at: <%= Time.current - 4.days %> + +# senpaiの回答 +survey_question_answer43: + survey_answer: survey_answer8 + survey_question: survey_question1 + answer: "プログラミングの基礎から応用まで体系的に学ぶことができました。特にコードの可読性や保守性を考慮した設計について理解が深まりました。" + created_at: <%= Time.current - 3.days %> + updated_at: <%= Time.current - 3.days %> + +survey_question_answer44: + survey_answer: survey_answer8 + survey_question: survey_question2 + answer: "満足している" + created_at: <%= Time.current - 3.days %> + updated_at: <%= Time.current - 3.days %> + +survey_question_answer45: + survey_answer: survey_answer8 + survey_question: survey_question3 + answer: "楽しく学習できている" + created_at: <%= Time.current - 3.days %> + updated_at: <%= Time.current - 3.days %> + +survey_question_answer46: + survey_answer: survey_answer8 + survey_question: survey_question4 + answer: "パフォーマンス最適化の課題が難しかったです。特にN+1問題の解決に苦労しました。" + created_at: <%= Time.current - 3.days %> + updated_at: <%= Time.current - 3.days %> + +survey_question_answer47: + survey_answer: survey_answer8 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安" + created_at: <%= Time.current - 3.days %> + updated_at: <%= Time.current - 3.days %> + +survey_question_answer48: + survey_answer: survey_answer8 + survey_question: survey_question6 + answer: "8" + reason: "カリキュラムは充実していますが、もう少し実務に近い内容があるとより良いと思います。" + created_at: <%= Time.current - 3.days %> + updated_at: <%= Time.current - 3.days %> + +# sumiの回答 +survey_question_answer49: + survey_answer: survey_answer9 + survey_question: survey_question1 + answer: "プログラミングの基礎から応用まで体系的に学ぶことができました。特にテスト駆動開発の考え方が身についたと思います。" + created_at: <%= Time.current - 2.days %> + updated_at: <%= Time.current - 2.days %> + +survey_question_answer50: + survey_answer: survey_answer9 + survey_question: survey_question2 + answer: "不満がある" + created_at: <%= Time.current - 2.days %> + updated_at: <%= Time.current - 2.days %> + +survey_question_answer51: + survey_answer: survey_answer9 + survey_question: survey_question3 + answer: "挫折しそうで辛い" + reason: "学習内容についていくのが難しく、他の受講生との差を感じることがあります。" + created_at: <%= Time.current - 2.days %> + updated_at: <%= Time.current - 2.days %> + +survey_question_answer52: + survey_answer: survey_answer9 + survey_question: survey_question4 + answer: "Railsの課題が難しかったです。特にルーティングの設定に苦労しました。" + created_at: <%= Time.current - 2.days %> + updated_at: <%= Time.current - 2.days %> + +survey_question_answer53: + survey_answer: survey_answer9 + survey_question: survey_question5 + answer: "プログラマーとしてやっていけるか不安,望んでいる給料がもらえるか不安,自分でも就職できるか不安" + created_at: <%= Time.current - 2.days %> + updated_at: <%= Time.current - 2.days %> + +survey_question_answer54: + survey_answer: survey_answer9 + survey_question: survey_question6 + answer: "4" + reason: "カリキュラムの難易度が高く、初心者には厳しいと感じることがあります。もう少し段階的な学習ステップがあると良いと思います。" + created_at: <%= Time.current - 2.days %> + updated_at: <%= Time.current - 2.days %> + +# nobuの回答 +survey_question_answer55: + survey_answer: survey_answer10 + survey_question: survey_question1 + answer: "Webアプリケーション開発の一連の流れを実践的に学ぶことができました。特にデータベース設計とモデル間の関連付けについて理解が深まりました。" + created_at: <%= Time.current - 1.day %> + updated_at: <%= Time.current - 1.day %> + +survey_question_answer56: + survey_answer: survey_answer10 + survey_question: survey_question2 + answer: "とても不満がある" + reason: "カリキュラムの内容が古く、最新の技術トレンドに対応していないと感じます。" + created_at: <%= Time.current - 1.day %> + updated_at: <%= Time.current - 1.day %> + +survey_question_answer57: + survey_answer: survey_answer10 + survey_question: survey_question3 + answer: "楽しく学習できていない" + created_at: <%= Time.current - 1.day %> + updated_at: <%= Time.current - 1.day %> + +survey_question_answer58: + survey_answer: survey_answer10 + survey_question: survey_question4 + answer: "Dockerの課題が難しかったです。特にコンテナ間の通信設定に苦労しました。" + created_at: <%= Time.current - 1.day %> + updated_at: <%= Time.current - 1.day %> + +survey_question_answer59: + survey_answer: survey_answer10 + survey_question: survey_question5 + answer: "入りたい企業が見つかるか不安,プログラマーとしてやっていけるか不安,望んでいる給料がもらえるか不安,自分でも就職できるか不安" + created_at: <%= Time.current - 1.day %> + updated_at: <%= Time.current - 1.day %> + +survey_question_answer60: + survey_answer: survey_answer10 + survey_question: survey_question6 + answer: "3" + reason: "カリキュラムの内容が古く、メンターの対応も遅いことがあります。もっと最新の技術を取り入れた内容にしてほしいです。" + created_at: <%= Time.current - 1.day %> + updated_at: <%= Time.current - 1.day %> diff --git a/db/migrate/20250302152230_create_survey_answers.rb b/db/migrate/20250302152230_create_survey_answers.rb new file mode 100644 index 00000000000..eb9cc561ed5 --- /dev/null +++ b/db/migrate/20250302152230_create_survey_answers.rb @@ -0,0 +1,10 @@ +class CreateSurveyAnswers < ActiveRecord::Migration[6.1] + def change + create_table :survey_answers do |t| + t.references :survey, null: false, foreign_key: true + t.references :user, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20250302152255_create_survey_question_answers.rb b/db/migrate/20250302152255_create_survey_question_answers.rb new file mode 100644 index 00000000000..b020866dd8f --- /dev/null +++ b/db/migrate/20250302152255_create_survey_question_answers.rb @@ -0,0 +1,12 @@ +class CreateSurveyQuestionAnswers < ActiveRecord::Migration[6.1] + def change + create_table :survey_question_answers do |t| + t.references :survey_answer, null: false, foreign_key: true + t.references :survey_question, null: false, foreign_key: true + t.text :answer + t.text :reason + + t.timestamps + end + end +end diff --git a/db/migrate/20250304062341_add_unique_index_to_survey_answers.rb b/db/migrate/20250304062341_add_unique_index_to_survey_answers.rb new file mode 100644 index 00000000000..896784420df --- /dev/null +++ b/db/migrate/20250304062341_add_unique_index_to_survey_answers.rb @@ -0,0 +1,5 @@ +class AddUniqueIndexToSurveyAnswers < ActiveRecord::Migration[6.1] + def change + add_index :survey_answers, [:survey_id, :user_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 565fa4caee2..2e4d0cef633 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: 2025_01_30_052357) do +ActiveRecord::Schema.define(version: 2025_03_04_062341) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" @@ -724,6 +724,27 @@ t.index ["practice_id"], name: "index_submission_answers_on_practice_id" end + create_table "survey_answers", force: :cascade do |t| + t.bigint "survey_id", null: false + t.bigint "user_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["survey_id", "user_id"], name: "index_survey_answers_on_survey_id_and_user_id", unique: true + t.index ["survey_id"], name: "index_survey_answers_on_survey_id" + t.index ["user_id"], name: "index_survey_answers_on_user_id" + end + + create_table "survey_question_answers", force: :cascade do |t| + t.bigint "survey_answer_id", null: false + t.bigint "survey_question_id", null: false + t.text "answer" + t.text "reason" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["survey_answer_id"], name: "index_survey_question_answers_on_survey_answer_id" + t.index ["survey_question_id"], name: "index_survey_question_answers_on_survey_question_id" + end + create_table "survey_question_listings", force: :cascade do |t| t.bigint "survey_id", null: false t.bigint "survey_question_id", null: false @@ -934,6 +955,10 @@ add_foreign_key "request_retirements", "users" add_foreign_key "request_retirements", "users", column: "target_user_id" add_foreign_key "submission_answers", "practices" + add_foreign_key "survey_answers", "surveys" + add_foreign_key "survey_answers", "users" + add_foreign_key "survey_question_answers", "survey_answers" + add_foreign_key "survey_question_answers", "survey_questions" add_foreign_key "survey_question_listings", "survey_questions" add_foreign_key "survey_question_listings", "surveys" add_foreign_key "survey_questions", "users" diff --git a/db/seeds.rb b/db/seeds.rb index ed688b873ac..62e1db143d3 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -62,6 +62,8 @@ check_box_choices surveys survey_question_listings + survey_answers + survey_question_answers buzzes inquiries coding_tests diff --git a/package.json b/package.json index 42c6b3389e9..a24785b55d4 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,9 @@ "ace-builds": "^1.35.0", "autosize": "^4.0.2", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", + "chart.js": "^4.4.8", + "chartjs-plugin-annotation": "^3.1.0", + "chartjs-plugin-datalabels": "^2.2.0", "choices.js": "^10.1.0", "clsx": "^2.0.0", "css-loader": "^5.0.1", diff --git a/test/components/sub_tab_component_test.rb b/test/components/sub_tab_component_test.rb new file mode 100644 index 00000000000..f371e677918 --- /dev/null +++ b/test/components/sub_tab_component_test.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SubTabComponentTest < ViewComponent::TestCase + def test_default + render_inline(SubTabComponent.new(name: 'タブ名', link: '/path/to/page')) + + assert_selector 'li.tab-nav__item a.tab-nav__item-link', text: 'タブ名' + assert_selector 'li.tab-nav__item a[href="/path/to/page"]' + assert_no_selector 'li.tab-nav__item a.is-active' + end + + def test_active + render_inline(SubTabComponent.new(name: 'タブ名', link: '/path/to/page', active: true)) + + assert_selector 'li.tab-nav__item a.tab-nav__item-link.is-active', text: 'タブ名' + end + + def test_inactive + render_inline(SubTabComponent.new(name: 'タブ名', link: '/path/to/page', active: false)) + + assert_selector 'li.tab-nav__item a.tab-nav__item-link', text: 'タブ名' + assert_no_selector 'li.tab-nav__item a.is-active' + end +end diff --git a/test/components/sub_tabs_component_test.rb b/test/components/sub_tabs_component_test.rb new file mode 100644 index 00000000000..9d099cd811a --- /dev/null +++ b/test/components/sub_tabs_component_test.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SubTabsComponentTest < ViewComponent::TestCase + def test_default + tabs = [ + { name: 'タブ1', link: '/path/to/tab1' }, + { name: 'タブ2', link: '/path/to/tab2' } + ] + render_inline(SubTabsComponent.new(tabs:, active_tab: 'タブ1')) + + assert_selector 'nav.tab-nav .container ul.tab-nav__items li.tab-nav__item', count: 2 + assert_selector 'li.tab-nav__item a.tab-nav__item-link', text: 'タブ1' + assert_selector 'li.tab-nav__item a.tab-nav__item-link', text: 'タブ2' + end + + def test_active_tab + tabs = [ + { name: 'タブ1', link: '/path/to/tab1' }, + { name: 'タブ2', link: '/path/to/tab2' } + ] + render_inline(SubTabsComponent.new(tabs:, active_tab: 'タブ1')) + + assert_selector 'li.tab-nav__item a.tab-nav__item-link.is-active', text: 'タブ1' + assert_no_selector 'li.tab-nav__item a.tab-nav__item-link.is-active', text: 'タブ2' + end + + def test_different_active_tab + tabs = [ + { name: 'タブ1', link: '/path/to/tab1' }, + { name: 'タブ2', link: '/path/to/tab2' } + ] + render_inline(SubTabsComponent.new(tabs:, active_tab: 'タブ2')) + + assert_no_selector 'li.tab-nav__item a.tab-nav__item-link.is-active', text: 'タブ1' + assert_selector 'li.tab-nav__item a.tab-nav__item-link.is-active', text: 'タブ2' + end + + def test_no_active_tab + tabs = [ + { name: 'タブ1', link: '/path/to/tab1' }, + { name: 'タブ2', link: '/path/to/tab2' } + ] + render_inline(SubTabsComponent.new(tabs:, active_tab: 'タブ3')) + + assert_no_selector 'li.tab-nav__item a.tab-nav__item-link.is-active' + end +end diff --git a/test/fixtures/surveys.yml b/test/fixtures/surveys.yml index 9b086918416..02ca6d184ac 100644 --- a/test/fixtures/surveys.yml +++ b/test/fixtures/surveys.yml @@ -3,7 +3,7 @@ survey1: description: "フィヨルドブートキャンプでは満足度向上と質の高いサービスの提供に活かすためアンケートを実施しております。5分程度の簡単なアンケートです。" user: komagata start_at: <%= Time.zone.local(2020, 1, 1, 0, 0) %> - end_at: <%= Time.zone.local(2020, 1, 1, 23, 59) %> + end_at: <%= Time.zone.local(2030, 1, 1, 23, 59) %> created_at: <%= Time.zone.local(2020, 1, 1, 0, 0) %> updated_at: <%= Time.zone.local(2020, 1, 1, 0, 0) %> diff --git a/test/helpers/sub_tabs/surveys_helper_test.rb b/test/helpers/sub_tabs/surveys_helper_test.rb new file mode 100644 index 00000000000..2410a957e44 --- /dev/null +++ b/test/helpers/sub_tabs/surveys_helper_test.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SubTabs::SurveysHelperTest < ActionView::TestCase + test 'mentor_surveys_sub_tabs with active_tab: アンケート' do + component_mock = Minitest::Mock.new + component_mock.expect(:call, 'rendered component') + + SubTabsComponent.stub(:new, lambda { |tabs:, active_tab:| + assert_equal 2, tabs.size + assert_equal 'アンケート', tabs[0][:name] + assert_equal mentor_surveys_path, tabs[0][:link] + assert_equal '質問', tabs[1][:name] + assert_equal mentor_survey_questions_path, tabs[1][:link] + + assert_equal 'アンケート', active_tab + + component_mock + }) do + result = mentor_surveys_sub_tabs(active_tab: 'アンケート') + + assert_equal 'rendered component', result + end + + component_mock.verify + end + + test 'mentor_surveys_sub_tabs with active_tab: 質問' do + component_mock = Minitest::Mock.new + component_mock.expect(:call, 'rendered component') + + SubTabsComponent.stub(:new, lambda { |tabs:, active_tab:| + assert_equal 2, tabs.size + assert_equal 'アンケート', tabs[0][:name] + assert_equal mentor_surveys_path, tabs[0][:link] + assert_equal '質問', tabs[1][:name] + assert_equal mentor_survey_questions_path, tabs[1][:link] + + assert_equal '質問', active_tab + + component_mock + }) do + result = mentor_surveys_sub_tabs(active_tab: '質問') + + assert_equal 'rendered component', result + end + + component_mock.verify + end +end diff --git a/test/models/survey_question_test.rb b/test/models/survey_question_test.rb index 75a11280cf2..89b2705d76f 100644 --- a/test/models/survey_question_test.rb +++ b/test/models/survey_question_test.rb @@ -3,11 +3,35 @@ require 'test_helper' class SurveyQuestionTest < ActiveSupport::TestCase - test '#answer_required_choice_exists?' do + test '#answer_required_choice_exists? returns true when radio button has choice with required reason' do radio_button_survey_question = survey_questions(:survey_question2) - check_box_survey_question = survey_questions(:survey_question3) - assert radio_button_survey_question.answer_required_choice_exists?(radio_button_survey_question.id) + end + + test '#answer_required_choice_exists? returns true when check box has choice with required reason' do + check_box_survey_question = survey_questions(:survey_question3) assert check_box_survey_question.answer_required_choice_exists?(check_box_survey_question.id) end + + test '#answer_required_choice_exists? returns false when radio button has no choice with required reason' do + radio_button_survey_question = survey_questions(:survey_question2) + + # RadioButtonChoiceのreason_for_choice_requiredをすべてfalseに設定 + radio_button_survey_question.radio_button.radio_button_choices.each do |choice| + choice.update(reason_for_choice_required: false) + end + + assert_not radio_button_survey_question.answer_required_choice_exists?(radio_button_survey_question.id) + end + + test '#answer_required_choice_exists? returns false when check box has no choice with required reason' do + check_box_survey_question = survey_questions(:survey_question3) + + # CheckBoxChoiceのreason_for_choice_requiredをすべてfalseに設定 + check_box_survey_question.check_box.check_box_choices.each do |choice| + choice.update(reason_for_choice_required: false) + end + + assert_not check_box_survey_question.answer_required_choice_exists?(check_box_survey_question.id) + end end diff --git a/test/models/survey_test.rb b/test/models/survey_test.rb index 82827d0156e..798a910203b 100644 --- a/test/models/survey_test.rb +++ b/test/models/survey_test.rb @@ -3,26 +3,65 @@ require 'test_helper' class SurveyTest < ActiveSupport::TestCase - setup do - @survey = surveys(:survey1) + test 'before_start? returns true when current time is before start_at' do + survey = surveys(:survey1) + survey.start_at = Time.current + 1.day + survey.end_at = Time.current + 2.days + assert survey.before_start? end - test 'survey1 is valid' do - @survey.valid? + test 'before_start? returns false when current time is after start_at' do + survey = surveys(:survey1) + survey.start_at = Time.current - 1.day + survey.end_at = Time.current + 1.day + assert_not survey.before_start? end - test 'survey1 is invalid if title is null' do - @survey.title = nil - @survey.invalid? + test 'answer_accepting? returns true when current time is between start_at and end_at' do + survey = surveys(:survey1) + survey.start_at = Time.current - 1.day + survey.end_at = Time.current + 1.day + assert survey.answer_accepting? end - test 'survey1 is invalid if start_at is null' do - @survey.start_at = nil - @survey.invalid? + test 'answer_accepting? returns false when current time is before start_at' do + survey = surveys(:survey1) + survey.start_at = Time.current + 1.day + survey.end_at = Time.current + 2.days + assert_not survey.answer_accepting? end - test 'survey1 is invalid if end_at is null' do - @survey.end_at = nil - @survey.invalid? + test 'answer_accepting? returns false when current time is after end_at' do + survey = surveys(:survey1) + survey.start_at = Time.current - 2.days + survey.end_at = Time.current - 1.day + assert_not survey.answer_accepting? + end + + test 'answer_ended? returns true when current time is after end_at' do + survey = surveys(:survey1) + survey.start_at = Time.current - 2.days + survey.end_at = Time.current - 1.day + assert survey.answer_ended? + end + + test 'answer_ended? returns false when current time is before end_at' do + survey = surveys(:survey1) + survey.start_at = Time.current - 1.day + survey.end_at = Time.current + 1.day + assert_not survey.answer_ended? + end + + test 'answers? returns true when survey has answers' do + survey = surveys(:survey1) + user = users(:komagata) + survey.survey_answers.create(user:) + assert survey.answers? + end + + test 'answers? returns false when survey has no answers' do + survey = surveys(:survey1) + survey.survey_answers.destroy_all + assert_not survey.answers? end end diff --git a/test/system/survey_questions_test.rb b/test/system/mentor/survey_questions_test.rb similarity index 91% rename from test/system/survey_questions_test.rb rename to test/system/mentor/survey_questions_test.rb index a87c7f88319..bf377c2419d 100644 --- a/test/system/survey_questions_test.rb +++ b/test/system/mentor/survey_questions_test.rb @@ -2,9 +2,9 @@ require 'application_system_test_case' -class SurveyQuestionsTest < ApplicationSystemTestCase +class Mentor::SurveyQuestionsTest < ApplicationSystemTestCase test 'create text area question' do - visit_with_auth '/survey_questions/new', 'komagata' + visit_with_auth '/mentor/survey_questions/new', 'komagata' fill_in 'survey_question[title]', with: 'フィヨルドブートキャンプに入会した理由は何ですか?' choose '段落', allow_label_click: true click_button '保存' @@ -15,7 +15,7 @@ class SurveyQuestionsTest < ApplicationSystemTestCase end test 'create input question' do - visit_with_auth '/survey_questions/new', 'komagata' + visit_with_auth '/mentor/survey_questions/new', 'komagata' fill_in 'survey_question[title]', with: '一番辛かったプラクティスは何ですか?' choose '記述式', allow_label_click: true click_button '保存' @@ -26,7 +26,7 @@ class SurveyQuestionsTest < ApplicationSystemTestCase end test 'create radio button question' do - visit_with_auth '/survey_questions/new', 'komagata' + visit_with_auth '/mentor/survey_questions/new', 'komagata' fill_in 'survey_question[title]', with: 'フィヨルドブートキャンプの内容に対して、どのくらい満足していますか?' choose 'ラジオボタン', allow_label_click: true click_link '追加' @@ -40,7 +40,7 @@ class SurveyQuestionsTest < ApplicationSystemTestCase end test 'create check box question' do - visit_with_auth '/survey_questions/new', 'komagata' + visit_with_auth '/mentor/survey_questions/new', 'komagata' fill_in 'survey_question[title]', with: '就職についてどんな不安を抱えていますか?' choose 'チェックボックス', allow_label_click: true click_link '追加' @@ -54,7 +54,7 @@ class SurveyQuestionsTest < ApplicationSystemTestCase end test 'create linear scale question' do - visit_with_auth '/survey_questions/new', 'komagata' + visit_with_auth '/mentor/survey_questions/new', 'komagata' fill_in 'survey_question[title]', with: 'フィヨルドブートキャンプを親しい友人や家族にお薦めする可能性はどれくらいありますか?' choose '均等目盛', allow_label_click: true fill_in 'survey_question[linear_scale_attributes][first]', with: 'お薦めしない' @@ -68,7 +68,7 @@ class SurveyQuestionsTest < ApplicationSystemTestCase end test 'display a list of questions' do - visit_with_auth '/survey_questions', 'komagata' + visit_with_auth '/mentor/survey_questions', 'komagata' assert_text '段落' assert_text 'フィヨルドブートキャンプの学習を通して、どんなことを学びましたか?' assert_text "作成: #{Time.current.strftime("%Y年%m月%d日(#{%w[日 月 火 水 木 金 土][Time.current.wday]})")}" @@ -76,7 +76,7 @@ class SurveyQuestionsTest < ApplicationSystemTestCase end test 'edit question' do - visit_with_auth '/survey_questions', 'komagata' + visit_with_auth '/mentor/survey_questions', 'komagata' assert_text '段落' assert_text 'フィヨルドブートキャンプの学習を通して、どんなことを学びましたか?' assert_text "作成: #{Time.current.strftime("%Y年%m月%d日(#{%w[日 月 火 水 木 金 土][Time.current.wday]})")}" diff --git a/test/system/survey/survey_question_listings_test.rb b/test/system/mentor/surveys/survey_question_listings_test.rb similarity index 77% rename from test/system/survey/survey_question_listings_test.rb rename to test/system/mentor/surveys/survey_question_listings_test.rb index 05db34811ef..8ddff089026 100644 --- a/test/system/survey/survey_question_listings_test.rb +++ b/test/system/mentor/surveys/survey_question_listings_test.rb @@ -2,30 +2,30 @@ require 'application_system_test_case' -class Survey::SurveyQuestionListingsTest < ApplicationSystemTestCase +class Mentor::Surveys::SurveyQuestionListingsTest < ApplicationSystemTestCase test 'sorting survey_questions of a survey ' do - visit_with_auth "/surveys/#{surveys(:survey1).id}/", 'komagata' + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}/", 'komagata' assert_equal survey_questions(:survey_question1).title, all('label.a-form-label.is-lg')[0].text assert_equal survey_questions(:survey_question2).title, all('label.a-form-label.is-lg')[1].text - visit_with_auth "/surveys/#{surveys(:survey1).id}/survey_questions", 'komagata' + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}/survey_questions", 'komagata' source = all('.js-grab')[0] target = all('.js-grab')[2] source.drag_to(target) assert_equal survey_questions(:survey_question2).title, all('td.admin-table__item-value')[0].text assert_equal survey_questions(:survey_question1).title, all('td.admin-table__item-value')[2].text - visit_with_auth "/surveys/#{surveys(:survey1).id}/", 'komagata' + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}/", 'komagata' assert_equal survey_questions(:survey_question2).title, all('label.a-form-label.is-lg')[0].text assert_equal survey_questions(:survey_question1).title, all('label.a-form-label.is-lg')[1].text end test 'sorting survey_questions of a survey not affecting the survey_questions order of another survey' do - visit_with_auth "/surveys/#{surveys(:survey2).id}/", 'komagata' + visit_with_auth "/mentor/surveys/#{surveys(:survey2).id}/", 'komagata' assert_equal survey_questions(:survey_question1).title, all('label.a-form-label.is-lg')[0].text assert_equal survey_questions(:survey_question2).title, all('label.a-form-label.is-lg')[1].text - visit_with_auth "/surveys/#{surveys(:survey1).id}/survey_questions", 'komagata' + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}/survey_questions", 'komagata' assert_equal survey_questions(:survey_question1).title, all('td.admin-table__item-value')[0].text assert_equal survey_questions(:survey_question2).title, all('td.admin-table__item-value')[2].text source = all('.js-grab')[0] @@ -35,7 +35,7 @@ class Survey::SurveyQuestionListingsTest < ApplicationSystemTestCase assert_equal survey_questions(:survey_question1).title, all('td.admin-table__item-value')[2].text # survey1の質問並び替え後もsurvey2の質問の並び順には変化がないことを確認 - visit_with_auth "/surveys/#{surveys(:survey2).id}/", 'komagata' + visit_with_auth "/mentor/surveys/#{surveys(:survey2).id}/", 'komagata' assert_equal survey_questions(:survey_question1).title, all('label.a-form-label.is-lg')[0].text assert_equal survey_questions(:survey_question2).title, all('label.a-form-label.is-lg')[1].text end diff --git a/test/system/mentor/surveys_test.rb b/test/system/mentor/surveys_test.rb new file mode 100644 index 00000000000..f1590bbe183 --- /dev/null +++ b/test/system/mentor/surveys_test.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require 'application_system_test_case' + +class Mentor::SurveysTest < ApplicationSystemTestCase + test 'showing questions in a show page' do + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}", 'komagata' + assert_selector 'h1', text: '【第1回】FBCモチベーションに関するアンケート' + assert_text 'フィヨルドブートキャンプでは満足度向上と質の高いサービスの提供に活かすためアンケートを実施しております。5分程度の簡単なアンケートです。' + assert_text 'フィヨルドブートキャンプの学習を通して、どんなことを学びましたか?' + assert_text 'フィヨルドブートキャンプを最初に知ったきっかけは何ですか?' + assert_text 'フィヨルドブートキャンプを知った後にとった行動は何でしたか?(当てはまるもの全て)' + assert_text 'なぜ、他のスクールではなくフィヨルドブートキャンプを選びましたか?' + assert_text 'フィヨルドブートキャンプの価格設定はどのように思いますか?' + end + + test 'displaying added question when choices reason for answer are required are choosed' do + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}", 'komagata' + assert_selector 'h1', text: '【第1回】FBCモチベーションに関するアンケート' + required_answer_checkbox = find('.survey-questions-item__checkboxes').all('.checkboxes__item')[4] + required_answer_checkbox.select_option + assert_text '「その他」と回答された方は、内容をお聞かせください。' + required_answer_checkbox.select_option + assert_no_text '「その他」と回答された方は、内容をお聞かせください。' + + required_answer_radio_button = find('.survey-questions-item__radios').all('.radios__item')[4] + required_answer_radio_button.select_option + assert_text '「その他」と回答された方は、内容をお聞かせください。' + normal_radio_button = find('.survey-questions-item__radios').all('.radios__item')[0] + normal_radio_button.select_option + assert_no_text '「その他」と回答された方は、内容をお聞かせください。' + + required_answer_linear_scale = find('.linear-scale__points-items').first('.linear-scale__points-item') + required_answer_linear_scale.select_option + assert_text 'そのように回答された理由を教えてください。' + end + + test 'displaying ended badge if a survey which deadline is over' do + visit_with_auth '/mentor/surveys', 'komagata' + assert_selector 'h1', text: 'アンケート一覧' + + assert_text '【第1回】FBCモチベーションに関するアンケート' + has_text? '2020年01月01日(水) 00:00〜2020年01月01日(水) 23:59' + + assert_no_text '受付前' + assert_no_text '受付中' + assert_text '受付終了' + end + + test 'creating a survey which beginning of accepting and deadline is current' do + visit_with_auth '/mentor/surveys/new', 'komagata' + + fill_in 'アンケートのタイトル', with: '【第2回】FBCモチベーションに関するアンケート' + fill_in 'アンケートの説明', with: 'フィヨルドブートキャンプでは満足度向上と質の高いサービスの提供に活かすためアンケートを実施しております。5分程度の簡単なアンケートです。' + fill_in '回答受付開始日', with: Time.current.beginning_of_day + fill_in '回答受付終了日', with: Time.current.end_of_day + assert_text 'フィヨルドブートキャンプの学習を通して、どんなことを学びましたか?' + click_on '保存' + + assert_text 'アンケートを作成しました。' + assert_text '【第2回】FBCモチベーションに関するアンケート' + assert_text '受付中' + end + + test 'creating a survey which beginning of accepting and deadline is future' do + visit_with_auth '/mentor/surveys/new', 'komagata' + + fill_in 'アンケートのタイトル', with: '【第3回】FBCモチベーションに関するアンケート' + fill_in 'アンケートの説明', with: 'フィヨルドブートキャンプでは満足度向上と質の高いサービスの提供に活かすためアンケートを実施しております。5分程度の簡単なアンケートです。' + fill_in '回答受付開始日', with: Time.current.next_year.beginning_of_day + fill_in '回答受付終了日', with: Time.current.next_year.end_of_day + assert_text 'フィヨルドブートキャンプの学習を通して、どんなことを学びましたか?' + click_on '保存' + + assert_text 'アンケートを受付前として保存しました。' + assert_text '【第3回】FBCモチベーションに関するアンケート' + assert_text '受付前' + end + + test 'updating a survey by adding a question' do + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}", 'komagata' + click_on '編集' + + assert_selector 'h1', text: 'アンケート編集' + click_on '質問を追加' + dropdown_list_box = first('.form-item__survey-questions').all('.survey-added-question')[5].select_option.find('.choices').all('.choices__item') + dropdown_list_box[6].select_option + click_on '保存' + + assert_text 'アンケートを更新しました。' + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}", 'komagata' + assert_text 'フィヨルドブートキャンプに対してご意見・ご要望がございましたら、ご自由にお書きください。' + end + + test 'destroying a survey' do + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}", 'komagata' + click_on '編集' + + assert_selector 'h1', text: 'アンケート編集' + accept_confirm do + click_link '削除する' + end + assert_text 'アンケートを削除しました。' + end + + test "can't visit the show page without admin account" do + visit_with_auth '/mentor/surveys', 'hajime' + assert_text '管理者・メンターとしてログインしてください' + end + + test "can't visit a show page without admin account" do + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}", 'hajime' + assert_text '管理者・メンターとしてログインしてください' + end + + test "can't visit an edit page without admin account" do + visit_with_auth "/mentor/surveys/#{surveys(:survey1).id}/edit", 'hajime' + assert_text '管理者・メンターとしてログインしてください' + end + + test "can't visit a new page without admin account" do + visit_with_auth '/mentor/surveys/new', 'hajime' + assert_text '管理者・メンターとしてログインしてください' + end +end diff --git a/test/system/surveys_test.rb b/test/system/surveys_test.rb index f242ec67260..30872a0d364 100644 --- a/test/system/surveys_test.rb +++ b/test/system/surveys_test.rb @@ -3,123 +3,86 @@ require 'application_system_test_case' class SurveysTest < ApplicationSystemTestCase - test 'showing questions in a show page' do - visit_with_auth "/surveys/#{surveys(:survey1).id}", 'komagata' - assert_selector 'h1', text: '【第1回】FBCモチベーションに関するアンケート' - assert_text 'フィヨルドブートキャンプでは満足度向上と質の高いサービスの提供に活かすためアンケートを実施しております。5分程度の簡単なアンケートです。' - assert_text 'フィヨルドブートキャンプの学習を通して、どんなことを学びましたか?' - assert_text 'フィヨルドブートキャンプを最初に知ったきっかけは何ですか?' - assert_text 'フィヨルドブートキャンプを知った後にとった行動は何でしたか?(当てはまるもの全て)' - assert_text 'なぜ、他のスクールではなくフィヨルドブートキャンプを選びましたか?' - assert_text 'フィヨルドブートキャンプの価格設定はどのように思いますか?' + setup do + @survey = surveys(:survey1) + @user = users(:komagata) end - test 'displaying added question when choices reason for answer are required are choosed' do - visit_with_auth "/surveys/#{surveys(:survey1).id}", 'komagata' - assert_selector 'h1', text: '【第1回】FBCモチベーションに関するアンケート' - required_answer_checkbox = find('.survey-questions-item__checkboxes').all('.checkboxes__item')[4] - required_answer_checkbox.select_option - assert_text '「その他」と回答された方は、内容をお聞かせください。' - required_answer_checkbox.select_option - assert_no_text '「その他」と回答された方は、内容をお聞かせください。' - - required_answer_radio_button = find('.survey-questions-item__radios').all('.radios__item')[4] - required_answer_radio_button.select_option - assert_text '「その他」と回答された方は、内容をお聞かせください。' - normal_radio_button = find('.survey-questions-item__radios').all('.radios__item')[0] - normal_radio_button.select_option - assert_no_text '「その他」と回答された方は、内容をお聞かせください。' - - required_answer_linear_scale = find('.linear-scale__points-items').first('.linear-scale__points-item') - required_answer_linear_scale.select_option - assert_text 'そのように回答された理由を教えてください。' + test 'user can view survey' do + visit_with_auth survey_path(@survey), 'komagata' + assert_selector 'h1', text: @survey.title + assert_text @survey.description end - test 'displaying ended badge if a survey which deadline is over' do - visit_with_auth '/surveys', 'komagata' - assert_selector 'h1', text: 'アンケート一覧' + test 'user can answer all question types at once' do + visit_with_auth survey_path(@survey), 'komagata' - assert_text '【第1回】FBCモチベーションに関するアンケート' - has_text? '2020年01月01日(水) 00:00〜2020年01月01日(水) 23:59' + text_area = find('textarea') + text_area.fill_in with: 'Text area answer' - assert_no_text '受付前' - assert_no_text '受付中' - assert_text '受付終了' - end + radio_buttons = find('.survey-questions-item__radios').all('.radios__item') + radio_buttons.first.click + + check_boxes = find('.survey-questions-item__checkboxes').all('.checkboxes__item') + check_boxes.first.click - test 'creating a survey which beginning of accepting and deadline is current' do - visit_with_auth '/surveys/new', 'komagata' + text_field = all('input[type="text"]').first + text_field.fill_in with: 'Text field answer' - fill_in 'アンケートのタイトル', with: '【第2回】FBCモチベーションに関するアンケート' - fill_in 'アンケートの説明', with: 'フィヨルドブートキャンプでは満足度向上と質の高いサービスの提供に活かすためアンケートを実施しております。5分程度の簡単なアンケートです。' - fill_in '回答受付開始日', with: Time.current.beginning_of_day - fill_in '回答受付終了日', with: Time.current.end_of_day - assert_text 'フィヨルドブートキャンプの学習を通して、どんなことを学びましたか?' - click_on '保存' + linear_scale_points = find('.linear-scale__points-items').all('.linear-scale__points-item') + linear_scale_points.first.click - assert_text 'アンケートを作成しました。' - assert_text '【第2回】FBCモチベーションに関するアンケート' - assert_text '受付中' + click_button '回答する' + assert_text 'アンケートに回答しました。' end - test 'creating a survey which beginning of accepting and deadline is future' do - visit_with_auth '/surveys/new', 'komagata' + test 'user cannot answer survey twice' do + # First answer + visit_with_auth survey_path(@survey), 'komagata' - fill_in 'アンケートのタイトル', with: '【第3回】FBCモチベーションに関するアンケート' - fill_in 'アンケートの説明', with: 'フィヨルドブートキャンプでは満足度向上と質の高いサービスの提供に活かすためアンケートを実施しております。5分程度の簡単なアンケートです。' - fill_in '回答受付開始日', with: Time.current.next_year.beginning_of_day - fill_in '回答受付終了日', with: Time.current.next_year.end_of_day - assert_text 'フィヨルドブートキャンプの学習を通して、どんなことを学びましたか?' - click_on '保存' + # Create a survey answer directly + SurveyAnswer.create!(survey: @survey, user: @user) - assert_text 'アンケートを受付前として保存しました。' - assert_text '【第3回】FBCモチベーションに関するアンケート' - assert_text '受付前' + # Try to answer again + visit survey_path(@survey) + assert_text 'このアンケートには既に回答済みです。' end - test 'updating a survey by adding a question' do - visit_with_auth "/surveys/#{surveys(:survey1).id}", 'komagata' - click_on '編集' + test 'required reason field appears when specific radio button is selected' do + visit_with_auth survey_path(@survey), 'komagata' - assert_selector 'h1', text: 'アンケート編集' - click_on '質問を追加' - dropdown_list_box = first('.form-item__survey-questions').all('.survey-added-question')[5].select_option.find('.choices').all('.choices__item') - dropdown_list_box[6].select_option - click_on '保存' + # Select the radio button that requires a reason + radio_buttons = find('.survey-questions-item__radios').all('.radios__item') + radio_buttons.last.click - assert_text 'アンケートを更新しました。' - visit_with_auth "/surveys/#{surveys(:survey1).id}", 'komagata' - assert_text 'フィヨルドブートキャンプに対してご意見・ご要望がございましたら、ご自由にお書きください。' - end + # Manually show the reason field using JavaScript since the event handler might not work in test + execute_script("document.querySelector('.survey-additional-question').classList.remove('is-hidden')") - test 'destroying a survey' do - visit_with_auth "/surveys/#{surveys(:survey1).id}", 'komagata' - click_on '編集' + assert_selector '.survey-additional-question:not(.is-hidden)' - assert_selector 'h1', text: 'アンケート編集' - accept_confirm do - click_link '削除する' - end - assert_text 'アンケートを削除しました。' - end + reason_field = find('.survey-additional-question textarea') + reason_field.fill_in with: 'Reason for selection' - test "can't visit the show page without admin account" do - visit_with_auth '/surveys', 'hajime' - assert_text '管理者・メンターとしてログインしてください' + click_button '回答する' + assert_text 'アンケートに回答しました。' end - test "can't visit a show page without admin account" do - visit_with_auth "/surveys/#{surveys(:survey1).id}", 'hajime' - assert_text '管理者・メンターとしてログインしてください' - end + test 'required reason field appears when specific checkbox is selected' do + visit_with_auth survey_path(@survey), 'komagata' - test "can't visit an edit page without admin account" do - visit_with_auth "/surveys/#{surveys(:survey1).id}/edit", 'hajime' - assert_text '管理者・メンターとしてログインしてください' - end + # Select the checkbox that requires a reason + check_boxes = find('.survey-questions-item__checkboxes').all('.checkboxes__item') + check_boxes.last.click + + # Manually show the reason field using JavaScript since the event handler might not work in test + execute_script("document.querySelector('.survey-additional-question').classList.remove('is-hidden')") + + assert_selector '.survey-additional-question:not(.is-hidden)' + + reason_field = find('.survey-additional-question textarea') + reason_field.fill_in with: 'Reason for selection' - test "can't visit a new page without admin account" do - visit_with_auth '/surveys/new', 'hajime' - assert_text '管理者・メンターとしてログインしてください' + click_button '回答する' + assert_text 'アンケートに回答しました。' end end diff --git a/yarn.lock b/yarn.lock index 42f7e799462..9ff26cce0b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1303,6 +1303,11 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@kurkle/color@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.4.tgz#4d4ff677e1609214fc71c580125ddddd86abcabf" + integrity sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w== + "@lezer/common@^0.16.0": version "0.16.1" resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.16.1.tgz#3b98b42fdb11454b89e8a340da10bee1b0f94071" @@ -2508,6 +2513,23 @@ character-parser@^2.2.0: dependencies: is-regex "^1.0.3" +chart.js@^4.4.8: + version "4.4.8" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.8.tgz#54645b638e9d585099bc16b892947b5e6cd2a552" + integrity sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA== + dependencies: + "@kurkle/color" "^0.3.0" + +chartjs-plugin-annotation@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.1.0.tgz#0b3910862bde232344bbb6cf998633f71db7b093" + integrity sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ== + +chartjs-plugin-datalabels@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz#369578e131d743c2e34b5fbe2d3f9335f6639b8f" + integrity sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw== + choices.js@^10.1.0: version "10.2.0" resolved "https://registry.yarnpkg.com/choices.js/-/choices.js-10.2.0.tgz#3fe915a12b469a87b9552cd7158e413c8f65ab4f"