Subscription completed
This commit is contained in:
parent
2a383d0c3d
commit
46a75504d8
23 changed files with 929 additions and 230 deletions
43
Services/Implemnetation/NewsLetterRepository.cs
Normal file
43
Services/Implemnetation/NewsLetterRepository.cs
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
19
Services/Interaces/INewsLetterRepository.cs
Normal file
19
Services/Interaces/INewsLetterRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
7
Web/AIConfiguration/OpenAIOptions.cs
Normal file
7
Web/AIConfiguration/OpenAIOptions.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace Web.AIConfiguration
|
||||
{
|
||||
public class OpenAIOptions
|
||||
{
|
||||
public string? ApiKey { get; set; }
|
||||
}
|
||||
}
|
||||
135
Web/Areas/Admin/Controllers/NewslettersController.cs
Normal file
135
Web/Areas/Admin/Controllers/NewslettersController.cs
Normal 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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
106
Web/Areas/Admin/Controllers/OpenAIContentController.cs
Normal file
106
Web/Areas/Admin/Controllers/OpenAIContentController.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Web/Areas/Admin/Views/Contents/GenerateContent.cshtml
Normal file
68
Web/Areas/Admin/Views/Contents/GenerateContent.cshtml
Normal 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>
|
||||
}
|
||||
|
|
@ -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>
|
||||
*@
|
||||
75
Web/Areas/Admin/Views/OpenAIContent/GenerateContent.cshtml
Normal file
75
Web/Areas/Admin/Views/OpenAIContent/GenerateContent.cshtml
Normal 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>
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
|||
65
Web/Areas/Admin/Views/newsletters/Create.cshtml
Normal file
65
Web/Areas/Admin/Views/newsletters/Create.cshtml
Normal 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" />
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
45
Web/Areas/Admin/Views/newsletters/Delete.cshtml
Normal file
45
Web/Areas/Admin/Views/newsletters/Delete.cshtml
Normal 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>
|
||||
|
||||
|
||||
66
Web/Areas/Admin/Views/newsletters/Index.cshtml
Normal file
66
Web/Areas/Admin/Views/newsletters/Index.cshtml
Normal 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>
|
||||
|
||||
|
|
@ -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");
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
11
Web/ViewModel/NewsLetterVM/NewsLetterViewModel.cs
Normal file
11
Web/ViewModel/NewsLetterVM/NewsLetterViewModel.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
13
Web/ViewModel/NewsLetterVM/SendNewsLetterViewModel.cs
Normal file
13
Web/ViewModel/NewsLetterVM/SendNewsLetterViewModel.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
54
Web/Views/Shared/_SubscriptionLayout.cshtml
Normal file
54
Web/Views/Shared/_SubscriptionLayout.cshtml
Normal 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>
|
||||
35
Web/Views/Subscription/Confirmation.cshtml
Normal file
35
Web/Views/Subscription/Confirmation.cshtml
Normal 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>
|
||||
|
||||
|
||||
|
||||
32
Web/Views/Subscription/UnsubscribeConfirmation.cshtml
Normal file
32
Web/Views/Subscription/UnsubscribeConfirmation.cshtml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue