Real time email event tracking completed

This commit is contained in:
Qais Yousuf 2024-05-09 14:08:10 +02:00
parent e80a2675e6
commit 4fb4e979e6
34 changed files with 3664 additions and 126 deletions

View file

@ -35,6 +35,8 @@ namespace Data
public DbSet<ResponseDetail> ResponseDetails { get; set; }
public DbSet<ResponseAnswer> ResponseAnswers { get; set; }
public DbSet<SentNewsletterEamil> SentNewsletterEamils { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{

View file

@ -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; }
}
}

View file

@ -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; }
}
}

View file

@ -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<EmailStatistic> 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<int>(),
Delivered = data[0]["Delivered"].Value<int>(),
Opened = data[0]["Opened"].Value<int>(),
Clicked = data[0]["Clicked"].Value<int>(),
Bounced = data[0]["Bounced"].Value<int>(),
Blocked = data[0]["Blocked"].Value<int>(),
MarkedAsSpam = data[0]["Spam"].Value<int>()
};
return stats;
}
}
}

View file

@ -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<EmailStatistic> FetchEmailStatsAsync();
}
}

View file

@ -6,7 +6,7 @@ using Web.ViewModel.AddressVM;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles ="Admin")]
public class AddressController : Controller
{
private readonly IAddressRepository _addresContext;

View file

@ -8,7 +8,7 @@ using Web.ViewModel.BannerVM;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class BannerController : Controller
{
private readonly IBannerRepository _banner;

View file

@ -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;

View file

@ -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<IActionResult> 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 = $"<h4>Hey {user.Name}</h4><br>{viewModel.Body}<br><h5>Søren Eggert Lundsteen Olsen<br>Seosoft ApS</h5><hr><h6>Hovedgaden 3<br>Jordrup<br>Kolding 6064<br>Denmark</h6><br/><br/><div style='text-align: center;'><a href='{confirmationUrl}' style='display: inline-block; background-color: #6c757d; color: #fff; padding: 4px 8px; text-decoration: none; border-radius: 4px;'>Unsubscribe</a></div>";
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 = $@"<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<title>Email Confirmation</title>
<style>
body {{
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f9f9f9;
}}
.container {{
max-width: 800px;
margin: 0 auto;
padding: 20px;
border: 0.5px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}}
h4, h5, h6 {{
margin: 0;
}}
hr {{
border: none;
border-top: 1px solid #ccc;
margin: 10px 0;
}}
a.button {{
display: inline-block;
padding: 5px 10px;
background-color: #6c757d;
color: #fff;
text-decoration: none;
border-radius: 4px;
}}
a.button:hover {{
background-color: #5a6268;
}}
a {{
color: #007bff;
text-decoration: none;
}}
a:hover {{
text-decoration: underline;
}}
</style>
</head>
<body>
<div class=""container"">
<h4>Hey {user.Name},</h4>
<p>{viewModel.Body}</p><br>
<h5>Søren Eggert Lundsteen Olsen</h5>
<h5><a href=""https://www.seosoft.dk/"" target=""_blank"">SeoSoft ApS</a></h5>
<hr>
<h6>Hovedgaden 3<br>Jordrup<br>Kolding 6064<br>Denmark</h6>
<div style=""text-align: center;"">
<a href=""{confirmationUrl}"" class=""button"">Unsubscribe</a>
</div>
</div>
</body>";
var email = new EmailToSend(user.Email, viewModel.Subject, emailBody);
var isSent = await _emailServices.SendConfirmationEmailAsync(email);
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<title>Email Confirmation</title>
<style>
body {{
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f9f9f9;
}}
.container {{
max-width: 800px;
margin: 0 auto;
padding: 20px;
border: 0.5px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}}
h4, h5, h6 {{
margin: 0;
}}
hr {{
border: none;
border-top: 1px solid #ccc;
margin: 10px 0;
}}
a.button {{
display: inline-block;
padding: 5px 10px;
background-color: #6c757d;
color: #fff;
text-decoration: none;
border-radius: 4px;
}}
a.button:hover {{
background-color: #5a6268;
}}
a {{
color: #007bff;
text-decoration: none;
}}
a:hover {{
text-decoration: underline;
}}
</style>
</head>
<body>
<div class=""container"">
<h4>Hey {user.Name},</h4>
<p>{viewModel.Body}</p><br>
<h5>Søren Eggert Lundsteen Olsen</h5>
<h5><a href=""https://www.seosoft.dk/"" target=""_blank"">SeoSoft ApS</a></h5>
<hr>
<h6>Hovedgaden 3<br>Jordrup<br>Kolding 6064<br>Denmark</h6>
<div style=""text-align: center;"">
<a href=""{confirmationUrl}"" class=""button"">Unsubscribe</a>
</div>
</div>
</body>";
// 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<IActionResult> 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<string>("email");
string eventType = e.Value<string>("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<IActionResult> EmailStats()
{
var emails = await _context.SentNewsletterEamils.ToListAsync();
return View(emails);
}
public async Task<IActionResult> GetEmailStatsData()
{
var model = await _context.SentNewsletterEamils.ToListAsync();
return Json(model); // Returns the list of emails as JSON.
}
public async Task<JsonResult> 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<IActionResult> 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 = $@"<head>
// <meta charset=""UTF-8"">
// <meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
// <title>Email Confirmation</title>
// <style>
// body {{
// font-family: Arial, sans-serif;
// line-height: 1.6;
// margin: 0;
// padding: 0;
// background-color: #f9f9f9;
// }}
// .container {{
// max-width: 800px;
// margin: 0 auto;
// padding: 20px;
// border: 0.5px solid #ccc;
// border-radius: 5px;
// background-color: #f9f9f9;
// }}
// h4, h5, h6 {{
// margin: 0;
// }}
// hr {{
// border: none;
// border-top: 1px solid #ccc;
// margin: 10px 0;
// }}
// a.button {{
// display: inline-block;
// padding: 5px 10px;
// background-color: #6c757d;
// color: #fff;
// text-decoration: none;
// border-radius: 4px;
// }}
// a.button:hover {{
// background-color: #5a6268;
// }}
// a {{
// color: #007bff;
// text-decoration: none;
// }}
// a:hover {{
// text-decoration: underline;
// }}
// </style>
// </head>
// <body>
// <div class=""container"">
// <h4>Hey {user.Name},</h4>
// <p>{viewModel.Body}</p><br>
// <h5>Søren Eggert Lundsteen Olsen</h5>
// <h5><a href=""https://www.seosoft.dk/"" target=""_blank"">SeoSoft ApS</a></h5>
// <hr>
// <h6>Hovedgaden 3<br>Jordrup<br>Kolding 6064<br>Denmark</h6>
// <div style=""text-align: center;"">
// <a href=""{confirmationUrl}"" class=""button"">Unsubscribe</a>
// </div>
// </div>
// </body>";
// 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)
{

View file

@ -5,7 +5,7 @@ using System.Text;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class OpenAIContentController : Controller
{
private readonly IConfiguration _configuration;

View file

@ -9,7 +9,7 @@ using Web.ViewModel.PageVM;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class PageController : Controller
{
private readonly IPageRepository _pageRepository;

View file

@ -22,7 +22,7 @@ namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class QuestionnaireController : Controller
{
private readonly IQuestionnaireRepository _questionnaire;

View file

@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class RegisterController : Controller
{
public IActionResult Index()

View file

@ -6,7 +6,7 @@ using Web.ViewModel.AccountVM;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class RolesController : Controller
{
private readonly RoleManager<IdentityRole> _roleManager;

View file

@ -7,7 +7,7 @@ using Web.ViewModel.SocialMediaVM;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class SocialMediaController : Controller
{
private readonly ISocialMediaRepository _context;

View file

@ -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");
// }
//}
}
}

View file

@ -7,7 +7,7 @@ using Model;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class UserResponseController : Controller
{
private readonly SurveyContext _context;

View file

@ -8,7 +8,7 @@ using Web.ViewModel.AccountVM;
namespace Web.Areas.Admin.Controllers
{
[Authorize(Roles = "Admin")]
public class UsersController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;

View file

@ -14,7 +14,7 @@
<h4 class="card-title">Survey analysis list</h4>
<form asp-action="DeleteMultiple" method="post">
<form asp-action="DeleteUnusedQuestionnaires" method="post">
<table class="table table-responsive w-100 d-block d-md-table table-hover ">
<thead class="w-100">

View file

@ -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

View file

@ -0,0 +1,284 @@
@model IEnumerable<Model.SentNewsletterEamil>
@{
ViewData["Title"] = "EmailStats";
}
<style>
.badge-Sent{
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #6831c5;
;
}
.badge-Deliverd{
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #44aebd;
}
.badge-Opend {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #22a877;
}
.badge-Clicked {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #3b76cb;
}
.badge-Bounced {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #9585f4;
}
.badge-Spam {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #f06b66;
}
.badge-Blocked {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #98712d;
}
.badge-Unsubscribed {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #cb4a49;
}
</style>
<div class="container-fluid mt-4 mb-5">
<div class="col-10 offset-1">
<p>
<a asp-action="Index" class="btn btn-primary btn-sm">Back to list</a>
</p>
<h1>EmailStats</h1>
<table class="table table-responsive w-100 d-block d-md-table table-bordered table-hover">
<thead>
<tr>
<th>Recipient</th>
<th>Sent Date</th>
<th>Subject</th>
<th>Sent</th>
<th>Delivered</th>
<th>Opened</th>
<th>Clicked</th>
<th>Bounced</th>
<th>Spam</th>
<th>Blocked</th>
<th>Unsubscribed</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@item.RecipientEmail</td>
<td>@item.SentDate</td>
<td>@item.Subject</td>
<td>
@if (item.IsSent)
{
<span class="badge-Sent">Sent</span>
}
else
{
<span class="badge badge-secondary">Pending</span>
}
</td>
<td>
@if (item.IsDelivered)
{
<span class="badge-Deliverd">Delivered</span>
}
else
{
<span class="badge badge-secondary">Pending</span>
}
</td>
<td>
@if (item.IsOpened)
{
<span class="badge-Opend">Opened</span>
}
else
{
<span class="badge badge-secondary">Pending</span>
}
</td>
<td>
@if (item.IsClicked)
{
<span class="badge-Clicked">Clicked</span>
}
else
{
<span class="badge badge-secondary">Pending</span>
}
</td>
<td>
@if (item.IsBounced)
{
<span class="badge-Bounced">Bounced</span>
}
else
{
<span class="badge badge-secondary">Normal</span>
}
</td>
<td>
@if (item.IsSpam)
{
<span class="badge-Spam">Spam</span>
}
else
{
<span class="badge badge-secondary">Normal</span>
}
</td>
<td>
@if (item.IsBlocked)
{
<span class="badge-Blocked">Blocked</span>
}
else
{
<span class="badge badge-secondary">Normal</span>
}
</td>
<td>
@if (item.IsUnsubscribed)
{
<span class="badge-Unsubscribed">Unsubscribed</span>
}
else
{
<span class="badge badge-secondary">Normal</span>
}
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<h2>Email Statistics</h2>
<div id="piechart" style="width: 900px; height: 500px;"></div>
@section Scripts {
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', { 'packages': ['corechart'] });
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
fetch('@Url.Action("GetChartData", "Newsletters")')
.then(response => response.json())
.then(data => {
var dataTable = new google.visualization.DataTable();
dataTable.addColumn('string', 'Type');
dataTable.addColumn('number', 'Count');
dataTable.addRows([
['Sent', data.Sent],
['Delivered', data.Delivered],
['Opened', data.Opened],
['Clicked', data.Clicked],
['Bounced', data.Bounced],
['Spam', data.Spam],
['Blocked', data.Blocked],
['Unsubscribed', data.Unsubscribed]
]);
var options = {
title: 'Email Interaction Overview',
width: 900,
height: 500
};
var chart = new google.visualization.PieChart(document.getElementById('piechart'));
chart.draw(dataTable, options);
})
.catch(error => console.error('Failed to fetch email stats:', error));
}
</script>
}

View file

@ -3,7 +3,120 @@
@{
ViewData["Title"] = "Newsletter list";
}
<style>
.badge-Sent {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #6831c5;
;
}
.badge-Deliverd {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #44aebd;
}
.badge-Opend {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #22a877;
}
.badge-Clicked {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #3b76cb;
}
.badge-Bounced {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #9585f4;
}
.badge-Spam {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #f06b66;
}
.badge-Blocked {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #98712d;
}
.badge-Unsubscribed {
display: inline-block;
padding: 0.25em 0.4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 0.25rem;
color: #fff;
background-color: #cb4a49;
}
</style>
<div class="container mt-5">
<partial name="_Notification" />
@ -18,7 +131,7 @@
</div>
<p>
<a asp-action="Create" class="btn btn-primary @(@ViewBag.TotalSubscribedUsers <= 0 ? "disabled" : "")">compose newsletter</a>
<a asp-action="Create" class="btn btn-primary btn-sm @(@ViewBag.TotalSubscribedUsers <= 0 ? "disabled" : "")">compose newsletter</a>
</p>
<table class="table table-responsive w-100 d-block d-md-table ">
@ -62,5 +175,82 @@
</div>
</div>
</div>
<div class="container">
<h4>
<i class="bi bi-broadcast p-1 border-1 shadow-sm"></i> Real-Time Email Event Tracking
</h4>
<p>
<a asp-action="EmailStats" class="btn btn-primary btn-sm">View email tracking with chart</a>
</p>
<table class="table table-responsive w-100 d-block d-md-table table-bordered table-hover">
<thead>
<tr>
<th>Recipient</th>
<th>Sent Date</th>
<th>Subject</th>
<th>Sent</th>
<th>Delivered</th>
<th>Opened</th>
<th>Clicked</th>
<th>Bounced</th>
<th>Spam</th>
<th>Blocked</th>
<th>Unsubscribed</th>
</tr>
</thead>
<tbody id="emailStatsTableBody">
<!-- Rows will be dynamically inserted here -->
</tbody>
</table>
</div>
@section Scripts{
<script>
document.addEventListener('DOMContentLoaded', function () {
function fetchData() {
fetch('@Url.Action("GetEmailStatsData", "Newsletters")')
.then(response => response.json())
.then(data => {
updateTable(data);
})
.catch(error => console.error('Error fetching data:', error));
}
function updateTable(data) {
const tableBody = document.getElementById('emailStatsTableBody');
tableBody.innerHTML = ''; // Clear existing table rows
data.forEach(item => {
const row = `
<tr>
<td>${item.recipientEmail}</td>
<td>${item.sentDate}</td>
<td>${item.subject}</td>
<td>${item.isSent ? '<span class="badge-Sent">Sent</span>' : '<span class="badge badge-secondary">Pending</span>'}</td>
<td>${item.isDelivered ? '<span class="badge-Deliverd">Delivered</span>' : '<span class="badge badge-secondary">Pending</span>'}</td>
<td>${item.isOpened ? '<span class="badge-Opend">Opened</span>' : '<span class="badge badge-secondary">Pending</span>'}</td>
<td>${item.isClicked ? '<span class="badge-Clicked">Clicked</span>' : '<span class="badge badge-secondary">Pending</span>'}</td>
<td>${item.isBounced ? '<span class="badge-Opend">Bounced</span>' : '<span class="badge badge-secondary">Normal</span>'}</td>
<td>${item.isSpam ? '<span class="badge-Spam">Spamed</span>' : '<span class="badge badge-secondary">Normal</span>'}</td>
<td>${item.isBlocked ? '<span class="badge-Blocked">Blocked</span>' : '<span class="badge badge-secondary">Normal</span>'}</td>
<td>${item.isUnsubscribed ? '<span class="badge-Unsubscribed">Unsubscribed</span>' : '<span class="badge badge-secondary">Normal</span>'}</td>
<!-- Continue for each status -->
</tr>
`;
tableBody.innerHTML += row; // Append new row
});
}
setInterval(fetchData, 5000); // Fetch data every 5 seconds
});
</script>
}

View file

@ -0,0 +1,48 @@
@model IEnumerable<Web.ViewModel.NewsLetterVM.EmailEventViewModel>
@{
ViewData["Title"] = "LastReceivedEventsData";
Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml";
}
<h1>LastReceivedEventsData</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Email)
</th>
<th>
@Html.DisplayNameFor(model => model.EventType)
</th>
<th>
@Html.DisplayNameFor(model => model.EventTime)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.EventType)
</td>
<td>
@Html.DisplayFor(modelItem => item.EventTime)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</tbody>
</table>

View file

@ -0,0 +1,28 @@
@model string
@{
ViewData["Title"] = "Email Statistics";
Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml";
}
<h2>@ViewData["Title"]</h2>
<div class="container">
<table class="table table-bordered">
<thead>
<tr>
<title>Email Statistics</title>
</tr>
</thead>
<tbody>
<tr>
<h1>Email Statistics</h1>
<pre>@Model</pre>
</tr>
</tbody>
</table>
</div>

View file

@ -74,6 +74,10 @@ namespace Web.Extesions
{
services.AddTransient<IEmailServices, EmailServices>();
}
public static void MailStatConfiguration(this IServiceCollection services)
{
services.AddTransient<IEmailStatsService, EmailStatsService>();
}
//public static void MailConfiguration(this IServiceCollection services, IConfiguration configuration)
//{

View file

@ -0,0 +1,814 @@
// <auto-generated />
using System;
using Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Web.Migrations
{
[DbContext(typeof(SurveyContext))]
[Migration("20240506120728_SentEmailNewsletterModelAdded")]
partial class SentEmailNewsletterModelAdded
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Model.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("CVR")
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Mobile")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PostalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("State")
.HasColumnType("nvarchar(max)");
b.Property<string>("Street")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Addresss");
});
modelBuilder.Entity("Model.Answer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionId")
.HasColumnType("int");
b.Property<string>("Text")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.ToTable("Answers");
});
modelBuilder.Entity("Model.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Model.Banner", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ImageUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LinkUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Banners");
});
modelBuilder.Entity("Model.Footer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("CreatedBy")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ImageUlr")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("LastUpdated")
.HasColumnType("datetime2");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Owner")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Sitecopyright")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("UpdatedBy")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Footers");
});
modelBuilder.Entity("Model.FooterSocialMedia", b =>
{
b.Property<int>("FooterId")
.HasColumnType("int");
b.Property<int>("SocialId")
.HasColumnType("int");
b.HasKey("FooterId", "SocialId");
b.HasIndex("SocialId");
b.ToTable("FooterSocialMedias");
});
modelBuilder.Entity("Model.Page", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BannerId")
.HasColumnType("int");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("FooterId")
.HasColumnType("int");
b.Property<string>("Slug")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("BannerId");
b.HasIndex("FooterId");
b.ToTable("Pages");
});
modelBuilder.Entity("Model.Question", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionnaireId")
.HasColumnType("int");
b.Property<string>("Text")
.HasColumnType("nvarchar(max)");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("QuestionnaireId");
b.ToTable("Questions");
});
modelBuilder.Entity("Model.Questionnaire", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Questionnaires");
});
modelBuilder.Entity("Model.Response", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionnaireId")
.HasColumnType("int");
b.Property<DateTime>("SubmissionDate")
.HasColumnType("datetime2");
b.Property<string>("UserEmail")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserName")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionnaireId");
b.ToTable("Responses");
});
modelBuilder.Entity("Model.ResponseAnswer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("AnswerId")
.HasColumnType("int");
b.Property<int>("ResponseDetailId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ResponseDetailId");
b.ToTable("ResponseAnswers");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionId")
.HasColumnType("int");
b.Property<int>("QuestionType")
.HasColumnType("int");
b.Property<int>("ResponseId")
.HasColumnType("int");
b.Property<string>("TextResponse")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.HasIndex("ResponseId");
b.ToTable("ResponseDetails");
});
modelBuilder.Entity("Model.SentNewsletterEamil", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Body")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsBounced")
.HasColumnType("bit");
b.Property<bool>("IsClicked")
.HasColumnType("bit");
b.Property<bool>("IsDelivered")
.HasColumnType("bit");
b.Property<bool>("IsOpened")
.HasColumnType("bit");
b.Property<bool>("IsSpam")
.HasColumnType("bit");
b.Property<string>("RecipientEmail")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("SentDate")
.HasColumnType("datetime2");
b.Property<string>("Subject")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("SentNewsletterEamils");
});
modelBuilder.Entity("Model.SocialMedia", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("SocialMedia");
});
modelBuilder.Entity("Model.Subscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsSubscribed")
.HasColumnType("bit");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Subscriptions");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Model.Answer", b =>
{
b.HasOne("Model.Question", "Question")
.WithMany("Answers")
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Question");
});
modelBuilder.Entity("Model.FooterSocialMedia", b =>
{
b.HasOne("Model.Footer", "Footer")
.WithMany("FooterSocialMedias")
.HasForeignKey("FooterId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.SocialMedia", "SocialMedia")
.WithMany("FooterSocialMedias")
.HasForeignKey("SocialId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Footer");
b.Navigation("SocialMedia");
});
modelBuilder.Entity("Model.Page", b =>
{
b.HasOne("Model.Banner", "banner")
.WithMany()
.HasForeignKey("BannerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.Footer", "footer")
.WithMany()
.HasForeignKey("FooterId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("banner");
b.Navigation("footer");
});
modelBuilder.Entity("Model.Question", b =>
{
b.HasOne("Model.Questionnaire", "Questionnaire")
.WithMany("Questions")
.HasForeignKey("QuestionnaireId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Questionnaire");
});
modelBuilder.Entity("Model.Response", b =>
{
b.HasOne("Model.Questionnaire", "Questionnaire")
.WithMany()
.HasForeignKey("QuestionnaireId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Questionnaire");
});
modelBuilder.Entity("Model.ResponseAnswer", b =>
{
b.HasOne("Model.ResponseDetail", "ResponseDetail")
.WithMany("ResponseAnswers")
.HasForeignKey("ResponseDetailId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ResponseDetail");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.HasOne("Model.Question", "Question")
.WithMany()
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Model.Response", "Response")
.WithMany("ResponseDetails")
.HasForeignKey("ResponseId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Question");
b.Navigation("Response");
});
modelBuilder.Entity("Model.Footer", b =>
{
b.Navigation("FooterSocialMedias");
});
modelBuilder.Entity("Model.Question", b =>
{
b.Navigation("Answers");
});
modelBuilder.Entity("Model.Questionnaire", b =>
{
b.Navigation("Questions");
});
modelBuilder.Entity("Model.Response", b =>
{
b.Navigation("ResponseDetails");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.Navigation("ResponseAnswers");
});
modelBuilder.Entity("Model.SocialMedia", b =>
{
b.Navigation("FooterSocialMedias");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,43 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Web.Migrations
{
/// <inheritdoc />
public partial class SentEmailNewsletterModelAdded : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "SentNewsletterEamils",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
RecipientEmail = table.Column<string>(type: "nvarchar(max)", nullable: true),
Subject = table.Column<string>(type: "nvarchar(max)", nullable: true),
Body = table.Column<string>(type: "nvarchar(max)", nullable: true),
SentDate = table.Column<DateTime>(type: "datetime2", nullable: false),
IsDelivered = table.Column<bool>(type: "bit", nullable: false),
IsOpened = table.Column<bool>(type: "bit", nullable: false),
IsClicked = table.Column<bool>(type: "bit", nullable: false),
IsBounced = table.Column<bool>(type: "bit", nullable: false),
IsSpam = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SentNewsletterEamils", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "SentNewsletterEamils");
}
}
}

View file

@ -0,0 +1,820 @@
// <auto-generated />
using System;
using Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Web.Migrations
{
[DbContext(typeof(SurveyContext))]
[Migration("20240508104039_EmailTrackingModelChnaged")]
partial class EmailTrackingModelChnaged
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Model.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("CVR")
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Mobile")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PostalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("State")
.HasColumnType("nvarchar(max)");
b.Property<string>("Street")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Addresss");
});
modelBuilder.Entity("Model.Answer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionId")
.HasColumnType("int");
b.Property<string>("Text")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.ToTable("Answers");
});
modelBuilder.Entity("Model.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Model.Banner", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ImageUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LinkUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Banners");
});
modelBuilder.Entity("Model.Footer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("CreatedBy")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ImageUlr")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("LastUpdated")
.HasColumnType("datetime2");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Owner")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Sitecopyright")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("UpdatedBy")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Footers");
});
modelBuilder.Entity("Model.FooterSocialMedia", b =>
{
b.Property<int>("FooterId")
.HasColumnType("int");
b.Property<int>("SocialId")
.HasColumnType("int");
b.HasKey("FooterId", "SocialId");
b.HasIndex("SocialId");
b.ToTable("FooterSocialMedias");
});
modelBuilder.Entity("Model.Page", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BannerId")
.HasColumnType("int");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("FooterId")
.HasColumnType("int");
b.Property<string>("Slug")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("BannerId");
b.HasIndex("FooterId");
b.ToTable("Pages");
});
modelBuilder.Entity("Model.Question", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionnaireId")
.HasColumnType("int");
b.Property<string>("Text")
.HasColumnType("nvarchar(max)");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("QuestionnaireId");
b.ToTable("Questions");
});
modelBuilder.Entity("Model.Questionnaire", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Questionnaires");
});
modelBuilder.Entity("Model.Response", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionnaireId")
.HasColumnType("int");
b.Property<DateTime>("SubmissionDate")
.HasColumnType("datetime2");
b.Property<string>("UserEmail")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserName")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionnaireId");
b.ToTable("Responses");
});
modelBuilder.Entity("Model.ResponseAnswer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("AnswerId")
.HasColumnType("int");
b.Property<int>("ResponseDetailId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ResponseDetailId");
b.ToTable("ResponseAnswers");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionId")
.HasColumnType("int");
b.Property<int>("QuestionType")
.HasColumnType("int");
b.Property<int>("ResponseId")
.HasColumnType("int");
b.Property<string>("TextResponse")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.HasIndex("ResponseId");
b.ToTable("ResponseDetails");
});
modelBuilder.Entity("Model.SentNewsletterEamil", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Body")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsBounced")
.HasColumnType("bit");
b.Property<bool>("IsClicked")
.HasColumnType("bit");
b.Property<bool>("IsDelivered")
.HasColumnType("bit");
b.Property<bool>("IsOpened")
.HasColumnType("bit");
b.Property<bool>("IsSent")
.HasColumnType("bit");
b.Property<bool>("IsSpam")
.HasColumnType("bit");
b.Property<bool>("IsUnsubscribed")
.HasColumnType("bit");
b.Property<string>("RecipientEmail")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("SentDate")
.HasColumnType("datetime2");
b.Property<string>("Subject")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("SentNewsletterEamils");
});
modelBuilder.Entity("Model.SocialMedia", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("SocialMedia");
});
modelBuilder.Entity("Model.Subscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsSubscribed")
.HasColumnType("bit");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Subscriptions");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Model.Answer", b =>
{
b.HasOne("Model.Question", "Question")
.WithMany("Answers")
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Question");
});
modelBuilder.Entity("Model.FooterSocialMedia", b =>
{
b.HasOne("Model.Footer", "Footer")
.WithMany("FooterSocialMedias")
.HasForeignKey("FooterId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.SocialMedia", "SocialMedia")
.WithMany("FooterSocialMedias")
.HasForeignKey("SocialId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Footer");
b.Navigation("SocialMedia");
});
modelBuilder.Entity("Model.Page", b =>
{
b.HasOne("Model.Banner", "banner")
.WithMany()
.HasForeignKey("BannerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.Footer", "footer")
.WithMany()
.HasForeignKey("FooterId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("banner");
b.Navigation("footer");
});
modelBuilder.Entity("Model.Question", b =>
{
b.HasOne("Model.Questionnaire", "Questionnaire")
.WithMany("Questions")
.HasForeignKey("QuestionnaireId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Questionnaire");
});
modelBuilder.Entity("Model.Response", b =>
{
b.HasOne("Model.Questionnaire", "Questionnaire")
.WithMany()
.HasForeignKey("QuestionnaireId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Questionnaire");
});
modelBuilder.Entity("Model.ResponseAnswer", b =>
{
b.HasOne("Model.ResponseDetail", "ResponseDetail")
.WithMany("ResponseAnswers")
.HasForeignKey("ResponseDetailId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ResponseDetail");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.HasOne("Model.Question", "Question")
.WithMany()
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Model.Response", "Response")
.WithMany("ResponseDetails")
.HasForeignKey("ResponseId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Question");
b.Navigation("Response");
});
modelBuilder.Entity("Model.Footer", b =>
{
b.Navigation("FooterSocialMedias");
});
modelBuilder.Entity("Model.Question", b =>
{
b.Navigation("Answers");
});
modelBuilder.Entity("Model.Questionnaire", b =>
{
b.Navigation("Questions");
});
modelBuilder.Entity("Model.Response", b =>
{
b.Navigation("ResponseDetails");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.Navigation("ResponseAnswers");
});
modelBuilder.Entity("Model.SocialMedia", b =>
{
b.Navigation("FooterSocialMedias");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Web.Migrations
{
/// <inheritdoc />
public partial class EmailTrackingModelChnaged : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsSent",
table: "SentNewsletterEamils",
type: "bit",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "IsUnsubscribed",
table: "SentNewsletterEamils",
type: "bit",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsSent",
table: "SentNewsletterEamils");
migrationBuilder.DropColumn(
name: "IsUnsubscribed",
table: "SentNewsletterEamils");
}
}
}

View file

@ -0,0 +1,823 @@
// <auto-generated />
using System;
using Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Web.Migrations
{
[DbContext(typeof(SurveyContext))]
[Migration("20240508104306_EmailTrackingModelChangedWithIsBlockedPrperty")]
partial class EmailTrackingModelChangedWithIsBlockedPrperty
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Model.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("CVR")
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Mobile")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PostalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("State")
.HasColumnType("nvarchar(max)");
b.Property<string>("Street")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Addresss");
});
modelBuilder.Entity("Model.Answer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionId")
.HasColumnType("int");
b.Property<string>("Text")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.ToTable("Answers");
});
modelBuilder.Entity("Model.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.HasColumnType("nvarchar(max)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Model.Banner", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ImageUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LinkUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Banners");
});
modelBuilder.Entity("Model.Footer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("CreatedBy")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ImageUlr")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("LastUpdated")
.HasColumnType("datetime2");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Owner")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Sitecopyright")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("UpdatedBy")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Footers");
});
modelBuilder.Entity("Model.FooterSocialMedia", b =>
{
b.Property<int>("FooterId")
.HasColumnType("int");
b.Property<int>("SocialId")
.HasColumnType("int");
b.HasKey("FooterId", "SocialId");
b.HasIndex("SocialId");
b.ToTable("FooterSocialMedias");
});
modelBuilder.Entity("Model.Page", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BannerId")
.HasColumnType("int");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("FooterId")
.HasColumnType("int");
b.Property<string>("Slug")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("BannerId");
b.HasIndex("FooterId");
b.ToTable("Pages");
});
modelBuilder.Entity("Model.Question", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionnaireId")
.HasColumnType("int");
b.Property<string>("Text")
.HasColumnType("nvarchar(max)");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("QuestionnaireId");
b.ToTable("Questions");
});
modelBuilder.Entity("Model.Questionnaire", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Questionnaires");
});
modelBuilder.Entity("Model.Response", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionnaireId")
.HasColumnType("int");
b.Property<DateTime>("SubmissionDate")
.HasColumnType("datetime2");
b.Property<string>("UserEmail")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserName")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionnaireId");
b.ToTable("Responses");
});
modelBuilder.Entity("Model.ResponseAnswer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("AnswerId")
.HasColumnType("int");
b.Property<int>("ResponseDetailId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ResponseDetailId");
b.ToTable("ResponseAnswers");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("QuestionId")
.HasColumnType("int");
b.Property<int>("QuestionType")
.HasColumnType("int");
b.Property<int>("ResponseId")
.HasColumnType("int");
b.Property<string>("TextResponse")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.HasIndex("ResponseId");
b.ToTable("ResponseDetails");
});
modelBuilder.Entity("Model.SentNewsletterEamil", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Body")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsBlocked")
.HasColumnType("bit");
b.Property<bool>("IsBounced")
.HasColumnType("bit");
b.Property<bool>("IsClicked")
.HasColumnType("bit");
b.Property<bool>("IsDelivered")
.HasColumnType("bit");
b.Property<bool>("IsOpened")
.HasColumnType("bit");
b.Property<bool>("IsSent")
.HasColumnType("bit");
b.Property<bool>("IsSpam")
.HasColumnType("bit");
b.Property<bool>("IsUnsubscribed")
.HasColumnType("bit");
b.Property<string>("RecipientEmail")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("SentDate")
.HasColumnType("datetime2");
b.Property<string>("Subject")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("SentNewsletterEamils");
});
modelBuilder.Entity("Model.SocialMedia", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("SocialMedia");
});
modelBuilder.Entity("Model.Subscription", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsSubscribed")
.HasColumnType("bit");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Subscriptions");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Model.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Model.Answer", b =>
{
b.HasOne("Model.Question", "Question")
.WithMany("Answers")
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Question");
});
modelBuilder.Entity("Model.FooterSocialMedia", b =>
{
b.HasOne("Model.Footer", "Footer")
.WithMany("FooterSocialMedias")
.HasForeignKey("FooterId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.SocialMedia", "SocialMedia")
.WithMany("FooterSocialMedias")
.HasForeignKey("SocialId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Footer");
b.Navigation("SocialMedia");
});
modelBuilder.Entity("Model.Page", b =>
{
b.HasOne("Model.Banner", "banner")
.WithMany()
.HasForeignKey("BannerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Model.Footer", "footer")
.WithMany()
.HasForeignKey("FooterId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("banner");
b.Navigation("footer");
});
modelBuilder.Entity("Model.Question", b =>
{
b.HasOne("Model.Questionnaire", "Questionnaire")
.WithMany("Questions")
.HasForeignKey("QuestionnaireId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Questionnaire");
});
modelBuilder.Entity("Model.Response", b =>
{
b.HasOne("Model.Questionnaire", "Questionnaire")
.WithMany()
.HasForeignKey("QuestionnaireId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Questionnaire");
});
modelBuilder.Entity("Model.ResponseAnswer", b =>
{
b.HasOne("Model.ResponseDetail", "ResponseDetail")
.WithMany("ResponseAnswers")
.HasForeignKey("ResponseDetailId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ResponseDetail");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.HasOne("Model.Question", "Question")
.WithMany()
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Model.Response", "Response")
.WithMany("ResponseDetails")
.HasForeignKey("ResponseId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Question");
b.Navigation("Response");
});
modelBuilder.Entity("Model.Footer", b =>
{
b.Navigation("FooterSocialMedias");
});
modelBuilder.Entity("Model.Question", b =>
{
b.Navigation("Answers");
});
modelBuilder.Entity("Model.Questionnaire", b =>
{
b.Navigation("Questions");
});
modelBuilder.Entity("Model.Response", b =>
{
b.Navigation("ResponseDetails");
});
modelBuilder.Entity("Model.ResponseDetail", b =>
{
b.Navigation("ResponseAnswers");
});
modelBuilder.Entity("Model.SocialMedia", b =>
{
b.Navigation("FooterSocialMedias");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Web.Migrations
{
/// <inheritdoc />
public partial class EmailTrackingModelChangedWithIsBlockedPrperty : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsBlocked",
table: "SentNewsletterEamils",
type: "bit",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsBlocked",
table: "SentNewsletterEamils");
}
}
}

View file

@ -540,6 +540,55 @@ namespace Web.Migrations
b.ToTable("ResponseDetails");
});
modelBuilder.Entity("Model.SentNewsletterEamil", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Body")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsBlocked")
.HasColumnType("bit");
b.Property<bool>("IsBounced")
.HasColumnType("bit");
b.Property<bool>("IsClicked")
.HasColumnType("bit");
b.Property<bool>("IsDelivered")
.HasColumnType("bit");
b.Property<bool>("IsOpened")
.HasColumnType("bit");
b.Property<bool>("IsSent")
.HasColumnType("bit");
b.Property<bool>("IsSpam")
.HasColumnType("bit");
b.Property<bool>("IsUnsubscribed")
.HasColumnType("bit");
b.Property<string>("RecipientEmail")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("SentDate")
.HasColumnType("datetime2");
b.Property<string>("Subject")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("SentNewsletterEamils");
});
modelBuilder.Entity("Model.SocialMedia", b =>
{
b.Property<int>("Id")

View file

@ -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<SurveyContext>();
builder.Services.AddTransient<NavigationViewComponent>();
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",

View file

@ -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; }
}
}