SurveyVista/Web/Areas/Admin/Views/Questionnaire/Details.cshtml
2026-03-07 02:37:33 +01:00

309 lines
20 KiB
Text

@model QuestionnaireViewModel
@{
ViewData["Title"] = "Questionnaire Details";
var totalAnswers = Model.Questions?.Sum(q => q.Answers?.Count ?? 0) ?? 0;
var otherCount = Model.Questions?.Count(q => q.Answers != null && q.Answers.Any(a => a.IsOtherOption)) ?? 0;
var typeCount = Model.Questions?.Select(q => q.Type).Distinct().Count() ?? 0;
var qCount = Model.Questions?.Count ?? 0;
}
<style>
@@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
:root {
--bg:#0f1923;--bg-card:#1a2332;--bg-elevated:#1f2b3d;--bg-input:#16202e;
--text-primary:#e8edf2;--text-secondary:#9ba8b9;--text-muted:#5e6e82;--text-faint:#3d4e63;
--teal:#33b3ae;--teal-soft:rgba(51,179,174,0.1);--teal-medium:rgba(51,179,174,0.2);--teal-glow:rgba(51,179,174,0.12);--teal-dark:#2a9490;
--amber:#f59e0b;--amber-soft:rgba(245,158,11,0.1);--amber-border:rgba(245,158,11,0.35);
--purple:#7c3aed;--purple-soft:rgba(124,58,237,0.1);--purple-border:rgba(124,58,237,0.35);
--red:#ef4444;--red-soft:rgba(239,68,68,0.1);
--green:#10b981;--green-soft:rgba(16,185,129,0.1);--green-border:rgba(16,185,129,0.35);
--info:#06b6d4;--info-soft:rgba(6,182,212,0.1);--info-border:rgba(6,182,212,0.35);
--border:rgba(255,255,255,0.06);--border-light:rgba(255,255,255,0.04);
--shadow-xs:0 1px 3px rgba(0,0,0,0.2);--shadow-sm:0 2px 8px rgba(0,0,0,0.25);--shadow-md:0 4px 16px rgba(0,0,0,0.3);--shadow-lg:0 8px 32px rgba(0,0,0,0.35);--shadow-teal:0 4px 20px rgba(51,179,174,0.2);
--radius-sm:8px;--radius-md:12px;--radius-lg:16px;
--transition:all 0.2s cubic-bezier(0.4,0,0.2,1);--transition-spring:all 0.35s cubic-bezier(0.34,1.56,0.64,1);
}
@@keyframes slideUp{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}
@@keyframes scaleIn{from{opacity:0;transform:scale(0.96)}to{opacity:1;transform:scale(1)}}
@@keyframes fadeIn{from{opacity:0}to{opacity:1}}
@@keyframes pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.08)}}
@@keyframes shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}
*{box-sizing:border-box}
.details-page{font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;background:var(--bg);min-height:100vh;padding:32px 40px 80px;-webkit-font-smoothing:antialiased;color:var(--text-primary)}
/* ===== PAGE HEADER ===== */
.page-top{max-width:1100px;margin:0 auto 28px;animation:slideUp .5s ease both}
.page-breadcrumb{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--text-muted);margin-bottom:16px}
.page-breadcrumb a{color:var(--text-muted);text-decoration:none;transition:var(--transition)}
.page-breadcrumb a:hover{color:var(--teal)}
.page-breadcrumb .sep{color:var(--text-faint)}
.page-title-row{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap}
.page-title{font-size:28px;font-weight:700;color:var(--text-primary);letter-spacing:-0.5px;margin:0;display:flex;align-items:center;gap:14px}
.page-title-icon{width:44px;height:44px;border-radius:var(--radius-md);background:linear-gradient(135deg,var(--teal),var(--teal-dark));display:flex;align-items:center;justify-content:center;font-size:20px;color:#fff;box-shadow:var(--shadow-teal)}
.page-title-accent{display:inline-block;width:8px;height:8px;background:var(--teal);border-radius:50%;margin-left:4px;vertical-align:super;animation:pulse 2s ease infinite}
.btn-back{display:inline-flex;align-items:center;gap:8px;padding:10px 22px;background:var(--bg-card);color:var(--text-secondary);border:1.5px solid var(--border);border-radius:var(--radius-sm);font-size:13px;font-weight:500;font-family:inherit;cursor:pointer;transition:var(--transition);text-decoration:none}
.btn-back:hover{background:var(--bg-elevated);border-color:rgba(255,255,255,0.1);color:var(--text-primary);text-decoration:none}
/* ===== STATS BAR ===== */
.stats-bar{max-width:1100px;margin:0 auto 24px;display:grid;grid-template-columns:repeat(4,1fr);gap:16px;animation:slideUp .5s ease .08s both}
.stat-card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-md);padding:20px 24px;text-align:center;transition:var(--transition);position:relative;overflow:hidden}
.stat-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;border-radius:3px 3px 0 0}
.stat-card:nth-child(1)::before{background:var(--teal)}
.stat-card:nth-child(2)::before{background:var(--info)}
.stat-card:nth-child(3)::before{background:var(--amber)}
.stat-card:nth-child(4)::before{background:var(--purple)}
.stat-card:hover{box-shadow:var(--shadow-sm);border-color:rgba(255,255,255,0.1);transform:translateY(-2px)}
.stat-number{font-size:32px;font-weight:800;line-height:1;margin-bottom:6px;background:linear-gradient(135deg,var(--text-primary),var(--text-secondary));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.stat-card:nth-child(1) .stat-number{background:linear-gradient(135deg,var(--teal),#5eead4);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.stat-card:nth-child(2) .stat-number{background:linear-gradient(135deg,var(--info),#67e8f9);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.stat-card:nth-child(3) .stat-number{background:linear-gradient(135deg,var(--amber),#fcd34d);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.stat-card:nth-child(4) .stat-number{background:linear-gradient(135deg,var(--purple),#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.stat-label{font-size:12px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.6px}
/* ===== META CARD ===== */
.meta-card{max-width:1100px;margin:0 auto 24px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:28px 32px;box-shadow:var(--shadow-xs);animation:slideUp .5s ease .12s both;transition:var(--transition)}
.meta-card:hover{box-shadow:var(--shadow-sm);border-color:rgba(255,255,255,0.08)}
.meta-section-header{display:flex;align-items:center;gap:12px;margin-bottom:20px}
.meta-section-icon{width:36px;height:36px;border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;font-size:16px;color:#fff}
.meta-section-icon.info-icon{background:var(--info)}
.meta-section-title{font-size:16px;font-weight:700;color:var(--text-primary);margin:0}
.meta-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.meta-item{padding:16px 20px;background:var(--bg-elevated);border:1px solid var(--border-light);border-radius:var(--radius-sm);transition:var(--transition)}
.meta-item:hover{border-color:rgba(255,255,255,0.08)}
.meta-item.full-width{grid-column:1/-1}
.meta-label{font-size:11px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.6px;margin-bottom:8px;display:flex;align-items:center;gap:6px}
.meta-label i{font-size:12px;color:var(--teal)}
.meta-value{font-size:15px;font-weight:600;color:var(--text-primary);line-height:1.4;word-break:break-word}
.meta-value.id-value{color:var(--teal);font-family:'JetBrains Mono',monospace}
.description-value{font-size:14px;font-weight:400;color:var(--text-secondary);line-height:1.7}
.description-value p{margin:0}
.empty-desc{color:var(--text-faint);font-style:italic;font-weight:400}
/* ===== QUESTIONS SECTION ===== */
.questions-section{max-width:1100px;margin:0 auto;animation:slideUp .5s ease .18s both}
.questions-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;flex-wrap:wrap;gap:12px}
.questions-title{display:flex;align-items:center;gap:12px}
.questions-icon{width:36px;height:36px;border-radius:var(--radius-sm);background:var(--green);display:flex;align-items:center;justify-content:center;font-size:16px;color:#fff}
.questions-title h2{font-size:18px;font-weight:700;color:var(--text-primary);margin:0}
.questions-count-pill{display:flex;align-items:center;gap:6px;background:var(--green-soft);border:1px solid var(--green-border);border-radius:100px;padding:6px 16px;font-size:12px;font-weight:600;color:var(--green)}
/* ===== QUESTION CARD ===== */
.q-card{background:var(--bg-card);border:1.5px solid var(--border);border-radius:var(--radius-md);margin-bottom:16px;overflow:hidden;transition:var(--transition);box-shadow:var(--shadow-xs)}
.q-card:hover{box-shadow:var(--shadow-sm);border-color:rgba(255,255,255,0.1)}
.q-card-header{padding:18px 24px;display:flex;align-items:center;gap:14px;cursor:pointer;transition:var(--transition);position:relative}
.q-card-header:hover{background:var(--bg-elevated)}
.q-card-header::after{content:'';position:absolute;bottom:0;left:24px;right:24px;height:1px;background:var(--border-light)}
.q-num{width:32px;height:32px;border-radius:var(--radius-sm);background:var(--teal);color:#fff;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;flex-shrink:0}
.q-text-wrap{flex:1;min-width:0}
.q-text{font-size:15px;font-weight:600;color:var(--text-primary);margin:0;line-height:1.5}
.q-meta-tags{display:flex;align-items:center;gap:8px;margin-top:6px;flex-wrap:wrap}
.q-tag{font-size:10px;font-weight:600;padding:3px 10px;border-radius:100px;white-space:nowrap;text-transform:uppercase;letter-spacing:0.3px}
.q-tag.type-tag{color:var(--teal);background:var(--teal-soft);border:1px solid var(--teal-medium)}
.q-tag.id-tag{color:var(--text-muted);background:var(--bg-input);border:1px solid var(--border)}
.q-tag.ans-count-tag{color:var(--info);background:var(--info-soft);border:1px solid var(--info-border)}
.q-tag.other-tag{color:var(--amber);background:var(--amber-soft);border:1px solid var(--amber-border)}
.q-chevron{color:var(--text-faint);font-size:14px;transition:transform .25s ease;padding:4px;flex-shrink:0}
.q-chevron.open{transform:rotate(180deg)}
/* ===== ANSWER AREA ===== */
.q-answers{display:none;padding:20px 24px;animation:fadeIn .2s ease}
.q-answers.show{display:block}
.ans-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:10px}
.ans-card{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;background:var(--bg-elevated);border:1.5px solid var(--border);border-radius:var(--radius-sm);transition:var(--transition);position:relative}
.ans-card:hover{border-color:rgba(255,255,255,0.1);background:var(--bg-hover)}
.ans-card.is-other{border-color:var(--amber-border);background:var(--amber-soft)}
.ans-card.is-other::before{content:'OTHER';position:absolute;top:-7px;left:12px;background:var(--amber);color:#fff;padding:1px 7px;border-radius:4px;font-size:8px;font-weight:700;letter-spacing:0.5px}
.ans-bullet{width:24px;height:24px;border-radius:6px;background:var(--info-soft);border:1px solid var(--info-border);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:var(--info);flex-shrink:0;margin-top:1px}
.ans-card.is-other .ans-bullet{background:var(--amber-soft);border-color:var(--amber-border);color:var(--amber)}
.ans-content{flex:1;min-width:0}
.ans-text{font-size:13px;font-weight:500;color:var(--text-primary);line-height:1.5;word-break:break-word}
.ans-id{font-size:10px;color:var(--text-faint);margin-top:3px;font-family:'JetBrains Mono',monospace}
/* ===== EMPTY STATE ===== */
.empty-state{text-align:center;padding:48px 24px;color:var(--text-muted)}
.empty-state-icon{width:64px;height:64px;border-radius:var(--radius-lg);background:var(--bg-elevated);display:flex;align-items:center;justify-content:center;font-size:28px;color:var(--text-faint);margin:0 auto 16px}
.empty-state h3{font-size:16px;font-weight:600;color:var(--text-secondary);margin:0 0 6px}
.empty-state p{font-size:13px;color:var(--text-muted);margin:0}
.no-answers-msg{text-align:center;padding:20px;color:var(--text-muted);font-size:13px;font-style:italic}
/* ===== FOOTER ===== */
.footer-row{max-width:1100px;margin:28px auto 0;display:flex;justify-content:center;gap:12px;animation:slideUp .5s ease .25s both}
.btn-edit{display:inline-flex;align-items:center;gap:8px;padding:12px 28px;background:var(--teal);color:#fff;border:none;border-radius:var(--radius-sm);font-size:14px;font-weight:600;font-family:inherit;cursor:pointer;transition:var(--transition-spring);box-shadow:var(--shadow-teal);text-decoration:none}
.btn-edit:hover{background:var(--teal-dark);transform:translateY(-2px);box-shadow:0 8px 28px rgba(51,179,174,0.25);color:#fff;text-decoration:none}
/* ===== RESPONSIVE ===== */
@@media(max-width:768px){
.details-page{padding:16px}
.stats-bar{grid-template-columns:repeat(2,1fr);gap:10px}
.meta-grid{grid-template-columns:1fr}
.ans-grid{grid-template-columns:1fr}
.page-title-row{flex-direction:column;align-items:flex-start}
.footer-row{flex-direction:column}
.footer-row .btn-back,.footer-row .btn-edit{width:100%;justify-content:center}
}
</style>
<div class="details-page">
<!-- Page Header -->
<div class="page-top">
<div class="page-breadcrumb">
<a href="@Url.Action("Index", "Questionnaire")">Questionnaires</a>
<span class="sep">/</span>
<span>Details</span>
</div>
<div class="page-title-row">
<h1 class="page-title">
<span class="page-title-icon"><i class="bi bi-file-text-fill"></i></span>
Questionnaire Details<span class="page-title-accent"></span>
</h1>
<a href="@Url.Action("Index", "Questionnaire")" class="btn-back"><i class="bi bi-arrow-left"></i> Back to List</a>
</div>
</div>
<!-- Stats Bar -->
<div class="stats-bar">
<div class="stat-card">
<div class="stat-number">@qCount</div>
<div class="stat-label">Questions</div>
</div>
<div class="stat-card">
<div class="stat-number">@totalAnswers</div>
<div class="stat-label">Total Answers</div>
</div>
<div class="stat-card">
<div class="stat-number">@otherCount</div>
<div class="stat-label">Other Options</div>
</div>
<div class="stat-card">
<div class="stat-number">@typeCount</div>
<div class="stat-label">Question Types</div>
</div>
</div>
<!-- Overview Meta -->
<div class="meta-card">
<div class="meta-section-header">
<div class="meta-section-icon info-icon"><i class="bi bi-info-circle-fill"></i></div>
<h2 class="meta-section-title">Overview</h2>
</div>
<div class="meta-grid">
<div class="meta-item">
<div class="meta-label"><i class="bi bi-hash"></i> ID</div>
<div class="meta-value id-value">#@Model.Id</div>
</div>
<div class="meta-item">
<div class="meta-label"><i class="bi bi-card-heading"></i> Title</div>
<div class="meta-value">@Model.Title</div>
</div>
<div class="meta-item full-width">
<div class="meta-label"><i class="bi bi-card-text"></i> Description</div>
@if (!string.IsNullOrEmpty(Model.Description))
{
<div class="description-value">@Html.Raw(Model.Description)</div>
}
else
{
<div class="description-value empty-desc">No description provided</div>
}
</div>
</div>
</div>
<!-- Questions -->
<div class="questions-section">
<div class="questions-header">
<div class="questions-title">
<div class="questions-icon"><i class="bi bi-question-circle-fill"></i></div>
<h2>Questions</h2>
</div>
<div class="questions-count-pill">
<i class="bi bi-check2-circle"></i>
@qCount Question@(qCount != 1 ? "s" : "")
</div>
</div>
@if (Model.Questions != null && Model.Questions.Any())
{
<div class="questions-list">
@foreach (var question in Model.Questions.Select((q, index) => new { Q = q, Idx = index }))
{
var ansCount = question.Q.Answers?.Count ?? 0;
var hasOther = question.Q.Answers != null && question.Q.Answers.Any(a => a.IsOtherOption);
<div class="q-card" data-qi="@question.Idx">
<div class="q-card-header" onclick="toggleQ(@question.Idx)">
<span class="q-num">@(question.Idx + 1)</span>
<div class="q-text-wrap">
<p class="q-text">@question.Q.Text</p>
<div class="q-meta-tags">
<span class="q-tag type-tag">@question.Q.Type.ToString().Replace("_", " ")</span>
<span class="q-tag id-tag">ID: @question.Q.Id</span>
<span class="q-tag ans-count-tag"><i class="bi bi-list-ul"></i> @ansCount answer@(ansCount != 1 ? "s" : "")</span>
@if (hasOther)
{
<span class="q-tag other-tag"><i class="bi bi-chat-dots"></i> Has Other</span>
}
</div>
</div>
<span class="q-chevron" id="chevron_@question.Idx"><i class="bi bi-chevron-down"></i></span>
</div>
<div class="q-answers" id="qAns_@question.Idx">
@if (question.Q.Answers != null && question.Q.Answers.Any())
{
<div class="ans-grid">
@foreach (var answer in question.Q.Answers.Select((a, ai) => new { A = a, Ai = ai }))
{
<div class="ans-card @(answer.A.IsOtherOption ? "is-other" : "")">
<span class="ans-bullet">@((char)('A' + answer.Ai))</span>
<div class="ans-content">
<div class="ans-text">@answer.A.Text</div>
<div class="ans-id">ID: @answer.A.Id</div>
</div>
</div>
}
</div>
}
else
{
<div class="no-answers-msg"><i class="bi bi-inbox"></i> No answer options for this question</div>
}
</div>
</div>
}
</div>
}
else
{
<div class="empty-state">
<div class="empty-state-icon"><i class="bi bi-question-circle"></i></div>
<h3>No Questions</h3>
<p>This questionnaire doesn't contain any questions yet.</p>
</div>
}
</div>
<!-- Footer -->
<div class="footer-row">
<a href="@Url.Action("Index", "Questionnaire")" class="btn-back"><i class="bi bi-arrow-left"></i> Back to List</a>
<a href="@Url.Action("Edit", "Questionnaire", new { id = Model.Id })" class="btn-edit"><i class="bi bi-pencil"></i> Edit Questionnaire</a>
</div>
</div>
<script>
function toggleQ(idx) {
const body = document.getElementById('qAns_' + idx);
const chev = document.getElementById('chevron_' + idx);
body.classList.toggle('show');
chev.classList.toggle('open');
}
// Auto-expand first question
document.addEventListener('DOMContentLoaded', function () {
const first = document.querySelector('.q-answers');
const firstChev = document.querySelector('.q-chevron');
if (first) { first.classList.add('show'); }
if (firstChev) { firstChev.classList.add('open'); }
});
</script>