= turbo_stream_from @respondent_category, "tasks" .container-fluid#transcriptSections v-cloak="" data-controller="sections-list" .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: "Sections" } \ ]) / 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 | All Sections / Sticky List Header .list-header.d-flex.align-items-center.fs-12.fw-bold.bg-light.p-3.rounded-top.position-sticky.top-0 style="z-index: 10;" .col-primary.flex-grow-1 Name .col-actions.text-center.px-3 colspan="2" Actions / Main Draggable List Container .list-container.flex-grow-1.overflow-auto ref="scrollContainer" style="min-height: 0;" .dragArea.rounded-bottom.border-top-0 draggable v-model="masterSections" :animation="200" @end="onDragEnd" handle=".drag-handle" item-key="id" tag="div" class="draggable-list" :scroll="scrollElement" :scrollSensitivity="100" :scrollSpeed="20" :forceFallback="true" :delay="1" scroll-container=".list-container" template #item="{ element: section, index }" .list-row.d-flex.align-items-center.p-3.border-bottom.fs-12 :key="section.id" :data-section-id="section.id" .col-primary.flex-grow-1 .d-flex.align-items-center .drag-handle.me-2.cursor-move vue-feather type="menu" class="text-muted mt-1" size="14" div v-if="currentEditingSection != section.id" | {{ section.name }} div v-if="currentEditingSection == section.id" .d-flex.align-items-center .flex-grow-1.me-2 input.form-control type="text" :id="'section_edit_' + section.id" :value="section.name" data-project-id="#{@respondent_category.project_id}" data-respondent-category-id="#{@respondent_category.id}" @keyup.enter="editSection(section.id, index)" @keyup.esc="stopSectionEdit()" button.btn.btn-clear type="button" @click="stopSectionEdit()" vue-feather type="x" class="text-primary" size="18" small Press Enter to save .col-actions.text-center.px-3 .d-flex.justify-content-center div v-if="currentEditingSection != section.id" button.btn.btn-clear.me-2 type="button" @click="startSectionEdit(section.id)" vue-feather type="edit" class="text-primary" size="14" div v-if="currentEditingSection == section.id" .rounded.px-2.py-1.me-2 style="background-color: #F44174;" @click="editSection(section.id, index)" vue-feather type="check" class="text-white mt-1" size="14" button.btn.btn-clear type="button" @click="deleteSection(section.id)" vue-feather type="trash-2" class="text-primary" 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 Section .add-item-wrapper.flex-grow-1.me-4.position-relative button.btn.btn-outline-primary.d-flex.align-items-center type="button" v-if="sectionInputEnabled == false" @click="toggleSectionInput()" vue-feather type="plus" class="me-2" size="18" span Add New Section .new-item-input v-if="sectionInputEnabled == true" ref="newSectionInputContainer" .d-flex.align-items-center .flex-grow-1.me-2 input.form-control type="text" id="sectionAddInput" data-project-id="#{@respondent_category.project_id}" data-respondent-category-id="#{@respondent_category.id}" @keyup.enter="addNewSection()" @keyup.esc="toggleSectionInput()" placeholder="Enter section name" ref="newSectionInput" button.btn.btn-primary.d-flex.align-items-center.me-2 type="button" @click="addNewSection()" vue-feather type="check" class="text-white me-1" size="14" span Save button.btn.btn-secondary.d-flex.align-items-center type="button" @click="toggleSectionInput()" vue-feather type="x" class="me-1" size="14" span Cancel / Absolutely positioned tip to completely prevent layout shift small.text-muted.position-absolute.start-0.fs-12 style="bottom: -22px;" Tip: Press Enter to save / Right Side: Bulk Actions .action-buttons-wrapper.d-flex.align-items-center.gap-2.flex-shrink-0 = link_to download_sections_project_respondent_category_path(@project, @respondent_category), class: "btn btn-outline-primary btn-sm d-flex align-items-center" do vue-feather type="download" class="me-2" size="14" | Download button.btn.btn-outline-primary.btn-sm.d-flex.align-items-center type="button" data-bs-toggle="modal" data-bs-target="#importModal" vue-feather type="file-plus" class="me-2" size="14" span Import CSV = render "respondent_categories/dg_sections_import_button", respondent_category: @respondent_category, sections_generating: @sections_generating .toast-container.position-fixed.top-0.end-0.p-3 .toast id="sectionSuccessToast" 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="sectionErrorToast" 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_master_sections_path, multipart: true, local: true do |f| = f.hidden_field :respondent_category_id, value: @respondent_category.id = f.hidden_field :project_id, value: @project.id .modal-header h5.modal-title#importModalLabel Import Sections 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 section names. .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_master_sections_path, multipart: true, local: true do |f| = f.hidden_field :respondent_category_id, value: @respondent_category.id = f.hidden_field :project_id, value: @project.id .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..." }