Sending the survey to multiple users created
This commit is contained in:
parent
644893ded8
commit
d733b2cdb1
6 changed files with 167 additions and 118 deletions
|
|
@ -465,120 +465,69 @@ namespace Web.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var questionnairePath = _configuration["Email:Questionnaire"];
|
var questionnairePath = _configuration["Email:Questionnaire"];
|
||||||
int surveyId = viewModel.QuestionnaireId;
|
DateTime currentDateTime = viewModel.ExpirationDateTime.HasValue ? viewModel.ExpirationDateTime.Value : DateTime.Now;
|
||||||
|
DateTime expiryDateTime = currentDateTime; // This line might need adjustment if you are actually setting an expiry.
|
||||||
var userEmailEncoded = HttpUtility.UrlEncode(viewModel.Email);
|
|
||||||
|
|
||||||
|
|
||||||
DateTime currentDateTime;
|
|
||||||
if (viewModel.ExpirationDateTime.HasValue)
|
|
||||||
{
|
|
||||||
currentDateTime = viewModel.ExpirationDateTime.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
currentDateTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the expiration date and time by adding 5 minutes to the current date and time
|
|
||||||
DateTime expiryDateTime = currentDateTime;
|
|
||||||
|
|
||||||
// Generate a unique token, for example, using a cryptographic library or a GUID
|
|
||||||
string token = Guid.NewGuid().ToString();
|
string token = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
// Append the expiration date and time to the token (you might want to encrypt it for security)
|
|
||||||
string tokenWithExpiry = $"{token}|{expiryDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ")}";
|
string tokenWithExpiry = $"{token}|{expiryDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ")}";
|
||||||
|
var emailList = viewModel.Emails.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}?t={tokenWithExpiry}&E={userEmailEncoded}";
|
.Select(email => email.Trim())
|
||||||
|
.ToList();
|
||||||
//var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}?t={tokenWithExpiry}&E={userEmail}";
|
|
||||||
|
|
||||||
//var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}";
|
|
||||||
|
|
||||||
|
|
||||||
var toEmail = viewModel.Email;
|
|
||||||
var question = _questionnaire.GetQuesById(viewModel.Id);
|
var question = _questionnaire.GetQuesById(viewModel.Id);
|
||||||
|
|
||||||
var subject = question.Title;
|
var subject = question.Title;
|
||||||
|
|
||||||
// Construct the email body with HTML formatting
|
bool allEmailsSent = true;
|
||||||
|
foreach (var email in emailList)
|
||||||
|
{
|
||||||
|
var userName = email.Substring(0, email.IndexOf('@')); // This assumes the email is valid and contains an '@'
|
||||||
|
userName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(userName.Replace(".", " ")); // Optional: Improve formatting, replace dots and capitalize names
|
||||||
|
var userEmailEncoded = HttpUtility.UrlEncode(email);
|
||||||
|
var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}?t={tokenWithExpiry}&E={userEmailEncoded}";
|
||||||
|
|
||||||
string emailBody = $@"
|
string emailBody = $@"
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<style>
|
<style>
|
||||||
/* Inline CSS styles */
|
body {{ font-family: Arial, sans-serif; }}
|
||||||
body {{
|
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; border: 0.5px solid #ccc; border-radius: 5px; background-color: #f9f9f9; }}
|
||||||
font-family: Arial, sans-serif;
|
.button {{ display: inline-block; padding: 10px 20px; background-color: #007bff; color: #ffffff; text-decoration: none; border-radius: 4px; }}
|
||||||
}}
|
.button:hover {{ background-color: #0056b3; }}
|
||||||
|
|
||||||
.container {{
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
border: 0.5px solid #ccc;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}}
|
|
||||||
.button {{
|
|
||||||
display: inline-block;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background-color: #007bff;
|
|
||||||
color: #ffffff;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
}}
|
|
||||||
.button:hover {{
|
|
||||||
background-color: #0056b3;
|
|
||||||
}}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class='container'>
|
<div class='container'>
|
||||||
<h4>Hey {viewModel.Name},</h4>
|
<h4>Hey {userName},</h4>
|
||||||
<h5>{subject}</h5>
|
<h5>{subject}</h5>
|
||||||
<p>Thank you for participating in our survey. Your feedback is valuable to us.</p>
|
<p>Thank you for participating in our survey. Your feedback is valuable to us.</p>
|
||||||
<p>Please click the button below to start the survey:</p>
|
<p>Please click the button below to start the survey:</p>
|
||||||
<p class=""text-danger"">The survey will be expire :{expiryDateTime.ToLongDateString()} Time: {expiryDateTime.ToShortTimeString()} </p>
|
<p class='text-danger'>The survey will expire: {expiryDateTime.ToLongDateString()} Time: {expiryDateTime.ToShortTimeString()}</p>
|
||||||
<div style='text-align: center;'>
|
<div style='text-align: center;'>
|
||||||
<a href='{completeUrl}' class='button'>Start Survey</a>
|
<a href='{completeUrl}' class='button'>Start Survey</a>
|
||||||
<br>
|
|
||||||
|
|
||||||
</div><br>
|
</div><br>
|
||||||
<p><strong>Søren Eggert Lundsteen Olsen</strong><br>
|
<p><strong>Søren Eggert Lundsteen Olsen</strong><br>
|
||||||
Seosoft ApS<br>
|
Seosoft ApS<br>
|
||||||
<hr>
|
<hr>
|
||||||
Hovedgaden 3
|
Hovedgaden 3 Jordrup<br>
|
||||||
Jordrup<br>
|
|
||||||
Kolding 6064<br>
|
Kolding 6064<br>
|
||||||
Denmark</p>
|
Denmark</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>";
|
</html>";
|
||||||
|
|
||||||
|
var emailSend = new EmailToSend(email, subject, emailBody);
|
||||||
// Call the SendConfirmationEmailAsync method to send the email
|
|
||||||
var emailSend = new EmailToSend(toEmail, subject, emailBody);
|
|
||||||
|
|
||||||
bool emailSent = await _emailServices.SendConfirmationEmailAsync(emailSend);
|
bool emailSent = await _emailServices.SendConfirmationEmailAsync(emailSend);
|
||||||
|
if (!emailSent)
|
||||||
if (emailSent)
|
|
||||||
{
|
{
|
||||||
// Email sent successfully
|
allEmailsSent = false;
|
||||||
// You can redirect to a success page or return a success message
|
ModelState.AddModelError(string.Empty, "Failed to send questionnaire via email to: " + email);
|
||||||
TempData["Success"] = "Questionnaire sent successfully";
|
|
||||||
return RedirectToAction(nameof(Index));
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (allEmailsSent)
|
||||||
{
|
{
|
||||||
// Email failed to send
|
TempData["Success"] = "Questionnaire sent successfully to all recipients.";
|
||||||
// You can return an error message or handle it as needed
|
return RedirectToAction(nameof(Index));
|
||||||
ModelState.AddModelError(string.Empty, "Failed to send questionnaire via email.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Model;
|
using Model;
|
||||||
using Web.ViewModel.QuestionnaireVM;
|
using Web.ViewModel.QuestionnaireVM;
|
||||||
using Web.ViewModel.QuestionVM;
|
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Web.Areas.Admin.Controllers
|
namespace Web.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -109,6 +110,35 @@ namespace Web.Areas.Admin.Controllers
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//[HttpGet]
|
||||||
|
//public IActionResult GenerateReport(int id)
|
||||||
|
//{
|
||||||
|
// var viewModel = GetQuestionnaireData(id);
|
||||||
|
// if (viewModel == null)
|
||||||
|
// {
|
||||||
|
// return NotFound("No questionnaire found with the given ID.");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var webReport = new WebReport();
|
||||||
|
|
||||||
|
// // Load your FastReport report design
|
||||||
|
// webReport.Report.Load(Path.Combine(env.WebRootPath, "Reports", "QuestionnaireReport.frx"));
|
||||||
|
|
||||||
|
// // Register the data source
|
||||||
|
// webReport.Report.RegisterData(new[] { viewModel }, "Questionnaire");
|
||||||
|
|
||||||
|
// webReport.Report.Prepare();
|
||||||
|
|
||||||
|
// using (MemoryStream ms = new MemoryStream())
|
||||||
|
// {
|
||||||
|
// webReport.Report.Export(new FastReport.Export.PdfSimple.PDFSimpleExport(), ms);
|
||||||
|
// return File(ms.ToArray(), "application/pdf", "QuestionnaireReport.pdf");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,13 @@
|
||||||
<span asp-validation-for="QuestionnaireId" class="text-danger"></span>
|
<span asp-validation-for="QuestionnaireId" class="text-danger"></span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="Name" class="control-label"></label>
|
<label asp-for="Emails" class="control-label">Email Addresses (separate with commas)</label>
|
||||||
<input asp-for="Name" class="form-control" />
|
<textarea asp-for="Emails" class="form-control"></textarea>
|
||||||
<span asp-validation-for="Name" class="text-danger"></span>
|
<span asp-validation-for="Emails" class="text-danger"></span>
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="Email" class="control-label"></label>
|
|
||||||
<input asp-for="Email" class="form-control" />
|
|
||||||
<span asp-validation-for="Email" class="text-danger"></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="ExpirationDateTime" class="control-label"></label>
|
<label asp-for="ExpirationDateTime" class="control-label"></label>
|
||||||
<input asp-for="ExpirationDateTime" class="form-control"/>
|
<input asp-for="ExpirationDateTime" class="form-control"/>
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,70 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@* @section Scripts {
|
||||||
|
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
google.charts.load('current', { 'packages': ['corechart', 'bar','line','table'] }); // Ensure 'bar' package is loaded if using BarChart
|
||||||
|
google.charts.setOnLoadCallback(drawCharts);
|
||||||
|
|
||||||
|
function drawCharts() {
|
||||||
|
@foreach (var question in Model.Questions.Select((value, index) => new { Value = value, Index = index }))
|
||||||
|
{
|
||||||
|
<text>
|
||||||
|
var type = '@question.Value.Type.ToString()';
|
||||||
|
console.log('Type:', type); // Debug output to check type
|
||||||
|
var chartContainer = document.getElementById('chart_div_@question.Value.Id');
|
||||||
|
var chart; // Declare chart variable here for scope
|
||||||
|
|
||||||
|
var data = new google.visualization.DataTable();
|
||||||
|
data.addColumn('string', 'Option');
|
||||||
|
data.addColumn('number', 'Count');
|
||||||
|
|
||||||
|
</text>
|
||||||
|
if (question.Value.Type == QuestionType.Text || question.Value.Type == QuestionType.Slider || question.Value.Type == QuestionType.Open_ended)
|
||||||
|
{
|
||||||
|
<text>
|
||||||
|
console.log('Initializing Bar Chart'); // Debug output
|
||||||
|
</text>
|
||||||
|
@foreach (var item in question.Value.SelectedText)
|
||||||
|
{
|
||||||
|
<text>
|
||||||
|
data.addRow(['@Html.Raw(item)', @question.Value.Answers]);
|
||||||
|
</text>
|
||||||
|
}
|
||||||
|
<text>
|
||||||
|
chart = new google.visualization.Table(chartContainer);
|
||||||
|
</text>
|
||||||
|
} else {
|
||||||
|
<text>
|
||||||
|
console.log('Initializing Pie Chart'); // Debug output
|
||||||
|
</text>
|
||||||
|
@foreach (var answer in question.Value.Answers)
|
||||||
|
{
|
||||||
|
<text>
|
||||||
|
data.addRow(['@Html.Raw(answer.Text)', @answer.Count]);
|
||||||
|
</text>
|
||||||
|
}
|
||||||
|
<text>
|
||||||
|
chart = new google.visualization.PieChart(chartContainer);
|
||||||
|
</text>
|
||||||
|
}
|
||||||
|
<text>
|
||||||
|
var options = {
|
||||||
|
'title': 'Question @(question.Index + 1): @Html.Raw(question.Value.Text)',
|
||||||
|
is3D: type !== "Text" && type !== "Slider" && type !== "Open_ended", // Only use 3D for Pie Charts
|
||||||
|
'titleTextStyle': { color: '#17a2b8', fontSize: 12, bold: true },
|
||||||
|
'colors': ['#cc0000', '#00cc00', '#0000cc', '#cccc00', '#00cccc', '#cc00cc', '#008080', '#808000', '#800080', '#800000', '#808080', '#404040']
|
||||||
|
};
|
||||||
|
|
||||||
|
chart.draw(data, options); // Draw chart outside conditional blocks
|
||||||
|
</text>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
*@
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
@ -152,7 +216,13 @@
|
||||||
data.addRow(['@Html.Raw(item)', 1]);
|
data.addRow(['@Html.Raw(item)', 1]);
|
||||||
</text>
|
</text>
|
||||||
}
|
}
|
||||||
|
<text>
|
||||||
|
// Initialize Bar Chart for Text, Slider, or Open-ended questions
|
||||||
|
var chart = new google.visualization.BarChart(chartContainer);
|
||||||
|
</text>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@foreach (var answer in question.Value.Answers)
|
@foreach (var answer in question.Value.Answers)
|
||||||
|
|
@ -167,7 +237,9 @@
|
||||||
'title': 'Question @(question.Index + 1): @Html.Raw(question.Value.Text)',
|
'title': 'Question @(question.Index + 1): @Html.Raw(question.Value.Text)',
|
||||||
is3D: true,
|
is3D: true,
|
||||||
'titleTextStyle': { color: '#17a2b8', fontSize: 12, bold: true },
|
'titleTextStyle': { color: '#17a2b8', fontSize: 12, bold: true },
|
||||||
'colors': colors
|
'colors': colors,
|
||||||
|
'pieSliceText': 'percentage', // Display percentages on the chart slices
|
||||||
|
'tooltip': { text: 'percentage' } // Configure tooltip to show percentage
|
||||||
};
|
};
|
||||||
|
|
||||||
if (chartContainer) {
|
if (chartContainer) {
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ namespace Web.ViewModel.QuestionnaireVM
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string? Name { get; set; }
|
public string? Emails { get; set; }
|
||||||
[Required]
|
|
||||||
public string? Email { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[DisplayName("Set expiration date and time for the URL")]
|
[DisplayName("Set expiration date and time for the URL")]
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
|
||||||
<PackageReference Include="OpenAI" Version="1.11.0" />
|
<PackageReference Include="OpenAI" Version="1.11.0" />
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="8.0.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue