diff --git a/Data/SurveyContext.cs b/Data/SurveyContext.cs index 09c235d..ddaca2f 100644 --- a/Data/SurveyContext.cs +++ b/Data/SurveyContext.cs @@ -34,7 +34,9 @@ namespace Data public DbSet Subscriptions { get; set; } - + public DbSet Responses { get; set; } + public DbSet ResponseDetails { get; set; } + public DbSet ResponseAnswers { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -72,16 +74,18 @@ namespace Data .HasKey(a => a.Id); - //modelBuilder.Entity() - // .HasOne(p => p.footer) - // .WithMany() - // .HasForeignKey(p => p.FooterId) - // .IsRequired(false) - // .OnDelete(DeleteBehavior.Cascade); - - + // Questionnaire to Response relationship + modelBuilder.Entity() + .HasOne(rd => rd.Response) + .WithMany(r => r.ResponseDetails) + .HasForeignKey(rd => rd.ResponseId) + .OnDelete(DeleteBehavior.Cascade); // This is safe if only Responses are being deleted leading to ResponseDetails - + modelBuilder.Entity() + .HasOne(rd => rd.Question) + .WithMany() + .HasForeignKey(rd => rd.QuestionId) + .OnDelete(DeleteBehavior.Restrict); base.OnModelCreating(modelBuilder); diff --git a/Model/Response.cs b/Model/Response.cs new file mode 100644 index 0000000..08957a0 --- /dev/null +++ b/Model/Response.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + public class Response + { + public int Id { get; set; } + public int QuestionnaireId { get; set; } // Foreign Key to Questionnaire + + [ForeignKey("QuestionnaireId")] + public Questionnaire? Questionnaire { get; set; } + public string? UserName { get; set; } // To store the user's name + public string? UserEmail { get; set; } // To store the user's email + public DateTime SubmissionDate { get; set; } + public List ResponseDetails { get; set; } = new List(); + } +} diff --git a/Model/ResponseAnswer.cs b/Model/ResponseAnswer.cs new file mode 100644 index 0000000..7480cb9 --- /dev/null +++ b/Model/ResponseAnswer.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + public class ResponseAnswer + { + public int Id { get; set; } + public int ResponseDetailId { get; set; } + + [ForeignKey("ResponseDetailId")] + public ResponseDetail? ResponseDetail { get; set; } + public int AnswerId { get; set; } + } +} diff --git a/Model/ResponseDetail.cs b/Model/ResponseDetail.cs new file mode 100644 index 0000000..d56daeb --- /dev/null +++ b/Model/ResponseDetail.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + public class ResponseDetail + { + public int Id { get; set; } + + + public int ResponseId { get; set; } + + [ForeignKey("ResponseId")] + public Response? Response { get; set; } + public int QuestionId { get; set; } + + [ForeignKey("QuestionId")] + public Question? Question { get; set; } + public QuestionType QuestionType { get; set; } + public string? TextResponse { get; set; } + public List ResponseAnswers { get; set; } = new List(); + } +} diff --git a/Web/Areas/Admin/Controllers/QuestionnaireController.cs b/Web/Areas/Admin/Controllers/QuestionnaireController.cs index 5e8dc40..fe0d9f0 100644 --- a/Web/Areas/Admin/Controllers/QuestionnaireController.cs +++ b/Web/Areas/Admin/Controllers/QuestionnaireController.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.WebUtilities; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Elfie.Extensions; +using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Model; using Services.EmailSend; @@ -465,12 +466,11 @@ namespace Web.Areas.Admin.Controllers { - // Build the email body with questionnaire details + var questionnairePath = _configuration["Email:Questionnaire"]; int surveyId = viewModel.QuestionnaireId; - //DateTime currentDateTime = DateTime.Now; - //DateTime currentDateTime = viewModel.ExpirationDateTime; + DateTime currentDateTime; if (viewModel.ExpirationDateTime.HasValue) { @@ -478,8 +478,7 @@ namespace Web.Areas.Admin.Controllers } else { - // Handle the case when ExpirationDateTime is null - // For example, you can assign the current date and time + currentDateTime = DateTime.Now; } @@ -494,9 +493,9 @@ namespace Web.Areas.Admin.Controllers - //var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}?token={tokenWithExpiry}"; + var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}?t={tokenWithExpiry}"; - var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}"; + //var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}"; var toEmail = viewModel.Email; @@ -583,6 +582,32 @@ namespace Web.Areas.Admin.Controllers // If model state is not valid, return the view with validation errors return View(viewModel); } + + + + + [HttpGet] + public async Task ViewResponse(int id) // Pass the response ID + { + var response = await _context.Responses + .Include(r => r.ResponseDetails) + .ThenInclude(rd => rd.ResponseAnswers) + .Include(r => r.ResponseDetails) + .ThenInclude(rd => rd.Question) // Include questions for detailed display + .ThenInclude(q => q.Answers) // Include all possible answers for each question + .FirstOrDefaultAsync(r => r.Id == id); // Find the response by ID + + + if (response == null) + { + return NotFound(); // If no response is found, return a NotFound result + } + + return View(response); // Pass the response to the view + } + + + public string GenerateExpiryToken(DateTime expiryDate) { // Generate a unique token, for example, using a cryptographic library or a GUID @@ -593,7 +618,7 @@ namespace Web.Areas.Admin.Controllers return tokenWithExpiry; } - + } diff --git a/Web/Areas/Admin/Controllers/UserResponseController.cs b/Web/Areas/Admin/Controllers/UserResponseController.cs new file mode 100644 index 0000000..4801186 --- /dev/null +++ b/Web/Areas/Admin/Controllers/UserResponseController.cs @@ -0,0 +1,50 @@ +using Data; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Model; + +namespace Web.Areas.Admin.Controllers +{ + public class UserResponseController : Controller + { + private readonly SurveyContext _context; + + public UserResponseController(SurveyContext context) + { + _context = context; + } + public async Task Index() + { + var responses = await GetAllResponsesWithDetailsAsync(); // Fetch the data + return View(responses); // Pass the data to the view + } + + private async Task> GetAllResponsesWithDetailsAsync() + { + return await _context.Responses + .Include(r => r.Questionnaire) // Ensure the Questionnaire data is included + .OrderBy(r => r.Id) // Optional: Order by submission date + .ToListAsync(); + } + + [HttpGet] + public async Task ViewResponse(int id) // Pass the response ID + { + var response = await _context.Responses + .Include(r => r.ResponseDetails) + .ThenInclude(rd => rd.Question) + .ThenInclude(q => q.Answers) // Load all possible answers for the questions + .Include(r => r.ResponseDetails) + .ThenInclude(rd => rd.ResponseAnswers) // Load the answers selected by the user + .AsNoTracking() + .FirstOrDefaultAsync(r => r.Id == id); + + if (response == null) + { + return NotFound(); // If no response is found, return a NotFound result + } + + return View(response); // Pass the response to the view + } + } +} diff --git a/Web/Areas/Admin/Views/Questionnaire/Index.cshtml b/Web/Areas/Admin/Views/Questionnaire/Index.cshtml index 1b68b6e..68adee0 100644 --- a/Web/Areas/Admin/Views/Questionnaire/Index.cshtml +++ b/Web/Areas/Admin/Views/Questionnaire/Index.cshtml @@ -86,10 +86,11 @@ - Delete | - Edit| - Details | - Send + Delete | + Edit| + Details | + Send + } diff --git a/Web/Areas/Admin/Views/Questionnaire/ViewResponse.cshtml b/Web/Areas/Admin/Views/Questionnaire/ViewResponse.cshtml new file mode 100644 index 0000000..2b3a94a --- /dev/null +++ b/Web/Areas/Admin/Views/Questionnaire/ViewResponse.cshtml @@ -0,0 +1,41 @@ + + +@model Response + +@{ + ViewData["Title"] = "ViewResponses"; +} + + + +
+

Response by: @Model.UserName on @Model.SubmissionDate.ToString("dd/MM/yyyy")

+ + @foreach (var detail in Model.ResponseDetails) + { +
Question: @detail.Question.Text
+ +
All Available Answers:
+
    + @foreach (var answer in detail.Question.Answers) + { +
  • @answer.Text
  • + } +
+
Provided Answers:
+
    + @foreach (var answer in detail.ResponseAnswers) + { +
  • @detail.Question.Answers.FirstOrDefault(a => a.Id == answer.AnswerId)?.Text
  • + } +
+ } +
+ diff --git a/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml b/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml index d3954c4..0ef86b3 100644 --- a/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml +++ b/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml @@ -39,6 +39,10 @@ Survey +
  • + + Response +
  • Subscibers
  • @@ -84,7 +88,7 @@ -
    +
    @RenderBody()
    diff --git a/Web/Areas/Admin/Views/UserResponse/Index.cshtml b/Web/Areas/Admin/Views/UserResponse/Index.cshtml new file mode 100644 index 0000000..1ea35fc --- /dev/null +++ b/Web/Areas/Admin/Views/UserResponse/Index.cshtml @@ -0,0 +1,56 @@ +@model IEnumerable + +@{ + ViewData["Title"] = "Response"; +} + + +
    + + + +
    +
    User Reponse
    +
    +

    User response list

    + + + + + + + + + + + + + + + + + @foreach (var item in Model) + { + + + + + + + + + + } + + + +
    IdQuestionnaireUserNameUserEmailSubmission DateAction
    @item.Id @item.Questionnaire.Title@item.UserName@item.UserName@item.SubmissionDate + Delete | + Details +
    + +
    +
    +
    + + diff --git a/Web/Areas/Admin/Views/UserResponse/ViewResponse.cshtml b/Web/Areas/Admin/Views/UserResponse/ViewResponse.cshtml new file mode 100644 index 0000000..8a2c7d4 --- /dev/null +++ b/Web/Areas/Admin/Views/UserResponse/ViewResponse.cshtml @@ -0,0 +1,462 @@ +@model Response + +@{ + ViewData["Title"] = "ViewResponses"; +} + + + + @{ + int numberindex = 0; + } + + +
    + @*

    Questionnaire: @Model.Questionnaire.Title

    *@ + Response Details for @Model.UserName (@Model.UserEmail) +
    Submitted on: @Model.SubmissionDate.ToString("yyyy-MM-ddTHH:mm:ss")
    + + + + + @foreach (var detail in Model.ResponseDetails) + { +
    +
    + Question Type:@detail.QuestionType.ToString() +
    +
    +
    Question @((numberindex = numberindex + 1)): @detail.Question.Text
    +
    + @switch (detail.QuestionType) + { + case QuestionType.Text: + case QuestionType.Open_ended: +

    Answer: @detail.TextResponse

    + break; + case QuestionType.Multiple_choice: + case QuestionType.CheckBox: + case QuestionType.TrueFalse: + Answer: + @if (detail.Question.Type == QuestionType.TrueFalse) + { + +
    + @foreach (var answer in detail.Question.Answers) + { + // Ensuring each question has a unique group name by using the Question ID + string groupName = $"response{detail.Question.Id}"; +
    + ra.AnswerId == answer.Id) ? "checked" : "")> + +
    + } +
    + } + else + { + +
    + @foreach (var answer in detail.Question.Answers) + { +
    + ra.AnswerId == answer.Id) ? "checked" : "")> + +
    + } +
    + } + break; + + case QuestionType.Rating: + @if (detail.QuestionType == QuestionType.Rating) + { + User Selected rate +
      + @foreach (var answer in detail.Question.Answers.OrderBy(a => a.Id)) + { +
    • + +
      + @answer.Text +
      + + + + +
    • + } +
    + } + + break; + case QuestionType.Likert: + Answer: +
      + @foreach (var answer in detail.Question.Answers.OrderBy(a => a.Id)) + { + // Construct a unique name for each question-answer pair to ensure radio buttons are not linked. + var radioName = $"likertResponse{detail.Question.Id}-{answer.Id}"; + var isAnswerSelected = detail.ResponseAnswers.Any(ra => ra.AnswerId == answer.Id); + +
    • + +
    • + } +
    + + + break; + case QuestionType.Matrix: + + + Answer: + @* + + + + + *@ + + @foreach (var answer in detail.Question.Answers) + { + var isSelected = detail.ResponseAnswers.Any(ra => ra.AnswerId == answer.Id); + + + + + } + +
    User response
    + @answer.Text + + +
    + break; + + + case QuestionType.Ranking: +
    + +
    + Original Order of Answers: +
      + @foreach (var answer in detail.Question.Answers) + { + var userIndex = detail.ResponseAnswers.FindIndex(ra => ra.AnswerId == answer.Id) + 1; +
    • + @userIndex + @answer.Text +
    • + } +
    +
    + + +
    + User Order of answers: +
      + @for (int i = 0; i < detail.ResponseAnswers.Count; i++) + { + var ra = detail.ResponseAnswers[i]; + var answer = detail.Question.Answers.FirstOrDefault(a => a.Id == ra.AnswerId); +
    • + @(i + 1) + @answer.Text +
    • + } +
    +
    +
    + + break; + case QuestionType.Image: +
    +
    + +
    + answers: +
    + @foreach (var answer in detail.Question.Answers) + { + var isSelected = detail.ResponseAnswers.Any(ra => ra.AnswerId == answer.Id); +
    + + +
    + } +
    +
    +
    +
    + + break; + case QuestionType.Slider: +
    +
    + +
    + Answer +
      + @foreach (var answer in detail.Question.Answers) + { + // Retrieve the response that corresponds to the current answer + var responseAnswer = detail.ResponseAnswers.FirstOrDefault(ra => ra.AnswerId == answer.Id); + // Parse the response text to an integer + int sliderValue = 0; + Int32.TryParse(responseAnswer?.ResponseDetail.TextResponse, out sliderValue); + +
    • + +
      + 0 + + 100 +
      +
      + User Response: @sliderValue +
      +
    • + } +
    +
    +
    +
    + + break; + // Handle other question types as previously detailed + + // Other cases as previously detailed + default: +

    Unsupported question type.

    + break; + } +
    +
    +
    + } +
    + + Back to List +
    +
    + + + + + + diff --git a/Web/Controllers/QuestionnaireResponseController.cs b/Web/Controllers/QuestionnaireResponseController.cs index 0bae076..a7e2fbe 100644 --- a/Web/Controllers/QuestionnaireResponseController.cs +++ b/Web/Controllers/QuestionnaireResponseController.cs @@ -36,9 +36,43 @@ namespace Web.Controllers return View(); } - public IActionResult DisplayQuestionnaire(int id) + public IActionResult DisplayQuestionnaire(int id,string t) { - + // Check if the token is null or empty + if (string.IsNullOrEmpty(t)) + { + ViewBag.ErrorMessage = "The URL is invalid. Please provide a valid token."; + return View("Error"); + } + + // Split the token to extract the expiration date and time + string[] tokenParts = t.Split('|'); + if (tokenParts.Length != 2) + { + ViewBag.ErrorMessage = "The URL is invalid. Please provide a valid token."; + return View("Error"); + } + + string expiryDateTimeString = tokenParts[1]; + + // Parse the expiration datetime in UTC format + if (!DateTime.TryParseExact(expiryDateTimeString, "yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTime expiryDateTimeUtc)) + { + ViewBag.ErrorMessage = "The URL is invalid. Please provide a valid token."; + return View("Error"); + } + + // Convert the expiration datetime to local time + DateTime expiryDateTimeLocal = expiryDateTimeUtc.ToLocalTime(); + + // Check if the token is expired (accounting for UTC+2 offset) + if (expiryDateTimeLocal < DateTime.Now.AddHours(2)) + { + + return RedirectToAction(nameof(Error)); + } + + // Retrieve the questionnaire using the numeric ID var questionnaire = _questionnaireRepository.GetQuestionnaireWithQuestionAndAnswer(id); return View(MapToViewModel(questionnaire)); @@ -46,48 +80,49 @@ namespace Web.Controllers [HttpPost] public IActionResult DisplayQuestionnaire([FromForm] ResponseQuestionnaireViewModel questionnaire) { - //for (int i = 0; i < questionnaire.Questions.Count; i++) + //bool hasSubmitted = _context.Responses.Any(r => r.QuestionnaireId == questionnaire.Id && r.UserEmail == questionnaire.Email); + + //if (hasSubmitted) //{ - // var question = questionnaire.Questions[i]; - // List selectedTexts = new List(); - - // // Assuming SelectedAnswerIds and SelectedTexts are parallel arrays - // for (int j = 0; j < question.SelectedAnswerIds.Count; j++) - // { - // int selectedId = question.SelectedAnswerIds[j]; - // if (question.SelectedAnswerIds.Contains(selectedId)) // Ensure the ID was actually selected - // { - // selectedTexts.Add(question.SelectedText[j]); - // Console.WriteLine($"Selected Text{selectedTexts}") - // } - // } - - // question.SelectedText = selectedTexts; // Now contains only the texts of selected answers + // TempData["ErrorMessage"] = "You have already completed this survey."; + // return RedirectToAction("ThankYou"); //} - // Process the updated model further as needed - /* return Json(questionnaire);*/ // Redirect to a results view, or handle as necessary - - foreach (var question in questionnaire.Questions) + var response = new Response { - var dbQuestion = _context.Questions.Include(q => q.Answers).FirstOrDefault(q => q.Id == question.Id); - - if (dbQuestion != null) + QuestionnaireId = questionnaire.Id, + UserName = questionnaire.UserName, + UserEmail = questionnaire.Email, + SubmissionDate = DateTime.UtcNow, + ResponseDetails = questionnaire.Questions.Select(q => new ResponseDetail { - foreach (var selectedId in question.SelectedAnswerIds) - { - var selectedAnswer = dbQuestion.Answers.FirstOrDefault(a => a.Id == selectedId); - if (selectedAnswer != null) - { - Console.WriteLine($"Selected Answer Text: {selectedAnswer.Text}"); - Console.WriteLine($"Selected Answer Id: {selectedAnswer.Id}"); - // Here you could further process each selected answer, e.g., saving user responses - } - } - } - } - return Json(questionnaire); + QuestionId = q.Id, + QuestionType=q.Type, + // Handle TextResponse based on question type + TextResponse = (q.Type == QuestionType.Open_ended || q.Type == QuestionType.Text || q.Type==QuestionType.Slider) + ? string.Join(" ", q.SelectedText) // Ensure SelectedText is appropriately used based on question type + : null, + ResponseAnswers = q.SelectedAnswerIds + .Select(aid => new ResponseAnswer { AnswerId = aid }) + .ToList() // Ensure that the list is initialized correctly + }).ToList() + }; + + _context.Responses.Add(response); + _context.SaveChanges(); + + TempData["UserName"] = questionnaire.UserName; + + return RedirectToAction(nameof(ThankYou)); + + } + + [HttpGet] + public IActionResult ThankYou() + { + ViewBag.UserName = TempData["UserName"]; + return View(); } @@ -121,41 +156,7 @@ namespace Web.Controllers } -//// Check if the token is null or empty -//if (string.IsNullOrEmpty(t)) -//{ -// ViewBag.ErrorMessage = "The URL is invalid. Please provide a valid token."; -// return View("Error"); -//} - -//// Split the token to extract the expiration date and time -//string[] tokenParts = t.Split('|'); -//if (tokenParts.Length != 2) -//{ -// ViewBag.ErrorMessage = "The URL is invalid. Please provide a valid token."; -// return View("Error"); -//} - -//string expiryDateTimeString = tokenParts[1]; - -//// Parse the expiration datetime in UTC format -//if (!DateTime.TryParseExact(expiryDateTimeString, "yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTime expiryDateTimeUtc)) -//{ -// ViewBag.ErrorMessage = "The URL is invalid. Please provide a valid token."; -// return View("Error"); -//} - -//// Convert the expiration datetime to local time -//DateTime expiryDateTimeLocal = expiryDateTimeUtc.ToLocalTime(); - -//// Check if the token is expired (accounting for UTC+2 offset) -//if (expiryDateTimeLocal < DateTime.Now.AddHours(2)) -//{ - -// return RedirectToAction(nameof(Error)); -//} - -// Retrieve the questionnaire using the numeric ID + diff --git a/Web/Migrations/20240327153248_DataBaseCreated.Designer.cs b/Web/Migrations/20240327153248_DataBaseCreated.Designer.cs deleted file mode 100644 index 5fda664..0000000 --- a/Web/Migrations/20240327153248_DataBaseCreated.Designer.cs +++ /dev/null @@ -1,368 +0,0 @@ -// -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("20240327153248_DataBaseCreated")] - partial class DataBaseCreated - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.2") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Model.Address", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CVR") - .HasColumnType("nvarchar(max)"); - - b.Property("City") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Country") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Mobile") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("PostalCode") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("State") - .HasColumnType("nvarchar(max)"); - - b.Property("Street") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Addresss"); - }); - - modelBuilder.Entity("Model.Answer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("QuestionId") - .HasColumnType("int"); - - b.Property("Text") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("QuestionId"); - - b.ToTable("Answers"); - }); - - modelBuilder.Entity("Model.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ImageUrl") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("LinkUrl") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Banners"); - }); - - modelBuilder.Entity("Model.Footer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ImageUlr") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("LastUpdated") - .HasColumnType("datetime2"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Owner") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Sitecopyright") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("UpdatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Footers"); - }); - - modelBuilder.Entity("Model.FooterSocialMedia", b => - { - b.Property("FooterId") - .HasColumnType("int"); - - b.Property("SocialId") - .HasColumnType("int"); - - b.HasKey("FooterId", "SocialId"); - - b.HasIndex("SocialId"); - - b.ToTable("FooterSocialMedias"); - }); - - modelBuilder.Entity("Model.Page", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("BannerId") - .HasColumnType("int"); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("FooterId") - .HasColumnType("int"); - - b.Property("Slug") - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("BannerId"); - - b.HasIndex("FooterId"); - - b.ToTable("Pages"); - }); - - modelBuilder.Entity("Model.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("QuestionnaireId") - .HasColumnType("int"); - - b.Property("Text") - .HasColumnType("nvarchar(max)"); - - b.Property("Type") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("QuestionnaireId"); - - b.ToTable("Questions"); - }); - - modelBuilder.Entity("Model.Questionnaire", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Questionnaires"); - }); - - modelBuilder.Entity("Model.SocialMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Url") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("SocialMedia"); - }); - - 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.Footer", b => - { - b.Navigation("FooterSocialMedias"); - }); - - modelBuilder.Entity("Model.Question", b => - { - b.Navigation("Answers"); - }); - - modelBuilder.Entity("Model.Questionnaire", b => - { - b.Navigation("Questions"); - }); - - modelBuilder.Entity("Model.SocialMedia", b => - { - b.Navigation("FooterSocialMedias"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Web/Migrations/20240401115718_SubscriptionModelAdded.cs b/Web/Migrations/20240401115718_SubscriptionModelAdded.cs deleted file mode 100644 index b1f51b0..0000000 --- a/Web/Migrations/20240401115718_SubscriptionModelAdded.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Web.Migrations -{ - /// - public partial class SubscriptionModelAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Subscriptions", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Name = table.Column(type: "nvarchar(max)", nullable: false), - Email = table.Column(type: "nvarchar(max)", nullable: false), - IsSubscribed = table.Column(type: "bit", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Subscriptions", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Subscriptions"); - } - } -} diff --git a/Web/Migrations/20240403083337_RequriedfiledRemoved.Designer.cs b/Web/Migrations/20240403083337_RequriedfiledRemoved.Designer.cs deleted file mode 100644 index 3b5e78e..0000000 --- a/Web/Migrations/20240403083337_RequriedfiledRemoved.Designer.cs +++ /dev/null @@ -1,391 +0,0 @@ -// -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("20240403083337_RequriedfiledRemoved")] - partial class RequriedfiledRemoved - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.2") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Model.Address", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CVR") - .HasColumnType("nvarchar(max)"); - - b.Property("City") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Country") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Mobile") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("PostalCode") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("State") - .HasColumnType("nvarchar(max)"); - - b.Property("Street") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Addresss"); - }); - - modelBuilder.Entity("Model.Answer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("QuestionId") - .HasColumnType("int"); - - b.Property("Text") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("QuestionId"); - - b.ToTable("Answers"); - }); - - modelBuilder.Entity("Model.Banner", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ImageUrl") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("LinkUrl") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Banners"); - }); - - modelBuilder.Entity("Model.Footer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ImageUlr") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("LastUpdated") - .HasColumnType("datetime2"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Owner") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Sitecopyright") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("UpdatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Footers"); - }); - - modelBuilder.Entity("Model.FooterSocialMedia", b => - { - b.Property("FooterId") - .HasColumnType("int"); - - b.Property("SocialId") - .HasColumnType("int"); - - b.HasKey("FooterId", "SocialId"); - - b.HasIndex("SocialId"); - - b.ToTable("FooterSocialMedias"); - }); - - modelBuilder.Entity("Model.Page", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("BannerId") - .HasColumnType("int"); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("FooterId") - .HasColumnType("int"); - - b.Property("Slug") - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("BannerId"); - - b.HasIndex("FooterId"); - - b.ToTable("Pages"); - }); - - modelBuilder.Entity("Model.Question", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("QuestionnaireId") - .HasColumnType("int"); - - b.Property("Text") - .HasColumnType("nvarchar(max)"); - - b.Property("Type") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("QuestionnaireId"); - - b.ToTable("Questions"); - }); - - modelBuilder.Entity("Model.Questionnaire", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Questionnaires"); - }); - - modelBuilder.Entity("Model.SocialMedia", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Url") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("SocialMedia"); - }); - - modelBuilder.Entity("Model.Subscription", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IsSubscribed") - .HasColumnType("bit"); - - b.Property("Name") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Subscriptions"); - }); - - 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.Footer", b => - { - b.Navigation("FooterSocialMedias"); - }); - - modelBuilder.Entity("Model.Question", b => - { - b.Navigation("Answers"); - }); - - modelBuilder.Entity("Model.Questionnaire", b => - { - b.Navigation("Questions"); - }); - - modelBuilder.Entity("Model.SocialMedia", b => - { - b.Navigation("FooterSocialMedias"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Web/Migrations/20240403083337_RequriedfiledRemoved.cs b/Web/Migrations/20240403083337_RequriedfiledRemoved.cs deleted file mode 100644 index 359740b..0000000 --- a/Web/Migrations/20240403083337_RequriedfiledRemoved.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Web.Migrations -{ - /// - public partial class RequriedfiledRemoved : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Name", - table: "Subscriptions", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(max)"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Name", - table: "Subscriptions", - type: "nvarchar(max)", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); - } - } -} diff --git a/Web/Migrations/20240401115718_SubscriptionModelAdded.Designer.cs b/Web/Migrations/20240424144801_initial.Designer.cs similarity index 74% rename from Web/Migrations/20240401115718_SubscriptionModelAdded.Designer.cs rename to Web/Migrations/20240424144801_initial.Designer.cs index 90c7c41..b934df0 100644 --- a/Web/Migrations/20240401115718_SubscriptionModelAdded.Designer.cs +++ b/Web/Migrations/20240424144801_initial.Designer.cs @@ -12,8 +12,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Web.Migrations { [DbContext(typeof(SurveyContext))] - [Migration("20240401115718_SubscriptionModelAdded")] - partial class SubscriptionModelAdded + [Migration("20240424144801_initial")] + partial class initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -262,6 +262,83 @@ namespace Web.Migrations b.ToTable("Questionnaires"); }); + modelBuilder.Entity("Model.Response", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("QuestionnaireId") + .HasColumnType("int"); + + b.Property("SubmissionDate") + .HasColumnType("datetime2"); + + b.Property("UserEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("QuestionnaireId"); + + b.ToTable("Responses"); + }); + + modelBuilder.Entity("Model.ResponseAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AnswerId") + .HasColumnType("int"); + + b.Property("ResponseDetailId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDetailId"); + + b.ToTable("ResponseAnswers"); + }); + + modelBuilder.Entity("Model.ResponseDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("QuestionId") + .HasColumnType("int"); + + b.Property("QuestionType") + .HasColumnType("int"); + + b.Property("ResponseId") + .HasColumnType("int"); + + b.Property("TextResponse") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("QuestionId"); + + b.HasIndex("ResponseId"); + + b.ToTable("ResponseDetails"); + }); + modelBuilder.Entity("Model.SocialMedia", b => { b.Property("Id") @@ -299,7 +376,6 @@ namespace Web.Migrations .HasColumnType("bit"); b.Property("Name") - .IsRequired() .HasColumnType("nvarchar(max)"); b.HasKey("Id"); @@ -367,6 +443,47 @@ namespace Web.Migrations 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"); @@ -382,6 +499,16 @@ namespace Web.Migrations 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"); diff --git a/Web/Migrations/20240327153248_DataBaseCreated.cs b/Web/Migrations/20240424144801_initial.cs similarity index 67% rename from Web/Migrations/20240327153248_DataBaseCreated.cs rename to Web/Migrations/20240424144801_initial.cs index 03dce3a..683add0 100644 --- a/Web/Migrations/20240327153248_DataBaseCreated.cs +++ b/Web/Migrations/20240424144801_initial.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace Web.Migrations { /// - public partial class DataBaseCreated : Migration + public partial class initial : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -97,6 +97,21 @@ namespace Web.Migrations table.PrimaryKey("PK_SocialMedia", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Subscriptions", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(max)", nullable: true), + Email = table.Column(type: "nvarchar(max)", nullable: false), + IsSubscribed = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Subscriptions", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Pages", columns: table => new @@ -147,6 +162,28 @@ namespace Web.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "Responses", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + QuestionnaireId = table.Column(type: "int", nullable: false), + UserName = table.Column(type: "nvarchar(max)", nullable: true), + UserEmail = table.Column(type: "nvarchar(max)", nullable: true), + SubmissionDate = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Responses", x => x.Id); + table.ForeignKey( + name: "FK_Responses_Questionnaires_QuestionnaireId", + column: x => x.QuestionnaireId, + principalTable: "Questionnaires", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "FooterSocialMedias", columns: table => new @@ -191,6 +228,54 @@ namespace Web.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "ResponseDetails", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ResponseId = table.Column(type: "int", nullable: false), + QuestionId = table.Column(type: "int", nullable: false), + QuestionType = table.Column(type: "int", nullable: false), + TextResponse = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ResponseDetails", x => x.Id); + table.ForeignKey( + name: "FK_ResponseDetails_Questions_QuestionId", + column: x => x.QuestionId, + principalTable: "Questions", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ResponseDetails_Responses_ResponseId", + column: x => x.ResponseId, + principalTable: "Responses", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ResponseAnswers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ResponseDetailId = table.Column(type: "int", nullable: false), + AnswerId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ResponseAnswers", x => x.Id); + table.ForeignKey( + name: "FK_ResponseAnswers_ResponseDetails_ResponseDetailId", + column: x => x.ResponseDetailId, + principalTable: "ResponseDetails", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateIndex( name: "IX_Answers_QuestionId", table: "Answers", @@ -215,6 +300,26 @@ namespace Web.Migrations name: "IX_Questions_QuestionnaireId", table: "Questions", column: "QuestionnaireId"); + + migrationBuilder.CreateIndex( + name: "IX_ResponseAnswers_ResponseDetailId", + table: "ResponseAnswers", + column: "ResponseDetailId"); + + migrationBuilder.CreateIndex( + name: "IX_ResponseDetails_QuestionId", + table: "ResponseDetails", + column: "QuestionId"); + + migrationBuilder.CreateIndex( + name: "IX_ResponseDetails_ResponseId", + table: "ResponseDetails", + column: "ResponseId"); + + migrationBuilder.CreateIndex( + name: "IX_Responses_QuestionnaireId", + table: "Responses", + column: "QuestionnaireId"); } /// @@ -233,7 +338,10 @@ namespace Web.Migrations name: "Pages"); migrationBuilder.DropTable( - name: "Questions"); + name: "ResponseAnswers"); + + migrationBuilder.DropTable( + name: "Subscriptions"); migrationBuilder.DropTable( name: "SocialMedia"); @@ -244,6 +352,15 @@ namespace Web.Migrations migrationBuilder.DropTable( name: "Footers"); + migrationBuilder.DropTable( + name: "ResponseDetails"); + + migrationBuilder.DropTable( + name: "Questions"); + + migrationBuilder.DropTable( + name: "Responses"); + migrationBuilder.DropTable( name: "Questionnaires"); } diff --git a/Web/Migrations/SurveyContextModelSnapshot.cs b/Web/Migrations/SurveyContextModelSnapshot.cs index 2c2a25b..e370823 100644 --- a/Web/Migrations/SurveyContextModelSnapshot.cs +++ b/Web/Migrations/SurveyContextModelSnapshot.cs @@ -259,6 +259,83 @@ namespace Web.Migrations b.ToTable("Questionnaires"); }); + modelBuilder.Entity("Model.Response", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("QuestionnaireId") + .HasColumnType("int"); + + b.Property("SubmissionDate") + .HasColumnType("datetime2"); + + b.Property("UserEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("QuestionnaireId"); + + b.ToTable("Responses"); + }); + + modelBuilder.Entity("Model.ResponseAnswer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AnswerId") + .HasColumnType("int"); + + b.Property("ResponseDetailId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ResponseDetailId"); + + b.ToTable("ResponseAnswers"); + }); + + modelBuilder.Entity("Model.ResponseDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("QuestionId") + .HasColumnType("int"); + + b.Property("QuestionType") + .HasColumnType("int"); + + b.Property("ResponseId") + .HasColumnType("int"); + + b.Property("TextResponse") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("QuestionId"); + + b.HasIndex("ResponseId"); + + b.ToTable("ResponseDetails"); + }); + modelBuilder.Entity("Model.SocialMedia", b => { b.Property("Id") @@ -363,6 +440,47 @@ namespace Web.Migrations 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"); @@ -378,6 +496,16 @@ namespace Web.Migrations 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"); diff --git a/Web/Program.cs b/Web/Program.cs index 8e4417b..900f605 100644 --- a/Web/Program.cs +++ b/Web/Program.cs @@ -51,6 +51,8 @@ if (!app.Environment.IsDevelopment()) app.UseHsts(); } + + app.UseHttpsRedirection(); app.UseStaticFiles(); diff --git a/Web/ViewModel/QuestionnaireVM/ResponseQuestionnaireViewModel.cs b/Web/ViewModel/QuestionnaireVM/ResponseQuestionnaireViewModel.cs index 259946b..8b08f57 100644 --- a/Web/ViewModel/QuestionnaireVM/ResponseQuestionnaireViewModel.cs +++ b/Web/ViewModel/QuestionnaireVM/ResponseQuestionnaireViewModel.cs @@ -1,4 +1,5 @@ -using Web.ViewModel.QuestionVM; +using System.ComponentModel.DataAnnotations; +using Web.ViewModel.QuestionVM; namespace Web.ViewModel.QuestionnaireVM { @@ -9,8 +10,15 @@ namespace Web.ViewModel.QuestionnaireVM public string? Title { get; set; } // Title of the questionnaire public string? Description { get; set; } // Description of the questionnaire - // Collection of questions - public List Questions { get; set; } = new List(); + [Required] + public string? UserName { get; set; } + + [Required] + public string? Email { get; set; } + + + // Collection of questions + public List Questions { get; set; } = new List(); } diff --git a/Web/Views/QuestionnaireResponse/DisplayQuestionnaire.cshtml b/Web/Views/QuestionnaireResponse/DisplayQuestionnaire.cshtml index 3e9791b..69e93bb 100644 --- a/Web/Views/QuestionnaireResponse/DisplayQuestionnaire.cshtml +++ b/Web/Views/QuestionnaireResponse/DisplayQuestionnaire.cshtml @@ -33,7 +33,7 @@ } .QuestionContainer { - padding-top: 100px; + padding-top: 50px; padding-bottom: 100px; } @@ -320,9 +320,14 @@

    @Model.Title

    @Html.Raw(Model.Description)

    - - - +
    + + +
    +
    + + +
    @@ -341,6 +346,7 @@
    + @for (int i = 0; i < Model.Questions.Count; i++) { var question = Model.Questions[i]; @@ -359,7 +365,7 @@

    @(i + 1). @question.Text

    - + @switch (question.Type) { @@ -368,7 +374,7 @@ { - + } break; case QuestionType.CheckBox: @@ -380,7 +386,7 @@ @foreach (var answer in question.Answers) {
    - + @@ -625,7 +631,7 @@
    break; } - +
    @if (i > 0) { @@ -638,7 +644,16 @@
    } - +
    @@ -799,6 +814,11 @@ showStep(currentStep); }); + + + + + // document.addEventListener("DOMContentLoaded", function () { // const stepper = document.querySelector('.stepper'); // if (!stepper) { diff --git a/Web/Views/QuestionnaireResponse/Error.cshtml b/Web/Views/QuestionnaireResponse/Error.cshtml index e6d0f03..7e1288b 100644 --- a/Web/Views/QuestionnaireResponse/Error.cshtml +++ b/Web/Views/QuestionnaireResponse/Error.cshtml @@ -167,29 +167,6 @@ -@*
    -
    -
    -
    - -
    -
    @ViewBag.ErrorMessage
    - Contact -
    - -
    - - -
    -
    - -
    - -
    - - -
    *@ - diff --git a/Web/Views/QuestionnaireResponse/ThankYou.cshtml b/Web/Views/QuestionnaireResponse/ThankYou.cshtml new file mode 100644 index 0000000..8161dab --- /dev/null +++ b/Web/Views/QuestionnaireResponse/ThankYou.cshtml @@ -0,0 +1,164 @@ + +@{ + ViewData["Title"] = "Thank You"; + Layout = "~/Views/Shared/_QuestionnaireResponse.cshtml"; +} + + + +
    + + +
    +
    + + +
    +
    +

    Hey @ViewBag.UserName

    + + + +
    Thank you for taking the time to submit the form! Your input is invaluable to us and will help us improve our services.
    + +
    +
    + + +
    +
    + + +
    +
    + +
    + + + + + + + + + diff --git a/Web/appsettings.json b/Web/appsettings.json index 08b448f..58be4eb 100644 --- a/Web/appsettings.json +++ b/Web/appsettings.json @@ -15,7 +15,8 @@ "ApplicationName": "Online Survey", "ConfirmEmailPath": "Subscription/Confirmation", "unsubscribePath": "Subscription/UnsubscribeConfirmation", - "Questionnaire": "QuestionnaireResponse/DisplayQuestionnaire" + "Questionnaire": "QuestionnaireResponse/DisplayQuestionnaire", + "EncryptionKey": "" }, "MailJet": {