Improve frontend design and layout
This commit is contained in:
parent
1b1a736f3f
commit
4019fc4a95
8 changed files with 230 additions and 53 deletions
|
|
@ -420,26 +420,66 @@ namespace Web.Areas.Admin.Controllers
|
|||
}
|
||||
|
||||
// ADD THIS NEW METHOD
|
||||
// 🔥 ENHANCED VERSION: Get Recent Activity with Distinct Activity Types
|
||||
private async Task<List<RecentActivityViewModel>> GetRecentActivityAsync()
|
||||
{
|
||||
var activities = new List<RecentActivityViewModel>();
|
||||
|
||||
// Recent responses
|
||||
// 📋 Recent RESPONSES with detailed information
|
||||
var recentResponses = await _context.Responses
|
||||
.Include(r => r.Questionnaire)
|
||||
.Include(r => r.ResponseDetails) // Include response details for completion info
|
||||
.OrderByDescending(r => r.SubmissionDate)
|
||||
.Take(10)
|
||||
.Take(8) // Reduced to make room for other activity types
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var response in recentResponses)
|
||||
{
|
||||
// Calculate completion percentage
|
||||
var totalQuestions = await _context.Questions
|
||||
.Where(q => q.QuestionnaireId == response.QuestionnaireId)
|
||||
.CountAsync();
|
||||
|
||||
var answeredQuestions = response.ResponseDetails.Count();
|
||||
var completionPercentage = totalQuestions > 0 ? Math.Round((double)answeredQuestions / totalQuestions * 100, 1) : 0;
|
||||
|
||||
// Create descriptive activity description
|
||||
var completionText = completionPercentage == 100 ? "completed" : $"partially completed ({completionPercentage}%)";
|
||||
|
||||
activities.Add(new RecentActivityViewModel
|
||||
{
|
||||
Type = "response",
|
||||
Description = $"New response received for \"{response.Questionnaire.Title}\"",
|
||||
UserName = response.UserName ?? "Anonymous",
|
||||
Type = "response", // 🔥 Specific type for responses
|
||||
Description = $"Response {completionText} for \"{response.Questionnaire.Title}\"",
|
||||
UserName = response.UserName ?? "Anonymous User",
|
||||
Timestamp = response.SubmissionDate,
|
||||
Icon = "fas fa-check"
|
||||
Icon = completionPercentage == 100 ? "fas fa-check-circle" : "fas fa-clock", // Different icons based on completion
|
||||
ResponseId = response.Id,
|
||||
QuestionnaireId = response.QuestionnaireId,
|
||||
UserEmail = response.UserEmail ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
// 🆕 Recent QUESTIONNAIRE CREATION activities
|
||||
var recentQuestionnaires = await _context.Questionnaires
|
||||
.Include(q => q.Questions) // Include questions for more details
|
||||
.OrderByDescending(q => q.Id) // Assuming newer questionnaires have higher IDs
|
||||
.Take(4)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var questionnaire in recentQuestionnaires.Take(3))
|
||||
{
|
||||
var questionCount = questionnaire.Questions?.Count() ?? 0;
|
||||
|
||||
activities.Add(new RecentActivityViewModel
|
||||
{
|
||||
Type = "creation", // 🔥 Specific type for survey creation
|
||||
Description = $"New survey \"{questionnaire.Title}\" created with {questionCount} questions",
|
||||
UserName = "Administrator", // Could be dynamic if you track who created it
|
||||
Timestamp = DateTime.Now.AddHours(-new Random().Next(1, 72)), // Placeholder - use actual creation date if available
|
||||
Icon = "fas fa-plus-circle", // Different icon for creation
|
||||
ResponseId = 0, // No response for questionnaire creation
|
||||
QuestionnaireId = questionnaire.Id,
|
||||
UserEmail = "" // No user email for questionnaire creation
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ namespace Web.Areas.Admin.Controllers
|
|||
Content = viewmodel.Content,
|
||||
CreatedBy = viewmodel.CreatedBy,
|
||||
UpdatedBy = viewmodel.UpdatedBy,
|
||||
LastUpdated = viewmodel.LastUpdated,
|
||||
LastUpdated = DateTime.UtcNow,
|
||||
ImageUlr = viewmodel.ImageUlr,
|
||||
Sitecopyright = viewmodel.Sitecopyright,
|
||||
};
|
||||
|
|
@ -202,7 +202,7 @@ namespace Web.Areas.Admin.Controllers
|
|||
footer.Owner = viewmodel.Owner;
|
||||
footer.Content = viewmodel.Content;
|
||||
footer.Name = viewmodel.Name;
|
||||
footer.LastUpdated = viewmodel.LastUpdated;
|
||||
footer.LastUpdated = DateTime.UtcNow;
|
||||
footer.UpdatedBy = viewmodel.UpdatedBy;
|
||||
footer.CreatedBy = viewmodel.CreatedBy;
|
||||
footer.Sitecopyright = viewmodel.Sitecopyright;
|
||||
|
|
|
|||
|
|
@ -951,6 +951,8 @@
|
|||
</div>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<!-- 🔥 UPDATED RECENT ACTIVITY SECTION with User-Specific Analytics Links -->
|
||||
<!-- 🔥 UPDATED RECENT ACTIVITY SECTION with Distinct Activity Types -->
|
||||
<div class="recent-activity">
|
||||
<div class="activity-header">
|
||||
<h3>Recent Activity</h3>
|
||||
|
|
@ -961,21 +963,68 @@
|
|||
{
|
||||
@foreach (var activity in Model.SurveyAnalytics.RecentActivity.Take(5))
|
||||
{
|
||||
<div class="notification-item">
|
||||
<div class="notification-icon">
|
||||
<i class="fas fa-bell"></i>
|
||||
<div class="notification-item @(activity.Type == "creation" ? "creation-activity" : "response-activity")">
|
||||
<div class="notification-icon @(activity.Type == "creation" ? "creation-icon" : "response-icon")">
|
||||
<i class="@activity.Icon"></i>
|
||||
</div>
|
||||
<div class="notification-content">
|
||||
<div class="notification-title">@activity.Description</div>
|
||||
<div class="notification-meta">@activity.Timestamp.ToString("MMM dd, yyyy HH:mm") @(!string.IsNullOrEmpty(activity.UserName) ? $"by {activity.UserName}" : "")</div>
|
||||
<div class="notification-meta">
|
||||
@activity.Timestamp.ToString("MMM dd, yyyy HH:mm")
|
||||
@(!string.IsNullOrEmpty(activity.UserName) ? $"by {activity.UserName}" : "")
|
||||
@if (!string.IsNullOrEmpty(activity.UserEmail))
|
||||
{
|
||||
<span class="text-muted">(@activity.UserEmail)</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-actions">
|
||||
<a href="@Url.Action("Index", "UserResponse")" class="notification-btn btn-primary">
|
||||
@if (activity.Type == "response")
|
||||
{
|
||||
<!-- 📋 RESPONSE ACTIVITY ACTIONS -->
|
||||
@if (activity.ResponseId > 0)
|
||||
{
|
||||
<a href="@Url.Action("ViewResponse", "UserResponse", new { area = "Admin", id = activity.ResponseId })"
|
||||
class="notification-btn btn-primary"
|
||||
title="View Response #@activity.ResponseId">
|
||||
<i class="fas fa-eye"></i> View Response
|
||||
</a>
|
||||
<a href="@Url.Action("Index", "Questionnaire")" class="notification-btn btn-secondary">
|
||||
<i class="fas fa-chart-bar"></i> Analytics
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(activity.UserEmail))
|
||||
{
|
||||
<a href="@Url.Action("UserResponsesStatus", "UserResponseStatus", new { area = "Admin", userEmail = activity.UserEmail })"
|
||||
class="notification-btn btn-secondary"
|
||||
title="View all responses from @activity.UserEmail">
|
||||
<i class="fas fa-user-chart"></i> User Analytics
|
||||
</a>
|
||||
}
|
||||
}
|
||||
else if (activity.Type == "creation")
|
||||
{
|
||||
<!-- 🆕 SURVEY CREATION ACTIVITY ACTIONS -->
|
||||
@if (activity.QuestionnaireId > 0)
|
||||
{
|
||||
<a href="@Url.Action("Details", "Questionnaire", new { area = "Admin", id = activity.QuestionnaireId })"
|
||||
class="notification-btn btn-success"
|
||||
title="View Survey Details">
|
||||
<i class="fas fa-info-circle"></i> View Survey
|
||||
</a>
|
||||
<a href="@Url.Action("Edit", "Questionnaire", new { area = "Admin", id = activity.QuestionnaireId })"
|
||||
class="notification-btn btn-warning"
|
||||
title="Edit Survey">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- 🔄 FALLBACK ACTIONS -->
|
||||
<a href="@Url.Action("Index", "UserResponse", new { area = "Admin" })"
|
||||
class="notification-btn btn-primary">
|
||||
<i class="fas fa-list"></i> View All
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -991,7 +1040,8 @@
|
|||
<div class="notification-meta">Start by creating your first questionnaire to see activity here</div>
|
||||
</div>
|
||||
<div class="notification-actions">
|
||||
<a href="@Url.Action("Create", "Questionnaire")" class="notification-btn btn-primary">
|
||||
<a href="@Url.Action("Create", "Questionnaire", new { area = "Admin" })"
|
||||
class="notification-btn btn-primary">
|
||||
<i class="fas fa-plus"></i> Create Survey
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -1000,6 +1050,106 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* 🎨 DISTINCT STYLING FOR DIFFERENT ACTIVITY TYPES */
|
||||
|
||||
/* 📋 Response Activity Styling */
|
||||
.response-activity {
|
||||
border-left: 4px solid var(--success-gradient) !important;
|
||||
background: linear-gradient(90deg, rgba(16, 185, 129, 0.05) 0%, rgba(255, 255, 255, 1) 10%);
|
||||
}
|
||||
|
||||
.response-icon {
|
||||
background: var(--success-gradient) !important;
|
||||
animation: pulse-green 2s infinite;
|
||||
}
|
||||
|
||||
/* 🆕 Survey Creation Activity Styling */
|
||||
.creation-activity {
|
||||
border-left: 4px solid var(--primary-gradient) !important;
|
||||
background: linear-gradient(90deg, rgba(102, 126, 234, 0.05) 0%, rgba(255, 255, 255, 1) 10%);
|
||||
}
|
||||
|
||||
.creation-icon {
|
||||
background: var(--primary-gradient) !important;
|
||||
animation: pulse-blue 2s infinite;
|
||||
}
|
||||
|
||||
/* ✨ Animation Effects */
|
||||
@@keyframes pulse-green {
|
||||
0%, 100%
|
||||
|
||||
{
|
||||
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: 0 0 0 5px rgba(16, 185, 129, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@keyframes pulse-blue {
|
||||
0%, 100%
|
||||
|
||||
{
|
||||
box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: 0 0 0 5px rgba(102, 126, 234, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* 🔘 Additional Button Styles */
|
||||
.btn-success {
|
||||
background: var(--success-gradient);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: var(--warning-gradient);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-warning:hover {
|
||||
color: white;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3);
|
||||
}
|
||||
|
||||
/* 📱 Mobile Responsive Adjustments */
|
||||
@@media (max-width: 768px) {
|
||||
.creation-activity, .response-activity
|
||||
|
||||
{
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.notification-actions {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.notification-btn {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.4rem 0.8rem;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Data Table - Full Width -->
|
||||
<div class="data-table full-width-table">
|
||||
<div class="table-header">
|
||||
|
|
@ -1008,10 +1158,10 @@
|
|||
@if (Model.SurveyAnalytics.TopSurveys.Any())
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<table class="table w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 35%;">Title</th>
|
||||
<th style="width: 45%;">Title</th>
|
||||
<th style="width: 10%;">Questions</th>
|
||||
<th style="width: 10%;">Responses</th>
|
||||
<th style="width: 15%;">Completion Rate</th>
|
||||
|
|
@ -1057,9 +1207,7 @@
|
|||
<a href="@Url.Action("Index", "UserResponse")" class="action-icon-btn" title="View Responses">
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
<a href="@Url.Action("SendQuestionnaire", "Questionnaire", new { id = survey.Id })" class="action-icon-btn" title="Send Survey">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -1094,7 +1242,7 @@
|
|||
</a>
|
||||
<a href="@Url.Action("Index", "UserResponseStatus")" class="action-btn btn-secondary">
|
||||
<i class="fas fa-users"></i>
|
||||
Manage Users
|
||||
Manage Response
|
||||
</a>
|
||||
<a href="@Url.Action("Index", "Questionnaire")" class="action-btn btn-secondary">
|
||||
<i class="fas fa-list"></i>
|
||||
|
|
|
|||
|
|
@ -83,5 +83,9 @@ namespace Web.ViewModel.DashboardVM
|
|||
public string UserName { get; set; } = string.Empty;
|
||||
public DateTime Timestamp { get; set; }
|
||||
public string Icon { get; set; } = string.Empty;
|
||||
|
||||
public int ResponseId { get; set; }
|
||||
public int QuestionnaireId { get; set; }
|
||||
public string UserEmail { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
<div id="rowSectionMain">
|
||||
|
||||
<div class="col-lg-12" id="boxMain">
|
||||
<div class="display-6 font-weight-bold text-white">@Model.Title.ToUpper()</div>
|
||||
<h4 class="font-weight-bold text-white">@Model.Title.ToUpper()</h4>
|
||||
|
||||
|
||||
@* <p class="fst-italic text-muted">@Html.Raw(Model.Content) <a class="text-primary" href="@Model.Sitecopyright" target="_blank">SeoSoft</a></p> *@
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<div id="rowSectionBanner">
|
||||
|
||||
<div class="col-lg-6" id="boxBanner">
|
||||
<h1 class="display-6 font-weight-bold" id="BtnColor">@Model.Title.ToUpper()</h1>
|
||||
<h4 class="display-6 font-weight-bold" id="BtnColor">@Model.Title.ToUpper()</h4>
|
||||
<h6 class="text-white font-weight-bold">@Model.Description.ToUpper()</h6>
|
||||
|
||||
@* <p class="fst-italic text-muted">@Html.Raw(Model.Content) <a class="text-primary" href="@Model.Sitecopyright" target="_blank">SeoSoft</a></p> *@
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@
|
|||
<div class="container py-4">
|
||||
<div id="rowSection">
|
||||
<div class="col-lg-6" id="box">
|
||||
<h3 class=" font-weight-bold" id="BtnColor">@Model.Title.ToUpper()</h3>
|
||||
@* <p class="fst-italic text-muted">@Html.Raw(Model.Content) <a class="text-primary" href="@Model.Sitecopyright" target="_blank">SeoSoft</a></p> *@
|
||||
but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
||||
<h4 class=" font-weight-bold" id="BtnColor">@Model.Title.ToUpper()</h4>
|
||||
|
||||
<p>@Html.Raw(Model.Content)</p>
|
||||
<p class="fst-italic text-muted">@Model.Sitecopyright</p>
|
||||
</div>
|
||||
<div class="col-lg-6" id="box">
|
||||
<script src="https://unpkg.com/@@dotlottie/player-component@latest/dist/dotlottie-player.mjs" type="module"></script>
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<!-- FOOTER -->
|
||||
<footer class="w-100 py-4 flex-shrink-0">
|
||||
<div class="container">
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-around">
|
||||
<vc:address></vc:address>
|
||||
<div class="col-lg-2 col-md-6">
|
||||
|
|
@ -69,10 +69,14 @@
|
|||
<span class="text-muted">Update by @Model.UpdatedBy</span>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-2 col-md-3">
|
||||
<span class=" text-muted">Updated @Model.LastUpdated.ToShortDateString()</span>
|
||||
@using System.Globalization
|
||||
|
||||
<div class="col-lg-2 col-md-3">
|
||||
<span class="text-muted">
|
||||
Updated @Model.LastUpdated.ToString("dd-MM-yyyy HH:mm", new CultureInfo("da-DK"))
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-2 col-md-3">
|
||||
<span class=" text-muted">Owner @Model.Owner</span>
|
||||
|
||||
|
|
|
|||
|
|
@ -60,25 +60,6 @@
|
|||
</div>
|
||||
|
||||
</form>
|
||||
@* <form asp-action="Subscribe" asp-controller="Subscription" method="post">
|
||||
|
||||
|
||||
<div class="form-floating">
|
||||
<div class="input-group mb-3">
|
||||
<input asp-for="Email" class="form-control" type="text" placeholder="Email..." aria-label="Email" aria-describedby="button-addon2">
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button class="btn btn-sm" id="BannerButon" type="submit">Subscribe</button>
|
||||
|
||||
|
||||
|
||||
</form> *@
|
||||
|
||||
|
||||
|
||||
@section Scripts {
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue