Subscription completed

This commit is contained in:
Qais Yousuf 2024-04-05 17:27:17 +02:00
parent 2a383d0c3d
commit 46a75504d8
23 changed files with 929 additions and 230 deletions

View file

@ -0,0 +1,43 @@
using Data;
using Microsoft.EntityFrameworkCore;
using Model;
using Services.Interaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace Services.Implemnetation
{
public class NewsLetterRepository : INewsLetterRepository
{
private readonly SurveyContext _context;
public NewsLetterRepository(SurveyContext context)
{
_context = context;
}
public void Delete(int id)
{
var IdToDelete = GetById(id);
_context.Subscriptions.Remove(IdToDelete);
}
public List<Subscription> GetAll()
{
return _context.Subscriptions.AsNoTracking().ToList();
}
public Subscription GetById(int id)
{
return _context.Subscriptions.AsNoTracking().Where(x => x.Id == id).FirstOrDefault();
}
}
}

View file

@ -0,0 +1,19 @@
using Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Services.Interaces
{
public interface INewsLetterRepository
{
List<Subscription> GetAll();
Subscription GetById(int id);
void Delete(int id);
}
}

View file

@ -0,0 +1,7 @@
namespace Web.AIConfiguration
{
public class OpenAIOptions
{
public string? ApiKey { get; set; }
}
}

View file

@ -0,0 +1,135 @@
using Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualStudio.TextTemplating;
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages.Manage;
using OpenAI_API;
using Services.EmailSend;
using Services.Implemnetation;
using Services.Interaces;
using Web.AIConfiguration;
using Web.ViewModel.NewsLetterVM;
namespace Web.Areas.Admin.Controllers
{
public class NewslettersController : Controller
{
private readonly INewsLetterRepository _repository;
private readonly SurveyContext _context;
private readonly IEmailServices _emailServices;
private readonly IConfiguration _configuration;
public NewslettersController(INewsLetterRepository repository,SurveyContext context,IEmailServices emailServices,IConfiguration configuration)
{
_repository = repository;
_context = context;
_emailServices = emailServices;
_configuration = configuration;
}
public IActionResult Index()
{
var totalSubscribedUsers = _context.Subscriptions.Count(s => s.IsSubscribed);
// Pass the total count to the view
ViewBag.TotalSubscribedUsers = totalSubscribedUsers;
var newsLetterFromdb = _repository.GetAll();
var viewmodel = new List<NewsLetterViewModel>();
foreach (var item in newsLetterFromdb)
{
viewmodel.Add(new NewsLetterViewModel
{
Id=item.Id,
Name=item.Name,
Email=item.Email,
IsSubscribed=item.IsSubscribed
});
}
return View(viewmodel);
}
public IActionResult Create()
{
var totalSubscribedUsers = _context.Subscriptions.Count(s => s.IsSubscribed);
// Pass the total count to the view
ViewBag.TotalSubscribedUsers = totalSubscribedUsers;
return View();
}
[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 = $"<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>";
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 ex)
{
// 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)
{
var newsLetterFromDb = _repository.GetById(id);
var viewmodel = new NewsLetterViewModel
{
Id=newsLetterFromDb.Id,
Email=newsLetterFromDb.Email,
Name=newsLetterFromDb.Name,
IsSubscribed=newsLetterFromDb.IsSubscribed
};
return View(viewmodel);
}
[HttpPost]
[ActionName("Delete")]
public IActionResult DeleteConfirm(int id)
{
_repository.Delete(id);
_context.SaveChanges();
TempData["Success"] = "Subscriber deleted successfully";
return RedirectToAction(nameof(Index));
}
}
}

View file

@ -0,0 +1,106 @@
using Microsoft.AspNetCore.Mvc;
using System.Text;
namespace Web.Areas.Admin.Controllers
{
public class OpenAIContentController : Controller
{
private readonly IConfiguration _configuration;
public OpenAIContentController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet]
public IActionResult GenerateContent()
{
return View();
}
// GET: /YourController/GenerateContent
[HttpPost]
public async Task<IActionResult> GenerateContent([FromBody] GenerateContentRequest request)
{
try
{
// Retrieve the input text from the request
string inputText = request.InputText;
// Validate input text (optional)
if (string.IsNullOrWhiteSpace(inputText))
{
return BadRequest("Input text cannot be empty.");
}
// Retrieve OpenAI API key from configuration
string apiKey = _configuration["OpenAI:ApiKey"];
// Call OpenAI API to generate content using the input text
string generatedContent = await GenerateContentWithOpenAI(apiKey, inputText);
// Return the generated content
return Ok(generatedContent);
}
catch (Exception ex)
{
// Log the exception
Console.WriteLine(ex.Message);
// Return an error response
return StatusCode(500, "Error occurred while generating content.");
}
}
// Method to generate content using OpenAI API
private async Task<string> GenerateContentWithOpenAI(string apiKey, string inputText)
{
try
{
using (var httpClient = new HttpClient())
{
// Set up HTTP client and request
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiKey);
// Prepare request body
var requestBody = new
{
model = "babbage-002",
prompt = inputText,
max_tokens = 100
};
// Serialize request body to JSON
var jsonContent = Newtonsoft.Json.JsonConvert.SerializeObject(requestBody);
// Make HTTP POST request to OpenAI API
var response = await httpClient.PostAsync("https://api.openai.com/v1/completions", new StringContent(jsonContent, Encoding.UTF8, "application/json"));
// Check if request was successful
//response.EnsureSuccessStatusCode();
// Read response content
string responseBody = await response.Content.ReadAsStringAsync();
// Return generated content
return responseBody;
}
}
catch (Exception ex)
{
// Log exception
Console.WriteLine(ex.Message);
throw;
}
}
// Model class to represent request body
public class GenerateContentRequest
{
public string InputText { get; set; }
}
}
}

View file

@ -1,83 +0,0 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Web.Areas.Admin.Controllers
{
public class sdController : Controller
{
// GET: sdController
public ActionResult Index()
{
return View();
}
// GET: sdController/Details/5
public ActionResult Details(int id)
{
return View();
}
// GET: sdController/Create
public ActionResult Create()
{
return View();
}
// POST: sdController/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IFormCollection collection)
{
try
{
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
// GET: sdController/Edit/5
public ActionResult Edit(int id)
{
return View();
}
// POST: sdController/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, IFormCollection collection)
{
try
{
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
// GET: sdController/Delete/5
public ActionResult Delete(int id)
{
return View();
}
// POST: sdController/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id, IFormCollection collection)
{
try
{
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
}
}

View file

@ -0,0 +1,68 @@
@model string
<div class="container mt-4">
<div class="card justify-content-center">
<div class="card-body">
<h5 class="card-title">Generate Content</h5>
<div class="row">
<form id="generateForm">
<div class="mb-3 col-12">
<label for="inputText" class="control-label">Input Text</label>
<textarea id="inputText" name="inputText" class="form-control" rows="4"></textarea>
<span class="text-danger" id="inputTextError"></span>
</div>
<div class="mb-3 ml-3">
<button type="button" id="generateButton" class="btn btn-primary">Generate</button>
</div>
</form>
</div>
<div id="generatedContent" style="display: none;">
<div class="mt-4">
<h5>Generated Content</h5>
<p id="generatedText"></p>
</div>
</div>
</div>
</div>
</div>
@section scripts {
<script>
$(document).ready(function () {
// Handle click event on generate button
$("#generateButton").click(function () {
// Clear previous error message
$("#inputTextError").text("");
// Get input text
var inputText = $("#inputText").val();
// Validate input text
if (inputText.trim() === "") {
$("#inputTextError").text("Input text is required.");
return;
}
// Make AJAX request to generate content
$.ajax({
url: "/Contents/GenerateContent",
type: "POST",
contentType: "application/json",
data: JSON.stringify({ inputText: inputText }),
success: function (data) {
// Display generated content
$("#generatedText").html(data);
$("#generatedContent").show();
},
error: function (xhr, status, error) {
// Display error message
console.error(xhr.responseText);
}
});
});
});
</script>
}

View file

@ -79,90 +79,3 @@
@*
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Id)
</th>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Owner)
</th>
<th>
@Html.DisplayNameFor(model => model.Content)
</th>
<th>
@Html.DisplayNameFor(model => model.CreatedBy)
</th>
<th>
@Html.DisplayNameFor(model => model.UpdatedBy)
</th>
<th>
@Html.DisplayNameFor(model => model.LastUpdated)
</th>
<th>
@Html.DisplayNameFor(model => model.ImageUlr)
</th>
<th>
@Html.DisplayNameFor(model => model.Sitecopyright)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Owner)
</td>
<td>
@Html.DisplayFor(modelItem => item.Content)
</td>
<td>
@Html.DisplayFor(modelItem => item.CreatedBy)
</td>
<td>
@Html.DisplayFor(modelItem => item.UpdatedBy)
</td>
<td>
@Html.DisplayFor(modelItem => item.LastUpdated)
</td>
<td>
@Html.DisplayFor(modelItem => item.ImageUlr)
</td>
<td>
@Html.DisplayFor(modelItem => item.Sitecopyright)
</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,75 @@
@model string
@{
ViewData["Title"] = "GenerateContent";
}
<div class="container mt-4">
<div class="card justify-content-center">
<div class="card-body">
<h5 class="card-title">Generate Content</h5>
<div class="row">
<form id="generateForm">
<div class="mb-3 col-12">
<label for="inputText" class="control-label">Input Text</label>
<textarea id="inputText" name="inputText" class="form-control" rows="4"></textarea>
<span class="text-danger" id="inputTextError"></span>
</div>
<div class="mb-3 ml-3">
<button type="button" id="generateButton" class="btn btn-primary">Generate</button>
</div>
</form>
</div>
<div id="generatedContent" style="display: none;">
<div class="mt-4">
<h5>Generated Content</h5>
<p id="generatedText"></p>
</div>
</div>
</div>
</div>
</div>
@section scripts {
<script>
$(document).ready(function () {
// Handle click event on generate button
$("#generateButton").click(function () {
// Clear previous error message
$("#inputTextError").text("");
// Get input text
var inputText = $("#inputText").val();
// Validate input text
if (inputText.trim() === "") {
$("#inputTextError").text("Input text is required.");
return;
}
// Make AJAX request to generate content
$.ajax({
url: "/admin/OpenAIContent/GenerateContent",
type: "POST",
contentType: "application/json",
data: JSON.stringify({ inputText: inputText }),
success: function (data) {
// Display generated content
$("#generatedText").html(data);
$("#generatedContent").show();
},
error: function (xhr, status, error) {
// Display error message
console.error(xhr.responseText);
}
});
});
});
</script>
}

View file

@ -39,6 +39,10 @@
<a asp-controller="Questionnaire" asp-action="index"><span class="bi bi-question-circle"></span> Survey</a>
</li>
<li>
<a asp-controller="newsletters" asp-action="index"><i class="bi bi-envelope"></i> Subscibers</a>
</li>
</ul>

View file

@ -0,0 +1,65 @@
@model Web.ViewModel.NewsLetterVM.SendNewsLetterViewModel
@{
ViewData["Title"] = "NewsLetter";
}
<div class="container mt-4">
<div class="card justify-content-center">
<div class="card-body">
<h5 class="card-title ml-2">Send newsletter</h5>
<div class="alert alert-info" role="alert">
This email will be sent to all users who are subscribed to our newsletter.
<br />
Total Subscribed Users: <strong>@ViewBag.TotalSubscribedUsers</strong>
</div>
<div class="row ">
<!-- 12 columns for textboxes -->
<div id>
</div>
<form asp-action="Create">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="mb-3 col-12">
<label asp-for="Subject" class="control-label"></label>
<input asp-for="Subject" class="form-control" />
<span asp-validation-for="Subject" class="text-danger"></span>
</div>
<div class="mb-3 col-12">
<label asp-for="Body" class="control-label"></label>
<textarea asp-for="Body" class="form-control"></textarea>
<span asp-validation-for="Body" class="text-danger"></span>
</div>
<div class="mb-3 ml-3">
<input type="submit" value="Send Email" class="btn btn-sm btn-primary @(@ViewBag.TotalSubscribedUsers <= 0 ? "disabled" : "")" /> |
<a asp-action="Index" class="btn btn-primary-outline btn-sm">Back to list</a>
</div>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
<script src="https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.11.4/ckeditor.js"></script>
<script>
CKEDITOR.replace("Body");
</script>
@{
<partial name="_ValidationScriptsPartial" />
}
}

View file

@ -0,0 +1,45 @@
@model Web.ViewModel.NewsLetterVM.NewsLetterViewModel
@{
ViewData["Title"] = "Delete";
}
<div class="container mt-4">
<div class="card justify-content-center">
<div class="card-body">
<h5 class="card-title">Delete Address</h5>
<h6 class="text-danger">Are you sure you want to delete the <span class="badge bg-danger">@Model.Name</span></h6>
<div class="row ">
<!-- 12 columns for textboxes -->
<form asp-action="Delete" asp-controller="Newsletters">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="mb-3 col-12">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" disabled />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="mb-3 col-12">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" disabled />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="mb-3 col-12">
<label asp-for="IsSubscribed" class="control-label"></label>
@Html.DisplayFor(model => model.IsSubscribed)
</div>
<div class="mb-3">
<input type="submit" value="Delete" class="btn btn-outline-danger" /> | <a asp-action="Index" class="btn btn-primary">Back to list</a>
</div>
</form>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,66 @@
@model IEnumerable<Web.ViewModel.NewsLetterVM.NewsLetterViewModel>
@{
ViewData["Title"] = "Newsletter list";
}
<div class="container mt-5">
<partial name="_Notification" />
<div class="card bg-default mb-3 ">
<div class="card-header">Subscribers</div>
<div class="card-body">
<h4 class="card-title">Subscribers list</h4>
<div class="alert alert-info" role="alert">
Total Subscribed Users: <strong>@ViewBag.TotalSubscribedUsers</strong>
</div>
<p>
<a asp-action="Create" class="btn btn-primary @(@ViewBag.TotalSubscribedUsers <= 0 ? "disabled" : "")">compose newsletter</a>
</p>
<table class="table table-responsive w-100 d-block d-md-table ">
<thead>
<tr>
<th scope="col">Id</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">IsSubscribed</th>
<th scope="col" class="d-flex justify-content-end">Action</th>
</tr>
</thead>
<tbody class="justify-content-center">
@foreach (var item in Model)
{
<tr class=" table-secondary">
<td>@item.Id</td>
<td>@item.Name</td>
<td>@item.Email</td>
<td>
@if(item.IsSubscribed==true)
{
<span class="badge badge-success p-1">Subscribed @Html.DisplayFor(modelItem => item.IsSubscribed)</span>
}
else
{
<span class="badge badge-secondary p-1">Not subscribed @Html.DisplayFor(modelItem => item.IsSubscribed)</span>
}
</td>
<td class="d-flex justify-content-end">
<a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger btn-m"><i class="bi bi-trash"></i> Delete</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>

View file

@ -49,7 +49,7 @@ namespace Web.Controllers
if (existingSubscription != null)
{
TempData["error"] = "Email already exists.";
TempData["error"] = "Email already subscribed.";
return RedirectToAction("", "home");
}
@ -63,11 +63,18 @@ namespace Web.Controllers
To confirm your subscription, please click the following button:<br><br>
<a href=""{confirmationUrl}"" style=""display: inline-block; padding: 10px 20px; background-color: #28a745; color: #fff; text-decoration: none;"">Confirm Subscription</a><br><br>
If you have any questions or need assistance, feel free to contact us at help@seosoft.dk<br><br>
Best regards,<br>
<h5>
Søren Eggert Lundsteen Olsen<br>
Seosoft ApS
</h5>
<hr>
<h6>
Hovedgaden 3
Jordrup<br>
Kolding 6064<br>
Denmark
</6>
";
var newEmail = new EmailToSend(email, subject, body);
@ -83,7 +90,7 @@ namespace Web.Controllers
_context.Subscriptions.Add(subscriber);
await _context.SaveChangesAsync();
TempData["success"] = "Subscription successful.";
TempData["success"] = "Subscription successful. Please confirm your email.";
return RedirectToAction("", "home");
}
catch (Exception ex)
@ -100,65 +107,120 @@ namespace Web.Controllers
}
[HttpGet]
public async Task<IActionResult> Confirmation(string email)
{
try
{
// Find the subscription with the provided email
var subscription = await _context.Subscriptions.FirstOrDefaultAsync(s => s.Email == email);
if (subscription != null)
{
if (subscription.IsSubscribed)
{
// If IsSubscribed is already true, inform the user that the email is already confirmed
ViewBag.Message = "Your email is already confirmed. Thank you!";
}
else
{
// Update the IsSubscribed property to true
subscription.IsSubscribed = true;
_context.Subscriptions.Update(subscription);
await _context.SaveChangesAsync();
// Send a "thank you" email to the user
string subject = "Thank You for Confirming Your Subscription";
string body = $"Dear {subscription.Name},<br><br>" +
"Thank you for confirming your subscription. " +
"You are now subscribed to our service.<br><br>" +
"<h5>Søren Eggert Lundsteen Olsen<br>Seosoft ApS</h5>" +
"<hr>" +
"<h6>Hovedgaden 3<br>Jordrup<br>Kolding 6064<br>Denmark</h6>";
//private async Task SendConfirmationEmail(Subscription subscriber)
//{
// var confirmationLink = Url.Action("Unsubscribe", "Subscription", new { id = subscriber.Id }, protocol: HttpContext.Request.Scheme);
var thankYouEmail = new EmailToSend(subscription.Email, subject, body);
await _mailSerivces.SendConfirmationEmailAsync(thankYouEmail);
// Inform the user that the email has been confirmed
ViewBag.Message = "Thank you for confirming your email. You are now subscribed!";
}
return View(subscription); // You can return a view to show a confirmation message
}
else
{
ViewBag.Message = "You have been unsubscribed from our service. Thank you!";
return View(subscription);
}
}
catch (Exception ex)
{
// Log or handle the exception as needed
return View("Error"); // You can return a view to show an error message
}
}
[HttpGet]
public async Task<IActionResult> UnsubscribeConfirmation(string email)
{
try
{
// Find the subscription with the provided email
var subscription = await _context.Subscriptions.FirstOrDefaultAsync(s => s.Email == email);
if (subscription != null)
{
if (subscription.IsSubscribed)
{
// Update the IsSubscribed property to false
subscription.IsSubscribed = false;
_context.Subscriptions.Remove(subscription);
await _context.SaveChangesAsync();
// Inform the user that the email has been unsubscribed
ViewBag.Message = "You have been unsubscribed from our service. Thank you!";
// Optionally, send an email confirmation to the user
string subject = "Unsubscribe Confirmation";
string body = "You have successfully unsubscribed from our newsletter. We're sorry to see you go <br><br>" +
"<h5>Søren Eggert Lundsteen Olsen<br>Seosoft ApS</h5>"+
"<hr>" +
"<h6>Hovedgaden 3<br>Jordrup<br>Kolding 6064<br>Denmark</h6>";
var thankYouEmail = new EmailToSend(subscription.Email, subject, body);
await _mailSerivces.SendConfirmationEmailAsync(thankYouEmail);
return View(subscription); // You can return a view to show a confirmation message
}
else
{
// If IsSubscribed is already false, inform the user that the email is already unsubscribed
ViewBag.Message = "Your email is already unsubscribed. Thank you!";
return View(subscription); // You can return a view to show a message
}
}
else
{
// Inform the user that the unsubscription process couldn't be completed
ViewBag.Message = "Your email does not exist in our subscription list. Please subscribe first.";
return View(subscription); // You can return a view to show an error message
}
}
catch (Exception ex)
{
// Log or handle the exception as needed
return View("Error"); // You can return a view to show an error message
}
}
// var request = new MailjetRequest
// {
// Resource = Send.Resource,
// }
// .Property(Send.Messages, new JArray
// {
// new JObject
// {
// {
// "From",
// new JObject
// {
// {"Email", "qais@seosoft.dk"},
// {"Name", "SeoSoft"}
// }
// },
// {
// "To",
// new JArray
// {
// new JObject
// {
// {"Email", subscriber.Email},
// {"Name", subscriber.Name}
// }
// }
// },
// {"Subject", "Subscription Confirmation"},
// {"HTMLPart", $@"<p>Hello {subscriber.Name},</p>
// <p>Thank you for subscribing!</p>
// <p>To unsubscribe, click <a href='{confirmationLink}'>here</a>.</p>"
// }
// }
// });
// try
// {
// var response = await _mailjetClient.PostAsync(request);
// if (!response.IsSuccessStatusCode)
// {
// // Handle error if sending email fails
// // For example, log the error or take appropriate action
// // You can throw an exception if needed
// throw new Exception("Failed to send confirmation email");
// }
// }
// catch (Exception ex)
// {
// // Log the exception or take appropriate action
// // For example:
// // _logger.LogError(ex, "Failed to send confirmation email");
// }
//}
}
}

View file

@ -1,9 +1,14 @@
using Data;
using Mailjet.Client;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Services.Implemnetation;
using Services.Interaces;
using System.Configuration;
using OpenAI_API;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Web.AIConfiguration;
namespace Web.Extesions
{
@ -50,6 +55,10 @@ namespace Web.Extesions
{
services.AddScoped<IQuestionRepository, QuestionRepository>();
}
public static void ConfigureNewsLetter(this IServiceCollection services)
{
services.AddScoped<INewsLetterRepository, NewsLetterRepository>();
}
public static void MailConfiguration(this IServiceCollection services)
{
services.AddTransient<IEmailServices, EmailServices>();
@ -82,5 +91,15 @@ namespace Web.Extesions
// Other configurations...
}
public static void ConfigureOpenAI(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<OpenAIOptions>(configuration.GetSection("OpenAI"));
services.AddSingleton<IOpenAIAPI, OpenAIAPI>();
}
}
}

View file

@ -23,6 +23,8 @@ builder.Services.AddDbContext<SurveyContext>(options =>
builder.Services.ConfigurePageServices();
builder.Services.ConfigureBannerServices();
builder.Services.ConfigureAddress();
@ -32,7 +34,10 @@ builder.Services.ConfigureQuestionnarie();
builder.Services.ConfigureQuestion();
builder.Services.AddScoped<SurveyContext>();
builder.Services.AddTransient<NavigationViewComponent>();
builder.Services.ConfigureNewsLetter();
builder.Services.MailConfiguration();
builder.Services.ConfigureOpenAI(config);

View file

@ -0,0 +1,11 @@
namespace Web.ViewModel.NewsLetterVM
{
public class NewsLetterViewModel
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Email { get; set; }
public bool IsSubscribed { get; set; }
}
}

View file

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
using System.Reflection.Metadata.Ecma335;
namespace Web.ViewModel.NewsLetterVM
{
public class SendNewsLetterViewModel
{
[Required]
public string? Subject { get; set; }
[Required]
public string? Body { get; set; }
}
}

View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Web</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/Web.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light shadow-lg">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Online survey</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon text-white"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
</ul>
<ul class="navbar-nav">
<li class="nav-item">
<a href="#" class="btn btn-sm " id="BannerButon"> Sign in <i class="bi bi-person-check-fill"></i></a> |
<a href="#" class="btn btn-sm" id="BannerButon"> Sign up <i class="bi bi-person-fill-add"></i></a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main role="main">
@RenderBody()
</main>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View file

@ -0,0 +1,35 @@
@model Subscription
@{
ViewData["Title"] = "Confirmation";
Layout = "~/Views/Shared/_SubscriptionLayout.cshtml";
}
<div class="d-flex flex-column" id="BannerBackground">
<section class="hero text-white">
<div class="container py-1">
@* <div class="col-12" id="boxBanner">
<h1 class="display-6 text-white font-weight-bold">@Model.Description.ToUpper()</h1>
</div> *@
<div id="rowSectionBanner">
<div class="col-lg-6" id="boxBanner">
<h1 class="display-6 font-weight-bold" id="BtnColor">Hey @Model?.Name</h1>
@* <p class="fst-italic text-muted">@Html.Raw(Model.Content) <a class="text-primary" href="@Model.Sitecopyright" target="_blank">SeoSoft</a></p> *@
<p class="text-white">@ViewBag.Message</p>
<a href="/home" class="btn btn-sm mt-1" id="BannerButon"><i class="bi bi-arrow-left-square-fill"></i> Back to home </a>
</div>
<div class="col-lg-6" id="boxBanner">
<script src="https://unpkg.com/@@dotlottie/player-component@latest/dist/dotlottie-player.mjs" type="module"></script>
<dotlottie-player src="https://lottie.host/543409f4-933c-456f-923c-f936be0d0da6/UxmKdWjkuD.json" class="img-fluid" speed="1" style="width: auto; height: auto;" direction="1" playMode="normal" autoplay></dotlottie-player>
</div>
</div>
</div>
</section>
</div>

View file

@ -0,0 +1,32 @@
@model Subscription
@{
ViewData["Title"] = "UnsubscribeConfirmation";
Layout = "~/Views/Shared/_SubscriptionLayout.cshtml";
}
<div class="d-flex flex-column" id="BannerBackground">
<section class="hero text-white">
<div class="container py-1">
@* <div class="col-12" id="boxBanner">
<h1 class="display-6 text-white font-weight-bold">@Model.Description.ToUpper()</h1>
</div> *@
<div id="rowSectionBanner">
<div class="col-lg-6" id="boxBanner">
<h1 class="display-6 font-weight-bold" id="BtnColor">Hey @Model?.Name</h1>
@* <p class="fst-italic text-muted">@Html.Raw(Model.Content) <a class="text-primary" href="@Model.Sitecopyright" target="_blank">SeoSoft</a></p> *@
<p class="text-white">@ViewBag.Message</p>
<a href="/home" class="btn btn-sm mt-1" id="BannerButon"><i class="bi bi-arrow-left-square-fill"></i> Back to home </a>
</div>
<div class="col-lg-6" id="boxBanner">
<script src="https://unpkg.com/@@dotlottie/player-component@latest/dist/dotlottie-player.mjs" type="module"></script>
<dotlottie-player src="https://lottie.host/543409f4-933c-456f-923c-f936be0d0da6/UxmKdWjkuD.json" class="img-fluid" speed="1" style="width: auto; height: auto;" direction="1" playMode="normal" autoplay></dotlottie-player>
</div>
</div>
</div>
</section>
</div>

View file

@ -20,6 +20,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
<PackageReference Include="OpenAI" Version="1.11.0" />
</ItemGroup>
<ItemGroup>
@ -29,6 +30,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="NewFolder\" />
<Folder Include="wwwroot\Images\" />
</ItemGroup>

View file

@ -13,12 +13,15 @@
"Email": {
"From": "mr.qais.yousuf@gmail.com",
"ApplicationName": "Online Survey",
"ConfirmEmailPath": "Subscription/Confirmation"
//"ResetPasswordPath": "account/reset-password"
"ConfirmEmailPath": "Subscription/Confirmation",
"unsubscribePath": "Subscription/UnsubscribeConfirmation"
},
"MailJet": {
"ApiKey": "f545eee3a4743464b9d25fb9c5ab3f6c",
"SecretKey": "9fa430ef00873fdefe333fdc40ee3f8f"
},
"OpenAI": {
"ApiKey": "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"
}
}