Add backend condition logic for user questionnaire submission handling
This commit is contained in:
parent
2ce5b50c97
commit
f6a03302fd
9 changed files with 1236 additions and 114 deletions
|
|
@ -23,5 +23,15 @@ namespace Model
|
|||
public QuestionType QuestionType { get; set; }
|
||||
public string? TextResponse { get; set; }
|
||||
public List<ResponseAnswer> ResponseAnswers { get; set; } = new List<ResponseAnswer>();
|
||||
|
||||
public ResponseStatus Status { get; set; } = ResponseStatus.Shown;
|
||||
public string? SkipReason { get; set; } // Why it was skipped (JSON of condition)
|
||||
}
|
||||
|
||||
public enum ResponseStatus
|
||||
{
|
||||
Answered = 1, // Question was answered
|
||||
Shown = 2, // Question was shown but left blank
|
||||
Skipped = 3 // Question was skipped due to conditional logic
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,9 +46,10 @@ namespace Web.Areas.Admin.Controllers
|
|||
{
|
||||
var responses = await _context.Responses
|
||||
.Include(r => r.Questionnaire)
|
||||
.ThenInclude(q => q.Questions.OrderBy(qu => qu.Id))
|
||||
.Include(r => r.ResponseDetails)
|
||||
.ThenInclude(rd => rd.Question)
|
||||
.ThenInclude(q => q.Answers) // Include the Answers entity
|
||||
.ThenInclude(q => q.Answers)
|
||||
.Include(r => r.ResponseDetails)
|
||||
.ThenInclude(rd => rd.ResponseAnswers)
|
||||
.Where(r => r.UserEmail == userEmail)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
ViewData["Title"] = "User Responses";
|
||||
}
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.stepper-wrapper {
|
||||
display: flex;
|
||||
|
|
@ -57,8 +55,6 @@
|
|||
}
|
||||
|
||||
#QuestionnairCard {
|
||||
|
||||
|
||||
padding: 2rem;
|
||||
border: 0.5px solid #e7e7e7;
|
||||
border-radius: 3px;
|
||||
|
|
@ -66,10 +62,44 @@
|
|||
-moz-box-shadow: 0px 0px 14px -7px rgba(0,0,0,0.75);
|
||||
box-shadow: 0px 0px 14px -7px rgba(0,0,0,0.75);
|
||||
}
|
||||
|
||||
.status-answered {
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
color: #155724;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.status-shown {
|
||||
background-color: #fff3cd;
|
||||
border-color: #ffeaa7;
|
||||
color: #856404;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.status-skipped {
|
||||
background-color: #d1ecf1;
|
||||
border-color: #bee5eb;
|
||||
color: #0c5460;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.status-unknown {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #dee2e6;
|
||||
color: #6c757d;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid mt-3">
|
||||
<p>
|
||||
<a asp-action="Index" class="btn btn-primary btn-sm">Back to list</a>
|
||||
|
|
@ -100,12 +130,8 @@
|
|||
<div class="col-md-12 ">
|
||||
<div id="chart_div" style="width: 100%; height: 400px;"></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Stepper -->
|
||||
<div class="stepper-wrapper">
|
||||
@foreach (var response in Model.Responses)
|
||||
|
|
@ -141,64 +167,92 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-5">
|
||||
<div id="survey_questions_answers_summary_chart_div_@response.Questionnaire.Id" style="width: 100%; height: 200px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Collapsible content -->
|
||||
<div class="collapse mt-3" id="collapseResponse-@response.Id">
|
||||
<table class="table table-responsive w-100 d-block d-md-table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Question</th>
|
||||
<th>Status</th>
|
||||
<th>Response</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var detail in response.ResponseDetails)
|
||||
@foreach (var question in response.Questionnaire.Questions.OrderBy(q => q.Id))
|
||||
{
|
||||
var responseDetail = response.ResponseDetails.FirstOrDefault(rd => rd.QuestionId == question.Id);
|
||||
|
||||
<tr>
|
||||
<td>@detail.Question.Text</td>
|
||||
<td>
|
||||
@if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended)
|
||||
{
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
|
||||
Question type
|
||||
<span class="badge text-bg-primary rounded-pill p-1">@detail.QuestionType</span>
|
||||
</li>
|
||||
</ul>
|
||||
<strong>@question.Text</strong>
|
||||
<br />
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
|
||||
Answer
|
||||
<span class="badge text-bg-primary rounded-pill p-1">@detail.TextResponse</span>
|
||||
</li>
|
||||
</ul>
|
||||
<small class="text-muted">Type: @question.Type</small>
|
||||
</td>
|
||||
<td>
|
||||
@if (responseDetail != null)
|
||||
{
|
||||
@switch (responseDetail.Status)
|
||||
{
|
||||
case ResponseStatus.Answered:
|
||||
<div class="status-answered">
|
||||
<strong>✅ Answered</strong>
|
||||
</div>
|
||||
break;
|
||||
|
||||
case ResponseStatus.Shown:
|
||||
<div class="status-shown">
|
||||
<strong>⚠️ Shown but not answered</strong>
|
||||
<br />
|
||||
<small>Question was displayed but left blank</small>
|
||||
</div>
|
||||
break;
|
||||
|
||||
case ResponseStatus.Skipped:
|
||||
<div class="status-skipped">
|
||||
<strong>⏭️ Skipped</strong>
|
||||
<br />
|
||||
<small>@responseDetail.SkipReason</small>
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
|
||||
Question type
|
||||
<span class="badge text-bg-primary rounded-pill p-1">@detail.QuestionType</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="status-unknown">
|
||||
<strong>❓ No response data</strong>
|
||||
<br />
|
||||
<ul class="list-group">
|
||||
@foreach (var answer in detail.ResponseAnswers)
|
||||
{
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Answer
|
||||
<span class="badge text-bg-primary rounded-pill p-1">@detail.Question.Answers.FirstOrDefault(a => a.Id == answer.AnswerId)?.Text</span>
|
||||
</li>
|
||||
<small>Status unknown</small>
|
||||
</div>
|
||||
}
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
@if (responseDetail != null && responseDetail.Status == ResponseStatus.Answered)
|
||||
{
|
||||
@if (responseDetail.QuestionType == QuestionType.Text || responseDetail.QuestionType == QuestionType.Slider || responseDetail.QuestionType == QuestionType.Open_ended)
|
||||
{
|
||||
<div class="alert alert-light">
|
||||
<strong>Answer:</strong> @responseDetail.TextResponse
|
||||
</div>
|
||||
}
|
||||
else if (responseDetail.ResponseAnswers.Any())
|
||||
{
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
@foreach (var answer in responseDetail.ResponseAnswers)
|
||||
{
|
||||
var answerText = question.Answers.FirstOrDefault(a => a.Id == answer.AnswerId)?.Text;
|
||||
<span class="badge bg-success">@answerText</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">No response provided</span>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -207,7 +261,6 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -215,9 +268,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
@section Scripts {
|
||||
<!-- Include Bootstrap 5 JS for collapse functionality -->
|
||||
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
using Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages.Manage;
|
||||
using Model;
|
||||
using Newtonsoft.Json;
|
||||
using Services.EmailSend;
|
||||
using Services.Implemnetation;
|
||||
using Services.Interaces;
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Web.ViewModel.AnswerVM;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Web.ViewModel.QuestionnaireVM;
|
||||
using Web.ViewModel.QuestionVM;
|
||||
|
||||
|
||||
namespace Web.Controllers
|
||||
{
|
||||
|
|
@ -110,9 +107,14 @@ namespace Web.Controllers
|
|||
}
|
||||
[HttpPost]
|
||||
public IActionResult DisplayQuestionnaire([FromForm] ResponseQuestionnaireViewModel questionnaire)
|
||||
|
||||
{
|
||||
|
||||
bool hasSubmitted = _context.Responses.Any(r => r.QuestionnaireId == questionnaire.Id && r.UserEmail == questionnaire.Email);
|
||||
|
||||
// Get the actual questionnaire from database
|
||||
var dbQuestionnaire = _questionnaireRepository.GetQuestionnaireWithQuestionAndAnswer(questionnaire.Id);
|
||||
|
||||
var cetZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
|
||||
var cetTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, cetZone);
|
||||
var response = new Response
|
||||
|
|
@ -121,17 +123,7 @@ namespace Web.Controllers
|
|||
UserName = questionnaire.UserName,
|
||||
UserEmail = questionnaire.Email,
|
||||
SubmissionDate = cetTime,
|
||||
ResponseDetails = questionnaire.Questions.Select(q => new ResponseDetail
|
||||
{
|
||||
QuestionId = q.Id,
|
||||
QuestionType = q.Type,
|
||||
TextResponse = (q.Type == QuestionType.Open_ended || q.Type == QuestionType.Text || q.Type == QuestionType.Slider)
|
||||
? string.Join(" ", q.SelectedText)
|
||||
: null,
|
||||
ResponseAnswers = q.SelectedAnswerIds
|
||||
.Select(aid => new ResponseAnswer { AnswerId = aid })
|
||||
.ToList()
|
||||
}).ToList()
|
||||
ResponseDetails = CreateResponseDetailsForAllQuestions(dbQuestionnaire, questionnaire.Questions, questionnaire.QuestionsShown, questionnaire.QuestionsSkipped)
|
||||
};
|
||||
|
||||
_context.Responses.Add(response);
|
||||
|
|
@ -166,6 +158,128 @@ namespace Web.Controllers
|
|||
return RedirectToAction(nameof(ThankYou));
|
||||
}
|
||||
|
||||
private List<ResponseDetail> CreateResponseDetailsForAllQuestions(Questionnaire questionnaire, List<ResponseQuestionViewModel> answeredQuestions, string questionsShownJson, string questionsSkippedJson)
|
||||
{
|
||||
var responseDetails = new List<ResponseDetail>();
|
||||
|
||||
// Parse tracking data
|
||||
List<int> questionsShown = new List<int>();
|
||||
List<SkippedQuestionInfo> questionsSkipped = new List<SkippedQuestionInfo>();
|
||||
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(questionsShownJson))
|
||||
{
|
||||
questionsShown = System.Text.Json.JsonSerializer.Deserialize<List<int>>(questionsShownJson) ?? new List<int>();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(questionsSkippedJson))
|
||||
{
|
||||
questionsSkipped = System.Text.Json.JsonSerializer.Deserialize<List<SkippedQuestionInfo>>(questionsSkippedJson) ?? new List<SkippedQuestionInfo>();
|
||||
}
|
||||
}
|
||||
catch (System.Text.Json.JsonException ex)
|
||||
{
|
||||
// Log error if needed
|
||||
Console.WriteLine($"Error parsing tracking data: {ex.Message}");
|
||||
}
|
||||
|
||||
// Get ALL questions from the questionnaire
|
||||
var allQuestions = questionnaire.Questions.OrderBy(q => q.Id).ToList();
|
||||
|
||||
foreach (var dbQuestion in allQuestions)
|
||||
{
|
||||
var questionNumber = GetQuestionNumber(dbQuestion.Id, allQuestions);
|
||||
var answeredQuestion = answeredQuestions.FirstOrDefault(aq => aq.Id == dbQuestion.Id);
|
||||
var skippedInfo = questionsSkipped.FirstOrDefault(sq => sq.QuestionNumber == questionNumber);
|
||||
|
||||
ResponseDetail responseDetail;
|
||||
|
||||
if (answeredQuestion != null && HasValidResponse(answeredQuestion))
|
||||
{
|
||||
// Question was ANSWERED
|
||||
responseDetail = new ResponseDetail
|
||||
{
|
||||
QuestionId = dbQuestion.Id,
|
||||
QuestionType = dbQuestion.Type,
|
||||
Status = ResponseStatus.Answered,
|
||||
TextResponse = (dbQuestion.Type == QuestionType.Open_ended ||
|
||||
dbQuestion.Type == QuestionType.Text ||
|
||||
dbQuestion.Type == QuestionType.Slider)
|
||||
? string.Join(" ", answeredQuestion.SelectedText)
|
||||
: null,
|
||||
ResponseAnswers = answeredQuestion.SelectedAnswerIds
|
||||
.Select(aid => new ResponseAnswer { AnswerId = aid })
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
else if (skippedInfo != null)
|
||||
{
|
||||
// Question was SKIPPED due to conditional logic
|
||||
responseDetail = new ResponseDetail
|
||||
{
|
||||
QuestionId = dbQuestion.Id,
|
||||
QuestionType = dbQuestion.Type,
|
||||
Status = ResponseStatus.Skipped,
|
||||
SkipReason = skippedInfo.Reason
|
||||
};
|
||||
}
|
||||
else if (questionsShown.Contains(questionNumber))
|
||||
{
|
||||
// Question was SHOWN but left blank
|
||||
responseDetail = new ResponseDetail
|
||||
{
|
||||
QuestionId = dbQuestion.Id,
|
||||
QuestionType = dbQuestion.Type,
|
||||
Status = ResponseStatus.Shown,
|
||||
SkipReason = null
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback - assume shown if no tracking data available
|
||||
responseDetail = new ResponseDetail
|
||||
{
|
||||
QuestionId = dbQuestion.Id,
|
||||
QuestionType = dbQuestion.Type,
|
||||
Status = ResponseStatus.Shown,
|
||||
SkipReason = null
|
||||
};
|
||||
}
|
||||
|
||||
responseDetails.Add(responseDetail);
|
||||
}
|
||||
|
||||
return responseDetails;
|
||||
}
|
||||
|
||||
private int GetQuestionNumber(int questionId, List<Question> allQuestions)
|
||||
{
|
||||
return allQuestions.FindIndex(q => q.Id == questionId) + 1;
|
||||
}
|
||||
|
||||
private bool HasValidResponse(ResponseQuestionViewModel question)
|
||||
{
|
||||
bool hasTextResponse = question.SelectedText != null &&
|
||||
question.SelectedText.Any(text => !string.IsNullOrWhiteSpace(text));
|
||||
|
||||
bool hasAnswerResponse = question.SelectedAnswerIds != null &&
|
||||
question.SelectedAnswerIds.Any();
|
||||
|
||||
return hasTextResponse || hasAnswerResponse;
|
||||
}
|
||||
|
||||
// Add this class for JSON deserialization
|
||||
public class SkippedQuestionInfo
|
||||
{
|
||||
[JsonPropertyName("questionNumber")]
|
||||
public int QuestionNumber { get; set; }
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string Reason { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
// ✅ COMPLETE CORRECTED METHOD: Danish Thank You Email Body
|
||||
private static string GenerateThankYouEmailBody(string userName)
|
||||
{
|
||||
|
|
|
|||
841
Web/Migrations/20250811130002_AddResponseStatus.Designer.cs
generated
Normal file
841
Web/Migrations/20250811130002_AddResponseStatus.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,841 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(SurveyContext))]
|
||||
[Migration("20250811130002_AddResponseStatus")]
|
||||
partial class AddResponseStatus
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex")
|
||||
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Address", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("CVR")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("City")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Country")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Mobile")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PostalCode")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("State")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Street")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Addresss");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Answer", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ConditionJson")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("QuestionId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("QuestionId");
|
||||
|
||||
b.ToTable("Answers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex")
|
||||
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Banner", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("LinkUrl")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Banners");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Footer", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("CreatedBy")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("ImageUlr")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("LastUpdated")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Owner")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Sitecopyright")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UpdatedBy")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Footers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.FooterSocialMedia", b =>
|
||||
{
|
||||
b.Property<int>("FooterId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SocialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("FooterId", "SocialId");
|
||||
|
||||
b.HasIndex("SocialId");
|
||||
|
||||
b.ToTable("FooterSocialMedias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Page", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("BannerId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("FooterId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BannerId");
|
||||
|
||||
b.HasIndex("FooterId");
|
||||
|
||||
b.ToTable("Pages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Question", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("QuestionnaireId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("QuestionnaireId");
|
||||
|
||||
b.ToTable("Questions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Questionnaire", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Questionnaires");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Response", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("QuestionnaireId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("SubmissionDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("UserEmail")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("QuestionnaireId");
|
||||
|
||||
b.ToTable("Responses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.ResponseAnswer", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("AnswerId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ResponseDetailId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ResponseDetailId");
|
||||
|
||||
b.ToTable("ResponseAnswers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.ResponseDetail", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("QuestionId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("QuestionType")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ResponseId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SkipReason")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TextResponse")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("QuestionId");
|
||||
|
||||
b.HasIndex("ResponseId");
|
||||
|
||||
b.ToTable("ResponseDetails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.SentNewsletterEamil", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Body")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Geo")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsBlocked")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsBounced")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsClicked")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDelivered")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsOpened")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsSent")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsSpam")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsUnsubscribed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTime>("ReceivedActivity")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("RecipientEmail")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("SentDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SentNewsletterEamils");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.SocialMedia", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SocialMedia");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Subscription", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<bool>("IsSubscribed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Subscriptions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Model.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Model.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Model.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Model.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Answer", b =>
|
||||
{
|
||||
b.HasOne("Model.Question", "Question")
|
||||
.WithMany("Answers")
|
||||
.HasForeignKey("QuestionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Question");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.FooterSocialMedia", b =>
|
||||
{
|
||||
b.HasOne("Model.Footer", "Footer")
|
||||
.WithMany("FooterSocialMedias")
|
||||
.HasForeignKey("FooterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Model.SocialMedia", "SocialMedia")
|
||||
.WithMany("FooterSocialMedias")
|
||||
.HasForeignKey("SocialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Footer");
|
||||
|
||||
b.Navigation("SocialMedia");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Page", b =>
|
||||
{
|
||||
b.HasOne("Model.Banner", "banner")
|
||||
.WithMany()
|
||||
.HasForeignKey("BannerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Model.Footer", "footer")
|
||||
.WithMany()
|
||||
.HasForeignKey("FooterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("banner");
|
||||
|
||||
b.Navigation("footer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Question", b =>
|
||||
{
|
||||
b.HasOne("Model.Questionnaire", "Questionnaire")
|
||||
.WithMany("Questions")
|
||||
.HasForeignKey("QuestionnaireId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Questionnaire");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Response", b =>
|
||||
{
|
||||
b.HasOne("Model.Questionnaire", "Questionnaire")
|
||||
.WithMany()
|
||||
.HasForeignKey("QuestionnaireId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Questionnaire");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.ResponseAnswer", b =>
|
||||
{
|
||||
b.HasOne("Model.ResponseDetail", "ResponseDetail")
|
||||
.WithMany("ResponseAnswers")
|
||||
.HasForeignKey("ResponseDetailId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ResponseDetail");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.ResponseDetail", b =>
|
||||
{
|
||||
b.HasOne("Model.Question", "Question")
|
||||
.WithMany()
|
||||
.HasForeignKey("QuestionId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Model.Response", "Response")
|
||||
.WithMany("ResponseDetails")
|
||||
.HasForeignKey("ResponseId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Question");
|
||||
|
||||
b.Navigation("Response");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Footer", b =>
|
||||
{
|
||||
b.Navigation("FooterSocialMedias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Question", b =>
|
||||
{
|
||||
b.Navigation("Answers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Questionnaire", b =>
|
||||
{
|
||||
b.Navigation("Questions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.Response", b =>
|
||||
{
|
||||
b.Navigation("ResponseDetails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.ResponseDetail", b =>
|
||||
{
|
||||
b.Navigation("ResponseAnswers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Model.SocialMedia", b =>
|
||||
{
|
||||
b.Navigation("FooterSocialMedias");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Web/Migrations/20250811130002_AddResponseStatus.cs
Normal file
39
Web/Migrations/20250811130002_AddResponseStatus.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddResponseStatus : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SkipReason",
|
||||
table: "ResponseDetails",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Status",
|
||||
table: "ResponseDetails",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SkipReason",
|
||||
table: "ResponseDetails");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Status",
|
||||
table: "ResponseDetails");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -531,6 +531,12 @@ namespace Web.Migrations
|
|||
b.Property<int>("ResponseId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SkipReason")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TextResponse")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ namespace Web.ViewModel.QuestionnaireVM
|
|||
|
||||
public List<ResponseQuestionViewModel> Questions { get; set; } = new List<ResponseQuestionViewModel>();
|
||||
|
||||
|
||||
public string QuestionsShown { get; set; } = string.Empty;
|
||||
public string QuestionsSkipped { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,6 +317,13 @@
|
|||
<input type="hidden" name="Title" value="@Model.Title">
|
||||
<input type="hidden" name="Description" value="@Model.Description">
|
||||
|
||||
<!-- Add these new hidden inputs for tracking -->
|
||||
<input type="hidden" id="questionsShown" name="QuestionsShown" value="">
|
||||
<input type="hidden" id="questionsSkipped" name="QuestionsSkipped" value="">
|
||||
<input type="hidden" name="Id" value="@Model.Id">
|
||||
<input type="hidden" name="Title" value="@Model.Title">
|
||||
<input type="hidden" name="Description" value="@Model.Description">
|
||||
|
||||
<h4>@Model.Title</h4>
|
||||
<p>@Html.Raw(Model.Description)</p>
|
||||
|
||||
|
|
@ -680,9 +687,32 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Track the actual navigation path
|
||||
// Track which questions were shown and skipped - FIXED VARIABLE NAMES
|
||||
let questionsShownArray = [1]; // Start with question 1
|
||||
let questionsSkippedArray = [];
|
||||
let navigationPath = [0]; // Start with question 1 (index 0)
|
||||
|
||||
function trackQuestionShown(questionNumber) {
|
||||
if (!questionsShownArray.includes(questionNumber)) {
|
||||
questionsShownArray.push(questionNumber);
|
||||
console.log('Question shown:', questionNumber, 'Total shown:', questionsShownArray);
|
||||
}
|
||||
}
|
||||
|
||||
function trackQuestionSkipped(questionNumber, reason) {
|
||||
const skipInfo = {
|
||||
questionNumber: questionNumber,
|
||||
reason: reason
|
||||
};
|
||||
questionsSkippedArray.push(skipInfo);
|
||||
console.log('Question skipped:', skipInfo);
|
||||
}
|
||||
|
||||
function updateTrackingInputs() {
|
||||
document.getElementById('questionsShown').value = JSON.stringify(questionsShownArray);
|
||||
document.getElementById('questionsSkipped').value = JSON.stringify(questionsSkippedArray);
|
||||
}
|
||||
|
||||
// Get conditions from the CURRENT step only
|
||||
function getCurrentStepConditions() {
|
||||
const currentStepElement = steps[currentStep];
|
||||
|
|
@ -732,8 +762,16 @@
|
|||
function jumpToQuestion(questionNumber) {
|
||||
const targetStepIndex = questionNumber - 1; // Convert to 0-based index
|
||||
if (targetStepIndex >= 0 && targetStepIndex < steps.length) {
|
||||
const currentQuestionNumber = getCurrentStepNumber();
|
||||
|
||||
// Track skipped questions between current and target
|
||||
for (let i = currentQuestionNumber + 1; i < questionNumber; i++) {
|
||||
trackQuestionSkipped(i, `Skipped due to jump condition from question ${currentQuestionNumber}`);
|
||||
}
|
||||
|
||||
currentStep = targetStepIndex;
|
||||
navigationPath.push(currentStep); // Track the path
|
||||
trackQuestionShown(questionNumber); // Track the target question as shown
|
||||
showStep(currentStep);
|
||||
updateStepper();
|
||||
console.log(`Jumped to question ${questionNumber}, path:`, navigationPath);
|
||||
|
|
@ -741,10 +779,21 @@
|
|||
}
|
||||
|
||||
function skipQuestions(skipCount) {
|
||||
const currentQuestionNumber = getCurrentStepNumber();
|
||||
const newStepIndex = currentStep + skipCount + 1;
|
||||
|
||||
// Track skipped questions
|
||||
for (let i = 1; i <= skipCount; i++) {
|
||||
const skippedQuestionNumber = currentQuestionNumber + i;
|
||||
if (skippedQuestionNumber <= steps.length) {
|
||||
trackQuestionSkipped(skippedQuestionNumber, `Skipped due to skip condition from question ${currentQuestionNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (newStepIndex < steps.length) {
|
||||
currentStep = newStepIndex;
|
||||
navigationPath.push(currentStep); // Track the path
|
||||
trackQuestionShown(getCurrentStepNumber()); // Track the new question as shown
|
||||
showStep(currentStep);
|
||||
updateStepper();
|
||||
} else {
|
||||
|
|
@ -758,6 +807,13 @@
|
|||
}
|
||||
|
||||
function endSurvey(endMessage) {
|
||||
const currentQuestionNumber = getCurrentStepNumber();
|
||||
|
||||
// Track all remaining questions as skipped
|
||||
for (let i = currentQuestionNumber + 1; i <= steps.length; i++) {
|
||||
trackQuestionSkipped(i, `Survey ended early from question ${currentQuestionNumber}`);
|
||||
}
|
||||
|
||||
// Hide all steps and show end message
|
||||
steps.forEach(step => step.style.display = 'none');
|
||||
|
||||
|
|
@ -827,7 +883,10 @@
|
|||
let currentStep = 0;
|
||||
|
||||
form.addEventListener('submit', function (event) {
|
||||
console.log('Form submission');
|
||||
updateTrackingInputs();
|
||||
console.log('Form submission with tracking data');
|
||||
console.log('Questions shown:', questionsShownArray);
|
||||
console.log('Questions skipped:', questionsSkippedArray);
|
||||
const formData = new FormData(form);
|
||||
formData.forEach((value, key) => {
|
||||
console.log(`${key}: ${value}`);
|
||||
|
|
@ -876,6 +935,7 @@
|
|||
if (currentStep < steps.length - 1) {
|
||||
currentStep++;
|
||||
navigationPath.push(currentStep); // Track normal navigation
|
||||
trackQuestionShown(getCurrentStepNumber()); // Track question as shown
|
||||
showStep(currentStep);
|
||||
updateStepper();
|
||||
console.log(`Normal next to question ${currentStep + 1}, path:`, navigationPath);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue