From 4fb4e979e66d45fec42116fc7ca3e569d7e0d2e2 Mon Sep 17 00:00:00 2001 From: Qais Yousuf Date: Thu, 9 May 2024 14:08:10 +0200 Subject: [PATCH] Real time email event tracking completed --- Data/SurveyContext.cs | 2 + Model/SentNewsletterEamil.cs | 26 + Services/EmailSend/EmailStatistic.cs | 19 + Services/Implemnetation/EmailStatsService.cs | 69 ++ Services/Interaces/IEmailStatsService.cs | 14 + .../Admin/Controllers/AddressController.cs | 2 +- .../Admin/Controllers/BannerController.cs | 2 +- .../Admin/Controllers/FooterController.cs | 2 +- .../Controllers/NewslettersController.cs | 402 +++++++-- .../Controllers/OpenAIContentController.cs | 2 +- Web/Areas/Admin/Controllers/PageController.cs | 2 +- .../Controllers/QuestionnaireController.cs | 2 +- .../Admin/Controllers/RegisterController.cs | 2 +- .../Admin/Controllers/RolesController.cs | 2 +- .../Controllers/SocialMediaController.cs | 2 +- .../Controllers/SurveyAnalysisController.cs | 33 +- .../Controllers/UserResponseController.cs | 2 +- .../Admin/Controllers/UsersController.cs | 2 +- .../Admin/Views/SurveyAnalysis/Index.cshtml | 2 +- Web/Areas/Admin/Views/_ViewImports.cshtml | 3 + .../Admin/Views/newsletters/EmailStats.cshtml | 284 ++++++ .../Admin/Views/newsletters/Index.cshtml | 192 +++- .../newsletters/LastReceivedEventsData.cshtml | 48 + .../Admin/Views/newsletters/ShowStat.cshtml | 28 + Web/Extesions/ServicesExtesions.cs | 4 + ..._SentEmailNewsletterModelAdded.Designer.cs | 814 +++++++++++++++++ ...506120728_SentEmailNewsletterModelAdded.cs | 43 + ...4039_EmailTrackingModelChnaged.Designer.cs | 820 +++++++++++++++++ ...0240508104039_EmailTrackingModelChnaged.cs | 40 + ...delChangedWithIsBlockedPrperty.Designer.cs | 823 ++++++++++++++++++ ...rackingModelChangedWithIsBlockedPrperty.cs | 29 + Web/Migrations/SurveyContextModelSnapshot.cs | 49 ++ Web/Program.cs | 8 +- .../NewsLetterVM/EmailEventViewModel.cs | 16 + 34 files changed, 3664 insertions(+), 126 deletions(-) create mode 100644 Model/SentNewsletterEamil.cs create mode 100644 Services/EmailSend/EmailStatistic.cs create mode 100644 Services/Implemnetation/EmailStatsService.cs create mode 100644 Services/Interaces/IEmailStatsService.cs create mode 100644 Web/Areas/Admin/Views/newsletters/EmailStats.cshtml create mode 100644 Web/Areas/Admin/Views/newsletters/LastReceivedEventsData.cshtml create mode 100644 Web/Areas/Admin/Views/newsletters/ShowStat.cshtml create mode 100644 Web/Migrations/20240506120728_SentEmailNewsletterModelAdded.Designer.cs create mode 100644 Web/Migrations/20240506120728_SentEmailNewsletterModelAdded.cs create mode 100644 Web/Migrations/20240508104039_EmailTrackingModelChnaged.Designer.cs create mode 100644 Web/Migrations/20240508104039_EmailTrackingModelChnaged.cs create mode 100644 Web/Migrations/20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty.Designer.cs create mode 100644 Web/Migrations/20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty.cs create mode 100644 Web/ViewModel/NewsLetterVM/EmailEventViewModel.cs diff --git a/Data/SurveyContext.cs b/Data/SurveyContext.cs index afd2489..3a89707 100644 --- a/Data/SurveyContext.cs +++ b/Data/SurveyContext.cs @@ -35,6 +35,8 @@ namespace Data public DbSet ResponseDetails { get; set; } public DbSet ResponseAnswers { get; set; } + public DbSet SentNewsletterEamils { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/Model/SentNewsletterEamil.cs b/Model/SentNewsletterEamil.cs new file mode 100644 index 0000000..c81626e --- /dev/null +++ b/Model/SentNewsletterEamil.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Model +{ + public class SentNewsletterEamil + { + public int Id { get; set; } + public string? RecipientEmail { get; set; } + public string? Subject { get; set; } + public string? Body { get; set; } + public DateTime SentDate { get; set; } + public bool IsDelivered { get; set; } + public bool IsOpened { get; set; } + public bool IsClicked { get; set; } + public bool IsBounced { get; set; } + public bool IsSpam { get; set; } + public bool IsSent { get; set; } + public bool IsUnsubscribed { get; set; } + public bool IsBlocked { get; set; } + + } +} diff --git a/Services/EmailSend/EmailStatistic.cs b/Services/EmailSend/EmailStatistic.cs new file mode 100644 index 0000000..ee6dadd --- /dev/null +++ b/Services/EmailSend/EmailStatistic.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Services.EmailSend +{ + public class EmailStatistic + { + public int TotalEmails { get; set; } + public int Delivered { get; set; } + public int Opened { get; set; } + public int Clicked { get; set; } + public int Bounced { get; set; } + public int Blocked { get; set; } + public int MarkedAsSpam { get; set; } + } +} diff --git a/Services/Implemnetation/EmailStatsService.cs b/Services/Implemnetation/EmailStatsService.cs new file mode 100644 index 0000000..7758676 --- /dev/null +++ b/Services/Implemnetation/EmailStatsService.cs @@ -0,0 +1,69 @@ +using Mailjet.Client.Resources; +using Mailjet.Client; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json.Linq; +using Services.EmailSend; +using Services.Interaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Services.Implemnetation +{ + public class EmailStatsService: IEmailStatsService + { + private readonly IConfiguration _configuration; + + public EmailStatsService(IConfiguration configuration) + { + _configuration = configuration; + } + + public async Task FetchEmailStatsAsync() + { + + var apiKey = _configuration["MailJet:ApiKey"]; + var apiSecret = _configuration["MailJet:SecretKey"]; + + MailjetClient client = new MailjetClient(apiKey, apiSecret); + + + // Construct the request to get statistics + MailjetRequest request = new MailjetRequest + { + Resource = Statcounters.Resource, + } + .Property(Statcounters.CounterSource, "APIKey") // assuming you want statistics based on API Key usage + .Property(Statcounters.CounterTiming, "Message") // assuming you want statistics about messages + .Property(Statcounters.CounterResolution, "Lifetime"); // assuming you want lifetime statistics + + // Execute the request + MailjetResponse response = await client.GetAsync(request); + if (response.IsSuccessStatusCode) + { + return ParseEmailStats(response.GetData()); + } + else + { + throw new InvalidOperationException($"Failed to fetch data: {response.StatusCode}, {response.GetErrorMessage()}"); + } + } + + private EmailStatistic ParseEmailStats(JToken data) + { + EmailStatistic stats = new EmailStatistic + { + TotalEmails = data[0]["Total"].Value(), + Delivered = data[0]["Delivered"].Value(), + Opened = data[0]["Opened"].Value(), + Clicked = data[0]["Clicked"].Value(), + Bounced = data[0]["Bounced"].Value(), + Blocked = data[0]["Blocked"].Value(), + MarkedAsSpam = data[0]["Spam"].Value() + }; + return stats; + } + } +} diff --git a/Services/Interaces/IEmailStatsService.cs b/Services/Interaces/IEmailStatsService.cs new file mode 100644 index 0000000..0593c65 --- /dev/null +++ b/Services/Interaces/IEmailStatsService.cs @@ -0,0 +1,14 @@ +using Services.EmailSend; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Services.Interaces +{ + public interface IEmailStatsService + { + Task FetchEmailStatsAsync(); + } +} diff --git a/Web/Areas/Admin/Controllers/AddressController.cs b/Web/Areas/Admin/Controllers/AddressController.cs index fd35c6a..7de345a 100644 --- a/Web/Areas/Admin/Controllers/AddressController.cs +++ b/Web/Areas/Admin/Controllers/AddressController.cs @@ -6,7 +6,7 @@ using Web.ViewModel.AddressVM; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles ="Admin")] + public class AddressController : Controller { private readonly IAddressRepository _addresContext; diff --git a/Web/Areas/Admin/Controllers/BannerController.cs b/Web/Areas/Admin/Controllers/BannerController.cs index 92026f5..b6fe21a 100644 --- a/Web/Areas/Admin/Controllers/BannerController.cs +++ b/Web/Areas/Admin/Controllers/BannerController.cs @@ -8,7 +8,7 @@ using Web.ViewModel.BannerVM; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class BannerController : Controller { private readonly IBannerRepository _banner; diff --git a/Web/Areas/Admin/Controllers/FooterController.cs b/Web/Areas/Admin/Controllers/FooterController.cs index 8abd8f7..101311d 100644 --- a/Web/Areas/Admin/Controllers/FooterController.cs +++ b/Web/Areas/Admin/Controllers/FooterController.cs @@ -15,7 +15,7 @@ using static Microsoft.EntityFrameworkCore.DbLoggerCategory; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class FooterController : Controller { private readonly IFooterRepository _footer; diff --git a/Web/Areas/Admin/Controllers/NewslettersController.cs b/Web/Areas/Admin/Controllers/NewslettersController.cs index c19134d..46d2266 100644 --- a/Web/Areas/Admin/Controllers/NewslettersController.cs +++ b/Web/Areas/Admin/Controllers/NewslettersController.cs @@ -3,8 +3,11 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Microsoft.VisualStudio.TextTemplating; using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages.Manage; +using Model; +using Newtonsoft.Json.Linq; using OpenAI_API; using Services.EmailSend; using Services.Implemnetation; @@ -15,13 +18,14 @@ using Web.ViewModel.NewsLetterVM; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class NewslettersController : Controller { private readonly INewsLetterRepository _repository; private readonly SurveyContext _context; private readonly IEmailServices _emailServices; private readonly IConfiguration _configuration; + private readonly IMemoryCache _memoryCache; public NewslettersController(INewsLetterRepository repository,SurveyContext context,IEmailServices emailServices,IConfiguration configuration) { @@ -29,6 +33,7 @@ namespace Web.Areas.Admin.Controllers _context = context; _emailServices = emailServices; _configuration = configuration; + } public IActionResult Index() { @@ -63,110 +68,343 @@ namespace Web.Areas.Admin.Controllers return View(); } + [HttpPost] + public async Task Create(SendNewsLetterViewModel viewModel) { - if(ModelState.IsValid) + if (ModelState.IsValid) { try { // Retrieve all subscribed users var subscribedUsers = await _context.Subscriptions.Where(s => s.IsSubscribed).ToListAsync(); - - - string confirmationPath = _configuration["Email:unsubscribePath"]; - // Send the newsletter email to each subscribed user - foreach (var user in subscribedUsers) - { - string confirmationUrl = $"{Request.Scheme}://{Request.Host}/{confirmationPath}?email={user.Email}"; - //string emailBody = $"

Hey {user.Name}


{viewModel.Body}
Søren Eggert Lundsteen Olsen
Seosoft ApS

Hovedgaden 3
Jordrup
Kolding 6064
Denmark


"; + + string confirmationPath = _configuration["Email:unsubscribePath"]; + // Send the newsletter email to each subscribed user + foreach (var user in subscribedUsers) + { + string confirmationUrl = $"{Request.Scheme}://{Request.Host}/{confirmationPath}?email={user.Email}"; string emailBody = $@" - - - Email Confirmation - - - -
-

Hey {user.Name},

-

{viewModel.Body}


- -
Søren Eggert Lundsteen Olsen
-
SeoSoft ApS
-
-
Hovedgaden 3
Jordrup
Kolding 6064
Denmark
- -
- "; - var email = new EmailToSend(user.Email, viewModel.Subject, emailBody); - var isSent = await _emailServices.SendConfirmationEmailAsync(email); + + + Email Confirmation + + + +
+

Hey {user.Name},

+

{viewModel.Body}


+ +
Søren Eggert Lundsteen Olsen
+
SeoSoft ApS
+
+
Hovedgaden 3
Jordrup
Kolding 6064
Denmark
+ +
+ "; - // Handle failure to send email if needed + var email = new EmailToSend(user.Email, viewModel.Subject, emailBody); + var isSent = await _emailServices.SendConfirmationEmailAsync(email); + + // Create a record for the sent email + var sentEmail = new SentNewsletterEamil + { + RecipientEmail = user.Email, + Subject = viewModel.Subject, + Body = emailBody, - } + SentDate = DateTime.UtcNow, + IsSent = isSent // Assuming isSent returns a boolean indicating success + }; + _context.SentNewsletterEamils.Add(sentEmail); - TempData["success"] = "Nesletter sent successfully."; - return RedirectToAction(nameof(Index)); - - + // Handle failure to send email if needed + } + await _context.SaveChangesAsync(); // Save changes for all sent emails + + TempData["success"] = "Newsletter sent successfully."; + return RedirectToAction(nameof(Index)); } catch (Exception ex) { // Log or handle the exception as needed - TempData["success"] = "something went wrong."; + TempData["error"] = "Something went wrong: " + ex.Message; return RedirectToAction(nameof(Index)); } } return View(viewModel); - - } + [HttpPost] + public async Task MailjetWebhook() + { + using (var reader = new StreamReader(Request.Body)) + { + var requestBody = await reader.ReadToEndAsync(); + Console.WriteLine("Received payload: " + requestBody); + Request.Body.Position = 0; + + try + { + var events = JArray.Parse(requestBody); + if (events == null) + { + return BadRequest("Parsed data is null"); + } + + foreach (JObject e in events) + { + string email = e.Value("email"); + string eventType = e.Value("event"); + Console.WriteLine($"Processing {eventType} for {email}"); + + var newsletterEmail = await _context.SentNewsletterEamils + .FirstOrDefaultAsync(n => n.RecipientEmail == email); + + if (newsletterEmail == null) + { + Console.WriteLine("No newsletter email record found for email: " + email); + continue; + } + + switch (eventType) + { + case "sent": + newsletterEmail.IsDelivered = true; + break; + + case "open": + newsletterEmail.IsOpened = true; + break; + case "click": + newsletterEmail.IsClicked = true; + break; + case "bounce": + newsletterEmail.IsBounced = true; + break; + case "spam": + newsletterEmail.IsSpam = true; + break; + case "unsub": + newsletterEmail.IsUnsubscribed = true; + break; + case "blocked": + newsletterEmail.IsBlocked = true; + break; + default: + Console.WriteLine($"Unhandled event type: {eventType}"); + break; + } + + _context.Entry(newsletterEmail).State = EntityState.Modified; + } + Console.WriteLine("Email got updated "); + + await _context.SaveChangesAsync(); + } + catch (Exception ex) + { + Console.WriteLine("Exception parsing JSON: " + ex.Message); + return BadRequest("Error parsing JSON: " + ex.Message); + } + } + + return Ok(); + } + + + + public async Task EmailStats() + { + var emails = await _context.SentNewsletterEamils.ToListAsync(); + return View(emails); + } + + public async Task GetEmailStatsData() + { + var model = await _context.SentNewsletterEamils.ToListAsync(); + return Json(model); // Returns the list of emails as JSON. + } + + public async Task GetChartData() + { + var emails = await _context.SentNewsletterEamils.ToListAsync(); + + var data = new + { + Sent = emails.Count(e => e.IsSent), + Delivered = emails.Count(e => e.IsDelivered), + Opened = emails.Count(e => e.IsOpened), + Clicked = emails.Count(e => e.IsClicked), + Bounced = emails.Count(e => e.IsBounced), + Spam = emails.Count(e => e.IsSpam), + Blocked = emails.Count(e => e.IsBlocked), + Unsubscribed = emails.Count(e => e.IsUnsubscribed) + }; + + return Json(data); + } + + + + + + + + + //[HttpPost] + //public async Task Create(SendNewsLetterViewModel viewModel) + //{ + // if(ModelState.IsValid) + // { + // try + // { + // // Retrieve all subscribed users + // var subscribedUsers = await _context.Subscriptions.Where(s => s.IsSubscribed).ToListAsync(); + + + // string confirmationPath = _configuration["Email:unsubscribePath"]; + // // Send the newsletter email to each subscribed user + // foreach (var user in subscribedUsers) + // { + // string confirmationUrl = $"{Request.Scheme}://{Request.Host}/{confirmationPath}?email={user.Email}"; + + // string emailBody = $@" + // + // + // Email Confirmation + // + // + // + //
+ //

Hey {user.Name},

+ //

{viewModel.Body}


+ + //
Søren Eggert Lundsteen Olsen
+ //
SeoSoft ApS
+ //
+ //
Hovedgaden 3
Jordrup
Kolding 6064
Denmark
+ //
+ // Unsubscribe + //
+ //
+ // "; + // var email = new EmailToSend(user.Email, viewModel.Subject, emailBody); + // var isSent = await _emailServices.SendConfirmationEmailAsync(email); + + // // Handle failure to send email if needed + + // } + + // TempData["success"] = "Nesletter sent successfully."; + // return RedirectToAction(nameof(Index)); + + + + // } + // catch (Exception) + // { + // // Log or handle the exception as needed + // TempData["success"] = "something went wrong."; + // return RedirectToAction(nameof(Index)); + // } + // } + // return View(viewModel); + + + //} + + + + [HttpGet] public IActionResult Delete(int id) { diff --git a/Web/Areas/Admin/Controllers/OpenAIContentController.cs b/Web/Areas/Admin/Controllers/OpenAIContentController.cs index 5f532b9..7f31771 100644 --- a/Web/Areas/Admin/Controllers/OpenAIContentController.cs +++ b/Web/Areas/Admin/Controllers/OpenAIContentController.cs @@ -5,7 +5,7 @@ using System.Text; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class OpenAIContentController : Controller { private readonly IConfiguration _configuration; diff --git a/Web/Areas/Admin/Controllers/PageController.cs b/Web/Areas/Admin/Controllers/PageController.cs index 05dd3bf..c9edd26 100644 --- a/Web/Areas/Admin/Controllers/PageController.cs +++ b/Web/Areas/Admin/Controllers/PageController.cs @@ -9,7 +9,7 @@ using Web.ViewModel.PageVM; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class PageController : Controller { private readonly IPageRepository _pageRepository; diff --git a/Web/Areas/Admin/Controllers/QuestionnaireController.cs b/Web/Areas/Admin/Controllers/QuestionnaireController.cs index 9a94cf4..796b3ba 100644 --- a/Web/Areas/Admin/Controllers/QuestionnaireController.cs +++ b/Web/Areas/Admin/Controllers/QuestionnaireController.cs @@ -22,7 +22,7 @@ namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class QuestionnaireController : Controller { private readonly IQuestionnaireRepository _questionnaire; diff --git a/Web/Areas/Admin/Controllers/RegisterController.cs b/Web/Areas/Admin/Controllers/RegisterController.cs index 07dc4e2..79df8eb 100644 --- a/Web/Areas/Admin/Controllers/RegisterController.cs +++ b/Web/Areas/Admin/Controllers/RegisterController.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class RegisterController : Controller { public IActionResult Index() diff --git a/Web/Areas/Admin/Controllers/RolesController.cs b/Web/Areas/Admin/Controllers/RolesController.cs index 8f01b35..40498db 100644 --- a/Web/Areas/Admin/Controllers/RolesController.cs +++ b/Web/Areas/Admin/Controllers/RolesController.cs @@ -6,7 +6,7 @@ using Web.ViewModel.AccountVM; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class RolesController : Controller { private readonly RoleManager _roleManager; diff --git a/Web/Areas/Admin/Controllers/SocialMediaController.cs b/Web/Areas/Admin/Controllers/SocialMediaController.cs index b70436c..d1305f3 100644 --- a/Web/Areas/Admin/Controllers/SocialMediaController.cs +++ b/Web/Areas/Admin/Controllers/SocialMediaController.cs @@ -7,7 +7,7 @@ using Web.ViewModel.SocialMediaVM; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class SocialMediaController : Controller { private readonly ISocialMediaRepository _context; diff --git a/Web/Areas/Admin/Controllers/SurveyAnalysisController.cs b/Web/Areas/Admin/Controllers/SurveyAnalysisController.cs index d2a18f7..9fdab2b 100644 --- a/Web/Areas/Admin/Controllers/SurveyAnalysisController.cs +++ b/Web/Areas/Admin/Controllers/SurveyAnalysisController.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Authorization; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class SurveyAnalysisController : Controller { private readonly SurveyContext _context; @@ -39,6 +39,8 @@ namespace Web.Areas.Admin.Controllers } + + [HttpGet] public IActionResult Analysis(int id) { @@ -113,35 +115,6 @@ namespace Web.Areas.Admin.Controllers } - //[HttpGet] - //public IActionResult GenerateReport(int id) - //{ - // var viewModel = GetQuestionnaireData(id); - // if (viewModel == null) - // { - // return NotFound("No questionnaire found with the given ID."); - // } - - // var webReport = new WebReport(); - - // // Load your FastReport report design - // webReport.Report.Load(Path.Combine(env.WebRootPath, "Reports", "QuestionnaireReport.frx")); - - // // Register the data source - // webReport.Report.RegisterData(new[] { viewModel }, "Questionnaire"); - - // webReport.Report.Prepare(); - - // using (MemoryStream ms = new MemoryStream()) - // { - // webReport.Report.Export(new FastReport.Export.PdfSimple.PDFSimpleExport(), ms); - // return File(ms.ToArray(), "application/pdf", "QuestionnaireReport.pdf"); - // } - //} - - - - } } diff --git a/Web/Areas/Admin/Controllers/UserResponseController.cs b/Web/Areas/Admin/Controllers/UserResponseController.cs index 1ad73b7..7e2ba8e 100644 --- a/Web/Areas/Admin/Controllers/UserResponseController.cs +++ b/Web/Areas/Admin/Controllers/UserResponseController.cs @@ -7,7 +7,7 @@ using Model; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class UserResponseController : Controller { private readonly SurveyContext _context; diff --git a/Web/Areas/Admin/Controllers/UsersController.cs b/Web/Areas/Admin/Controllers/UsersController.cs index e5a8b3e..8054c27 100644 --- a/Web/Areas/Admin/Controllers/UsersController.cs +++ b/Web/Areas/Admin/Controllers/UsersController.cs @@ -8,7 +8,7 @@ using Web.ViewModel.AccountVM; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + public class UsersController : Controller { private readonly UserManager _userManager; diff --git a/Web/Areas/Admin/Views/SurveyAnalysis/Index.cshtml b/Web/Areas/Admin/Views/SurveyAnalysis/Index.cshtml index 9d8013e..984b39e 100644 --- a/Web/Areas/Admin/Views/SurveyAnalysis/Index.cshtml +++ b/Web/Areas/Admin/Views/SurveyAnalysis/Index.cshtml @@ -14,7 +14,7 @@

Survey analysis list

-
+ diff --git a/Web/Areas/Admin/Views/_ViewImports.cshtml b/Web/Areas/Admin/Views/_ViewImports.cshtml index 8a7ce66..406aaa3 100644 --- a/Web/Areas/Admin/Views/_ViewImports.cshtml +++ b/Web/Areas/Admin/Views/_ViewImports.cshtml @@ -9,4 +9,7 @@ @using Web.ViewModel.QuestionnaireVM @using Web.ViewModel.PageVM @using Web.ViewModel.AccountVM +@using Web.ViewModel.NewsLetterVM +@using Services.EmailSend + @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/Web/Areas/Admin/Views/newsletters/EmailStats.cshtml b/Web/Areas/Admin/Views/newsletters/EmailStats.cshtml new file mode 100644 index 0000000..9fa4184 --- /dev/null +++ b/Web/Areas/Admin/Views/newsletters/EmailStats.cshtml @@ -0,0 +1,284 @@ +@model IEnumerable + +@{ + ViewData["Title"] = "EmailStats"; + +} + + +
+
+

+ Back to list +

+

EmailStats

+ +
+ + + + + + + + + + + + + + + + + @foreach (var item in Model) + { + + + + + + + + + + + + + + } + +
RecipientSent DateSubjectSentDeliveredOpenedClickedBouncedSpamBlockedUnsubscribed
@item.RecipientEmail@item.SentDate@item.Subject + @if (item.IsSent) + { + Sent + } + else + { + Pending + } + + @if (item.IsDelivered) + { + Delivered + } + else + { + Pending + } + + @if (item.IsOpened) + { + Opened + } + else + { + Pending + } + + @if (item.IsClicked) + { + Clicked + } + else + { + Pending + } + + @if (item.IsBounced) + { + Bounced + } + else + { + Normal + } + + @if (item.IsSpam) + { + Spam + } + else + { + Normal + } + + @if (item.IsBlocked) + { + Blocked + } + else + { + Normal + } + + @if (item.IsUnsubscribed) + { + Unsubscribed + } + else + { + Normal + } +
+ + + + + + + + + +

Email Statistics

+
+ +@section Scripts { + + +} diff --git a/Web/Areas/Admin/Views/newsletters/Index.cshtml b/Web/Areas/Admin/Views/newsletters/Index.cshtml index 0d16f5c..4fe3920 100644 --- a/Web/Areas/Admin/Views/newsletters/Index.cshtml +++ b/Web/Areas/Admin/Views/newsletters/Index.cshtml @@ -3,7 +3,120 @@ @{ ViewData["Title"] = "Newsletter list"; } +
@@ -18,7 +131,7 @@

- compose newsletter + compose newsletter

@@ -62,5 +175,82 @@ + + + + +
+ +

+ Real-Time Email Event Tracking +

+

+ View email tracking with chart +

+
+ + + + + + + + + + + + + + + + + + +
RecipientSent DateSubjectSentDeliveredOpenedClickedBouncedSpamBlockedUnsubscribed
+ + + + @section Scripts{ + + + } \ No newline at end of file diff --git a/Web/Areas/Admin/Views/newsletters/LastReceivedEventsData.cshtml b/Web/Areas/Admin/Views/newsletters/LastReceivedEventsData.cshtml new file mode 100644 index 0000000..8fe4a2f --- /dev/null +++ b/Web/Areas/Admin/Views/newsletters/LastReceivedEventsData.cshtml @@ -0,0 +1,48 @@ +@model IEnumerable + +@{ + ViewData["Title"] = "LastReceivedEventsData"; + Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml"; +} + +

LastReceivedEventsData

+ +

+ Create New +

+ + + + + + + + + + +@foreach (var item in Model) { + + + + + + +} + +
+ @Html.DisplayNameFor(model => model.Email) + + @Html.DisplayNameFor(model => model.EventType) + + @Html.DisplayNameFor(model => model.EventTime) +
+ @Html.DisplayFor(modelItem => item.Email) + + @Html.DisplayFor(modelItem => item.EventType) + + @Html.DisplayFor(modelItem => item.EventTime) + + @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) | + @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) | + @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ }) +
diff --git a/Web/Areas/Admin/Views/newsletters/ShowStat.cshtml b/Web/Areas/Admin/Views/newsletters/ShowStat.cshtml new file mode 100644 index 0000000..b42c220 --- /dev/null +++ b/Web/Areas/Admin/Views/newsletters/ShowStat.cshtml @@ -0,0 +1,28 @@ +@model string +@{ + ViewData["Title"] = "Email Statistics"; + Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml"; +} + + + + +

@ViewData["Title"]

+ +
+ + + + Email Statistics + + + + +

Email Statistics

+
@Model
+ + +
+
+ + diff --git a/Web/Extesions/ServicesExtesions.cs b/Web/Extesions/ServicesExtesions.cs index 5f31a04..60e849e 100644 --- a/Web/Extesions/ServicesExtesions.cs +++ b/Web/Extesions/ServicesExtesions.cs @@ -74,6 +74,10 @@ namespace Web.Extesions { services.AddTransient(); } + public static void MailStatConfiguration(this IServiceCollection services) + { + services.AddTransient(); + } //public static void MailConfiguration(this IServiceCollection services, IConfiguration configuration) //{ diff --git a/Web/Migrations/20240506120728_SentEmailNewsletterModelAdded.Designer.cs b/Web/Migrations/20240506120728_SentEmailNewsletterModelAdded.Designer.cs new file mode 100644 index 0000000..a45f8b7 --- /dev/null +++ b/Web/Migrations/20240506120728_SentEmailNewsletterModelAdded.Designer.cs @@ -0,0 +1,814 @@ +// +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("20240506120728_SentEmailNewsletterModelAdded")] + partial class SentEmailNewsletterModelAdded + { + /// + 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("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("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", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + 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.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("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("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.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.SentNewsletterEamil", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Body") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBounced") + .HasColumnType("bit"); + + b.Property("IsClicked") + .HasColumnType("bit"); + + b.Property("IsDelivered") + .HasColumnType("bit"); + + b.Property("IsOpened") + .HasColumnType("bit"); + + b.Property("IsSpam") + .HasColumnType("bit"); + + b.Property("RecipientEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("SentDate") + .HasColumnType("datetime2"); + + b.Property("Subject") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SentNewsletterEamils"); + }); + + 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("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Model.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Model.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", 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", 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 + } + } +} diff --git a/Web/Migrations/20240506120728_SentEmailNewsletterModelAdded.cs b/Web/Migrations/20240506120728_SentEmailNewsletterModelAdded.cs new file mode 100644 index 0000000..beeb333 --- /dev/null +++ b/Web/Migrations/20240506120728_SentEmailNewsletterModelAdded.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Web.Migrations +{ + /// + public partial class SentEmailNewsletterModelAdded : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SentNewsletterEamils", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RecipientEmail = table.Column(type: "nvarchar(max)", nullable: true), + Subject = table.Column(type: "nvarchar(max)", nullable: true), + Body = table.Column(type: "nvarchar(max)", nullable: true), + SentDate = table.Column(type: "datetime2", nullable: false), + IsDelivered = table.Column(type: "bit", nullable: false), + IsOpened = table.Column(type: "bit", nullable: false), + IsClicked = table.Column(type: "bit", nullable: false), + IsBounced = table.Column(type: "bit", nullable: false), + IsSpam = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SentNewsletterEamils", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SentNewsletterEamils"); + } + } +} diff --git a/Web/Migrations/20240508104039_EmailTrackingModelChnaged.Designer.cs b/Web/Migrations/20240508104039_EmailTrackingModelChnaged.Designer.cs new file mode 100644 index 0000000..93c1361 --- /dev/null +++ b/Web/Migrations/20240508104039_EmailTrackingModelChnaged.Designer.cs @@ -0,0 +1,820 @@ +// +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("20240508104039_EmailTrackingModelChnaged")] + partial class EmailTrackingModelChnaged + { + /// + 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("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("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", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + 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.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("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("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.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.SentNewsletterEamil", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Body") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBounced") + .HasColumnType("bit"); + + b.Property("IsClicked") + .HasColumnType("bit"); + + b.Property("IsDelivered") + .HasColumnType("bit"); + + b.Property("IsOpened") + .HasColumnType("bit"); + + b.Property("IsSent") + .HasColumnType("bit"); + + b.Property("IsSpam") + .HasColumnType("bit"); + + b.Property("IsUnsubscribed") + .HasColumnType("bit"); + + b.Property("RecipientEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("SentDate") + .HasColumnType("datetime2"); + + b.Property("Subject") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SentNewsletterEamils"); + }); + + 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("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Model.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Model.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", 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", 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 + } + } +} diff --git a/Web/Migrations/20240508104039_EmailTrackingModelChnaged.cs b/Web/Migrations/20240508104039_EmailTrackingModelChnaged.cs new file mode 100644 index 0000000..9613cf2 --- /dev/null +++ b/Web/Migrations/20240508104039_EmailTrackingModelChnaged.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Web.Migrations +{ + /// + public partial class EmailTrackingModelChnaged : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsSent", + table: "SentNewsletterEamils", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "IsUnsubscribed", + table: "SentNewsletterEamils", + type: "bit", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsSent", + table: "SentNewsletterEamils"); + + migrationBuilder.DropColumn( + name: "IsUnsubscribed", + table: "SentNewsletterEamils"); + } + } +} diff --git a/Web/Migrations/20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty.Designer.cs b/Web/Migrations/20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty.Designer.cs new file mode 100644 index 0000000..2f311b9 --- /dev/null +++ b/Web/Migrations/20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty.Designer.cs @@ -0,0 +1,823 @@ +// +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("20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty")] + partial class EmailTrackingModelChangedWithIsBlockedPrperty + { + /// + 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("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("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", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + 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.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("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("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.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.SentNewsletterEamil", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Body") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBlocked") + .HasColumnType("bit"); + + b.Property("IsBounced") + .HasColumnType("bit"); + + b.Property("IsClicked") + .HasColumnType("bit"); + + b.Property("IsDelivered") + .HasColumnType("bit"); + + b.Property("IsOpened") + .HasColumnType("bit"); + + b.Property("IsSent") + .HasColumnType("bit"); + + b.Property("IsSpam") + .HasColumnType("bit"); + + b.Property("IsUnsubscribed") + .HasColumnType("bit"); + + b.Property("RecipientEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("SentDate") + .HasColumnType("datetime2"); + + b.Property("Subject") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SentNewsletterEamils"); + }); + + 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("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Model.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Model.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", 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", 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 + } + } +} diff --git a/Web/Migrations/20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty.cs b/Web/Migrations/20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty.cs new file mode 100644 index 0000000..770f957 --- /dev/null +++ b/Web/Migrations/20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Web.Migrations +{ + /// + public partial class EmailTrackingModelChangedWithIsBlockedPrperty : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsBlocked", + table: "SentNewsletterEamils", + type: "bit", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsBlocked", + table: "SentNewsletterEamils"); + } + } +} diff --git a/Web/Migrations/SurveyContextModelSnapshot.cs b/Web/Migrations/SurveyContextModelSnapshot.cs index 332350f..0bede71 100644 --- a/Web/Migrations/SurveyContextModelSnapshot.cs +++ b/Web/Migrations/SurveyContextModelSnapshot.cs @@ -540,6 +540,55 @@ namespace Web.Migrations b.ToTable("ResponseDetails"); }); + modelBuilder.Entity("Model.SentNewsletterEamil", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Body") + .HasColumnType("nvarchar(max)"); + + b.Property("IsBlocked") + .HasColumnType("bit"); + + b.Property("IsBounced") + .HasColumnType("bit"); + + b.Property("IsClicked") + .HasColumnType("bit"); + + b.Property("IsDelivered") + .HasColumnType("bit"); + + b.Property("IsOpened") + .HasColumnType("bit"); + + b.Property("IsSent") + .HasColumnType("bit"); + + b.Property("IsSpam") + .HasColumnType("bit"); + + b.Property("IsUnsubscribed") + .HasColumnType("bit"); + + b.Property("RecipientEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("SentDate") + .HasColumnType("datetime2"); + + b.Property("Subject") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("SentNewsletterEamils"); + }); + modelBuilder.Entity("Model.SocialMedia", b => { b.Property("Id") diff --git a/Web/Program.cs b/Web/Program.cs index 4c74543..905b727 100644 --- a/Web/Program.cs +++ b/Web/Program.cs @@ -11,6 +11,7 @@ var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); +builder.Services.AddHttpClient(); var config = builder.Configuration; @@ -36,6 +37,7 @@ builder.Services.AddScoped(); builder.Services.AddTransient(); builder.Services.ConfigureNewsLetter(); builder.Services.MailConfiguration(); +builder.Services.MailStatConfiguration(); builder.Services.ConfigureOpenAI(config); @@ -68,7 +70,11 @@ app.MapControllers(); app.MapControllerRoute( name: "page", pattern: "{slug}", defaults: new { Controller = "Home", Action = "Index" }); - +app.Use(async (context, next) => +{ + context.Request.EnableBuffering(); // Enable buffering for reading request body multiple times + await next.Invoke(); +}); app.MapAreaControllerRoute( name: "MyAdminArea", diff --git a/Web/ViewModel/NewsLetterVM/EmailEventViewModel.cs b/Web/ViewModel/NewsLetterVM/EmailEventViewModel.cs new file mode 100644 index 0000000..8396b84 --- /dev/null +++ b/Web/ViewModel/NewsLetterVM/EmailEventViewModel.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Web.ViewModel.NewsLetterVM +{ + public class EmailEventViewModel + { + [Required] + public string Email { get; set; } + + [Required] + public string EventType { get; set; } + + [Required] + public DateTime EventTime { get; set; } + } +}