1740 lines
No EOL
74 KiB
Text
1740 lines
No EOL
74 KiB
Text
@model ResponseQuestionnaireViewModel
|
|
@{
|
|
ViewData["Title"] = "DisplayQuestionnaire";
|
|
Layout = "~/Views/Shared/_QuestionnaireResponse.cshtml";
|
|
}
|
|
|
|
<style>
|
|
/* Next Generation UI Enhancements */
|
|
@@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
|
/* Simplified CSS Variables */
|
|
:root {
|
|
--bg-primary: #0e1419;
|
|
--bg-secondary: #141b23;
|
|
--bg-tertiary: #1a212a;
|
|
--surface-primary: rgba(20, 28, 38, 0.75);
|
|
--surface-secondary: rgba(26, 35, 47, 0.55);
|
|
--surface-glass: rgba(255, 255, 255, 0.02);
|
|
--text-primary: #e2eaf0;
|
|
--text-secondary: #b4c1cd;
|
|
--text-muted: #8494a6;
|
|
--accent-primary: #4a90a4;
|
|
--accent-secondary: #6ba3b5;
|
|
--accent-tertiary: #5a7a8a;
|
|
--accent-gradient: linear-gradient(135deg, #4a90a4 0%, #6ba3b5 100%);
|
|
--border-subtle: rgba(255, 255, 255, 0.06);
|
|
--border-accent: rgba(74, 144, 164, 0.25);
|
|
--shadow-soft: 0 6px 24px rgba(0, 0, 0, 0.25);
|
|
--shadow-medium: 0 12px 36px rgba(0, 0, 0, 0.3);
|
|
--shadow-strong: 0 18px 48px rgba(0, 0, 0, 0.35);
|
|
--glow-accent: 0 0 30px rgba(74, 144, 164, 0.12);
|
|
--blur-light: blur(10px);
|
|
--blur-heavy: blur(16px);
|
|
}
|
|
|
|
/* Advanced Keyframes */
|
|
@@keyframes morphIn {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translateY(20px) scale(0.95);
|
|
filter: blur(8px);
|
|
}
|
|
|
|
100% {
|
|
opacity: 1;
|
|
transform: translateY(0) scale(1);
|
|
filter: blur(0);
|
|
}
|
|
}
|
|
|
|
@@keyframes glowPulse {
|
|
0%, 100% {
|
|
box-shadow: var(--glow-accent);
|
|
}
|
|
|
|
50% {
|
|
box-shadow: 0 0 45px rgba(74, 144, 164, 0.18);
|
|
}
|
|
}
|
|
|
|
@@keyframes floatUp {
|
|
0%, 100% {
|
|
transform: translateY(0px);
|
|
}
|
|
|
|
50% {
|
|
transform: translateY(-4px);
|
|
}
|
|
}
|
|
|
|
@@keyframes shimmer {
|
|
0% {
|
|
background-position: -200% center;
|
|
}
|
|
|
|
100% {
|
|
background-position: 200% center;
|
|
}
|
|
}
|
|
|
|
/* Base Styles */
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
html, body {
|
|
height: 100%;
|
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
|
|
}
|
|
|
|
body {
|
|
background: radial-gradient(circle at 20% 10%, rgba(74, 144, 164, 0.08) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(106, 163, 181, 0.06) 0%, transparent 50%), linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
|
|
color: var(--text-primary);
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* Enhanced Typography */
|
|
h4, h5, h6 {
|
|
color: var(--text-primary);
|
|
font-weight: 600;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
p, label {
|
|
color: var(--text-secondary);
|
|
line-height: 1.6;
|
|
font-weight: 400;
|
|
}
|
|
|
|
/* Main Container */
|
|
.QuestionContainer {
|
|
padding: 60px 0 100px;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/* Hero Card - Enhanced Glassmorphism */
|
|
.hero.container.card {
|
|
padding: 40px;
|
|
border: 1px solid var(--border-subtle);
|
|
background: linear-gradient(135deg, var(--surface-primary) 0%, var(--surface-secondary) 100%);
|
|
backdrop-filter: var(--blur-light);
|
|
-webkit-backdrop-filter: var(--blur-light);
|
|
border-radius: 24px;
|
|
box-shadow: var(--shadow-strong);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hero.container.card::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: var(--accent-gradient);
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.hero.container.card::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: -50%;
|
|
left: -50%;
|
|
width: 200%;
|
|
height: 200%;
|
|
background: radial-gradient(circle, rgba(74, 144, 164, 0.02) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
animation: floatUp 6s ease-in-out infinite;
|
|
}
|
|
|
|
/* Enhanced Form Controls */
|
|
.form-control {
|
|
width: 100%;
|
|
margin-top: 12px;
|
|
color: var(--text-primary);
|
|
background: var(--surface-glass);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: 16px;
|
|
padding: 16px 20px;
|
|
font-size: 15px;
|
|
font-weight: 400;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
backdrop-filter: blur(8px);
|
|
position: relative;
|
|
}
|
|
|
|
.form-control:focus {
|
|
background: rgba(26, 42, 64, 0.8);
|
|
border-color: var(--accent-primary);
|
|
box-shadow: 0 0 0 4px rgba(0, 212, 170, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
outline: none;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.form-control::placeholder {
|
|
color: var(--text-muted);
|
|
opacity: 0.8;
|
|
}
|
|
|
|
/* Other Option Styling - Enhanced with conditional visibility */
|
|
.other-text-container {
|
|
margin-top: 12px;
|
|
|
|
animation: morphIn 0.3s ease;
|
|
display: none !important; /* Initially hidden */
|
|
}
|
|
|
|
.other-text-container.show {
|
|
display: block !important;
|
|
}
|
|
|
|
.other-text-input {
|
|
background: var(--surface-glass);
|
|
border: 1px solid var(--border-subtle);
|
|
color: var(--text-primary);
|
|
border-radius: 12px;
|
|
padding: 12px 16px;
|
|
font-size: 14px;
|
|
resize: vertical;
|
|
min-height: 80px;
|
|
}
|
|
|
|
.other-text-input:focus {
|
|
border-color: var(--accent-primary);
|
|
box-shadow: 0 0 0 3px rgba(74, 144, 164, 0.15);
|
|
background: rgba(26, 42, 64, 0.8);
|
|
}
|
|
|
|
/* Enhanced styling for other option indicators */
|
|
|
|
|
|
.other-option-container::before {
|
|
content: "OTHER";
|
|
position: absolute;
|
|
top: -8px;
|
|
right: 12px;
|
|
background: var(--warning-color);
|
|
color: white;
|
|
padding: 2px 8px;
|
|
border-radius: 6px;
|
|
font-size: 0.7rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.5px;
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
/* Next-Gen Buttons */
|
|
.btn {
|
|
border-radius: 16px;
|
|
padding: 14px 24px;
|
|
border: none;
|
|
font-weight: 500;
|
|
font-size: 14px;
|
|
letter-spacing: 0.02em;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
position: relative;
|
|
overflow: hidden;
|
|
backdrop-filter: blur(8px);
|
|
}
|
|
|
|
.btn::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
|
transition: left 0.5s;
|
|
}
|
|
|
|
.btn:hover::before {
|
|
left: 100%;
|
|
}
|
|
|
|
.btn-sm {
|
|
padding: 12px 20px;
|
|
border-radius: 14px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: var(--accent-gradient);
|
|
color: white;
|
|
box-shadow: var(--shadow-soft), inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
|
position: relative;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: var(--shadow-medium), var(--glow-accent), inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
.btn-primary:active {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: var(--surface-primary);
|
|
border: 1px solid var(--border-subtle);
|
|
color: var(--text-secondary);
|
|
backdrop-filter: blur(12px);
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: var(--surface-secondary);
|
|
border-color: var(--border-accent);
|
|
transform: translateY(-1px);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.btn-info {
|
|
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Enhanced Stepper */
|
|
.stepper {
|
|
position: sticky;
|
|
top: 32px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
align-items: stretch;
|
|
z-index: 10;
|
|
}
|
|
|
|
.step-indicator {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
width: 100%;
|
|
max-width: 280px;
|
|
padding: 16px 20px;
|
|
border-radius: 20px;
|
|
background: var(--surface-glass);
|
|
color: var(--text-muted);
|
|
border: 1px solid var(--border-subtle);
|
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
backdrop-filter: var(--blur-light);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.step-indicator::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: var(--accent-gradient);
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
z-index: -1;
|
|
}
|
|
|
|
.step-indicator .step-number {
|
|
display: grid;
|
|
place-items: center;
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
background: var(--surface-secondary);
|
|
border: 1px solid var(--border-subtle);
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
flex: 0 0 auto;
|
|
transition: all 0.3s ease;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.step-indicator .step-label {
|
|
font-size: 14px;
|
|
letter-spacing: 0.02em;
|
|
font-weight: 500;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.step-indicator.active {
|
|
color: white;
|
|
border-color: var(--accent-primary);
|
|
background: var(--surface-primary);
|
|
box-shadow: var(--glow-accent);
|
|
animation: glowPulse 3s ease-in-out infinite;
|
|
}
|
|
|
|
.step-indicator.active::before {
|
|
opacity: 0.15;
|
|
}
|
|
|
|
.step-indicator.active .step-number {
|
|
background: var(--accent-gradient);
|
|
border-color: var(--accent-secondary);
|
|
color: white;
|
|
box-shadow: 0 4px 16px rgba(0, 212, 170, 0.3);
|
|
}
|
|
|
|
/* Enhanced Steps */
|
|
.step {
|
|
border: 1px solid var(--border-subtle);
|
|
background: var(--surface-glass);
|
|
border-radius: 24px;
|
|
padding: 32px;
|
|
margin-bottom: 24px;
|
|
box-shadow: var(--shadow-soft);
|
|
animation: morphIn 0.6s cubic-bezier(0.4, 0, 0.2, 1) both;
|
|
backdrop-filter: var(--blur-light);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.step::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: var(--accent-gradient);
|
|
opacity: 0.4;
|
|
}
|
|
|
|
.step p {
|
|
font-size: 16px;
|
|
margin-bottom: 24px;
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Enhanced Rating System */
|
|
.rating {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 16px;
|
|
padding: 8px 0;
|
|
}
|
|
|
|
.rating .rating-item {
|
|
display: inline-flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 16px 20px;
|
|
border-radius: 16px;
|
|
background: var(--surface-glass);
|
|
border: 1px solid var(--border-subtle);
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
backdrop-filter: blur(8px);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.rating .rating-item:hover {
|
|
background: var(--surface-secondary);
|
|
border-color: var(--accent-primary);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.rating .rating-label {
|
|
color: var(--text-primary);
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.rating .rating-star {
|
|
font-size: 32px;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.3));
|
|
}
|
|
|
|
.rating .rating-star:hover {
|
|
transform: translateY(-2px) scale(1.1);
|
|
color: #fbbf24;
|
|
filter: drop-shadow(0 4px 16px rgba(251, 191, 36, 0.4));
|
|
}
|
|
|
|
.rating .rating-star.selected {
|
|
color: #f59e0b;
|
|
filter: drop-shadow(0 4px 16px rgba(245, 158, 11, 0.6));
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
/* Enhanced Likert Scale */
|
|
.likert {
|
|
border-radius: 20px;
|
|
padding: 24px;
|
|
background: var(--surface-glass);
|
|
border: 1px solid var(--border-accent);
|
|
box-shadow: var(--shadow-soft);
|
|
backdrop-filter: var(--blur-light);
|
|
position: relative;
|
|
}
|
|
|
|
.likert::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: var(--accent-gradient);
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.likert .responses {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
margin-top: 16px;
|
|
}
|
|
|
|
.likert .likert-option {
|
|
flex: 1;
|
|
min-width: 140px;
|
|
text-align: center;
|
|
}
|
|
|
|
.likert label {
|
|
display: block;
|
|
cursor: pointer;
|
|
padding: 16px 20px;
|
|
background: var(--surface-secondary);
|
|
border: 1px solid var(--border-subtle);
|
|
border-radius: 16px;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
backdrop-filter: blur(8px);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.likert label::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: var(--accent-gradient);
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
z-index: -1;
|
|
}
|
|
|
|
.likert label:hover {
|
|
border-color: var(--accent-primary);
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 8px 24px rgba(0, 212, 170, 0.15);
|
|
}
|
|
|
|
.likert input[type="radio"]:checked + label {
|
|
background: var(--surface-primary);
|
|
color: white;
|
|
border-color: var(--accent-primary);
|
|
box-shadow: var(--glow-accent);
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.likert input[type="radio"]:checked + label::before {
|
|
opacity: 0.2;
|
|
}
|
|
|
|
/* Enhanced Matrix */
|
|
.matrix-question {
|
|
--mx-bg: var(--surface-glass);
|
|
--mx-head: var(--surface-primary);
|
|
--mx-cell: var(--surface-secondary);
|
|
}
|
|
|
|
.matrix-question .table {
|
|
width: 100%;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
overflow: hidden;
|
|
border-radius: 20px;
|
|
background: var(--mx-bg);
|
|
box-shadow: var(--shadow-soft);
|
|
backdrop-filter: var(--blur-light);
|
|
}
|
|
|
|
.matrix-question th, .matrix-question td {
|
|
border: 1px solid var(--border-subtle);
|
|
color: var(--text-primary);
|
|
padding: 16px 20px;
|
|
}
|
|
|
|
.matrix-question th {
|
|
background: var(--mx-head);
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
|
|
.matrix-question td {
|
|
background: var(--mx-cell);
|
|
text-align: center;
|
|
}
|
|
|
|
.matrix-choice {
|
|
display: block;
|
|
padding: 12px 16px;
|
|
border-radius: 12px;
|
|
border: 1px solid var(--border-subtle);
|
|
background: var(--surface-glass);
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
color: var(--text-primary);
|
|
font-weight: 500;
|
|
backdrop-filter: blur(8px);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.matrix-choice:hover {
|
|
border-color: var(--accent-primary);
|
|
background: var(--surface-primary);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.matrix-radio:checked + .matrix-choice {
|
|
background: var(--accent-gradient);
|
|
border-color: var(--accent-secondary);
|
|
color: white;
|
|
box-shadow: 0 4px 16px rgba(0, 212, 170, 0.3);
|
|
}
|
|
|
|
/* Enhanced Image Cards */
|
|
.card-deck {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
gap: 20px;
|
|
padding: 8px 0;
|
|
}
|
|
|
|
.card-deck .card {
|
|
cursor: pointer;
|
|
padding: 0;
|
|
border: 1px solid var(--border-subtle);
|
|
background: var(--surface-glass);
|
|
border-radius: 20px;
|
|
overflow: hidden;
|
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
box-shadow: var(--shadow-soft);
|
|
backdrop-filter: var(--blur-light);
|
|
position: relative;
|
|
}
|
|
|
|
.card-deck .card::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: var(--accent-gradient);
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
z-index: 1;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.card-deck .card:hover {
|
|
transform: translateY(-4px) scale(1.02);
|
|
border-color: var(--accent-primary);
|
|
box-shadow: var(--shadow-medium), var(--glow-accent);
|
|
}
|
|
|
|
.card-deck .image-card.selected {
|
|
border: 2px solid var(--accent-primary);
|
|
box-shadow: var(--glow-accent);
|
|
transform: translateY(-2px) scale(1.01);
|
|
}
|
|
|
|
.card-deck .image-card.selected::before {
|
|
opacity: 0.1;
|
|
}
|
|
|
|
.card-deck img {
|
|
width: 100%;
|
|
height: 160px;
|
|
object-fit: cover;
|
|
display: block;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.card-deck .card:hover img {
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
/* Enhanced Ranking */
|
|
.sortable-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
gap: 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.draggable-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 16px;
|
|
padding: 20px 24px;
|
|
margin: 0;
|
|
background: var(--surface-glass);
|
|
border: 1px solid var(--border-subtle);
|
|
color: var(--text-primary);
|
|
border-radius: 16px;
|
|
cursor: grab;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
backdrop-filter: var(--blur-light);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.draggable-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 4px;
|
|
background: var(--accent-gradient);
|
|
transform: scaleY(0);
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.draggable-item:hover {
|
|
background: var(--surface-secondary);
|
|
border-color: var(--accent-primary);
|
|
transform: translateX(8px);
|
|
box-shadow: var(--shadow-soft);
|
|
}
|
|
|
|
.draggable-item:hover::before {
|
|
transform: scaleY(1);
|
|
}
|
|
|
|
.draggable-item.selected {
|
|
background: var(--surface-primary);
|
|
border-color: var(--accent-primary);
|
|
box-shadow: var(--glow-accent);
|
|
transform: translateX(12px);
|
|
}
|
|
|
|
.draggable-item.selected::before {
|
|
transform: scaleY(1);
|
|
}
|
|
|
|
.answer-index {
|
|
font-weight: 700;
|
|
opacity: 0.9;
|
|
margin-right: 12px;
|
|
color: var(--accent-primary);
|
|
}
|
|
|
|
/* Enhanced Form Check */
|
|
.form-check {
|
|
margin-bottom: 16px;
|
|
position: relative;
|
|
}
|
|
|
|
.form-check-input {
|
|
margin-right: 12px;
|
|
accent-color: var(--accent-primary);
|
|
transform: scale(1.2);
|
|
}
|
|
|
|
.form-check-label {
|
|
color: var(--text-primary);
|
|
font-weight: 400;
|
|
cursor: pointer;
|
|
transition: color 0.2s ease;
|
|
padding-left: 8px;
|
|
}
|
|
|
|
.form-check-label:hover {
|
|
color: var(--accent-secondary);
|
|
}
|
|
|
|
/* Enhanced Range Slider */
|
|
.form-range {
|
|
width: 100%;
|
|
height: 8px;
|
|
background: var(--surface-secondary);
|
|
border-radius: 8px;
|
|
outline: none;
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
margin: 16px 0;
|
|
}
|
|
|
|
.form-range::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 24px;
|
|
height: 24px;
|
|
background: var(--accent-gradient);
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
box-shadow: 0 4px 12px rgba(0, 212, 170, 0.4);
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.form-range::-webkit-slider-thumb:hover {
|
|
transform: scale(1.2);
|
|
box-shadow: 0 6px 16px rgba(0, 212, 170, 0.6);
|
|
}
|
|
|
|
.form-range::-moz-range-thumb {
|
|
width: 24px;
|
|
height: 24px;
|
|
background: var(--accent-gradient);
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
border: none;
|
|
box-shadow: 0 4px 12px rgba(0, 212, 170, 0.4);
|
|
}
|
|
|
|
/* Output styling */
|
|
output {
|
|
color: var(--text-primary) !important;
|
|
font-weight: 600;
|
|
font-size: 18px;
|
|
margin-left: 16px;
|
|
padding: 8px 16px;
|
|
background: var(--surface-glass);
|
|
border-radius: 12px;
|
|
border: 1px solid var(--border-subtle);
|
|
backdrop-filter: blur(8px);
|
|
}
|
|
|
|
/* Enhanced Mobile Responsiveness */
|
|
@@media (max-width: 576px) {
|
|
.stepper {
|
|
display: none;
|
|
}
|
|
|
|
.step {
|
|
padding: 24px 20px;
|
|
border-radius: 20px;
|
|
}
|
|
|
|
.hero.container.card {
|
|
padding: 24px 20px;
|
|
border-radius: 20px;
|
|
}
|
|
|
|
.card-deck {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 16px;
|
|
}
|
|
|
|
.matrix-question .table {
|
|
display: block;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.likert .responses {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.rating .rating-item {
|
|
flex: 1 1 calc(50% - 8px);
|
|
}
|
|
|
|
.step .mt-3 {
|
|
position: sticky;
|
|
bottom: 0;
|
|
background: linear-gradient(180deg, transparent 0%, var(--bg-secondary) 85%);
|
|
padding: 16px 0 0;
|
|
backdrop-filter: var(--blur-heavy);
|
|
border-radius: 16px 16px 0 0;
|
|
margin: 0 -20px;
|
|
padding-left: 20px;
|
|
padding-right: 20px;
|
|
}
|
|
|
|
.draggable-item {
|
|
padding: 16px 20px;
|
|
}
|
|
}
|
|
|
|
@@media (max-width: 991px) {
|
|
.stepper {
|
|
position: relative;
|
|
top: 0;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
overflow-x: auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
padding-bottom: 8px;
|
|
gap: 12px;
|
|
}
|
|
|
|
.step-indicator {
|
|
min-width: 200px;
|
|
max-width: none;
|
|
}
|
|
}
|
|
|
|
/* Accessibility and Reduced Motion */
|
|
@@media (prefers-reduced-motion: reduce) {
|
|
* {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
}
|
|
|
|
/* Focus States for Accessibility */
|
|
.btn:focus,
|
|
.form-control:focus,
|
|
.form-check-input:focus,
|
|
.matrix-radio:focus + .matrix-choice {
|
|
outline: 2px solid var(--accent-primary);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
/* High Contrast Mode Support */
|
|
@@media (prefers-contrast: high) {
|
|
: root {
|
|
--border-subtle: rgba(255, 255, 255, 0.3);
|
|
--text-muted: #a0a0a0;
|
|
}
|
|
}
|
|
|
|
/* Dark Mode Enhancements */
|
|
@@media (prefers-color-scheme: dark) {
|
|
.hero.container.card {
|
|
box-shadow: var(--shadow-strong), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
}
|
|
}
|
|
|
|
/* Container sizing */
|
|
.container {
|
|
max-width: 1200px;
|
|
}
|
|
|
|
/* Hidden elements */
|
|
.hidden-textarea {
|
|
display: none;
|
|
}
|
|
|
|
.User-details {
|
|
display: none;
|
|
}
|
|
|
|
/* Error row styling */
|
|
#rowSectionError {
|
|
display: flex;
|
|
flex-wrap: nowrap;
|
|
justify-content: space-around;
|
|
align-items: center;
|
|
width: 100%;
|
|
}
|
|
</style>
|
|
|
|
<div class="QuestionContainer">
|
|
<section class="hero container card">
|
|
<form id="questionnaireForm" method="post" asp-action="DisplayQuestionnaire">
|
|
<input type="hidden" name="Id" value="@Model.Id">
|
|
<input type="hidden" name="Title" value="@Model.Title">
|
|
<input type="hidden" name="Description" value="@Model.Description">
|
|
|
|
<!-- Tracking -->
|
|
<input type="hidden" id="questionsShown" name="QuestionsShown" value="">
|
|
<input type="hidden" id="questionsSkipped" name="QuestionsSkipped" value="">
|
|
|
|
<!-- (Duplicate hidden fields retained intentionally to avoid changing behavior) -->
|
|
<input type="hidden" name="Id" value="@Model.Id">
|
|
<input type="hidden" name="Title" value="@Model.Title">
|
|
<input type="hidden" name="Description" value="@Model.Description">
|
|
|
|
<h4 class="mb-2">@Model.Title</h4>
|
|
<p class="mb-4">@Html.Raw(Model.Description)</p>
|
|
|
|
<div class="container px-0">
|
|
<div class="row">
|
|
<div class="col-12 col-md-6">
|
|
<div class="mb-3">
|
|
<label for="userName">Your Name</label>
|
|
<input type="text" class="form-control" id="userName" name="UserName" placeholder="Enter your name" required aria-required="true" autocomplete="name">
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-6">
|
|
<div class="mb-3">
|
|
<label for="Email">Email Address</label>
|
|
<input type="email" class="form-control" id="Email" name="Email" placeholder="Enter your email" required aria-required="true" autocomplete="email">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row align-items-start">
|
|
<!-- Stepper -->
|
|
<div class="col-md-3 mb-4 mb-md-0 d-none d-md-block">
|
|
<div class="stepper">
|
|
@for (int i = 0; i < Model.Questions.Count; i++)
|
|
{
|
|
var question = Model.Questions[i];
|
|
string stepClass = i == 0 ? "active" : "";
|
|
<div class="step-indicator @(stepClass)" data-step-index="@i">
|
|
<span class="step-number">@((i + 1))</span>
|
|
<span class="step-label">@question.Type</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Content -->
|
|
<div class="col-12 col-md-9">
|
|
@for (int i = 0; i < Model.Questions.Count; i++)
|
|
{
|
|
var question = Model.Questions[i];
|
|
// Check if this question has any other options
|
|
var hasOtherOptions = question.Answers?.Any(a => a.IsOtherOption == true) ?? false;
|
|
|
|
<input type="hidden" name="Id" value="@question.Id">
|
|
<input type="hidden" name="Questions[@i].Text" value="@question.Text">
|
|
<input type="hidden" name="Questions[@i].Id" value="@question.Id">
|
|
<input type="hidden" name="Questions[@i].Type" value="@((int)question.Type)">
|
|
@for (int j = 0; j < question.Answers.Count; j++)
|
|
{
|
|
var answer = question.Answers[j];
|
|
<input type="hidden" name="Questions[@i].Answers[@j].Id" value="@answer.Id">
|
|
<input type="hidden" name="Questions[@i].Answers[@j].Text" value="@answer.Text">
|
|
}
|
|
|
|
<div class="step @(i == 0 ? "active" : "")" data-question-id="@question.Id" data-has-other="@hasOtherOptions.ToString().ToLower()">
|
|
<p class="font-weight-normal">@(i + 1). @question.Text</p>
|
|
|
|
@switch (question.Type)
|
|
{
|
|
case QuestionType.Text:
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<input type="Text" class="form-control" id="question@(i + 1)" name="Questions[@i].SelectedText" rows="3" placeholder="Enter answer"></input>
|
|
<input class="form-control hidden-textarea" id="question@(i + 1)" name="Questions[@i].SelectedAnswerIds" value="@answer.Id" rows="3" placeholder="Enter answer" data-condition="@answer.ConditionJson"></input>
|
|
}
|
|
break;
|
|
|
|
case QuestionType.CheckBox:
|
|
case QuestionType.Multiple_choice:
|
|
case QuestionType.Demographic:
|
|
<div class="form-group">
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<div class="form-check mb-2 @(answer.IsOtherOption ? "other-option-container" : "")">
|
|
<input class="form-check-input @(answer.IsOtherOption ? "other-option-input" : "")"
|
|
id="question@(i)_answer@(answer.Id)"
|
|
type="checkbox"
|
|
name="Questions[@i].SelectedAnswerIds"
|
|
value="@answer.Id"
|
|
data-condition="@answer.ConditionJson"
|
|
data-question-index="@i"
|
|
data-answer-id="@answer.Id"
|
|
data-is-other="@answer.IsOtherOption.ToString().ToLower()">
|
|
<label class="form-check-label" for="question@(i)_answer@(answer.Id)">
|
|
@answer.Text
|
|
</label>
|
|
|
|
@* Only show other text container if this answer is actually an other option *@
|
|
@if (answer.IsOtherOption)
|
|
{
|
|
<div class="other-text-container" id="otherText_@(i)_@(answer.Id)" style="display: none;">
|
|
<textarea class="form-control other-text-input"
|
|
name="Questions[@i].OtherTexts[@answer.Id]"
|
|
id="otherTextArea_@(i)_@(answer.Id)"
|
|
rows="3"
|
|
placeholder="Please specify..."></textarea>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
break;
|
|
|
|
case QuestionType.TrueFalse:
|
|
<div class="form-check">
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<div class="form-check mb-2 @(answer.IsOtherOption ? "other-option-container" : "")">
|
|
<input class="form-check-input answer-input @(answer.IsOtherOption ? "other-option-input" : "")"
|
|
type="radio"
|
|
id="question@(i)_answer@(answer.Id)"
|
|
name="Questions[@i].SelectedAnswerIds"
|
|
value="@answer.Id"
|
|
data-condition="@answer.ConditionJson"
|
|
data-question-index="@i"
|
|
data-answer-id="@answer.Id"
|
|
data-is-other="@answer.IsOtherOption.ToString().ToLower()">
|
|
<label class="form-check-label" for="question@(i)_answer@(answer.Id)">
|
|
@answer.Text
|
|
</label>
|
|
|
|
@* Only show other text container if this answer is actually an other option *@
|
|
@if (answer.IsOtherOption)
|
|
{
|
|
<div class="other-text-container" id="otherText_@(i)_@(answer.Id)" style="display: none;">
|
|
<textarea class="form-control other-text-input"
|
|
name="Questions[@i].OtherTexts[@answer.Id]"
|
|
id="otherTextArea_@(i)_@(answer.Id)"
|
|
rows="3"
|
|
placeholder="Please specify..."></textarea>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
break;
|
|
|
|
case QuestionType.Open_ended:
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<textarea type="Text" class="form-control" id="question@(i + 1)" name="Questions[@i].SelectedText" value="@answer.Text" rows="3" placeholder="Enter answer"></textarea>
|
|
<input type="hidden" class="form-control" id="question@(i + 1)" name="Questions[@i].SelectedAnswerIds" value="@answer.Id" rows="3" placeholder="Enter answer" data-condition="@answer.ConditionJson"></input>
|
|
}
|
|
break;
|
|
|
|
case QuestionType.Slider:
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<input type="range" class="form-range" id="question@(i + 1)" name="Questions[@i].SelectedText" min="0" max="100" step="1">
|
|
<input type="hidden" class="form-range" id="question@(i + 1)" name="Questions[@i].SelectedAnswerIds" value="@answer.Id" min="0" max="100" step="1" data-condition="@answer.ConditionJson">
|
|
<output id="question@(i + 1)_output">50</output>
|
|
<script>
|
|
document.getElementById('question@(i + 1)').addEventListener('input', function () {
|
|
document.getElementById('question@(i + 1)_output').value = this.value;
|
|
});
|
|
</script>
|
|
}
|
|
break;
|
|
|
|
case QuestionType.Rating:
|
|
<div class="rating" data-question="@i">
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<div class="rating-item">
|
|
<div class="rating-label">@answer.Text</div>
|
|
<input type="radio" id="question@(i)_rating@(answer.Id)" name="Questions[@i].SelectedAnswerIds" value="@answer.Id" class="rating-input" hidden data-condition="@answer.ConditionJson">
|
|
<label for="question@(i)_rating@(answer.Id)" class="bi bi-star-fill rating-star"></label>
|
|
</div>
|
|
}
|
|
</div>
|
|
break;
|
|
|
|
case QuestionType.Ranking:
|
|
<div class="ranking-question">
|
|
<p class="ml-3">drag and drop to move the answers</p>
|
|
<ul class="sortable-list" id="ranking_@question.Id">
|
|
@{
|
|
int index = 1;
|
|
}
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<li class="draggable-item" draggable="true" id="@answer.Id" ondragstart="dragStart(event);" ondragover="allowDrop(event);" ondrop="drop(event);">
|
|
<span class="answer-index">@index. </span> @answer.Text
|
|
<i class="bi bi-grip-vertical mr-auto"></i>
|
|
<input type="hidden" name="Questions[@i].SelectedAnswerIds" value="@answer.Id" data-condition="@answer.ConditionJson">
|
|
<button type="button" class="up-button btn btn-primary btn-sm" onclick="moveUp(this.parentElement)"><i class="bi bi-arrow-up"></i> Up</button>
|
|
<button type="button" class="down-button btn btn-info btn-sm" onclick="moveDown(this.parentElement)"><i class="bi bi-arrow-down"></i> Down</button>
|
|
</li>
|
|
index++;
|
|
}
|
|
</ul>
|
|
</div>
|
|
<script>
|
|
function allowDrop(ev) { ev.preventDefault(); }
|
|
function dragStart(ev) { ev.dataTransfer.setData("text", ev.target.id); }
|
|
function drop(ev) {
|
|
ev.preventDefault();
|
|
var data = ev.dataTransfer.getData("text");
|
|
var draggedElement = document.getElementById(data);
|
|
var dropTarget = ev.target.closest('.draggable-item');
|
|
if (dropTarget) {
|
|
var targetRect = dropTarget.getBoundingClientRect();
|
|
var midPoint = targetRect.top + (targetRect.height / 2);
|
|
var list = dropTarget.parentNode;
|
|
if (ev.clientY < midPoint) {
|
|
list.insertBefore(draggedElement, dropTarget);
|
|
} else {
|
|
if (dropTarget.nextSibling) { list.insertBefore(draggedElement, dropTarget.nextSibling); }
|
|
else { list.appendChild(draggedElement); }
|
|
}
|
|
}
|
|
}
|
|
|
|
document.querySelectorAll('.draggable-item').forEach(item => {
|
|
item.addEventListener('click', function () {
|
|
removeSelected(this.parentNode);
|
|
this.classList.add('selected');
|
|
});
|
|
});
|
|
function removeSelected(parent) { parent.querySelectorAll('.draggable-item').forEach(sibling => { sibling.classList.remove('selected'); }); }
|
|
</script>
|
|
break;
|
|
|
|
case QuestionType.Likert:
|
|
<div class="likert">
|
|
<div class="statement">
|
|
<label>Please indicate your level of agreement with the following statements:</label>
|
|
<div class="responses">
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<label class="likert-option @(answer.IsOtherOption ? "other-option-container" : "")">
|
|
<input class="form-check-input @(answer.IsOtherOption ? "other-option-input" : "")"
|
|
type="radio"
|
|
id="question@(i)_answer@(answer.Id)"
|
|
name="Questions[@i].SelectedAnswerIds"
|
|
value="@answer.Id"
|
|
data-condition="@answer.ConditionJson"
|
|
data-question-index="@i"
|
|
data-answer-id="@answer.Id"
|
|
data-is-other="@answer.IsOtherOption.ToString().ToLower()"> @answer.Text
|
|
</label>
|
|
|
|
@* Only show other text container if this answer is actually an other option *@
|
|
@if (answer.IsOtherOption)
|
|
{
|
|
<div class="other-text-container" id="otherText_@(i)_@(answer.Id)" style="display: none;">
|
|
<textarea class="form-control other-text-input"
|
|
name="Questions[@i].OtherTexts[@answer.Id]"
|
|
id="otherTextArea_@(i)_@(answer.Id)"
|
|
rows="2"
|
|
placeholder="Please specify..."></textarea>
|
|
</div>
|
|
}
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
break;
|
|
|
|
case QuestionType.Matrix:
|
|
<div class="matrix-question">
|
|
<table class="table table-responsive">
|
|
<thead>
|
|
<tr>
|
|
<th>Question</th>
|
|
@foreach (var option in question.Answers)
|
|
{
|
|
<th class="@(option.IsOtherOption ? "other-option-container" : "")">@option.Text</th>
|
|
}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="matrix-prompt">@question.Text</td>
|
|
@foreach (var option in question.Answers)
|
|
{
|
|
<td class="matrix-cell @(option.IsOtherOption ? "other-option-container" : "")">
|
|
<input type="radio"
|
|
class="matrix-radio @(option.IsOtherOption ? "other-option-input" : "")"
|
|
id="q@(i)_opt_@option.Id"
|
|
name="Questions[@i].SelectedAnswerIds"
|
|
value="@option.Id"
|
|
data-condition="@option.ConditionJson"
|
|
data-question-index="@i"
|
|
data-answer-id="@option.Id"
|
|
data-is-other="@option.IsOtherOption.ToString().ToLower()">
|
|
<label for="q@(i)_opt_@option.Id" class="matrix-choice">
|
|
<span class="opt-text">@option.Text</span>
|
|
</label>
|
|
|
|
@* Only show other text container if this answer is actually an other option *@
|
|
@if (option.IsOtherOption)
|
|
{
|
|
<div class="other-text-container" id="otherText_@(i)_@(option.Id)" style="display: none;">
|
|
<textarea class="form-control other-text-input"
|
|
name="Questions[@i].OtherTexts[@option.Id]"
|
|
id="otherTextArea_@(i)_@(option.Id)"
|
|
rows="2"
|
|
placeholder="Please specify..."></textarea>
|
|
</div>
|
|
}
|
|
</td>
|
|
}
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
break;
|
|
|
|
case QuestionType.Image:
|
|
<script>
|
|
function selectImageCard(answerId, questionId) {
|
|
var cards = document.querySelectorAll('.image-question[data-question-id="' + questionId + '"] .card');
|
|
cards.forEach(function (card) { card.classList.remove('selected'); });
|
|
var selectedCard = document.getElementById('card_' + answerId);
|
|
selectedCard.classList.add('selected');
|
|
var radioButton = document.getElementById('image_answer_' + answerId);
|
|
radioButton.checked = true;
|
|
}
|
|
</script>
|
|
<div class="image-question" data-question-id="@question.Id">
|
|
<div class="card-deck">
|
|
@foreach (var answer in question.Answers)
|
|
{
|
|
<div class="card image-card @(answer.IsOtherOption ? "other-option-container" : "")" id="card_@answer.Id" onclick="selectImageCard('@answer.Id', '@question.Id')">
|
|
@if (answer.IsOtherOption)
|
|
{
|
|
<!-- Special styling for other option image cards -->
|
|
<div style="padding: 20px; text-align: center; background: linear-gradient(135deg, rgba(245, 158, 11, 0.1), rgba(251, 191, 36, 0.1)); height: 160px; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
|
<i class="bi bi-plus-circle" style="font-size: 2rem; color: var(--warning-color); margin-bottom: 8px;"></i>
|
|
<span style="color: var(--text-primary); font-weight: 600;">@answer.Text</span>
|
|
</div>
|
|
<input type="radio"
|
|
id="image_answer_@answer.Id"
|
|
name="Questions[@i].SelectedAnswerIds"
|
|
value="@answer.Id"
|
|
hidden
|
|
data-condition="@answer.ConditionJson"
|
|
class="other-option-input"
|
|
data-question-index="@i"
|
|
data-answer-id="@answer.Id"
|
|
data-is-other="true" />
|
|
|
|
@* Other text container for image other option *@
|
|
<div class="other-text-container" id="otherText_@(i)_@(answer.Id)" style="display: none; padding: 12px;">
|
|
<textarea class="form-control other-text-input"
|
|
name="Questions[@i].OtherTexts[@answer.Id]"
|
|
id="otherTextArea_@(i)_@(answer.Id)"
|
|
rows="2"
|
|
placeholder="Please describe your choice..."></textarea>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<img src="@answer.Text" alt="Image option" class="img-fluid" />
|
|
<input type="radio" id="image_answer_@answer.Id" name="Questions[@i].SelectedAnswerIds" value="@answer.Id" hidden data-condition="@answer.ConditionJson" />
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
break;
|
|
|
|
default:
|
|
<div class="alert alert-danger" role="alert">Unsupported question type.</div>
|
|
break;
|
|
}
|
|
|
|
<div class="mt-3">
|
|
@if (i > 0)
|
|
{
|
|
<button type="button" class="btn btn-secondary btn-sm mr-3 prev" id="BannerButon"><i class="bi bi-arrow-left"></i> Previous</button>
|
|
}
|
|
@if (i < Model.Questions.Count - 1)
|
|
{
|
|
<button type="button" class="btn btn-primary btn-sm next" id="BannerButon">Next <i class="bi bi-arrow-right"></i></button>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<div class="User-details" style="display:none;">
|
|
<div class="form-group">
|
|
<label for="userName">Your Name:</label>
|
|
<input type="text" class="form-control" id="userName" name="UserName" placeholder="Enter your name">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="userEmail">Email Address:</label>
|
|
<input type="email" class="form-control" id="userEmail" name="UserEmail" placeholder="Enter your email">
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary submit btn-sm mt-4" id="BannerButon">Submit</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
</div>
|
|
|
|
@section Scripts {
|
|
@{
|
|
<partial name="_ValidationScriptsPartial" />
|
|
}
|
|
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
const form = document.getElementById('questionnaireForm');
|
|
if (!form) { console.error('Form not found!'); return; }
|
|
|
|
// ===== ENHANCED OTHER OPTION FUNCTIONALITY - CONDITIONAL DISPLAY =====
|
|
function initializeOtherOptions() {
|
|
console.log('Initializing conditional other options...');
|
|
|
|
// Handle checkbox other options (Multiple choice, CheckBox, Demographic, etc.)
|
|
$(document).on('change', 'input[type="checkbox"]', function() {
|
|
console.log('Checkbox changed:', this);
|
|
const $checkbox = $(this);
|
|
const isOther = $checkbox.attr('data-is-other') === 'true';
|
|
|
|
if (isOther) {
|
|
console.log('Other checkbox detected');
|
|
const questionIndex = $checkbox.attr('data-question-index');
|
|
const answerId = $checkbox.attr('data-answer-id');
|
|
const $textContainer = $(`#otherText_${questionIndex}_${answerId}`);
|
|
const $textArea = $(`#otherTextArea_${questionIndex}_${answerId}`);
|
|
|
|
console.log('Looking for container:', `#otherText_${questionIndex}_${answerId}`);
|
|
console.log('Container found:', $textContainer.length);
|
|
|
|
if ($checkbox.is(':checked')) {
|
|
console.log('Showing text container');
|
|
$textContainer.addClass('show').slideDown(300);
|
|
$textArea.focus();
|
|
$textArea.prop('required', true);
|
|
} else {
|
|
console.log('Hiding text container');
|
|
$textContainer.removeClass('show').slideUp(300);
|
|
$textArea.val('');
|
|
$textArea.prop('required', false);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Handle radio button other options (TrueFalse, Likert, Matrix, Rating, Image, etc.)
|
|
$(document).on('change', 'input[type="radio"]', function() {
|
|
console.log('Radio button changed:', this);
|
|
const $radio = $(this);
|
|
const questionIndex = $radio.attr('data-question-index');
|
|
const isOther = $radio.attr('data-is-other') === 'true';
|
|
|
|
// First, hide all other text containers for this question
|
|
if (questionIndex) {
|
|
console.log('Hiding all other containers for question:', questionIndex);
|
|
$(`.other-text-container[id^="otherText_${questionIndex}_"]`).removeClass('show').slideUp(300);
|
|
$(`.other-text-input[id^="otherTextArea_${questionIndex}_"]`).prop('required', false).val('');
|
|
}
|
|
|
|
// If this is an other option and it's selected, show its text container
|
|
if (isOther && $radio.is(':checked')) {
|
|
console.log('Other radio selected');
|
|
const answerId = $radio.attr('data-answer-id');
|
|
const $textContainer = $(`#otherText_${questionIndex}_${answerId}`);
|
|
const $textArea = $(`#otherTextArea_${questionIndex}_${answerId}`);
|
|
|
|
console.log('Showing text container for radio');
|
|
$textContainer.addClass('show').slideDown(300);
|
|
$textArea.focus();
|
|
$textArea.prop('required', true);
|
|
}
|
|
});
|
|
|
|
// Enhanced image card selection with other option support
|
|
window.selectImageCard = function(answerId, questionId) {
|
|
console.log('Image card selected:', answerId, questionId);
|
|
var cards = document.querySelectorAll('.image-question[data-question-id="' + questionId + '"] .card');
|
|
cards.forEach(function (card) { card.classList.remove('selected'); });
|
|
|
|
var selectedCard = document.getElementById('card_' + answerId);
|
|
if (selectedCard) {
|
|
selectedCard.classList.add('selected');
|
|
selectedCard.style.animation = 'morphIn 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
|
|
}
|
|
|
|
var radioButton = document.getElementById('image_answer_' + answerId);
|
|
if (radioButton) {
|
|
radioButton.checked = true;
|
|
// Trigger change event to handle other option logic
|
|
$(radioButton).trigger('change');
|
|
}
|
|
};
|
|
|
|
// Debug: Show questions with other options
|
|
$('.step').each(function() {
|
|
const hasOther = $(this).attr('data-has-other') === 'true';
|
|
const questionId = $(this).attr('data-question-id');
|
|
console.log(`Question ${questionId}: Has other options = ${hasOther}`);
|
|
});
|
|
|
|
// Debug: List all other option inputs
|
|
console.log('Other option inputs found:', $('input[data-is-other="true"]').length);
|
|
$('input[data-is-other="true"]').each(function() {
|
|
console.log('Other input:', this, 'Question:', $(this).attr('data-question-index'), 'Answer:', $(this).attr('data-answer-id'));
|
|
});
|
|
}
|
|
|
|
// Initialize other options functionality when DOM is ready
|
|
$(document).ready(function() {
|
|
initializeOtherOptions();
|
|
});
|
|
|
|
// Tracking (Your existing code preserved)
|
|
let questionsShownArray = [1];
|
|
let questionsSkippedArray = [];
|
|
let navigationPath = [0];
|
|
|
|
function trackQuestionShown(questionNumber) {
|
|
if (!questionsShownArray.includes(questionNumber)) {
|
|
questionsShownArray.push(questionNumber);
|
|
}
|
|
}
|
|
function trackQuestionSkipped(questionNumber, reason) {
|
|
const skipInfo = { questionNumber: questionNumber, reason: reason };
|
|
questionsSkippedArray.push(skipInfo);
|
|
}
|
|
function updateTrackingInputs() {
|
|
document.getElementById('questionsShown').value = JSON.stringify(questionsShownArray);
|
|
document.getElementById('questionsSkipped').value = JSON.stringify(questionsSkippedArray);
|
|
}
|
|
|
|
function getCurrentStepConditions() {
|
|
const currentStepElement = steps[currentStep];
|
|
const activeConditions = [];
|
|
const currentStepInputs = currentStepElement.querySelectorAll('input:checked, input[type="text"]:not([value=""]), textarea:not([value=""])');
|
|
currentStepInputs.forEach(input => {
|
|
const condition = input.getAttribute('data-condition');
|
|
if (condition && condition !== '' && condition !== 'null') {
|
|
try { activeConditions.push(JSON.parse(condition)); } catch (e) { console.log('Invalid condition JSON:', condition); }
|
|
}
|
|
});
|
|
return activeConditions;
|
|
}
|
|
|
|
function shouldApplyCondition() {
|
|
const conditions = getCurrentStepConditions();
|
|
for (let condition of conditions) {
|
|
if (condition.ActionType === 1 && condition.TargetQuestionNumber) {
|
|
return { action: 'jumpTo', target: condition.TargetQuestionNumber };
|
|
} else if (condition.ActionType === 2 && condition.SkipCount) {
|
|
return { action: 'skip', count: condition.SkipCount };
|
|
} else if (condition.ActionType === 3) {
|
|
return { action: 'end', message: condition.EndMessage };
|
|
}
|
|
}
|
|
return { action: 'continue' };
|
|
}
|
|
|
|
function getCurrentStepNumber() { return currentStep + 1; }
|
|
|
|
function jumpToQuestion(questionNumber) {
|
|
const targetStepIndex = questionNumber - 1;
|
|
if (targetStepIndex >= 0 && targetStepIndex < steps.length) {
|
|
const currentQuestionNumber = getCurrentStepNumber();
|
|
for (let i = currentQuestionNumber + 1; i < questionNumber; i++) {
|
|
trackQuestionSkipped(i, `Skipped due to jump condition from question ${currentQuestionNumber}`);
|
|
}
|
|
currentStep = targetStepIndex;
|
|
navigationPath.push(currentStep);
|
|
trackQuestionShown(questionNumber);
|
|
showStep(currentStep);
|
|
updateStepper();
|
|
}
|
|
}
|
|
|
|
function skipQuestions(skipCount) {
|
|
const currentQuestionNumber = getCurrentStepNumber();
|
|
const newStepIndex = currentStep + skipCount + 1;
|
|
for (let i = 1; i <= skipCount; i++) {
|
|
const skippedQuestionNumber = currentQuestionNumber + i;
|
|
if (skippedQuestionNumber <= steps.length) {
|
|
trackQuestionSkipped(skippedQuestionNumber, `Skipped due to skip condition from question ${currentQuestionNumber}`);
|
|
}
|
|
}
|
|
if (newStepIndex < steps.length) {
|
|
currentStep = newStepIndex;
|
|
navigationPath.push(currentStep);
|
|
trackQuestionShown(getCurrentStepNumber());
|
|
showStep(currentStep);
|
|
updateStepper();
|
|
} else {
|
|
currentStep = steps.length - 1;
|
|
navigationPath.push(currentStep);
|
|
showStep(currentStep);
|
|
updateStepper();
|
|
}
|
|
}
|
|
|
|
function endSurvey(endMessage) {
|
|
const currentQuestionNumber = getCurrentStepNumber();
|
|
for (let i = currentQuestionNumber + 1; i <= steps.length; i++) {
|
|
trackQuestionSkipped(i, `Survey ended early from question ${currentQuestionNumber}`);
|
|
}
|
|
steps.forEach(step => step.style.display = 'none');
|
|
const endMessageDiv = document.createElement('div');
|
|
endMessageDiv.className = 'alert alert-success text-center';
|
|
endMessageDiv.innerHTML = `<h4>Survey Completed</h4><p>${endMessage || 'Thank you for completing the survey!'}</p>`;
|
|
const formContainer = document.querySelector('.col-md-9');
|
|
formContainer.appendChild(endMessageDiv);
|
|
submitButton.style.display = 'none';
|
|
}
|
|
|
|
// Rating - Enhanced with jQuery (Your existing code preserved)
|
|
$(document).ready(function () {
|
|
$('.rating-item').on('click', function () {
|
|
const $this = $(this);
|
|
const $siblings = $this.siblings('.rating-item');
|
|
const $prevAll = $this.prevAll('.rating-item');
|
|
|
|
// Reset all stars
|
|
$siblings.add($this).find('.rating-star').removeClass('selected');
|
|
|
|
// Add selected class to current and previous stars
|
|
$prevAll.add($this).find('.rating-star').addClass('selected');
|
|
|
|
// Check the radio input
|
|
$this.find('.rating-input').prop('checked', true).trigger('change');
|
|
|
|
// Add visual feedback
|
|
$this.addClass('rating-selected');
|
|
setTimeout(() => $this.removeClass('rating-selected'), 200);
|
|
});
|
|
|
|
// Hover effects
|
|
$('.rating-item').on('mouseenter', function () {
|
|
const $this = $(this);
|
|
$this.prevAll('.rating-item').add($this).find('.rating-star').addClass('rating-hover');
|
|
}).on('mouseleave', function () {
|
|
$('.rating-star').removeClass('rating-hover');
|
|
});
|
|
});
|
|
|
|
// Enhanced CSS for rating interactions (Your existing code preserved)
|
|
const ratingStyles = `
|
|
.rating-star.rating-hover {
|
|
color: #fbbf24 !important;
|
|
transform: translateY(-2px) scale(1.1);
|
|
}
|
|
.rating-item.rating-selected {
|
|
transform: scale(1.05);
|
|
}
|
|
`;
|
|
|
|
if (!document.getElementById('rating-styles')) {
|
|
const styleElement = document.createElement('style');
|
|
styleElement.id = 'rating-styles';
|
|
styleElement.textContent = ratingStyles;
|
|
document.head.appendChild(styleElement);
|
|
}
|
|
|
|
// Ranking (Your existing code preserved)
|
|
if (!window.hasEventListenersAdded) {
|
|
const upButtons = document.querySelectorAll('.up-button');
|
|
const downButtons = document.querySelectorAll('.down-button');
|
|
upButtons.forEach(button => { button.addEventListener('click', function () { moveUp(this); }); });
|
|
downButtons.forEach(button => { button.addEventListener('click', function () { moveDown(this); }); });
|
|
window.hasEventListenersAdded = true;
|
|
}
|
|
function moveUp(button) { var li = button.parentNode; if (li.previousElementSibling) { li.parentNode.insertBefore(li, li.previousElementSibling); } }
|
|
function moveDown(button) { var li = button.parentNode; if (li.nextElementSibling) { li.parentNode.insertBefore(li.nextElementSibling, li); } }
|
|
|
|
// Stepper (Your existing code preserved)
|
|
const steps = form.querySelectorAll('.step');
|
|
const stepIndicators = document.querySelectorAll('.step-indicator');
|
|
const submitButton = form.querySelector('.submit');
|
|
let currentStep = 0;
|
|
|
|
form.addEventListener('submit', function () { updateTrackingInputs(); });
|
|
|
|
function showStep(index) {
|
|
steps.forEach((step, i) => {
|
|
if (i === index) {
|
|
step.style.display = 'block';
|
|
step.style.animation = 'morphIn 0.6s cubic-bezier(0.4, 0, 0.2, 1) both';
|
|
} else {
|
|
step.style.display = 'none';
|
|
}
|
|
});
|
|
stepIndicators.forEach((indicator, i) => {
|
|
if (i === index) {
|
|
indicator.classList.add('active');
|
|
} else {
|
|
indicator.classList.remove('active');
|
|
}
|
|
});
|
|
submitButton.style.display = index === steps.length - 1 ? 'inline-flex' : 'none';
|
|
try {
|
|
stepIndicators[index].scrollIntoView({
|
|
behavior: 'smooth',
|
|
inline: 'center',
|
|
block: 'nearest'
|
|
});
|
|
} catch(e) {}
|
|
if (progressEl) {
|
|
const pct = Math.round(((index + 1) / steps.length) * 100);
|
|
progressEl.style.width = pct + '%';
|
|
progressEl.setAttribute('aria-valuenow', pct);
|
|
}
|
|
}
|
|
|
|
function goToNextStep() {
|
|
const conditionResult = shouldApplyCondition();
|
|
switch (conditionResult.action) {
|
|
case 'jumpTo': jumpToQuestion(conditionResult.target); break;
|
|
case 'skip': skipQuestions(conditionResult.count); break;
|
|
case 'end': endSurvey(conditionResult.message); break;
|
|
case 'continue':
|
|
default:
|
|
if (currentStep < steps.length - 1) {
|
|
currentStep++;
|
|
navigationPath.push(currentStep);
|
|
trackQuestionShown(getCurrentStepNumber());
|
|
showStep(currentStep);
|
|
updateStepper();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
function goToPrevStep() {
|
|
if (navigationPath.length > 1) {
|
|
navigationPath.pop();
|
|
currentStep = navigationPath[navigationPath.length - 1];
|
|
showStep(currentStep);
|
|
updateStepper();
|
|
}
|
|
}
|
|
|
|
function updateStepper() {
|
|
const currentStepIndex = currentStep;
|
|
stepIndicators.forEach((indicator, i) => {
|
|
if (i === currentStepIndex) indicator.classList.add('active');
|
|
else indicator.classList.remove('active');
|
|
});
|
|
if (progressEl) {
|
|
const pct = Math.round(((currentStep + 1) / steps.length) * 100);
|
|
progressEl.style.width = pct + '%';
|
|
progressEl.setAttribute('aria-valuenow', pct);
|
|
}
|
|
}
|
|
|
|
const nextButtons = form.querySelectorAll('.next');
|
|
nextButtons.forEach(button => { button.addEventListener('click', goToNextStep); });
|
|
const prevButtons = form.querySelectorAll('.prev');
|
|
prevButtons.forEach(button => { button.addEventListener('click', goToPrevStep); });
|
|
|
|
showStep(currentStep);
|
|
});
|
|
|
|
// Global helpers (kept for parity - Your existing code preserved)
|
|
function allowDrop(ev) { ev.preventDefault(); }
|
|
function dragStart(ev) { ev.dataTransfer.setData("text", ev.target.id); }
|
|
function drop(ev) {
|
|
ev.preventDefault();
|
|
var data = ev.dataTransfer.getData("text");
|
|
var draggedElement = document.getElementById(data);
|
|
var dropTarget = ev.target.closest('.draggable-item');
|
|
if (dropTarget) {
|
|
var targetRect = dropTarget.getBoundingClientRect();
|
|
var midPoint = targetRect.top + (targetRect.height / 2);
|
|
var list = dropTarget.parentNode;
|
|
if (ev.clientY < midPoint) { list.insertBefore(draggedElement, dropTarget); }
|
|
else { if (dropTarget.nextSibling) { list.insertBefore(draggedElement, dropTarget.nextSibling); } else { list.appendChild(draggedElement); } }
|
|
}
|
|
}
|
|
function removeSelected(parent) { parent.querySelectorAll('.draggable-item').forEach(sibling => { sibling.classList.remove('selected'); }); }
|
|
</script>
|
|
} |