diff --git a/Web/Areas/Admin/Controllers/AdminController.cs b/Web/Areas/Admin/Controllers/AdminController.cs index 2fa02ed..ff6415e 100644 --- a/Web/Areas/Admin/Controllers/AdminController.cs +++ b/Web/Areas/Admin/Controllers/AdminController.cs @@ -10,7 +10,7 @@ using Web.ViewModel.DashboardVM; namespace Web.Areas.Admin.Controllers { - [Authorize(Roles = "Admin")] + [Authorize(Roles = "Admin,Demo")] public class AdminController : Controller { private readonly SignInManager _signInManager; diff --git a/Web/Areas/Admin/Controllers/FooterController.cs b/Web/Areas/Admin/Controllers/FooterController.cs index c226f54..1f1e6ba 100644 --- a/Web/Areas/Admin/Controllers/FooterController.cs +++ b/Web/Areas/Admin/Controllers/FooterController.cs @@ -15,7 +15,8 @@ using static Microsoft.EntityFrameworkCore.DbLoggerCategory; namespace Web.Areas.Admin.Controllers { - + + public class FooterController : Controller { private readonly IFooterRepository _footer; diff --git a/Web/Areas/Admin/Controllers/NewslettersController.cs b/Web/Areas/Admin/Controllers/NewslettersController.cs index 9b7f4c4..f09c2af 100644 --- a/Web/Areas/Admin/Controllers/NewslettersController.cs +++ b/Web/Areas/Admin/Controllers/NewslettersController.cs @@ -77,6 +77,7 @@ namespace Web.Areas.Admin.Controllers return View(listViewModel); } + [Authorize(Roles = "Admin")] public IActionResult Create() { var totalSubscribedUsers = _context.Subscriptions.Count(s => s.IsSubscribed); @@ -102,7 +103,7 @@ namespace Web.Areas.Admin.Controllers } - + [Authorize(Roles = "Admin")] [HttpPost] public async Task Create(SendNewsLetterViewModel viewModel) { diff --git a/Web/Areas/Admin/Controllers/QuestionnaireController.cs b/Web/Areas/Admin/Controllers/QuestionnaireController.cs index 3d06988..3803768 100644 --- a/Web/Areas/Admin/Controllers/QuestionnaireController.cs +++ b/Web/Areas/Admin/Controllers/QuestionnaireController.cs @@ -97,6 +97,7 @@ namespace Web.Areas.Admin.Controllers }); } [HttpGet] + [Authorize(Roles = "Admin")] public IActionResult Create() @@ -120,6 +121,7 @@ namespace Web.Areas.Admin.Controllers return View(questionnaire); } [HttpPost] + [Authorize(Roles = "Admin")] public async Task Create(QuestionnaireViewModel viewmodel) { if (ModelState.IsValid) @@ -172,6 +174,7 @@ namespace Web.Areas.Admin.Controllers } [HttpGet] + [Authorize(Roles = "Admin")] public IActionResult Edit(int id) { var questionTypes = Enum.GetValues(typeof(QuestionType)) @@ -214,7 +217,7 @@ namespace Web.Areas.Admin.Controllers return View(viewModel); } - + [Authorize(Roles = "Admin")] [HttpPost] public async Task Edit(EditQuestionnaireViewModel viewModel) { @@ -510,6 +513,7 @@ namespace Web.Areas.Admin.Controllers } } [HttpGet] + [Authorize(Roles = "Admin")] public IActionResult Delete(int id) { var questionTypes = Enum.GetValues(typeof(QuestionType)).Cast(); @@ -830,6 +834,7 @@ namespace Web.Areas.Admin.Controllers // Add these methods to your existing QuestionnaireController class [HttpGet] + [Authorize(Roles = "Admin")] public IActionResult SetLogic(int id) { var questionnaire = _questionnaire.GetQuestionnaireWithQuestionAndAnswer(id); diff --git a/Web/Areas/Admin/Controllers/UsersController.cs b/Web/Areas/Admin/Controllers/UsersController.cs index 8054c27..3b52469 100644 --- a/Web/Areas/Admin/Controllers/UsersController.cs +++ b/Web/Areas/Admin/Controllers/UsersController.cs @@ -8,7 +8,8 @@ using Web.ViewModel.AccountVM; namespace Web.Areas.Admin.Controllers { - + + [Authorize(Roles = "Admin")] public class UsersController : Controller { private readonly UserManager _userManager; diff --git a/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml b/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml index 0740708..c93ad64 100644 --- a/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml +++ b/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml @@ -9,54 +9,419 @@ + + +
-
- -
- + +
+ @RenderBody() -
- - +
+ + - + @@ -221,99 +585,10 @@ - @* *@ - - - @await RenderSectionAsync("Scripts", required: false) @await RenderSectionAsync("Styles", required: false) - + - + \ No newline at end of file diff --git a/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml.css b/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml.css index c187c02..5f28270 100644 --- a/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml.css +++ b/Web/Areas/Admin/Views/Shared/_AdminLayout.cshtml.css @@ -1,48 +1 @@ -/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification -for details on configuring this project to bundle and minify static web assets. */ - -a.navbar-brand { - white-space: normal; - text-align: center; - word-break: break-all; -} - -a { - color: #0077cc; -} - -.btn-primary { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; -} - -.nav-pills .nav-link.active, .nav-pills .show > .nav-link { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; -} - -.border-top { - border-top: 1px solid #e5e5e5; -} -.border-bottom { - border-bottom: 1px solid #e5e5e5; -} - -.box-shadow { - box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); -} - -button.accept-policy { - font-size: 1rem; - line-height: inherit; -} - -.footer { - position: absolute; - bottom: 0; - width: 100%; - white-space: nowrap; - line-height: 60px; -} + \ No newline at end of file diff --git a/Web/Controllers/DemoRequestController.cs b/Web/Controllers/DemoRequestController.cs new file mode 100644 index 0000000..e90eb62 --- /dev/null +++ b/Web/Controllers/DemoRequestController.cs @@ -0,0 +1,444 @@ +using Data; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Model; +using Services.EmailSend; +using Services.Interaces; +using System.Text; + +namespace Web.Controllers +{ + public class DemoRequestController : Controller + { + private readonly UserManager _userManager; + private readonly SurveyContext _context; + private readonly IEmailServices _emailService; + + public DemoRequestController(UserManager userManager, SurveyContext context, IEmailServices emailService) + { + _userManager = userManager; + _context = context; + _emailService = emailService; + } + + // Danish Demo Endpoint + [HttpPost] + public async Task RequestDemo([FromBody] DemoRequest request) + { + return await CreateDemoAccount(request, "da"); + } + + // English Demo Endpoint + [HttpPost] + public async Task RequestDemoEN([FromBody] DemoRequest request) + { + return await CreateDemoAccount(request, "en"); + } + + // Shared logic for both languages + private async Task CreateDemoAccount(DemoRequest request, string language) + { + try + { + // Validate email + if (string.IsNullOrEmpty(request.Email)) + { + var errorMsg = language == "da" ? "Email er påkrævet" : "Email is required"; + return Json(new { success = false, message = errorMsg }); + } + + // Check if user already exists + var existingUser = await _userManager.FindByEmailAsync(request.Email); + if (existingUser != null) + { + var existsMsg = language == "da" ? "Email allerede i brug" : "Email already in use"; + return Json(new { success = false, message = existsMsg }); + } + + // Generate password + string demoPassword = GeneratePassword(); + + // Create demo user + var demoUser = new ApplicationUser + { + UserName = request.Email, + Email = request.Email, + EmailConfirmed = true, + FirstName = "Demo", + LastName = "User" + }; + + // Create user + var result = await _userManager.CreateAsync(demoUser, demoPassword); + + if (result.Succeeded) + { + // Assign Demo role + await _userManager.AddToRoleAsync(demoUser, "Demo"); + + // Send email with credentials (language-specific) + bool emailSent = await SendDemoCredentialsEmail(demoUser.Email, demoPassword, language); + + if (emailSent) + { + var successMsg = language == "da" + ? "Demo konto oprettet! Check din email for login detaljer." + : "Demo account created! Check your email for login details."; + return Json(new { success = true, message = successMsg }); + } + else + { + var emailErrorMsg = language == "da" + ? "Demo konto oprettet, men email kunne ikke sendes. Kontakt support." + : "Demo account created, but email could not be sent. Please contact support."; + return Json(new { success = true, message = emailErrorMsg }); + } + } + else + { + var errors = string.Join(", ", result.Errors.Select(e => e.Description)); + var userErrorMsg = language == "da" + ? "Fejl ved oprettelse af bruger: " + errors + : "Error creating user: " + errors; + return Json(new { success = false, message = userErrorMsg }); + } + } + catch (Exception ex) + { + var exceptionMsg = language == "da" ? "Fejl: " + ex.Message : "Error: " + ex.Message; + return Json(new { success = false, message = exceptionMsg }); + } + } + + // Language-aware email sending + private async Task SendDemoCredentialsEmail(string email, string password, string language) + { + if (language == "da") + { + return await SendDanishDemoEmail(email, password); + } + else + { + return await SendEnglishDemoEmail(email, password); + } + } + + // Danish email template + private async Task SendDanishDemoEmail(string email, string password) + { + var subject = "🚀 Din A-Survey Demo Konto er Klar!"; + + var htmlBody = $@" + + + + + + + +
+
+

🎯 Velkommen til A-Survey Demo!

+

Din demo konto er klar til brug med fulde AI-funktioner

+
+ +
+

🔐 Login Detaljer

+ +
+

📧 Email: {email}

+

🔑 Adgangskode: {password}

+
+ + + +
+

📅 Demo Information:

+
    +
  • Demo periode: 14 dage fra i dag
  • +
  • 🧠 AI-funktioner: Alle tilgængelige
  • +
  • 📊 Analytics: Komplet dashboard inkluderet
  • +
  • 📋 Surveys: Ubegrænset oprettelse
  • +
  • 🔄 Opgradering: Kan opgraderes til fuld version
  • +
+
+ +
+

💡 Kom Hurtigt i Gang:

+
    +
  1. Klik på login linket ovenfor
  2. +
  3. Indtast din email og adgangskode
  4. +
  5. Udforsk de forudindlæste demo surveys
  6. +
  7. Test AI-analyse funktionerne
  8. +
  9. Opret dine egne surveys
  10. +
+
+ +

Har du spørgsmål eller brug for hjælp?
+ Kontakt os på: info@seosoft.dk
+ Telefon: +45 61 77 73 36

+
+ + +
+ +"; + + try + { + var emailToSend = new EmailToSend(email, subject, htmlBody); + return await _emailService.SendConfirmationEmailAsync(emailToSend); + } + catch (Exception ex) + { + return false; + } + } + + // English email template + private async Task SendEnglishDemoEmail(string email, string password) + { + var subject = "🚀 Your A-Survey Demo Account is Ready!"; + + var htmlBody = $@" + + + + + + + +
+
+

🎯 Welcome to A-Survey Demo!

+

Your demo account is ready with full AI capabilities

+
+ +
+

🔐 Login Credentials

+ +
+

📧 Email: {email}

+

🔑 Password: {password}

+
+ + + +
+

📅 Demo Information:

+
    +
  • Demo period: 14 days from today
  • +
  • 🧠 AI features: All available
  • +
  • 📊 Analytics: Complete dashboard included
  • +
  • 📋 Surveys: Unlimited creation
  • +
  • 🔄 Upgrade: Convert to full version anytime
  • +
+
+ +
+

💡 Quick Start Guide:

+
    +
  1. Click the login button above
  2. +
  3. Enter your email and password
  4. +
  5. Explore the pre-loaded demo surveys
  6. +
  7. Test the AI analysis features
  8. +
  9. Create your own surveys
  10. +
+
+ +

Questions or need help?
+ Contact us at: info@seosoft.dk
+ Phone: +45 61 77 73 36

+
+ + +
+ +"; + + try + { + var emailToSend = new EmailToSend(email, subject, htmlBody); + return await _emailService.SendConfirmationEmailAsync(emailToSend); + } + catch (Exception ex) + { + return false; + } + } + + private string GeneratePassword() + { + var random = new Random(); + + // Ensure password meets all ASP.NET Identity requirements + var digits = "0123456789"; + var lowercase = "abcdefghijklmnopqrstuvwxyz"; + var uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + var special = "!@#$%"; + + // Guarantee at least one of each required type + var password = new StringBuilder(); + password.Append(digits[random.Next(digits.Length)]); // At least 1 digit + password.Append(lowercase[random.Next(lowercase.Length)]); // At least 1 lowercase + password.Append(uppercase[random.Next(uppercase.Length)]); // At least 1 uppercase + password.Append(special[random.Next(special.Length)]); // At least 1 special char + + // Fill the rest with random characters + var allChars = digits + lowercase + uppercase + special; + for (int i = 0; i < 4; i++) // Add 4 more random chars + { + password.Append(allChars[random.Next(allChars.Length)]); + } + + // Shuffle the password to randomize position of guaranteed chars + var passwordArray = password.ToString().ToCharArray(); + for (int i = passwordArray.Length - 1; i > 0; i--) + { + int j = random.Next(i + 1); + (passwordArray[i], passwordArray[j]) = (passwordArray[j], passwordArray[i]); + } + + return $"Demo{new string(passwordArray)}"; + } + } + + public class DemoRequest + { + public string Email { get; set; } + } +} \ No newline at end of file diff --git a/Web/Program.cs b/Web/Program.cs index 1d27498..528146e 100644 --- a/Web/Program.cs +++ b/Web/Program.cs @@ -41,7 +41,15 @@ builder.Services.AddSignalR(); builder.Services.ConfigureAIAnalysis(); - +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowSeoSoft", policy => + { + policy.WithOrigins("https://seosoft.dk", "https://unabused-terina-wavier.ngrok-free.dev") + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); var app = builder.Build(); @@ -65,7 +73,7 @@ app.UseAuthorization(); app.MapRazorPages(); app.MapControllers(); - +app.UseCors("AllowSeoSoft"); app.MapControllerRoute( name: "page", diff --git a/Web/Views/Account/Login.cshtml b/Web/Views/Account/Login.cshtml index 818c9cc..44c1512 100644 --- a/Web/Views/Account/Login.cshtml +++ b/Web/Views/Account/Login.cshtml @@ -1,192 +1,473 @@ @model Web.ViewModel.AccountVM.LoginViewModel - @{ ViewData["Title"] = "Login"; Layout = "~/Views/Shared/_LoginLayout.cshtml"; } - -
+ +
+
+
+
+
- +
- +
-
@section Scripts { diff --git a/Web/Views/Home/Index.cshtml b/Web/Views/Home/Index.cshtml index 0a1e12b..49f5129 100644 --- a/Web/Views/Home/Index.cshtml +++ b/Web/Views/Home/Index.cshtml @@ -3,38 +3,199 @@ ViewData["Title"] = "Home Page"; } + -
-
- @*
-

@Model.Description.ToUpper()

-
*@ -
+
-
-

@Model.Title.ToUpper()

- +
+
+
+
+
- @*

@Html.Raw(Model.Content) SeoSoft

*@ -

@Html.Raw(Model.Content)

- Contact - -
- -
+
+
+

@Model.Title.ToUpper()

+

@Html.Raw(Model.Content)

+ Contact
-
- - - + + @section Scripts { - - @{ } -} +} \ No newline at end of file diff --git a/Web/Views/Shared/Components/Address/Default.cshtml b/Web/Views/Shared/Components/Address/Default.cshtml index a29bc3a..9f6ce4f 100644 --- a/Web/Views/Shared/Components/Address/Default.cshtml +++ b/Web/Views/Shared/Components/Address/Default.cshtml @@ -4,7 +4,7 @@ -
+
Address
@foreach (var item in Model.Take(1)) { @@ -20,7 +20,7 @@

}
-
+
Contact
@foreach (var item in Model.Take(1)) { diff --git a/Web/Views/Shared/Components/Banner/Default.cshtml b/Web/Views/Shared/Components/Banner/Default.cshtml index 753816e..a8411e5 100644 --- a/Web/Views/Shared/Components/Banner/Default.cshtml +++ b/Web/Views/Shared/Components/Banner/Default.cshtml @@ -1,31 +1,446 @@ @model Banner -
+ + +
+
+ +
+
+
+
+
+ +
+
+
+

+ @Model.Title.ToUpper() + @Model.Description.ToUpper() +

+ +
+ +
+ @Html.Raw(Model.Content) +
+
- + +
+
- + +
- -
- - +
\ No newline at end of file diff --git a/Web/Views/Shared/Components/Footer/Default.cshtml b/Web/Views/Shared/Components/Footer/Default.cshtml index 43f8605..b6175f7 100644 --- a/Web/Views/Shared/Components/Footer/Default.cshtml +++ b/Web/Views/Shared/Components/Footer/Default.cshtml @@ -1,97 +1,524 @@ @model Footer +
- - -
-
-
-
-

@Model.Title.ToUpper()

- -

@Html.Raw(Model.Content)

-

@Model.Sitecopyright

-
-
- - -
+ +
+
+
+

@Model.Title.ToUpper()

+

@Html.Raw(Model.Content)

+

@Model.Sitecopyright

+
+
+ + +
- -