diff --git a/Model/SentNewsletterEamil.cs b/Model/SentNewsletterEamil.cs index c81626e..8315618 100644 --- a/Model/SentNewsletterEamil.cs +++ b/Model/SentNewsletterEamil.cs @@ -13,6 +13,7 @@ namespace Model public string? Subject { get; set; } public string? Body { get; set; } public DateTime SentDate { get; set; } + public DateTime ReceivedActivity { get; set; } public bool IsDelivered { get; set; } public bool IsOpened { get; set; } public bool IsClicked { get; set; } diff --git a/Web/Areas/Admin/Controllers/NewslettersController.cs b/Web/Areas/Admin/Controllers/NewslettersController.cs index 46d2266..551d1d0 100644 --- a/Web/Areas/Admin/Controllers/NewslettersController.cs +++ b/Web/Areas/Admin/Controllers/NewslettersController.cs @@ -204,47 +204,54 @@ namespace Web.Areas.Admin.Controllers string eventType = e.Value("event"); Console.WriteLine($"Processing {eventType} for {email}"); - var newsletterEmail = await _context.SentNewsletterEamils - .FirstOrDefaultAsync(n => n.RecipientEmail == email); + // Retrieve all matching newsletter email records + var newsletterEmails = await _context.SentNewsletterEamils + .Where(n => n.RecipientEmail == email) + .ToListAsync(); - if (newsletterEmail == null) + if (newsletterEmails == null || !newsletterEmails.Any()) { Console.WriteLine("No newsletter email record found for email: " + email); continue; } - switch (eventType) + foreach (var newsletterEmail in newsletterEmails) { - 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; - } + // Update the ReceivedActivity property to the current UTC time + newsletterEmail.ReceivedActivity = DateTime.UtcNow.ToLocalTime(); - _context.Entry(newsletterEmail).State = EntityState.Modified; + 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 "); + Console.WriteLine("Emails got updated "); await _context.SaveChangesAsync(); } @@ -256,9 +263,162 @@ namespace Web.Areas.Admin.Controllers } return Ok(); + + //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; + // } + + // // Update the ReceivedActivity property to the current UTC time + // newsletterEmail.ReceivedActivity = DateTime.UtcNow.ToLocalTime(); + + // 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 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() { @@ -266,145 +426,35 @@ namespace Web.Areas.Admin.Controllers 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(); + //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) + // var data = new // { - // 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); - + // 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); //} - [HttpGet] public IActionResult Delete(int id) { diff --git a/Web/Areas/Admin/Views/newsletters/EmailStats.cshtml b/Web/Areas/Admin/Views/newsletters/EmailStats.cshtml index 9fa4184..1ec8f15 100644 --- a/Web/Areas/Admin/Views/newsletters/EmailStats.cshtml +++ b/Web/Areas/Admin/Views/newsletters/EmailStats.cshtml @@ -124,13 +124,13 @@

Back to list

-

EmailStats

+

Real-Time Email Event

- +
- + @@ -147,7 +147,7 @@ { - +
RecipientSent DateActivity Date Subject Sent Delivered
@item.RecipientEmail@item.SentDate@item.ReceivedActivity @item.Subject @if (item.IsSent) @@ -234,51 +234,82 @@
+ - + + + + + +
+
+
+
+
+
+
+
+
+
+ +
+ +
-

Email Statistics

-
- @section Scripts { } + diff --git a/Web/Areas/Admin/Views/newsletters/Index.cshtml b/Web/Areas/Admin/Views/newsletters/Index.cshtml index 4fe3920..f8fdd24 100644 --- a/Web/Areas/Admin/Views/newsletters/Index.cshtml +++ b/Web/Areas/Admin/Views/newsletters/Index.cshtml @@ -117,11 +117,11 @@ background-color: #cb4a49; } -
+
-
+
Subscribers

Subscribers list

@@ -181,19 +181,19 @@ -
+

- Real-Time Email Event Tracking + Real-Time Email Event Tracking

View email tracking with chart

- +
- + @@ -232,7 +232,7 @@ const row = ` - + diff --git a/Web/Migrations/20240509135537_ReceviedDateTimeAddedtoSentNewsLetterModel.Designer.cs b/Web/Migrations/20240509135537_ReceviedDateTimeAddedtoSentNewsLetterModel.Designer.cs new file mode 100644 index 0000000..14a4f62 --- /dev/null +++ b/Web/Migrations/20240509135537_ReceviedDateTimeAddedtoSentNewsLetterModel.Designer.cs @@ -0,0 +1,826 @@ +// +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("20240509135537_ReceviedDateTimeAddedtoSentNewsLetterModel")] + partial class ReceviedDateTimeAddedtoSentNewsLetterModel + { + /// + 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("ReceivedActivity") + .HasColumnType("datetime2"); + + 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/20240509135537_ReceviedDateTimeAddedtoSentNewsLetterModel.cs b/Web/Migrations/20240509135537_ReceviedDateTimeAddedtoSentNewsLetterModel.cs new file mode 100644 index 0000000..79ae9ec --- /dev/null +++ b/Web/Migrations/20240509135537_ReceviedDateTimeAddedtoSentNewsLetterModel.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Web.Migrations +{ + /// + public partial class ReceviedDateTimeAddedtoSentNewsLetterModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ReceivedActivity", + table: "SentNewsletterEamils", + type: "datetime2", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ReceivedActivity", + table: "SentNewsletterEamils"); + } + } +} diff --git a/Web/Migrations/SurveyContextModelSnapshot.cs b/Web/Migrations/SurveyContextModelSnapshot.cs index 0bede71..9d0e381 100644 --- a/Web/Migrations/SurveyContextModelSnapshot.cs +++ b/Web/Migrations/SurveyContextModelSnapshot.cs @@ -575,6 +575,9 @@ namespace Web.Migrations b.Property("IsUnsubscribed") .HasColumnType("bit"); + b.Property("ReceivedActivity") + .HasColumnType("datetime2"); + b.Property("RecipientEmail") .HasColumnType("nvarchar(max)"); diff --git a/Web/ViewModel/NewsLetterVM/EmailStatsViewModel.cs b/Web/ViewModel/NewsLetterVM/EmailStatsViewModel.cs new file mode 100644 index 0000000..3da2cf7 --- /dev/null +++ b/Web/ViewModel/NewsLetterVM/EmailStatsViewModel.cs @@ -0,0 +1,10 @@ +using Model; + +namespace Web.ViewModel.NewsLetterVM +{ + public class EmailStatsViewModel + { + public IEnumerable? Emails { get; set; } + public IEnumerable? DailyActivities { get; set; } + } +}
RecipientSent DateActivity Date Subject Sent Delivered
${item.recipientEmail}${item.sentDate}${item.receivedActivity} ${item.subject} ${item.isSent ? 'Sent' : 'Pending'} ${item.isDelivered ? 'Delivered' : 'Pending'}