Improve overall questionnaire design and user response experience
This commit is contained in:
parent
d9f9b600d7
commit
b67eca0729
12 changed files with 6963 additions and 1325 deletions
|
|
@ -23,5 +23,7 @@ namespace Model
|
||||||
|
|
||||||
public bool IsOtherOption { get; set; } = false;
|
public bool IsOtherOption { get; set; } = false;
|
||||||
public string? ConditionJson { get; set; }
|
public string? ConditionJson { get; set; }
|
||||||
|
[NotMapped] // This won't be stored in database
|
||||||
|
public bool IsDeleted { get; set; } = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,10 @@ namespace Services.Implemnetation
|
||||||
|
|
||||||
public Questionnaire GetQuestionnaireWithQuestionAndAnswer(int? id)
|
public Questionnaire GetQuestionnaireWithQuestionAndAnswer(int? id)
|
||||||
{
|
{
|
||||||
return _context.Questionnaires.AsNoTracking().Include(x => x.Questions).ThenInclude(x => x.Answers).FirstOrDefault(x=>x.Id==id);
|
return _context.Questionnaires // ✅ No AsNoTracking for edit operations!
|
||||||
|
.Include(x => x.Questions)
|
||||||
|
.ThenInclude(x => x.Answers)
|
||||||
|
.FirstOrDefault(x => x.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Update(Questionnaire questionnaire)
|
public async Task Update(Questionnaire questionnaire)
|
||||||
|
|
|
||||||
|
|
@ -200,110 +200,130 @@ namespace Web.Areas.Admin.Controllers
|
||||||
|
|
||||||
if (ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
// Retrieve the existing questionnaire from the database
|
try
|
||||||
var existingQuestionnaire = _questionnaire.GetQuestionnaireWithQuestionAndAnswer(viewModel.Id);
|
{
|
||||||
|
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Step 1: Update the questionnaire basic info
|
||||||
|
var existingQuestionnaire = await _context.Questionnaires
|
||||||
|
.FirstOrDefaultAsync(q => q.Id == viewModel.Id);
|
||||||
|
|
||||||
if (existingQuestionnaire == null)
|
if (existingQuestionnaire == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the existing questionnaire with the data from the view model
|
|
||||||
existingQuestionnaire.Title = viewModel.Title;
|
existingQuestionnaire.Title = viewModel.Title;
|
||||||
existingQuestionnaire.Description = viewModel.Description;
|
existingQuestionnaire.Description = viewModel.Description;
|
||||||
|
|
||||||
var existingQuestionIds = existingQuestionnaire.Questions.Select(q => q.Id).ToList();
|
// Step 2: Get all existing questions for this questionnaire
|
||||||
|
var existingQuestions = await _context.Questions
|
||||||
|
.Where(q => q.QuestionnaireId == viewModel.Id)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
// Iterate through existing questions and remove those not found in the view model
|
// Step 3: Delete ALL answers first (foreign key constraint)
|
||||||
foreach (var existingQuestion in existingQuestionnaire.Questions.ToList())
|
if (existingQuestions.Any())
|
||||||
{
|
{
|
||||||
if (!viewModel.Questions.Any(q => q.Id == existingQuestion.Id))
|
var questionIds = existingQuestions.Select(q => q.Id).ToList();
|
||||||
{
|
var existingAnswers = await _context.Answers
|
||||||
existingQuestionnaire.Questions.Remove(existingQuestion);
|
.Where(a => questionIds.Contains(a.QuestionId))
|
||||||
}
|
.ToListAsync();
|
||||||
await _questionnaire.Update(existingQuestionnaire);
|
|
||||||
|
_context.Answers.RemoveRange(existingAnswers);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
var newQuestions = new List<Question>();
|
// Step 4: Delete ALL questions
|
||||||
|
_context.Questions.RemoveRange(existingQuestions);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
// Update or add new questions
|
// Step 5: Add new questions (only if provided and valid)
|
||||||
foreach (var questionViewModel in viewModel.Questions)
|
int newQuestionsAdded = 0;
|
||||||
|
if (viewModel.Questions != null && viewModel.Questions.Count > 0)
|
||||||
{
|
{
|
||||||
var existingQuestion = existingQuestionnaire.Questions.FirstOrDefault(q => q.Id == questionViewModel.Id);
|
var validQuestions = viewModel.Questions
|
||||||
|
.Where(q => !string.IsNullOrWhiteSpace(q.Text))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
if (questionViewModel.Id != 0)
|
foreach (var questionViewModel in validQuestions)
|
||||||
{
|
{
|
||||||
if (existingQuestion != null)
|
var newQuestion = new Question
|
||||||
{
|
{
|
||||||
existingQuestion.Text = questionViewModel.Text;
|
Text = questionViewModel.Text.Trim(),
|
||||||
existingQuestion.Type = questionViewModel.Type;
|
Type = questionViewModel.Type,
|
||||||
|
QuestionnaireId = viewModel.Id
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var answerViewModel in questionViewModel.Answers)
|
_context.Questions.Add(newQuestion);
|
||||||
{
|
await _context.SaveChangesAsync(); // Save to get the ID
|
||||||
// Check if the answer already exists
|
|
||||||
var existingAnswer = existingQuestion.Answers.FirstOrDefault(a => a.Id == answerViewModel.Id);
|
|
||||||
|
|
||||||
if (answerViewModel.Id == 0)
|
// Add answers for this question
|
||||||
|
if (questionViewModel.Answers != null)
|
||||||
{
|
{
|
||||||
// NEW: Add IsOtherOption property when creating new answers
|
var validAnswers = questionViewModel.Answers
|
||||||
existingQuestion.Answers.Add(new Answer
|
.Where(a => !string.IsNullOrWhiteSpace(a.Text))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var answerViewModel in validAnswers)
|
||||||
{
|
{
|
||||||
Text = answerViewModel.Text,
|
var newAnswer = new Answer
|
||||||
IsOtherOption = answerViewModel.IsOtherOption
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (answerViewModel.Text == null)
|
|
||||||
{
|
{
|
||||||
existingQuestion.Answers.Remove(existingAnswer);
|
Text = answerViewModel.Text.Trim(),
|
||||||
await _questionnaire.Update(existingQuestionnaire);
|
IsOtherOption = answerViewModel.IsOtherOption,
|
||||||
|
QuestionId = newQuestion.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Answers.Add(newAnswer);
|
||||||
}
|
}
|
||||||
else if (existingAnswer != null)
|
|
||||||
|
if (validAnswers.Any())
|
||||||
{
|
{
|
||||||
existingAnswer.Text = answerViewModel.Text;
|
await _context.SaveChangesAsync();
|
||||||
// NEW: Update IsOtherOption property for existing answers
|
|
||||||
existingAnswer.IsOtherOption = answerViewModel.IsOtherOption;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newQuestionsAdded++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 6: Final save and commit
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
await transaction.CommitAsync();
|
||||||
|
|
||||||
|
// Step 7: Get final count for success message
|
||||||
|
var finalQuestionCount = await _context.Questions
|
||||||
|
.Where(q => q.QuestionnaireId == viewModel.Id)
|
||||||
|
.CountAsync();
|
||||||
|
|
||||||
|
// Success message
|
||||||
|
if (finalQuestionCount == 0)
|
||||||
|
{
|
||||||
|
TempData["Success"] = "Questionnaire updated successfully. All questions have been removed.";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Create a new question
|
TempData["Success"] = $"Questionnaire updated successfully with {finalQuestionCount} question(s).";
|
||||||
var newQuestion = new Question
|
|
||||||
{
|
|
||||||
Text = questionViewModel.Text,
|
|
||||||
Type = questionViewModel.Type,
|
|
||||||
Answers = new List<Answer>()
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var answerViewModel in questionViewModel.Answers)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(answerViewModel.Text))
|
|
||||||
{
|
|
||||||
// NEW: Add IsOtherOption property when creating new answers
|
|
||||||
newQuestion.Answers.Add(new Answer
|
|
||||||
{
|
|
||||||
Text = answerViewModel.Text,
|
|
||||||
IsOtherOption = answerViewModel.IsOtherOption
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newQuestions.Add(newQuestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
existingQuestionnaire.Questions.AddRange(newQuestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _questionnaire.Update(existingQuestionnaire);
|
|
||||||
|
|
||||||
TempData["Success"] = "Questionnaire updated successfully";
|
|
||||||
return RedirectToAction(nameof(Index));
|
return RedirectToAction(nameof(Index));
|
||||||
}
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
await transaction.RollbackAsync();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError("", "An error occurred while updating the questionnaire. Please try again.");
|
||||||
|
return View(viewModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return View(viewModel);
|
return View(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Delete(int id)
|
public IActionResult Delete(int id)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,177 +1,749 @@
|
||||||
@model QuestionnaireViewModel
|
@model QuestionnaireViewModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Delete";
|
ViewData["Title"] = "Delete Questionnaire";
|
||||||
}
|
}
|
||||||
<div class="container mt-4">
|
|
||||||
<div class="card justify-content-center p-4 shadow rounded">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">Delete questionnaire</h5>
|
|
||||||
@* <h6 class="text-danger">Are you sure you want to delete this questionnaire: <span class="badge bg-danger p-2 shadow rounded">@Model.Title</span></h6> *@
|
|
||||||
<h6 class="text-danger">
|
|
||||||
Are you sure you want to delete:
|
|
||||||
|
|
||||||
<span class="badge bg-danger p-2 shadow rounded">
|
<style>
|
||||||
<span class="item-title ">@Html.Raw(Model.Title.Length >= 100 ? Model.Title.Substring(0, 100) : Model.Title)</span>
|
/* Modern Design System */
|
||||||
<span class="more-title " style="display:none;">@(Model.Title.Length > 30 ? Model.Title.Substring(30) : "")</span>
|
@@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
|
||||||
</span>
|
|
||||||
<button type="button" class="btn btn-link readMoreBtn">Read More</button>
|
|
||||||
|
|
||||||
</h6>
|
:root {
|
||||||
|
--danger-color: #ef4444;
|
||||||
|
--danger-light: #fca5a5;
|
||||||
|
--danger-dark: #dc2626;
|
||||||
|
--warning-color: #f59e0b;
|
||||||
|
--info-color: #06b6d4;
|
||||||
|
--success-color: #10b981;
|
||||||
|
--gray-50: #f8fafc;
|
||||||
|
--gray-100: #f1f5f9;
|
||||||
|
--gray-200: #e2e8f0;
|
||||||
|
--gray-300: #cbd5e1;
|
||||||
|
--gray-400: #94a3b8;
|
||||||
|
--gray-500: #64748b;
|
||||||
|
--gray-600: #475569;
|
||||||
|
--gray-700: #334155;
|
||||||
|
--gray-800: #1e293b;
|
||||||
|
--gray-900: #0f172a;
|
||||||
|
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||||
|
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
|
||||||
|
--border-radius-sm: 8px;
|
||||||
|
--border-radius-md: 12px;
|
||||||
|
--border-radius-lg: 16px;
|
||||||
|
--border-radius-xl: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<!-- 12 columns for textboxes -->
|
|
||||||
|
|
||||||
<form asp-action="Delete" asp-controller="Questionnaire">
|
.container {
|
||||||
<div asp-validation-summary="All" class="text-danger"></div>
|
max-width: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
<div class="mb-3 col-12">
|
/* Main Delete Card */
|
||||||
<label asp-for="Title" class="control-label"></label>
|
.delete-card {
|
||||||
<input asp-for="Title" class="form-control" disabled />
|
background: white;
|
||||||
<span asp-validation-for="Title" class="text-danger"></span>
|
border-radius: var(--border-radius-xl);
|
||||||
|
box-shadow: var(--shadow-2xl);
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 6px;
|
||||||
|
background: linear-gradient(90deg, var(--danger-color), var(--danger-light), var(--danger-dark));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header Section */
|
||||||
|
.delete-header {
|
||||||
|
background: linear-gradient(135deg, var(--danger-color) 0%, var(--danger-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 20"><defs><radialGradient id="a" cx="50%" cy="0%" r="100%"><stop offset="0%" stop-color="white" stop-opacity="0.1"/><stop offset="100%" stop-color="white" stop-opacity="0"/></radialGradient></defs><rect width="100" height="20" fill="url(%23a)"/></svg>');
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 1.5rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-icon i {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 800;
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-subtitle {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
font-weight: 500;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content Section */
|
||||||
|
.delete-content {
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Warning Alert */
|
||||||
|
.warning-alert {
|
||||||
|
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
||||||
|
border: 2px solid var(--warning-color);
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
padding: 2rem;
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-alert::before {
|
||||||
|
content: "⚠️";
|
||||||
|
position: absolute;
|
||||||
|
top: -15px;
|
||||||
|
left: 20px;
|
||||||
|
background: var(--warning-color);
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-content {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-list li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--gray-700);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-list li i {
|
||||||
|
color: var(--danger-color);
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Questionnaire Info */
|
||||||
|
.questionnaire-info {
|
||||||
|
background: linear-gradient(135deg, var(--gray-50) 0%, var(--gray-100) 100%);
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
padding: 2rem;
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-icon {
|
||||||
|
background: var(--info-color);
|
||||||
|
color: white;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 1rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
background: white;
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-500);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-800);
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-description {
|
||||||
|
background: white;
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
max-height: 120px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-description-content {
|
||||||
|
color: var(--gray-700);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats Section */
|
||||||
|
.stats-section {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
padding: 2rem;
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-title i {
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
color: var(--info-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: linear-gradient(135deg, var(--gray-50) 0%, var(--gray-100) 100%);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--danger-color);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-600);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.action-section {
|
||||||
|
background: var(--gray-50);
|
||||||
|
padding: 2.5rem 2rem;
|
||||||
|
border-top: 1px solid var(--gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
border: none;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 140px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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-danger-custom {
|
||||||
|
background: linear-gradient(135deg, var(--danger-color) 0%, var(--danger-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger-custom:hover {
|
||||||
|
background: linear-gradient(135deg, var(--danger-dark) 0%, #b91c1c 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary-custom {
|
||||||
|
background: linear-gradient(135deg, var(--gray-500) 0%, var(--gray-600) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary-custom:hover {
|
||||||
|
background: linear-gradient(135deg, var(--gray-600) 0%, var(--gray-700) 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced Modal */
|
||||||
|
.modal-content {
|
||||||
|
border-radius: var(--border-radius-xl);
|
||||||
|
border: none;
|
||||||
|
box-shadow: var(--shadow-2xl);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header-danger {
|
||||||
|
background: linear-gradient(135deg, var(--danger-color) 0%, var(--danger-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2rem 2.5rem;
|
||||||
|
border: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title-custom {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body-custom {
|
||||||
|
padding: 2.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-message {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: var(--gray-700);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-warning {
|
||||||
|
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
||||||
|
border: 1px solid var(--warning-color);
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer-custom {
|
||||||
|
padding: 2rem 2.5rem;
|
||||||
|
border: none;
|
||||||
|
background: var(--gray-50);
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success State */
|
||||||
|
.success-message {
|
||||||
|
color: var(--success-color);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading State */
|
||||||
|
.btn.loading {
|
||||||
|
opacity: 0.7;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.loading::after {
|
||||||
|
content: "";
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-left: 0.75rem;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-top-color: currentColor;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes spin {
|
||||||
|
0%
|
||||||
|
|
||||||
|
{
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@@media (max-width: 768px) {
|
||||||
|
.container
|
||||||
|
|
||||||
|
{
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-content {
|
||||||
|
padding: 2rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes fadeIn {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="delete-card fade-in">
|
||||||
|
<!-- Header Section -->
|
||||||
|
<div class="delete-header">
|
||||||
|
<div class="delete-icon">
|
||||||
|
<i class="bi bi-trash3-fill"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 col-12">
|
<h1 class="delete-title">Delete Questionnaire</h1>
|
||||||
<label asp-for="Description" class="control-label"></label>
|
<p class="delete-subtitle">This action will permanently remove this questionnaire and all associated data</p>
|
||||||
<textarea asp-for="Description" class="form-control" disabled></textarea>
|
|
||||||
<span asp-validation-for="Description" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3 col-12">
|
|
||||||
<span class="h5">Total Number of questions:<span class="badge-info p-2 shadow rounded"> @Model.Questions.Count</span></span><br />
|
|
||||||
@* <span class="h5">Total Number of answer: @Model.Questions.Select(x=>x.Answers.Select(x=>x.Id)).Count()</span> *@
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Add the delete confirmation modal trigger button -->
|
<!-- Content Section -->
|
||||||
|
<div class="delete-content">
|
||||||
|
<!-- Warning Alert -->
|
||||||
<div class="mb-3 container">
|
<div class="warning-alert">
|
||||||
<hr class="border border-primary border-2 opacity-50">
|
<div class="warning-content">
|
||||||
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal">Delete</button>
|
<h3 class="warning-title">⚠️ Critical Warning</h3>
|
||||||
|
<ul class="warning-list">
|
||||||
<button asp-action="Index" class="btn btn-info">Back to list <i class="bi bi-arrow-return-left"></i></button>
|
<li><i class="bi bi-x-circle-fill"></i>This action cannot be undone</li>
|
||||||
|
<li><i class="bi bi-x-circle-fill"></i>All questions and answers will be permanently deleted</li>
|
||||||
|
<li><i class="bi bi-x-circle-fill"></i>Any survey responses will be lost forever</li>
|
||||||
|
<li><i class="bi bi-x-circle-fill"></i>Associated data and analytics will be removed</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
|
||||||
|
<!-- Questionnaire Information -->
|
||||||
|
<div class="questionnaire-info">
|
||||||
|
<div class="info-header">
|
||||||
|
<div class="info-icon">
|
||||||
|
<i class="bi bi-file-text"></i>
|
||||||
|
</div>
|
||||||
|
<h2 class="info-title">Questionnaire Details</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Title</div>
|
||||||
|
<div class="info-value">@Model.Title</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">Questionnaire ID</div>
|
||||||
|
<div class="info-value">#@Model.Id</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-description">
|
||||||
|
<div class="info-label">Description</div>
|
||||||
|
<div class="info-description-content">
|
||||||
|
@if (!string.IsNullOrEmpty(Model.Description))
|
||||||
|
{
|
||||||
|
@Html.Raw(Model.Description)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<em class="text-muted">No description provided</em>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics -->
|
||||||
|
<div class="stats-section">
|
||||||
|
<h3 class="stats-title">
|
||||||
|
<i class="bi bi-graph-up"></i>
|
||||||
|
Content Statistics
|
||||||
|
</h3>
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="stat-number">@Model.Questions.Count</span>
|
||||||
|
<span class="stat-label">Questions</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="stat-number">@Model.Questions.Sum(q => q.Answers.Count)</span>
|
||||||
|
<span class="stat-label">Total Answers</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<span class="stat-number">@Model.Questions.Count(q => q.Answers.Any(a => a.IsOtherOption))</span>
|
||||||
|
<span class="stat-label">Other Options</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
<!-- Action Section -->
|
||||||
<div class="modal-dialog" role="document">
|
<div class="action-section">
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button type="button" class="btn btn-danger-custom" data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||||
|
<i class="bi bi-trash3-fill"></i>
|
||||||
|
Delete Questionnaire
|
||||||
|
</button>
|
||||||
|
<a asp-action="Index" class="btn btn-secondary-custom">
|
||||||
|
<i class="bi bi-arrow-left"></i>
|
||||||
|
Back to List
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Enhanced Delete Confirmation Modal -->
|
||||||
|
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header-danger">
|
||||||
<h5 class="modal-title" id="deleteModalLabel">Delete Confirmation</h5>
|
<h5 class="modal-title-custom" id="deleteModalLabel">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<i class="bi bi-exclamation-triangle-fill"></i>
|
||||||
<span aria-hidden="true">×</span>
|
Confirm Deletion
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body-custom">
|
||||||
|
<p class="modal-message" id="deleteMessage">
|
||||||
|
Are you absolutely sure you want to delete this questionnaire?
|
||||||
|
</p>
|
||||||
|
<div class="modal-warning">
|
||||||
|
<i class="bi bi-shield-exclamation"></i>
|
||||||
|
<strong>This action is irreversible!</strong> Once deleted, this questionnaire and all its data cannot be recovered.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer-custom">
|
||||||
|
<button type="button" class="btn btn-secondary-custom" data-bs-dismiss="modal">
|
||||||
|
<i class="bi bi-x"></i>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-danger-custom" id="deleteButton">
|
||||||
|
<i class="bi bi-trash3-fill"></i>
|
||||||
|
Yes, Delete Forever
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
|
||||||
<p id="deleteMessage">Are you sure you want to delete this questionnaire</p>
|
|
||||||
<p class="text-danger">If you delete, you can't recover it.</p>
|
|
||||||
<input type="text" class="form-control" id="deleteConfirmation" placeholder="Type the questionnaire name to confirm">
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
|
||||||
<button type="button" class="btn btn-danger" id="deleteButton" disabled>Delete</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Delete Confirmation Modal -->
|
|
||||||
|
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.11.4/ckeditor.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.11.4/ckeditor.js"></script>
|
||||||
<!-- Add these links in the <head> section of your HTML file -->
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js" integrity="sha512-aVKKRRi/Q/YV+4mjoKBsE4x3H+BkegoM/em46NNlCqNTmUYADjBbeNefNxYV7giUp0VxICtqdrbqU7iVaeZNXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
|
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
CKEDITOR.replace("Description");
|
|
||||||
</script>
|
|
||||||
@{
|
|
||||||
<partial name="_ValidationScriptsPartial" />
|
|
||||||
}
|
|
||||||
<script>
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var itemId = @Model.Id; // Assuming you can get the item ID from the model
|
var itemId = @Model.Id;
|
||||||
|
|
||||||
// Enable delete button when the input matches the item name
|
// Enhanced delete button click event
|
||||||
$('#deleteConfirmation').on('input', function () {
|
|
||||||
var itemName = '@Model.Title'; // Item name from the model
|
|
||||||
var inputText = $(this).val().trim().toLowerCase();
|
|
||||||
var isMatch = inputText === itemName.toLowerCase();
|
|
||||||
// var space = $(this).val.space();
|
|
||||||
$('#deleteButton').prop('disabled', !isMatch);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear input and disable button when modal is hidden
|
|
||||||
$('#deleteModal').on('hidden.bs.modal', function () {
|
|
||||||
$('#deleteConfirmation').val('');
|
|
||||||
$('#deleteButton').prop('disabled', true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete button click event
|
|
||||||
$('#deleteButton').on('click', function () {
|
$('#deleteButton').on('click', function () {
|
||||||
// Make an AJAX request to delete the item
|
var $btn = $(this);
|
||||||
|
var $modal = $('#deleteModal');
|
||||||
|
|
||||||
|
// Add loading state
|
||||||
|
$btn.addClass('loading').prop('disabled', true);
|
||||||
|
|
||||||
|
// Make AJAX request to delete the item
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/admin/Questionnaire/Delete/' + itemId,
|
url: '/admin/Questionnaire/Delete/' + itemId,
|
||||||
type: 'POST', // or 'DELETE' if you have a dedicated delete action
|
type: 'POST',
|
||||||
success: function (result) {
|
success: function (result) {
|
||||||
// Hide the confirmation details
|
// Update modal content to show success
|
||||||
$('#deleteConfirmation, .text-danger').hide();
|
$('.modal-header-danger').removeClass('modal-header-danger').css({
|
||||||
$('#deleteButton').hide();
|
'background': 'linear-gradient(135deg, #10b981 0%, #059669 100%)'
|
||||||
// Show the success message
|
});
|
||||||
$('#deleteMessage').text('Questionnaire deleted successfully.').addClass('text-success h4').show();
|
|
||||||
// Show the modal
|
$('.modal-title-custom').html('<i class="bi bi-check-circle-fill"></i> Success');
|
||||||
$('#deleteModal').modal('show');
|
|
||||||
// Automatically close the modal after 4 seconds
|
$('#deleteMessage').html('<div class="success-message"><i class="bi bi-check-circle-fill"></i>Questionnaire deleted successfully!</div>');
|
||||||
|
|
||||||
|
$('.modal-warning').hide();
|
||||||
|
$('.modal-footer-custom').hide();
|
||||||
|
|
||||||
|
// Automatically redirect after showing success
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
$('#deleteModal').modal('hide');
|
$modal.modal('hide');
|
||||||
// Redirect to the index action method after closing the modal
|
|
||||||
window.location.href = '/admin/Questionnaire/Index';
|
window.location.href = '/admin/Questionnaire/Index';
|
||||||
}, 1000);
|
}, 2000);
|
||||||
},
|
},
|
||||||
error: function (error) {
|
error: function (xhr, status, error) {
|
||||||
// Handle error
|
// Remove loading state
|
||||||
$('#deleteMessage').text('Failed to delete item.').show();
|
$btn.removeClass('loading').prop('disabled', false);
|
||||||
// Show the modal
|
|
||||||
$('#deleteModal').modal('show');
|
// Show error message
|
||||||
// Automatically close the modal after 4 seconds
|
$('#deleteMessage').html('<div class="text-danger"><i class="bi bi-exclamation-triangle-fill"></i> Failed to delete questionnaire. Please try again.</div>');
|
||||||
|
|
||||||
|
// Auto-hide error message after 3 seconds
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
$('#deleteModal').modal('hide');
|
$modal.modal('hide');
|
||||||
}, 1000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reset modal when hidden
|
||||||
|
$('#deleteModal').on('hidden.bs.modal', function () {
|
||||||
|
// Reset button state
|
||||||
|
$('#deleteButton').removeClass('loading').prop('disabled', false);
|
||||||
|
|
||||||
|
// Reset modal content if needed
|
||||||
|
$('.modal-header-danger').css({
|
||||||
|
'background': 'linear-gradient(135deg, var(--danger-color) 0%, var(--danger-dark) 100%)'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.modal-title-custom').html('<i class="bi bi-exclamation-triangle-fill"></i> Confirm Deletion');
|
||||||
|
$('#deleteMessage').text('Are you absolutely sure you want to delete this questionnaire?');
|
||||||
|
$('.modal-warning').show();
|
||||||
|
$('.modal-footer-custom').show();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
|
||||||
$(document).ready(function () {
|
|
||||||
$(".readMoreBtn").click(function () {
|
|
||||||
$(this).closest('.text-danger').find('.more-title').toggle();
|
|
||||||
$(this).text(function (_, text) {
|
|
||||||
return text === "Read More" ? "Read Less" : "Read More";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,121 +1,804 @@
|
||||||
@model QuestionnaireViewModel
|
@model QuestionnaireViewModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Details";
|
ViewData["Title"] = "Questionnaire Details";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Modern Design System */
|
||||||
|
@@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primary-color: #6366f1;
|
||||||
|
--primary-light: #8b5cf6;
|
||||||
|
--primary-dark: #4338ca;
|
||||||
|
--success-color: #10b981;
|
||||||
|
--success-light: #34d399;
|
||||||
|
--success-dark: #059669;
|
||||||
|
--info-color: #06b6d4;
|
||||||
|
--info-light: #22d3ee;
|
||||||
|
--info-dark: #0891b2;
|
||||||
|
--warning-color: #f59e0b;
|
||||||
|
--warning-light: #fbbf24;
|
||||||
|
--warning-dark: #d97706;
|
||||||
|
--gray-50: #f8fafc;
|
||||||
|
--gray-100: #f1f5f9;
|
||||||
|
--gray-200: #e2e8f0;
|
||||||
|
--gray-300: #cbd5e1;
|
||||||
|
--gray-400: #94a3b8;
|
||||||
|
--gray-500: #64748b;
|
||||||
|
--gray-600: #475569;
|
||||||
|
--gray-700: #334155;
|
||||||
|
--gray-800: #1e293b;
|
||||||
|
--gray-900: #0f172a;
|
||||||
|
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||||
|
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
|
||||||
|
--border-radius-sm: 8px;
|
||||||
|
--border-radius-md: 12px;
|
||||||
|
--border-radius-lg: 16px;
|
||||||
|
--border-radius-xl: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
<div class="container p-4">
|
* {
|
||||||
<div>
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
<a class="btn btn-primary" asp-action="Index">Back to List</a>
|
}
|
||||||
|
|
||||||
</div>
|
body {
|
||||||
|
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
<div class="card">
|
.container {
|
||||||
<div class="card-header">
|
max-width: 1200px;
|
||||||
Details
|
}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-body shadow rounded ">
|
/* Header Section */
|
||||||
<div class="card-title">
|
.page-header {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius-xl);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
padding: 2.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, var(--primary-color), var(--primary-light), var(--info-color));
|
||||||
|
}
|
||||||
|
|
||||||
<h4>Questionnaire details</h4>
|
.header-content {
|
||||||
<br />
|
display: flex;
|
||||||
<div>
|
align-items: center;
|
||||||
<span class="badge badge-warning shadow text-white m-1 p-2">ID</span>
|
justify-content: space-between;
|
||||||
</div>
|
flex-wrap: wrap;
|
||||||
<div>
|
gap: 1.5rem;
|
||||||
<span class="badge badge-primary shadow m-1 p-2">Questionnaire</span>
|
}
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="badge badge-success shadow m-1 p-2">Question</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="badge badge-info shadow m-1 p-2">Answer</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
.header-info {
|
||||||
<table class="table table-responsive table-bordered table-hover ">
|
display: flex;
|
||||||
<thead>
|
align-items: center;
|
||||||
<tr >
|
gap: 1.5rem;
|
||||||
<th scope="col" class="text-warning h5">ID</th>
|
}
|
||||||
<th scope="col" class="text-primary h5">Questionnaire</th>
|
|
||||||
<th scope="col" class="text-success h5 ">Questions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th scope="row"><span class="badge p-2 m-1 bg-warning shadow-sm rounded">@Model.Id</span></th>
|
|
||||||
<th scope="row">
|
|
||||||
|
|
||||||
<span class="badge p-2 m-1 bg-primary shadow-sm rounded">@Model.Title</span>
|
.header-icon {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%);
|
||||||
|
color: white;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.75rem;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
</th>
|
.header-text h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-subtitle {
|
||||||
|
color: var(--gray-600);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
<td class="h6">
|
.back-button {
|
||||||
<table class="table table-responsive w-100 d-block d-md-table table-bordered table-hover ">
|
background: linear-gradient(135deg, var(--gray-500) 0%, var(--gray-600) 100%);
|
||||||
<tr >
|
color: white;
|
||||||
<th class="text-success">Id</th>
|
border: none;
|
||||||
<th class="text-success">Question</th>
|
padding: 0.75rem 1.5rem;
|
||||||
<th class="text-success">Question Type</th>
|
border-radius: var(--border-radius-md);
|
||||||
<th class="text-info">Answers</th>
|
font-weight: 600;
|
||||||
</tr>
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-button:hover {
|
||||||
|
background: linear-gradient(135deg, var(--gray-600) 0%, var(--gray-700) 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main Content */
|
||||||
|
.main-content {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius-xl);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overview Section */
|
||||||
|
.overview-section {
|
||||||
|
background: linear-gradient(135deg, var(--gray-50) 0%, var(--gray-100) 100%);
|
||||||
|
padding: 2.5rem;
|
||||||
|
border-bottom: 1px solid var(--gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-icon {
|
||||||
|
background: var(--info-color);
|
||||||
|
color: white;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card {
|
||||||
|
background: white;
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card-icon.warning {
|
||||||
|
background: var(--warning-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card-icon.primary {
|
||||||
|
background: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card-icon.success {
|
||||||
|
background: var(--success-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card-icon.info {
|
||||||
|
background: var(--info-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-500);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-card-value {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-800);
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-card {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-content {
|
||||||
|
color: var(--gray-700);
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Questions Section */
|
||||||
|
.questions-section {
|
||||||
|
padding: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-icon {
|
||||||
|
background: var(--success-color);
|
||||||
|
color: white;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-title h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-count {
|
||||||
|
background: linear-gradient(135deg, var(--success-color) 0%, var(--success-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Question Cards */
|
||||||
|
.questions-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-card {
|
||||||
|
background: linear-gradient(135deg, var(--gray-50) 0%, white 100%);
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-header {
|
||||||
|
background: linear-gradient(135deg, var(--success-color) 0%, var(--success-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 1.5rem;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 20"><defs><radialGradient id="a" cx="50%" cy="0%" r="100%"><stop offset="0%" stop-color="white" stop-opacity="0.1"/><stop offset="100%" stop-color="white" stop-opacity="0"/></radialGradient></defs><rect width="100" height="20" fill="url(%23a)"/></svg>');
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-id {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-type {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-text {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Answers Section */
|
||||||
|
.answers-section {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-icon {
|
||||||
|
background: var(--info-color);
|
||||||
|
color: white;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-card {
|
||||||
|
background: white;
|
||||||
|
border: 2px solid var(--gray-200);
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
padding: 1rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-card:hover {
|
||||||
|
border-color: var(--info-color);
|
||||||
|
box-shadow: 0 0 0 3px rgba(6, 182, 212, 0.1);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-card.other-option {
|
||||||
|
border-color: var(--warning-color);
|
||||||
|
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 20%, white 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-card.other-option::before {
|
||||||
|
content: "OTHER";
|
||||||
|
position: absolute;
|
||||||
|
top: -8px;
|
||||||
|
left: 12px;
|
||||||
|
background: var(--warning-color);
|
||||||
|
color: white;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
font-size: 0.625rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: between;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-id {
|
||||||
|
background: linear-gradient(135deg, var(--info-color) 0%, var(--info-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-text {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-800);
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty State */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem;
|
||||||
|
color: var(--gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state i {
|
||||||
|
font-size: 3rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--gray-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state h3 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats Summary */
|
||||||
|
.stats-summary {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer Actions */
|
||||||
|
.footer-actions {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@@media (max-width: 768px) {
|
||||||
|
.container
|
||||||
|
|
||||||
@foreach (var question in Model.Questions)
|
|
||||||
{
|
{
|
||||||
<tr>
|
padding: 0 1rem;
|
||||||
<td> <span class="badge p-2 m-1 bg-success ">@question.Id</span></td>
|
}
|
||||||
<td>
|
|
||||||
<span class="badge p-2 m-1 bg-success ">@question.Text</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge p-2 m-1 bg-success ">@question.Type</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<table class="table-borderless">
|
|
||||||
<tr>
|
|
||||||
<th class="text-info">Id</th>
|
|
||||||
<th class="text-info">Answer</th>
|
|
||||||
</tr>
|
|
||||||
@foreach (var answer in question.Answers)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span class="badge p-2 m-1 bg-info shadow-sm">@answer.Id</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge p-2 m-1 bg-info shadow-sm">@answer.Text</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</table>
|
|
||||||
</td>
|
/* Animations */
|
||||||
</tr>
|
.fade-in {
|
||||||
|
animation: fadeIn 0.6s ease-out;
|
||||||
}
|
}
|
||||||
</table>
|
|
||||||
|
|
||||||
|
@@keyframes fadeIn {
|
||||||
|
from
|
||||||
|
|
||||||
</td>
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
|
||||||
</tr>
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
</tbody>
|
}
|
||||||
|
|
||||||
</table>
|
.slide-in {
|
||||||
<div>
|
animation: slideIn 0.4s ease-out;
|
||||||
<a class="btn btn-primary" asp-action="Index">Back to List</a>
|
}
|
||||||
|
|
||||||
|
@@keyframes slideIn {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<!-- Page Header -->
|
||||||
|
<div class="page-header fade-in">
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-info">
|
||||||
|
<div class="header-icon">
|
||||||
|
<i class="bi bi-file-text-fill"></i>
|
||||||
|
</div>
|
||||||
|
<div class="header-text">
|
||||||
|
<h1>Questionnaire Details</h1>
|
||||||
|
<p class="header-subtitle">Complete overview of questionnaire structure and content</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a asp-action="Index" class="back-button">
|
||||||
|
<i class="bi bi-arrow-left"></i>
|
||||||
|
Back to List
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Stats Summary -->
|
||||||
|
<div class="stats-summary fade-in">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">@Model.Questions.Count</span>
|
||||||
|
<span class="stat-label">Questions</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">@Model.Questions.Sum(q => q.Answers.Count)</span>
|
||||||
|
<span class="stat-label">Total Answers</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">@Model.Questions.Count(q => q.Answers.Any(a => a.IsOtherOption))</span>
|
||||||
|
<span class="stat-label">Other Options</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">@Model.Questions.Select(q => q.Type).Distinct().Count()</span>
|
||||||
|
<span class="stat-label">Question Types</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="main-content fade-in">
|
||||||
|
<!-- Overview Section -->
|
||||||
|
<div class="overview-section">
|
||||||
|
<div class="overview-header">
|
||||||
|
<div class="overview-icon">
|
||||||
|
<i class="bi bi-info-circle-fill"></i>
|
||||||
|
</div>
|
||||||
|
<h2 class="overview-title">Overview</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overview-grid">
|
||||||
|
<div class="overview-card slide-in">
|
||||||
|
<div class="overview-card-header">
|
||||||
|
<div class="overview-card-icon warning">
|
||||||
|
<i class="bi bi-hash"></i>
|
||||||
|
</div>
|
||||||
|
<span class="overview-card-label">ID</span>
|
||||||
|
</div>
|
||||||
|
<div class="overview-card-value">#@Model.Id</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overview-card slide-in">
|
||||||
|
<div class="overview-card-header">
|
||||||
|
<div class="overview-card-icon primary">
|
||||||
|
<i class="bi bi-card-heading"></i>
|
||||||
|
</div>
|
||||||
|
<span class="overview-card-label">Title</span>
|
||||||
|
</div>
|
||||||
|
<div class="overview-card-value">@Model.Title</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overview-card description-card slide-in">
|
||||||
|
<div class="overview-card-header">
|
||||||
|
<div class="overview-card-icon info">
|
||||||
|
<i class="bi bi-card-text"></i>
|
||||||
|
</div>
|
||||||
|
<span class="overview-card-label">Description</span>
|
||||||
|
</div>
|
||||||
|
<div class="description-content">
|
||||||
|
@if (!string.IsNullOrEmpty(Model.Description))
|
||||||
|
{
|
||||||
|
@Html.Raw(Model.Description)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<em class="text-muted">No description provided</em>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Questions Section -->
|
||||||
|
<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">
|
||||||
|
@Model.Questions.Count Question@(Model.Questions.Count != 1 ? "s" : "")
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (Model.Questions.Any())
|
||||||
|
{
|
||||||
|
<div class="questions-grid">
|
||||||
|
@foreach (var question in Model.Questions.Select((q, index) => new { Question = q, Index = index }))
|
||||||
|
{
|
||||||
|
<div class="question-card slide-in">
|
||||||
|
<div class="question-header">
|
||||||
|
<div class="question-meta">
|
||||||
|
<span class="question-id">ID: @question.Question.Id</span>
|
||||||
|
<span class="question-type">@question.Question.Type</span>
|
||||||
|
</div>
|
||||||
|
<h3 class="question-text">
|
||||||
|
Question @(question.Index + 1): @question.Question.Text
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="answers-section">
|
||||||
|
<div class="answers-header">
|
||||||
|
<div class="answers-icon">
|
||||||
|
<i class="bi bi-list-ul"></i>
|
||||||
|
</div>
|
||||||
|
<h4 class="answers-title">
|
||||||
|
Answers (@question.Question.Answers.Count)
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (question.Question.Answers.Any())
|
||||||
|
{
|
||||||
|
<div class="answers-grid">
|
||||||
|
@foreach (var answer in question.Question.Answers)
|
||||||
|
{
|
||||||
|
<div class="answer-card @(answer.IsOtherOption ? "other-option" : "")">
|
||||||
|
<div class="answer-meta">
|
||||||
|
<span class="answer-id">ID: @answer.Id</span>
|
||||||
|
</div>
|
||||||
|
<div class="answer-text">@answer.Text</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="bi bi-inbox"></i>
|
||||||
|
<h3>No Answers</h3>
|
||||||
|
<p>This question doesn't have any answer options.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="empty-state">
|
||||||
|
<i class="bi bi-question-circle"></i>
|
||||||
|
<h3>No Questions</h3>
|
||||||
|
<p>This questionnaire doesn't contain any questions yet.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer Actions -->
|
||||||
|
<div class="footer-actions">
|
||||||
|
<a asp-action="Index" class="back-button">
|
||||||
|
<i class="bi bi-arrow-left"></i>
|
||||||
|
Back to List
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,50 @@
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bulk Selection Controls */
|
||||||
|
.bulk-selection-controls {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
padding: 2rem;
|
||||||
|
margin: 2rem 0;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
display: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-selection-controls.show {
|
||||||
|
display: block;
|
||||||
|
animation: slideDown 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes slideDown {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection-summary {
|
||||||
|
background: linear-gradient(135deg, var(--info-color) 0%, #0891b2 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Question Container Enhancements */
|
/* Question Container Enhancements */
|
||||||
#customCard {
|
#customCard {
|
||||||
background: white;
|
background: white;
|
||||||
|
|
@ -328,6 +372,96 @@
|
||||||
transform: scaleY(1);
|
transform: scaleY(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.question.selected {
|
||||||
|
border-color: var(--danger-color);
|
||||||
|
background: linear-gradient(135deg, #fef2f2 0%, #fecaca 10%, white 100%);
|
||||||
|
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1), var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question.selected::before {
|
||||||
|
background: linear-gradient(135deg, var(--danger-color), #dc2626);
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Question Checkbox Styling */
|
||||||
|
.question-checkbox {
|
||||||
|
position: absolute;
|
||||||
|
top: 1.5rem;
|
||||||
|
right: 1.5rem;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-checkbox {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: 2px solid var(--gray-300);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-checkbox:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-checkbox.checked {
|
||||||
|
background: var(--danger-color);
|
||||||
|
border-color: var(--danger-color);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-checkbox input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Master checkbox styling */
|
||||||
|
.master-checkbox-container {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 2rem 0;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-checkbox {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: 2px solid var(--gray-300);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-checkbox:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-checkbox.checked {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.master-checkbox input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Question Separator */
|
/* Question Separator */
|
||||||
.question-separator {
|
.question-separator {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -366,11 +500,12 @@
|
||||||
/* Question Title Styling */
|
/* Question Title Styling */
|
||||||
.question-title {
|
.question-title {
|
||||||
background: linear-gradient(135deg, var(--gray-50) 0%, var(--gray-100) 100%);
|
background: linear-gradient(135deg, var(--gray-50) 0%, var(--gray-100) 100%);
|
||||||
padding: 2rem;
|
padding: 2rem 2rem 2rem 3rem;
|
||||||
border-radius: var(--border-radius-md);
|
border-radius: var(--border-radius-md);
|
||||||
border: 1px solid var(--gray-200);
|
border: 1px solid var(--gray-200);
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.question-title:hover {
|
.question-title:hover {
|
||||||
|
|
@ -689,7 +824,7 @@
|
||||||
|
|
||||||
<!-- Basic Information -->
|
<!-- Basic Information -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="Title" class="control-label">
|
<label asp-for="Title" class="control-label">
|
||||||
<i class="bi bi-card-heading me-2"></i>Survey Title
|
<i class="bi bi-card-heading me-2"></i>Survey Title
|
||||||
|
|
@ -698,7 +833,7 @@
|
||||||
<span asp-validation-for="Title" class="text-danger"></span>
|
<span asp-validation-for="Title" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="Description" class="control-label">
|
<label asp-for="Description" class="control-label">
|
||||||
<i class="bi bi-card-text me-2"></i>Description
|
<i class="bi bi-card-text me-2"></i>Description
|
||||||
|
|
@ -715,10 +850,48 @@
|
||||||
<h4><i class="bi bi-question-circle me-2"></i>Edit Questions or Add New Questions</h4>
|
<h4><i class="bi bi-question-circle me-2"></i>Edit Questions or Add New Questions</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Master Checkbox for Select All -->
|
||||||
|
<div class="master-checkbox-container">
|
||||||
|
<div class="master-checkbox" id="selectAllCheckbox">
|
||||||
|
<input type="checkbox" id="selectAllInput">
|
||||||
|
<i class="bi bi-check" style="display: none;"></i>
|
||||||
|
</div>
|
||||||
|
<label for="selectAllInput" class="mb-0 font-weight-bold">
|
||||||
|
<i class="bi bi-check-square me-2"></i>Select All Questions
|
||||||
|
</label>
|
||||||
|
<div class="ms-auto">
|
||||||
|
<span id="selectedCount" class="badge bg-primary">0 selected</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bulk Selection Controls -->
|
||||||
|
<div class="bulk-selection-controls" id="bulkControls">
|
||||||
|
<div class="selection-summary">
|
||||||
|
<i class="bi bi-info-circle"></i>
|
||||||
|
<span id="bulkSelectionText">0 questions selected</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-3 flex-wrap">
|
||||||
|
<button type="button" class="btn btn-danger" id="deleteSelectedQuestions">
|
||||||
|
<i class="bi bi-trash-fill"></i> Delete Selected Questions
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-secondary" id="deselectAll">
|
||||||
|
<i class="bi bi-x-circle"></i> Deselect All
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="questionsContainer">
|
<div id="questionsContainer">
|
||||||
@for (int i = 0; i < Model.Questions.Count; i++)
|
@for (int i = 0; i < Model.Questions.Count; i++)
|
||||||
{
|
{
|
||||||
<div class="question fade-in">
|
<div class="question fade-in" data-question-index="@i">
|
||||||
|
<!-- Question Checkbox -->
|
||||||
|
<div class="question-checkbox">
|
||||||
|
<div class="custom-checkbox question-select-checkbox">
|
||||||
|
<input type="checkbox" class="question-checkbox-input">
|
||||||
|
<i class="bi bi-check" style="display: none;"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="question-separator">
|
<div class="question-separator">
|
||||||
<div class="line-container">
|
<div class="line-container">
|
||||||
<hr class="line-start">
|
<hr class="line-start">
|
||||||
|
|
@ -837,7 +1010,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Enhanced Confirmation Modal -->
|
<!-- Enhanced Confirmation Modal for Single Question -->
|
||||||
<div class="modal fade" id="confirmDeleteModal" tabindex="-1" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
|
<div class="modal fade" id="confirmDeleteModal" tabindex="-1" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
@ -866,6 +1039,50 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Enhanced Bulk Delete Confirmation Modal -->
|
||||||
|
<div class="modal fade" id="confirmBulkDeleteModal" tabindex="-1" aria-labelledby="confirmBulkDeleteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="confirmBulkDeleteModalLabel">
|
||||||
|
<i class="bi bi-exclamation-triangle me-2"></i>Confirm Multiple Deletion
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<i class="bi bi-exclamation-triangle me-2"></i>
|
||||||
|
<strong>Critical Warning:</strong> You are about to delete <span id="deletionCount" class="fw-bold">0</span> question(s).
|
||||||
|
</div>
|
||||||
|
<p class="mb-3">This action will permanently remove:</p>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li class="mb-2"><i class="bi bi-check text-danger me-2"></i><span id="questionCount">0</span> questions</li>
|
||||||
|
<li class="mb-2"><i class="bi bi-check text-danger me-2"></i>All associated answers</li>
|
||||||
|
<li class="mb-2"><i class="bi bi-check text-danger me-2"></i>All related user responses</li>
|
||||||
|
</ul>
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
|
<strong>Note:</strong> This action cannot be undone. Please ensure you want to proceed with this bulk deletion.
|
||||||
|
</div>
|
||||||
|
<div class="preview-container" id="deletionPreview">
|
||||||
|
<h6 class="fw-bold mb-3">Questions to be deleted:</h6>
|
||||||
|
<div class="preview-list" style="max-height: 200px; overflow-y: auto; background: #f8f9fa; padding: 1rem; border-radius: 8px;">
|
||||||
|
<!-- Preview questions will be populated here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||||
|
<i class="bi bi-x"></i> Cancel
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-danger" id="confirmBulkDelete">
|
||||||
|
<i class="bi bi-trash"></i> Delete All Selected Questions
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.11.4/ckeditor.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.11.4/ckeditor.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -879,11 +1096,250 @@
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
let questionToDelete = null;
|
let questionToDelete = null;
|
||||||
|
let selectedQuestions = [];
|
||||||
|
|
||||||
|
// Update selection UI
|
||||||
|
function updateSelectionUI() {
|
||||||
|
const selectedCount = selectedQuestions.length;
|
||||||
|
$('#selectedCount').text(selectedCount + ' selected');
|
||||||
|
$('#bulkSelectionText').text(selectedCount + ' question' + (selectedCount !== 1 ? 's' : '') + ' selected');
|
||||||
|
|
||||||
|
if (selectedCount > 0) {
|
||||||
|
$('#bulkControls').addClass('show');
|
||||||
|
} else {
|
||||||
|
$('#bulkControls').removeClass('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update master checkbox state
|
||||||
|
const totalQuestions = $('.question-checkbox-input').length;
|
||||||
|
const masterCheckbox = $('#selectAllCheckbox');
|
||||||
|
const masterInput = $('#selectAllInput');
|
||||||
|
|
||||||
|
if (selectedCount === 0) {
|
||||||
|
masterCheckbox.removeClass('checked');
|
||||||
|
masterInput.prop('checked', false);
|
||||||
|
masterCheckbox.find('i').hide();
|
||||||
|
} else if (selectedCount === totalQuestions) {
|
||||||
|
masterCheckbox.addClass('checked');
|
||||||
|
masterInput.prop('checked', true);
|
||||||
|
masterCheckbox.find('i').show();
|
||||||
|
} else {
|
||||||
|
masterCheckbox.removeClass('checked');
|
||||||
|
masterInput.prop('checked', false);
|
||||||
|
masterCheckbox.find('i').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle individual question checkbox clicks
|
||||||
|
$(document).on('change', '.question-checkbox-input', function() {
|
||||||
|
const questionContainer = $(this).closest('.question');
|
||||||
|
const questionIndex = questionContainer.data('question-index');
|
||||||
|
const checkbox = $(this).closest('.custom-checkbox');
|
||||||
|
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
if (!selectedQuestions.includes(questionIndex)) {
|
||||||
|
selectedQuestions.push(questionIndex);
|
||||||
|
questionContainer.addClass('selected');
|
||||||
|
checkbox.addClass('checked');
|
||||||
|
checkbox.find('i').show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedQuestions = selectedQuestions.filter(q => q !== questionIndex);
|
||||||
|
questionContainer.removeClass('selected');
|
||||||
|
checkbox.removeClass('checked');
|
||||||
|
checkbox.find('i').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectionUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle checkbox visual clicks
|
||||||
|
$(document).on('click', '.question-select-checkbox', function() {
|
||||||
|
const input = $(this).find('input');
|
||||||
|
input.prop('checked', !input.is(':checked')).trigger('change');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle master checkbox
|
||||||
|
$('#selectAllCheckbox').click(function() {
|
||||||
|
const shouldSelectAll = selectedQuestions.length !== $('.question-checkbox-input').length;
|
||||||
|
|
||||||
|
$('.question-checkbox-input').each(function() {
|
||||||
|
const questionContainer = $(this).closest('.question');
|
||||||
|
const questionIndex = questionContainer.data('question-index');
|
||||||
|
const checkbox = $(this).closest('.custom-checkbox');
|
||||||
|
|
||||||
|
$(this).prop('checked', shouldSelectAll);
|
||||||
|
|
||||||
|
if (shouldSelectAll) {
|
||||||
|
if (!selectedQuestions.includes(questionIndex)) {
|
||||||
|
selectedQuestions.push(questionIndex);
|
||||||
|
}
|
||||||
|
questionContainer.addClass('selected');
|
||||||
|
checkbox.addClass('checked');
|
||||||
|
checkbox.find('i').show();
|
||||||
|
} else {
|
||||||
|
selectedQuestions = [];
|
||||||
|
questionContainer.removeClass('selected');
|
||||||
|
checkbox.removeClass('checked');
|
||||||
|
checkbox.find('i').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSelectionUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deselect all button
|
||||||
|
$('#deselectAll').click(function() {
|
||||||
|
selectedQuestions = [];
|
||||||
|
$('.question-checkbox-input').prop('checked', false);
|
||||||
|
$('.question').removeClass('selected');
|
||||||
|
$('.custom-checkbox').removeClass('checked');
|
||||||
|
$('.custom-checkbox i').hide();
|
||||||
|
updateSelectionUI();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle bulk delete button
|
||||||
|
$('#deleteSelectedQuestions').click(function() {
|
||||||
|
if (selectedQuestions.length === 0) {
|
||||||
|
alert('Please select at least one question to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update modal content
|
||||||
|
$('#deletionCount, #questionCount').text(selectedQuestions.length);
|
||||||
|
|
||||||
|
// Populate preview
|
||||||
|
const previewContainer = $('#deletionPreview .preview-list');
|
||||||
|
previewContainer.empty();
|
||||||
|
|
||||||
|
selectedQuestions.forEach(function(questionIndex) {
|
||||||
|
const questionContainer = $(`.question[data-question-index="${questionIndex}"]`);
|
||||||
|
const questionText = questionContainer.find('.item-question').text().trim();
|
||||||
|
const truncatedText = questionText.length > 80 ? questionText.substring(0, 80) + '...' : questionText;
|
||||||
|
|
||||||
|
previewContainer.append(`
|
||||||
|
<div class="mb-2 p-2 bg-white rounded border">
|
||||||
|
<strong>Question ${questionIndex + 1}:</strong> ${truncatedText}
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#confirmBulkDeleteModal').modal('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Confirm bulk deletion
|
||||||
|
$('#confirmBulkDelete').click(function() {
|
||||||
|
const $btn = $(this);
|
||||||
|
$btn.addClass('loading').prop('disabled', true);
|
||||||
|
|
||||||
|
// Sort in descending order to avoid index issues when removing
|
||||||
|
selectedQuestions.sort((a, b) => b - a);
|
||||||
|
|
||||||
|
// Remove selected questions with animation
|
||||||
|
let delay = 0;
|
||||||
|
selectedQuestions.forEach(function(questionIndex) {
|
||||||
|
setTimeout(function() {
|
||||||
|
const questionContainer = $(`.question[data-question-index="${questionIndex}"]`);
|
||||||
|
questionContainer.fadeOut(300, function() {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, delay);
|
||||||
|
delay += 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset selection state and re-index form after all deletions
|
||||||
|
setTimeout(function() {
|
||||||
|
selectedQuestions = [];
|
||||||
|
updateSelectionUI();
|
||||||
|
|
||||||
|
// Re-index all remaining questions and their form fields
|
||||||
|
reindexAllQuestions();
|
||||||
|
|
||||||
|
$('#confirmBulkDeleteModal').modal('hide');
|
||||||
|
$btn.removeClass('loading').prop('disabled', false);
|
||||||
|
|
||||||
|
// Show success message
|
||||||
|
const successMessage = $('<div class="alert alert-success fade-in" role="alert"><i class="bi bi-check-circle me-2"></i>Selected questions deleted successfully!</div>');
|
||||||
|
$('#customCard').prepend(successMessage);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
successMessage.fadeOut(300, function() {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
}, delay + 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Function to update question numbers after deletion
|
||||||
|
function updateQuestionNumbers() {
|
||||||
|
$('.question').each(function(index) {
|
||||||
|
$(this).find('.badge').text('Question ' + (index + 1));
|
||||||
|
$(this).attr('data-question-index', index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to properly re-index all form fields after question deletion
|
||||||
|
function reindexAllQuestions() {
|
||||||
|
$('.question').each(function(questionIndex) {
|
||||||
|
const questionContainer = $(this);
|
||||||
|
|
||||||
|
// Update data attribute and badge
|
||||||
|
questionContainer.attr('data-question-index', questionIndex);
|
||||||
|
questionContainer.find('.badge').text('Question ' + (questionIndex + 1));
|
||||||
|
|
||||||
|
// Update question form fields
|
||||||
|
questionContainer.find('input[name*="Questions["]').each(function() {
|
||||||
|
const name = $(this).attr('name');
|
||||||
|
if (name) {
|
||||||
|
const newName = name.replace(/Questions\[\d+\]/, `Questions[${questionIndex}]`);
|
||||||
|
$(this).attr('name', newName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
questionContainer.find('select[name*="Questions["]').each(function() {
|
||||||
|
const name = $(this).attr('name');
|
||||||
|
if (name) {
|
||||||
|
const newName = name.replace(/Questions\[\d+\]/, `Questions[${questionIndex}]`);
|
||||||
|
$(this).attr('name', newName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
questionContainer.find('textarea[name*="Questions["]').each(function() {
|
||||||
|
const name = $(this).attr('name');
|
||||||
|
if (name) {
|
||||||
|
const newName = name.replace(/Questions\[\d+\]/, `Questions[${questionIndex}]`);
|
||||||
|
$(this).attr('name', newName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update answer form fields
|
||||||
|
questionContainer.find('.answer-group').each(function(answerIndex) {
|
||||||
|
$(this).find('input[name*="Answers["]').each(function() {
|
||||||
|
const name = $(this).attr('name');
|
||||||
|
if (name) {
|
||||||
|
const newName = name.replace(/Questions\[\d+\]\.Answers\[\d+\]/, `Questions[${questionIndex}].Answers[${answerIndex}]`);
|
||||||
|
$(this).attr('name', newName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update answer labels
|
||||||
|
const label = $(this).find('label.control-label').first();
|
||||||
|
if (label.length) {
|
||||||
|
const labelText = label.text();
|
||||||
|
if (labelText.includes('Answer ')) {
|
||||||
|
const otherContent = label.find('.other-option-badge').length > 0 ?
|
||||||
|
' <span class="other-option-badge">Other Option</span>' : '';
|
||||||
|
label.html(`Answer ${answerIndex + 1}${otherContent}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Function to add a new regular answer
|
// Function to add a new regular answer
|
||||||
$(document).on('click', '.addAnswer', function () {
|
$(document).on('click', '.addAnswer', function () {
|
||||||
var questionContainer = $(this).closest('.question');
|
var questionContainer = $(this).closest('.question');
|
||||||
var newQuestionIndex = questionContainer.index();
|
var questionIndex = questionContainer.attr('data-question-index');
|
||||||
var newAnswerIndex = questionContainer.find('.answers .answer-group').length;
|
var newAnswerIndex = questionContainer.find('.answers .answer-group').length;
|
||||||
|
|
||||||
var answerHtml = `
|
var answerHtml = `
|
||||||
|
|
@ -891,8 +1347,8 @@
|
||||||
<div class="form-group mb-0">
|
<div class="form-group mb-0">
|
||||||
<label class="control-label">Answer ${newAnswerIndex + 1}</label>
|
<label class="control-label">Answer ${newAnswerIndex + 1}</label>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<input type="text" name="Questions[${newQuestionIndex}].Answers[${newAnswerIndex}].Text" class="form-control" placeholder="Enter answer text" />
|
<input type="text" name="Questions[${questionIndex}].Answers[${newAnswerIndex}].Text" class="form-control" placeholder="Enter answer text" />
|
||||||
<input type="hidden" name="Questions[${newQuestionIndex}].Answers[${newAnswerIndex}].IsOtherOption" value="false" />
|
<input type="hidden" name="Questions[${questionIndex}].Answers[${newAnswerIndex}].IsOtherOption" value="false" />
|
||||||
<button type="button" class="btn btn-danger btn-sm removeAnswer">
|
<button type="button" class="btn btn-danger btn-sm removeAnswer">
|
||||||
<i class="bi bi-trash3"></i>
|
<i class="bi bi-trash3"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -906,7 +1362,7 @@
|
||||||
// Function to add "Other" option
|
// Function to add "Other" option
|
||||||
$(document).on('click', '.addOtherOption', function () {
|
$(document).on('click', '.addOtherOption', function () {
|
||||||
var questionContainer = $(this).closest('.question');
|
var questionContainer = $(this).closest('.question');
|
||||||
var newQuestionIndex = questionContainer.index();
|
var questionIndex = questionContainer.attr('data-question-index');
|
||||||
var newAnswerIndex = questionContainer.find('.answers .answer-group').length;
|
var newAnswerIndex = questionContainer.find('.answers .answer-group').length;
|
||||||
|
|
||||||
// Check if "Other" option already exists
|
// Check if "Other" option already exists
|
||||||
|
|
@ -924,8 +1380,8 @@
|
||||||
<span class="other-option-badge">Other Option</span>
|
<span class="other-option-badge">Other Option</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<input type="text" name="Questions[${newQuestionIndex}].Answers[${newAnswerIndex}].Text" class="form-control" placeholder="Other (please specify)" value="Other (please specify)" />
|
<input type="text" name="Questions[${questionIndex}].Answers[${newAnswerIndex}].Text" class="form-control" placeholder="Other (please specify)" value="Other (please specify)" />
|
||||||
<input type="hidden" name="Questions[${newQuestionIndex}].Answers[${newAnswerIndex}].IsOtherOption" value="true" />
|
<input type="hidden" name="Questions[${questionIndex}].Answers[${newAnswerIndex}].IsOtherOption" value="true" />
|
||||||
<button type="button" class="btn btn-warning btn-sm removeAnswer">
|
<button type="button" class="btn btn-warning btn-sm removeAnswer">
|
||||||
<i class="bi bi-trash3"></i> Remove Other
|
<i class="bi bi-trash3"></i> Remove Other
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -942,8 +1398,28 @@
|
||||||
|
|
||||||
// Function to remove an answer
|
// Function to remove an answer
|
||||||
$(document).on('click', '.removeAnswer', function () {
|
$(document).on('click', '.removeAnswer', function () {
|
||||||
|
const questionContainer = $(this).closest('.question');
|
||||||
$(this).closest('.answer-group').fadeOut(300, function() {
|
$(this).closest('.answer-group').fadeOut(300, function() {
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
|
// Re-index answers in this question
|
||||||
|
const questionIndex = questionContainer.attr('data-question-index');
|
||||||
|
questionContainer.find('.answer-group').each(function(answerIndex) {
|
||||||
|
$(this).find('input[name*="Answers["]').each(function() {
|
||||||
|
const name = $(this).attr('name');
|
||||||
|
if (name) {
|
||||||
|
const newName = name.replace(/Questions\[\d+\]\.Answers\[\d+\]/, `Questions[${questionIndex}].Answers[${answerIndex}]`);
|
||||||
|
$(this).attr('name', newName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update answer labels
|
||||||
|
const label = $(this).find('label.control-label').first();
|
||||||
|
if (label.length) {
|
||||||
|
const otherContent = label.find('.other-option-badge').length > 0 ?
|
||||||
|
' <span class="other-option-badge">Other Option</span>' : '';
|
||||||
|
label.html(`Answer ${answerIndex + 1}${otherContent}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1002,8 +1478,16 @@
|
||||||
// Confirm deletion
|
// Confirm deletion
|
||||||
$('.confirm-delete').click(function () {
|
$('.confirm-delete').click(function () {
|
||||||
if (questionToDelete) {
|
if (questionToDelete) {
|
||||||
|
const questionIndex = questionToDelete.data('question-index');
|
||||||
|
|
||||||
|
// Remove from selection if it was selected
|
||||||
|
selectedQuestions = selectedQuestions.filter(q => q !== questionIndex);
|
||||||
|
updateSelectionUI();
|
||||||
|
|
||||||
questionToDelete.fadeOut(300, function() {
|
questionToDelete.fadeOut(300, function() {
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
|
// Re-index all remaining questions and their form fields
|
||||||
|
reindexAllQuestions();
|
||||||
});
|
});
|
||||||
$('#confirmDeleteModal').modal('hide');
|
$('#confirmDeleteModal').modal('hide');
|
||||||
questionToDelete = null;
|
questionToDelete = null;
|
||||||
|
|
@ -1019,7 +1503,15 @@
|
||||||
var newQuestionIndex = $('.question').length;
|
var newQuestionIndex = $('.question').length;
|
||||||
|
|
||||||
var questionHtml = `
|
var questionHtml = `
|
||||||
<div class="question fade-in">
|
<div class="question fade-in" data-question-index="${newQuestionIndex}">
|
||||||
|
<!-- Question Checkbox -->
|
||||||
|
<div class="question-checkbox">
|
||||||
|
<div class="custom-checkbox question-select-checkbox">
|
||||||
|
<input type="checkbox" class="question-checkbox-input">
|
||||||
|
<i class="bi bi-check" style="display: none;"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="question-separator">
|
<div class="question-separator">
|
||||||
<div class="line-container">
|
<div class="line-container">
|
||||||
<hr class="line-start">
|
<hr class="line-start">
|
||||||
|
|
@ -1101,6 +1593,9 @@
|
||||||
$btn.removeClass('loading').prop('disabled', false);
|
$btn.removeClass('loading').prop('disabled', false);
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize UI
|
||||||
|
updateSelectionUI();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
|
|
@ -1,97 +1,836 @@
|
||||||
@model IEnumerable<QuestionnaireViewModel>
|
@model IEnumerable<QuestionnaireViewModel>
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Questionnaire";
|
ViewData["Title"] = "Questionnaire Management";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Modern Design System */
|
||||||
|
@@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
|
||||||
|
|
||||||
<div class="container-fluid mt-3">
|
:root {
|
||||||
|
--primary-color: #6366f1;
|
||||||
|
--primary-light: #8b5cf6;
|
||||||
|
--primary-dark: #4338ca;
|
||||||
|
--success-color: #10b981;
|
||||||
|
--success-light: #34d399;
|
||||||
|
--success-dark: #059669;
|
||||||
|
--info-color: #06b6d4;
|
||||||
|
--info-light: #22d3ee;
|
||||||
|
--info-dark: #0891b2;
|
||||||
|
--warning-color: #f59e0b;
|
||||||
|
--warning-light: #fbbf24;
|
||||||
|
--warning-dark: #d97706;
|
||||||
|
--danger-color: #ef4444;
|
||||||
|
--danger-light: #fca5a5;
|
||||||
|
--danger-dark: #dc2626;
|
||||||
|
--purple-color: #6f42c1;
|
||||||
|
--purple-light: #8b5cf6;
|
||||||
|
--purple-dark: #5a2d91;
|
||||||
|
--gray-50: #f8fafc;
|
||||||
|
--gray-100: #f1f5f9;
|
||||||
|
--gray-200: #e2e8f0;
|
||||||
|
--gray-300: #cbd5e1;
|
||||||
|
--gray-400: #94a3b8;
|
||||||
|
--gray-500: #64748b;
|
||||||
|
--gray-600: #475569;
|
||||||
|
--gray-700: #334155;
|
||||||
|
--gray-800: #1e293b;
|
||||||
|
--gray-900: #0f172a;
|
||||||
|
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||||
|
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||||
|
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
|
||||||
|
--border-radius-sm: 8px;
|
||||||
|
--border-radius-md: 12px;
|
||||||
|
--border-radius-lg: 16px;
|
||||||
|
--border-radius-xl: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Header Section */
|
||||||
|
.page-header {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius-xl);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
padding: 2.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, var(--primary-color), var(--primary-light), var(--info-color), var(--success-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%);
|
||||||
|
color: white;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.75rem;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-text h1 {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-subtitle {
|
||||||
|
color: var(--gray-600);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-button {
|
||||||
|
background: linear-gradient(135deg, var(--success-color) 0%, var(--success-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-button::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-button:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-button:hover {
|
||||||
|
background: linear-gradient(135deg, var(--success-dark) 0%, #047857 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats Section */
|
||||||
|
.stats-section {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border-radius: var(--border-radius-lg);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 800;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
opacity: 0.9;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Questionnaire Grid */
|
||||||
|
.questionnaires-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
||||||
|
gap: 2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Questionnaire Card */
|
||||||
|
.questionnaire-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius-xl);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.questionnaire-card:hover {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
box-shadow: var(--shadow-2xl);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.questionnaire-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, var(--primary-color), var(--info-color), var(--success-color));
|
||||||
|
transform: scaleX(0);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
transform-origin: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questionnaire-card:hover::before {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Header */
|
||||||
|
.card-header-custom {
|
||||||
|
background: linear-gradient(135deg, var(--gray-50) 0%, var(--gray-100) 100%);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-id-badge {
|
||||||
|
background: linear-gradient(135deg, var(--warning-color) 0%, var(--warning-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
float: right;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title-custom {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-800);
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-word;
|
||||||
|
padding-right: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Content */
|
||||||
|
.card-content {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-count-section {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-count-number {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questions-count-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Preview Section */
|
||||||
|
.preview-section {
|
||||||
|
background: var(--gray-50);
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
padding: 1.25rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-icon {
|
||||||
|
background: var(--info-color);
|
||||||
|
color: white;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-title {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-700);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-question {
|
||||||
|
background: white;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-badge {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-badge {
|
||||||
|
background: linear-gradient(135deg, var(--info-color) 0%, var(--info-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-text {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--gray-700);
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answers-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-badge {
|
||||||
|
background: linear-gradient(135deg, var(--success-color) 0%, var(--success-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-questions-indicator {
|
||||||
|
background: var(--gray-200);
|
||||||
|
color: var(--gray-600);
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
border: 1px dashed var(--gray-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.action-buttons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: var(--gray-50);
|
||||||
|
border-top: 1px solid var(--gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border: none;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete {
|
||||||
|
background: linear-gradient(135deg, var(--danger-color) 0%, var(--danger-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete:hover {
|
||||||
|
background: linear-gradient(135deg, var(--danger-dark) 0%, #b91c1c 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-edit {
|
||||||
|
background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-edit:hover {
|
||||||
|
background: linear-gradient(135deg, var(--primary-dark) 0%, #3730a3 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-details {
|
||||||
|
background: linear-gradient(135deg, var(--info-color) 0%, var(--info-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-details:hover {
|
||||||
|
background: linear-gradient(135deg, var(--info-dark) 0%, #0e7490 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-send {
|
||||||
|
background: linear-gradient(135deg, var(--success-color) 0%, var(--success-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-send:hover {
|
||||||
|
background: linear-gradient(135deg, var(--success-dark) 0%, #047857 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-logic {
|
||||||
|
background: linear-gradient(135deg, var(--purple-color) 0%, var(--purple-dark) 100%);
|
||||||
|
color: white;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-logic:hover {
|
||||||
|
background: linear-gradient(135deg, var(--purple-dark) 0%, #4c1d95 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty State */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius-xl);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
border: 2px dashed var(--gray-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state-icon {
|
||||||
|
background: var(--gray-100);
|
||||||
|
color: var(--gray-400);
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0 auto 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state h3 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-700);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state p {
|
||||||
|
color: var(--gray-500);
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@@media (max-width: 1024px) {
|
||||||
|
.questionnaires-grid
|
||||||
|
|
||||||
|
{
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@@media (max-width: 768px) {
|
||||||
|
.container-fluid
|
||||||
|
|
||||||
|
{
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.questionnaires-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-logic {
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes fadeIn {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-in {
|
||||||
|
animation: slideIn 0.4s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@keyframes slideIn {
|
||||||
|
from
|
||||||
|
|
||||||
|
{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.stagger-animation {
|
||||||
|
animation: fadeIn 0.6s ease-out;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stagger-animation:nth-child(1) {
|
||||||
|
animation-delay: 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stagger-animation:nth-child(2) {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stagger-animation:nth-child(3) {
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stagger-animation:nth-child(4) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stagger-animation:nth-child(5) {
|
||||||
|
animation-delay: 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stagger-animation:nth-child(6) {
|
||||||
|
animation-delay: 0.6s;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!-- Notifications -->
|
||||||
<partial name="_Notification" />
|
<partial name="_Notification" />
|
||||||
|
|
||||||
<div class="card bg-default shadow ">
|
<!-- Page Header -->
|
||||||
<div class="card-header">Questionnaire</div>
|
<div class="page-header fade-in">
|
||||||
<div class="card-body">
|
<div class="header-content">
|
||||||
<h4 class="card-title">Questionnaire list</h4>
|
<div class="header-info">
|
||||||
<p>
|
<div class="header-icon">
|
||||||
<a asp-action="Create" class="btn btn-primary"><span><i class="bi bi-plus-square-fill"></i></span> Create New</a>
|
<i class="bi bi-clipboard-data-fill"></i>
|
||||||
</p>
|
</div>
|
||||||
<table class="table table-responsive w-100 d-block d-md-table table-hover">
|
<div class="header-text">
|
||||||
<thead >
|
<h1>Questionnaire Management</h1>
|
||||||
<tr>
|
<p class="header-subtitle">Create, manage, and analyze your questionnaires</p>
|
||||||
|
|
||||||
<th>Id</th>
|
|
||||||
<th>Title</th>
|
|
||||||
<th>Total Questions</th>
|
|
||||||
<th> <span class="badge badge-primary">Questions</span> | <span class="badge badge-info">Type</span> | <span class="badge badge-success">Answers </span></th>
|
|
||||||
|
|
||||||
<th>Action</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="w-100">
|
|
||||||
@foreach (var item in Model)
|
|
||||||
{
|
|
||||||
<tr class="table-secondary">
|
|
||||||
|
|
||||||
<td>@item.Id</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
@item.Title
|
|
||||||
</td>
|
|
||||||
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
<span class="badge shadow rounded text-bg-primary p-2">
|
|
||||||
Questions <span class="badge text-bg-secondary shadow rounded p-1">@item.Questions?.Count()</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td class="h5">
|
|
||||||
|
|
||||||
@foreach (var question in item.Questions.Take(1))
|
|
||||||
{
|
|
||||||
|
|
||||||
<span class="badge p-1 m-1 bg-primary shadow-sm"> Question:@question.Text</span>
|
|
||||||
<span class="badge p-1 m-1 bg-info shadow-sm">Type: @question.Type</span>
|
|
||||||
foreach (var answer in question.Answers)
|
|
||||||
{
|
|
||||||
<span class="badge p-1 m-1 bg-success shadow-sm"> Asnwer:@answer.Text</span>
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<!-- Your existing buttons -->
|
|
||||||
<a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger btn-sm">
|
|
||||||
🗑️ DELETE
|
|
||||||
</a>
|
|
||||||
<a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-primary btn-sm">
|
|
||||||
✏️ EDIT
|
|
||||||
</a>
|
|
||||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-info btn-sm">
|
|
||||||
👁️ DETAILS
|
|
||||||
</a>
|
|
||||||
<a asp-action="SendQuestionnaire" asp-route-id="@item.Id" class="btn btn-success btn-sm">
|
|
||||||
📧 SEND
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- NEW: SET LOGIC button with purple/orange styling -->
|
|
||||||
<a asp-action="SetLogic" asp-route-id="@item.Id" class="btn btn-sm" style="background-color: #6f42c1; color: white;">
|
|
||||||
🔀 SET LOGIC
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<a asp-action="Create" class="create-button">
|
||||||
|
<i class="bi bi-plus-circle-fill"></i>
|
||||||
|
Create New Questionnaire
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics Section -->
|
||||||
|
@if (Model.Any())
|
||||||
|
{
|
||||||
|
<div class="stats-section fade-in">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">@Model.Count()</span>
|
||||||
|
<span class="stat-label">Total Questionnaires</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">@Model.Sum(q => q.Questions?.Count ?? 0)</span>
|
||||||
|
<span class="stat-label">Total Questions</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">@Model.Sum(q => q.Questions?.Sum(x => x.Answers?.Count ?? 0) ?? 0)</span>
|
||||||
|
<span class="stat-label">Total Answers</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">@Model.SelectMany(q => q.Questions ?? new List<Question>()).Select(q => q.Type).Distinct().Count()</span>
|
||||||
|
<span class="stat-label">Question Types</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Questionnaires Grid -->
|
||||||
|
@if (Model.Any())
|
||||||
|
{
|
||||||
|
<div class="questionnaires-grid">
|
||||||
|
@foreach (var item in Model.Select((questionnaire, index) => new { Questionnaire = questionnaire, Index = index }))
|
||||||
|
{
|
||||||
|
<div class="questionnaire-card stagger-animation">
|
||||||
|
<!-- Card Header -->
|
||||||
|
<div class="card-header-custom">
|
||||||
|
<span class="card-id-badge">ID: @item.Questionnaire.Id</span>
|
||||||
|
<h3 class="card-title-custom">@item.Questionnaire.Title</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Card Content -->
|
||||||
|
<div class="card-content">
|
||||||
|
<!-- Questions Count -->
|
||||||
|
<div class="questions-count-section">
|
||||||
|
<span class="questions-count-number">@(item.Questionnaire.Questions?.Count ?? 0)</span>
|
||||||
|
<span class="questions-count-label">Question@(item.Questionnaire.Questions?.Count != 1 ? "s" : "")</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Preview Section -->
|
||||||
|
@if (item.Questionnaire.Questions?.Any() == true)
|
||||||
|
{
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-header">
|
||||||
|
<div class="preview-icon">
|
||||||
|
<i class="bi bi-eye"></i>
|
||||||
|
</div>
|
||||||
|
<h4 class="preview-title">Quick Preview</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preview-content">
|
||||||
|
@{
|
||||||
|
var firstQuestion = item.Questionnaire.Questions.FirstOrDefault();
|
||||||
|
var totalQuestions = item.Questionnaire.Questions.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (firstQuestion != null)
|
||||||
|
{
|
||||||
|
<div class="preview-question">
|
||||||
|
<div>
|
||||||
|
<span class="question-badge">Question: @firstQuestion.Text</span>
|
||||||
|
<span class="type-badge">Type: @firstQuestion.Type</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (firstQuestion.Answers?.Any() == true)
|
||||||
|
{
|
||||||
|
<div class="answers-container">
|
||||||
|
@foreach (var answer in firstQuestion.Answers.Take(3))
|
||||||
|
{
|
||||||
|
<span class="answer-badge">@answer.Text</span>
|
||||||
|
}
|
||||||
|
@if (firstQuestion.Answers.Count > 3)
|
||||||
|
{
|
||||||
|
<span class="answer-badge" style="background: var(--gray-400);">+@(firstQuestion.Answers.Count - 3) more</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (totalQuestions > 1)
|
||||||
|
{
|
||||||
|
<div class="more-questions-indicator">
|
||||||
|
+ @(totalQuestions - 1) more question@(totalQuestions - 1 != 1 ? "s" : "")
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="preview-section">
|
||||||
|
<div class="preview-header">
|
||||||
|
<div class="preview-icon">
|
||||||
|
<i class="bi bi-info-circle"></i>
|
||||||
|
</div>
|
||||||
|
<h4 class="preview-title">No Questions Yet</h4>
|
||||||
|
</div>
|
||||||
|
<p style="color: var(--gray-500); font-style: italic; margin: 0;">This questionnaire doesn't have any questions yet.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Buttons -->
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a asp-action="Edit" asp-route-id="@item.Questionnaire.Id" class="action-btn btn-edit">
|
||||||
|
<i class="bi bi-pencil-fill"></i>
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
<a asp-action="Details" asp-route-id="@item.Questionnaire.Id" class="action-btn btn-details">
|
||||||
|
<i class="bi bi-eye-fill"></i>
|
||||||
|
Details
|
||||||
|
</a>
|
||||||
|
<a asp-action="Delete" asp-route-id="@item.Questionnaire.Id" class="action-btn btn-delete">
|
||||||
|
<i class="bi bi-trash3-fill"></i>
|
||||||
|
Delete
|
||||||
|
</a>
|
||||||
|
<a asp-action="SendQuestionnaire" asp-route-id="@item.Questionnaire.Id" class="action-btn btn-send">
|
||||||
|
<i class="bi bi-envelope-fill"></i>
|
||||||
|
Send
|
||||||
|
</a>
|
||||||
|
<a asp-action="SetLogic" asp-route-id="@item.Questionnaire.Id" class="action-btn btn-logic">
|
||||||
|
<i class="bi bi-diagram-3-fill"></i>
|
||||||
|
Set Logic
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<!-- Empty State -->
|
||||||
|
<div class="empty-state fade-in">
|
||||||
|
<div class="empty-state-icon">
|
||||||
|
<i class="bi bi-clipboard-x"></i>
|
||||||
|
</div>
|
||||||
|
<h3>No Questionnaires Yet</h3>
|
||||||
|
<p>Get started by creating your first questionnaire to collect valuable insights.</p>
|
||||||
|
<a asp-action="Create" class="create-button">
|
||||||
|
<i class="bi bi-plus-circle-fill"></i>
|
||||||
|
Create Your First Questionnaire
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -10,5 +10,6 @@ namespace Web.ViewModel.AnswerVM
|
||||||
public int QuestionId { get; set; } // Foreign key for Question
|
public int QuestionId { get; set; } // Foreign key for Question
|
||||||
[ForeignKey("QuestionId")]
|
[ForeignKey("QuestionId")]
|
||||||
public Question? Question { get; set; }
|
public Question? Question { get; set; }
|
||||||
|
public bool IsDeleted { get; set; } = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue