- content_for :head do meta name="turbo-cache-control" content="no-cache" = turbo_stream_from @respondent_category, "tasks" .container-fluid .row .col-md-3 = render partial: "respondent_categories/sidebar" .col-md-9.d-flex.flex-column.overflow-hidden style="height: calc(100vh - 100px);" = render_breadcrumbs([ \ { name: 'Home', path: projects_path }, \ { name: @project.title, path: project_path(@project) }, \ { name: @respondent_category.name, path: [@project, @respondent_category] }, \ { name: "Queries" } \ ]) / Top Header Area .d-flex.align-items-center.justify-content-between.mb-4 .fs-30.semi-bold = @respondent_category.name .fs-20.medium.text-muted = t(:all_queries) / Vue App Container (Now a flex column itself to manage the table and footer) div#interviewQuestions.d-flex.flex-column.flex-grow-1.overflow-hidden v-cloak="" data-controller="interview-questions" / Scrollable Table Container .table-container.flex-grow-1.overflow-auto ref="scrollContainer" style="min-height: 0;" table.table.table-borderless-head.mb-0 / Sticky Table Header thead.table-light.fs-12.position-sticky.top-0 style="z-index: 5;" tr th Name th.text-center Sections th.text-center Order th tbody.fs-12 tr v-for="(question, index) in interviewQuestions" :key="question.id" :data-question-id="question.id" / Question Name & Edit td.align-middle style="max-width: 40vw;" .d-flex.align-items-center v-if="currentEditingQuestion != question.id" | {{ question.question }} a href="javascript:;" @click="startQuestionEdit(question.id)" vue-feather type="edit-2" class="text-primary mt-1 ms-2" size="12" div v-if="currentEditingQuestion == question.id" .d-flex.align-items-center .w-75 input.input-sm.form-control type="text" :id="'question_edit_' + question.id" :value="question.question" @keyup.enter="editQuestion(question.id, index, question.respondent_category_id)" @keyup.esc="stopQuestionEdit()" style="font-size: 0.75rem;" a.ms-2 href="javascript:;" @click="editQuestion(question.id, index, question.respondent_category_id)" vue-feather type="check" class="text-primary mt-1" size="18" a.ms-2 href="javascript:;" @click="stopQuestionEdit()" vue-feather type="x" class="text-primary mt-1" size="18" small Press Enter to save / Section Assignment td.align-middle .d-flex.align-items-center.justify-content-between div v-if="question.master_section_ids && question.master_section_ids.length > 0" | {{ displaySectionNames(question.master_section_ids) }} .text-primary v-if="!question.master_section_ids || question.master_section_ids.length == 0" | No Sections added .ms-2 div button.btn.btn-sm.fs-10 v-bind:class="{'btn-secondary': question.master_section_ids && question.master_section_ids.length > 0, 'btn-primary': !question.master_section_ids || question.master_section_ids.length == 0}" style="font-weight: 600 ; white-space: nowrap; padding: 5px; padding-top: 0px; padding-bottom: 0px;" @click="openAssignSectionModal(question.id)" | {{ question.master_section_ids && question.master_section_ids.length > 0 ? 'Edit Sections' : 'Add Sections' }} / Re-order Question td.align-middle.text-center button.btn.btn-sm.bg-secondary.me-1 @click="reorderQuestion(question.id, 'up')" vue-feather type="arrow-up" class="text-primary mt-1" size="12" button.btn.btn-sm.bg-secondary @click="reorderQuestion(question.id, 'down')" vue-feather type="arrow-down" class="text-primary mt-1" size="12" / Delete Question td.align-middle.text-center a href='javascript:;' @click="deleteQuestion(question.id, question.respondent_category_id)" vue-feather type="trash-2" class="text-primary mt-1" size="14" / STICKY FOOTER ACTION AREA .mt-auto.bg-white.pt-3.pb-4.border-top style="box-shadow: 0 -10px 15px -10px rgba(0,0,0,0.05);" .d-flex.align-items-center.justify-content-between.flex-nowrap / Left Side: Add Query .add-item-wrapper.flex-grow-1.me-4 button.btn.btn-outline-primary.d-flex.align-items-center type="button" @click="openAddQuestionModal()" vue-feather type="plus" class="me-2" size="18" span Add New Query / Right Side: Bulk Actions .action-buttons-wrapper.d-flex.align-items-center.gap-2.flex-shrink-0 button.btn.btn-outline-primary.btn-sm.d-flex.align-items-center type="button" data-bs-toggle="modal" data-bs-target="#importModal" i.bi.bi-file-earmark-plus.me-1 span Import Queries CSV = render "interview_questions/dg_queries_import_button", respondent_category: @respondent_category, queries_generating: @queries_generating / Modals & Toasts div class="modal fade" id="assignSectionDialog" div class="modal-dialog modal-dialog-centered" div class="modal-content" div class="modal-body p-4" .d-flex.align-items-center.justify-content-between .fs-24.medium Select Sections .d-flex.align-items-center button class="btn btn-light fs-12 px-3 py-2" style="font-weight: 600;" v-on:click="closeAssignSectionModal()" Cancel button class="btn btn-primary ms-2 fs-12 px-3 py-2" style="font-weight: 600;" v-on:click="assignSection()" Submit .fs-12.text-primary.mt-1 | {{ isModalOpen ? sectionAssignmentQuestion.question : "Question" }} .my-3 select class="form-select" id="sectionSelect" multiple="multiple" style="width: 100%" option v-for="sectionId in sectionOrder" v-bind:value="sectionId" | {{ masterSections[sectionId].name }} div class="modal fade" id="addQuestionModal" div class="modal-dialog modal-dialog-centered" div class="modal-content" div class="modal-body p-4" .d-flex.align-items-center.justify-content-between.mb-3 .fs-24.medium Add New Query .mb-3 label.form-label.fw-bold.fs-14 Query Text input.form-control type="text" id="newQuestionInput" v-model="newQuestionText" @keyup.enter="addNewQuestion()" placeholder="Enter your query here..." .mb-4 label.form-label.fw-bold.fs-14 Assign Sections (Optional) select class="form-select" id="newQuestionSectionSelect" multiple="multiple" style="width: 100%" option v-for="sectionId in sectionOrder" v-bind:value="sectionId" | {{ masterSections[sectionId].name }} .d-flex.justify-content-end button.btn.btn-light.px-4.me-2 @click="closeAddQuestionModal()" Cancel button.btn.btn-primary.px-4 @click="addNewQuestion()" Save .toast-container.position-fixed.top-0.end-0.p-3 .toast id="questionSuccessToast" role="alert" aria-live="assertive" aria-atomic="true" .toast-header i.feather-18.text-success data-feather="check-circle" strong.ms-2.me-auto Success! button.btn-close.btn-sm data-bs-dismiss="toast" aria-label="Close" .toast-body | {{ successMessage }} .toast-container.position-fixed.top-0.end-0.p-3 .toast id="questionErrorToast" role="alert" aria-live="assertive" aria-atomic="true" .toast-header i.feather-18.text-danger data-feather="x-octagon" strong.ms-2.me-auto Error! button.btn-close.btn-sm data-bs-dismiss="toast" aria-label="Close" .toast-body | {{ errorMessage }} .modal.fade#importModal tabindex="-1" aria-labelledby="importModalLabel" aria-hidden="true" .modal-dialog .modal-content = form_with url: import_project_respondent_category_interview_questions_path(@project, @respondent_category), multipart: true, local: true do |f| .modal-header h5.modal-title#importModalLabel Import Questions button.btn-close type="button" data-bs-dismiss="modal" aria-label="Close" .modal-body .alert.alert-info.fs-14 | Upload a CSV file. The first column must contain the questions. .mb-3 = f.label :file, "Select .csv File", class: "form-label fw-bold fs-14" = f.file_field :file, class: "form-control", accept: ".csv, text/csv", required: true .modal-footer button.btn.btn-light type="button" data-bs-dismiss="modal" Cancel = f.submit "Upload & Import", class: "btn btn-primary" .modal.fade#importDGModal tabindex="-1" aria-labelledby="importDGModalLabel" aria-hidden="true" .modal-dialog .modal-content = form_with url: import_with_dg_project_respondent_category_interview_questions_path(@project, @respondent_category), multipart: true, local: true do |f| .modal-header h5.modal-title#importDGModalLabel Import Discussion Guide (DG) button.btn-close type="button" data-bs-dismiss="modal" aria-label="Close" .modal-body .alert.alert-info.fs-14 | Upload a .docx file containing your Discussion Guide. .mb-3 = f.label :file, "Select .docx File", class: "form-label fw-bold fs-14" = f.file_field :file, class: "form-control", accept: ".docx, application/vnd.openxmlformats-officedocument.wordprocessingml.document", required: true .modal-footer button.btn.btn-light type="button" data-bs-dismiss="modal" Cancel = f.submit "Upload & Generate", class: "btn btn-primary", data: { turbo_submits_with: "Uploading..." }