From d3069246ebaec771a1b5c530e0bc0735ca624c88 Mon Sep 17 00:00:00 2001 From: Engincan VESKE Date: Mon, 16 Aug 2021 15:28:40 +0300 Subject: [PATCH] Admin: List events in event-management page --- .../Events/EventDetailDto.cs | 12 ++ .../Events/EventInListDto.cs | 16 ++ .../Events/EventListFilterDto.cs | 17 +++ .../Events/IEventAppService.cs | 16 ++ .../Events/UpdateEventDto.cs | 17 +++ .../EventHubPermissionDefinitionProvider.cs | 3 + .../Permissions/EventHubPermissions.cs | 6 + .../EventHubApplicationAutoMapperProfile.cs | 4 + .../Events/EventAppService.cs | 83 +++++++++++ .../Controllers/Events/EventController.cs | 45 ++++++ .../EventHubBlazorAutoMapperProfile.cs | 3 + .../Menus/EventHubMenuContributor.cs | 17 +++ src/EventHub.Admin.Web/Menus/EventHubMenus.cs | 6 + .../Pages/EventManagement.razor | 138 ++++++++++++++++++ .../Pages/EventManagement.razor.cs | 110 ++++++++++++++ .../Events/EventConsts.cs | 2 + .../Localization/EventHub/en.json | 13 +- 17 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 src/EventHub.Admin.Application.Contracts/Events/EventDetailDto.cs create mode 100644 src/EventHub.Admin.Application.Contracts/Events/EventInListDto.cs create mode 100644 src/EventHub.Admin.Application.Contracts/Events/EventListFilterDto.cs create mode 100644 src/EventHub.Admin.Application.Contracts/Events/IEventAppService.cs create mode 100644 src/EventHub.Admin.Application.Contracts/Events/UpdateEventDto.cs create mode 100644 src/EventHub.Admin.Application/Events/EventAppService.cs create mode 100644 src/EventHub.Admin.HttpApi.Host/Controllers/Events/EventController.cs create mode 100644 src/EventHub.Admin.Web/Pages/EventManagement.razor create mode 100644 src/EventHub.Admin.Web/Pages/EventManagement.razor.cs diff --git a/src/EventHub.Admin.Application.Contracts/Events/EventDetailDto.cs b/src/EventHub.Admin.Application.Contracts/Events/EventDetailDto.cs new file mode 100644 index 0000000..a6d5f32 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Events/EventDetailDto.cs @@ -0,0 +1,12 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Events +{ + public class EventDetailDto : EntityDto + { + public string Title { get; set; } + + public DateTime StartTime { get; set; } + } +} diff --git a/src/EventHub.Admin.Application.Contracts/Events/EventInListDto.cs b/src/EventHub.Admin.Application.Contracts/Events/EventInListDto.cs new file mode 100644 index 0000000..c4d148d --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Events/EventInListDto.cs @@ -0,0 +1,16 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Events +{ + public class EventInListDto : EntityDto + { + public string Title { get; set; } + + public string OrganizationDisplayName { get; set; } + + public int AttendeeCount { get; set; } + + public DateTime StartTime { get; set; } + } +} diff --git a/src/EventHub.Admin.Application.Contracts/Events/EventListFilterDto.cs b/src/EventHub.Admin.Application.Contracts/Events/EventListFilterDto.cs new file mode 100644 index 0000000..69758a5 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Events/EventListFilterDto.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Events +{ + public class EventListFilterDto : PagedAndSortedResultRequestDto + { + public DateTime? StartTime { get; set; } + + public string Title { get; set; } + + public string OrganizationDisplayName { get; set; } + + public int? MinAttendeeCount { get; set; } + public int? MaxAttendeeCount { get; set; } + } +} diff --git a/src/EventHub.Admin.Application.Contracts/Events/IEventAppService.cs b/src/EventHub.Admin.Application.Contracts/Events/IEventAppService.cs new file mode 100644 index 0000000..70bd7d5 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Events/IEventAppService.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace EventHub.Admin.Events +{ + public interface IEventAppService : IApplicationService + { + Task> GetListAsync(EventListFilterDto input); + + Task GetAsync(Guid id); + + Task UpdateAsync(Guid id, UpdateEventDto input); + } +} diff --git a/src/EventHub.Admin.Application.Contracts/Events/UpdateEventDto.cs b/src/EventHub.Admin.Application.Contracts/Events/UpdateEventDto.cs new file mode 100644 index 0000000..5b8f661 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Events/UpdateEventDto.cs @@ -0,0 +1,17 @@ +using System; +using System.ComponentModel.DataAnnotations; +using EventHub.Events; + +namespace EventHub.Admin.Events +{ + public class UpdateEventDto + { + [Required] + [StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)] + public string Title { get; set; } + + [Required] + [DataType(DataType.DateTime)] + public DateTime StartTime { get; set; } + } +} diff --git a/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs b/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs index b9790b9..d95d637 100644 --- a/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs +++ b/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs @@ -15,6 +15,9 @@ namespace EventHub.Admin.Permissions organizationsPermission.AddChild(EventHubPermissions.Organizations.Update, L("Permission:Edit")); organizationsPermission.AddChild(EventHubPermissions.Organizations.Delete, L("Permission:Delete")); organizationsPermission.AddChild(EventHubPermissions.Organizations.Memberships.Default, L("Permission:MembershipManagement")); + + var eventPermissions = eventHubGroup.AddPermission(EventHubPermissions.Events.Default, L("Permission:EventManagement")); + eventPermissions.AddChild(EventHubPermissions.Events.Update, L("Permission:Edit")); } private static LocalizableString L(string name) diff --git a/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs b/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs index a1003bc..0d30b60 100644 --- a/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs +++ b/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs @@ -16,5 +16,11 @@ public const string Default = Organizations.Default + ".Memberships"; } } + + public static class Events + { + public const string Default = GroupName + ".Events"; + public const string Update = Default + ".Update"; + } } } \ No newline at end of file diff --git a/src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs b/src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs index b11a97c..9eddbdb 100644 --- a/src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs +++ b/src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs @@ -1,6 +1,8 @@ using AutoMapper; +using EventHub.Admin.Events; using EventHub.Admin.Organizations; using EventHub.Admin.Organizations.Memberships; +using EventHub.Events; using EventHub.Organizations; using EventHub.Organizations.Memberships; using Volo.Abp.AutoMapper; @@ -19,6 +21,8 @@ namespace EventHub.Admin .Ignore(x => x.ProfilePictureContent); CreateMap(); + + CreateMap(); } } } diff --git a/src/EventHub.Admin.Application/Events/EventAppService.cs b/src/EventHub.Admin.Application/Events/EventAppService.cs new file mode 100644 index 0000000..331f4bc --- /dev/null +++ b/src/EventHub.Admin.Application/Events/EventAppService.cs @@ -0,0 +1,83 @@ +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using EventHub.Events; +using EventHub.Events.Registrations; +using EventHub.Organizations; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Domain.Repositories; + +namespace EventHub.Admin.Events +{ + //[Authorize(EventHubPermissions.Events.Default)] + public class EventAppService : EventHubAdminAppService, IEventAppService + { + private readonly IRepository _eventRepository; + private readonly IRepository _eventRegistrationRepository; + private readonly IRepository _organizationRepository; + + public EventAppService( + IRepository eventRepository, + IRepository eventRegistrationRepository, + IRepository organizationRepository + ) + { + _eventRepository = eventRepository; + _eventRegistrationRepository = eventRegistrationRepository; + _organizationRepository = organizationRepository; + } + + public async Task GetAsync(Guid id) + { + var @event = await _eventRepository.GetAsync(id); + + return ObjectMapper.Map(@event); + } + + public async Task> GetListAsync(EventListFilterDto input) + { + var eventQueryable = await _eventRepository.GetQueryableAsync(); + var eventRegistrationQueryable = await _eventRegistrationRepository.GetQueryableAsync(); + var organizationQueryable = await _organizationRepository.GetQueryableAsync(); + + var query = (from @event in eventQueryable + join organization in organizationQueryable on @event.OrganizationId equals organization.Id + select new EventInListDto + { + Id = @event.Id, + Title = @event.Title, + StartTime = @event.StartTime, + OrganizationDisplayName = organization.DisplayName, + AttendeeCount = (from eventRegistration in eventRegistrationQueryable + where eventRegistration.EventId == @event.Id + group @event by @event.Id into g + select g.Key).Count() + }) + .WhereIf(!string.IsNullOrWhiteSpace(input.Title), x => x.Title.ToLower().Contains(input.Title.ToLower())) + .WhereIf(!string.IsNullOrWhiteSpace(input.OrganizationDisplayName), x => x.OrganizationDisplayName.ToLower().Contains(input.OrganizationDisplayName.ToLower())) + .WhereIf(input.StartTime.HasValue, x => x.StartTime > input.StartTime) + .WhereIf(input.MinAttendeeCount.HasValue, x => x.AttendeeCount >= input.MinAttendeeCount) + .WhereIf(input.MaxAttendeeCount.HasValue, x => x.AttendeeCount <= input.MaxAttendeeCount); + + var totalCount = await AsyncExecuter.CountAsync(query); + query = query.OrderBy(string.IsNullOrWhiteSpace(input.Sorting) ? EventConsts.DefaultSorting : input.Sorting); + query = query.PageBy(input); + + var events = await AsyncExecuter.ToListAsync(query); + + return new PagedResultDto(totalCount, events); + } + + //[Authorize(EventHubPermissions.Events.Update)] + public async Task UpdateAsync(Guid id, UpdateEventDto input) + { + var @event = await _eventRepository.GetAsync(id); + + @event.SetTitle(input.Title); + @event.SetTime(input.StartTime, @event.EndTime); + + await _eventRepository.UpdateAsync(@event); + } + } +} diff --git a/src/EventHub.Admin.HttpApi.Host/Controllers/Events/EventController.cs b/src/EventHub.Admin.HttpApi.Host/Controllers/Events/EventController.cs new file mode 100644 index 0000000..bc8e4ab --- /dev/null +++ b/src/EventHub.Admin.HttpApi.Host/Controllers/Events/EventController.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EventHub.Admin.Events; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace EventHub.Admin.Controllers.Events +{ + [RemoteService(Name = EventHubAdminRemoteServiceConsts.RemoteServiceName)] + [Controller] + [Area("eventhub-admin")] + [ControllerName("Event")] + [Route("/api/eventhub/admin/event")] + public class EventController : AbpController, IEventAppService + { + private readonly IEventAppService _eventAppService; + + public EventController(IEventAppService eventAppService) + { + _eventAppService = eventAppService; + } + + [HttpGet("{id}")] + public Task GetAsync(Guid id) + { + return _eventAppService.GetAsync(id); + } + + [HttpGet] + public Task> GetListAsync(EventListFilterDto input) + { + return _eventAppService.GetListAsync(input); + } + + [HttpPut] + public Task UpdateAsync(Guid id, UpdateEventDto input) + { + return _eventAppService.UpdateAsync(id, input); + } + } +} diff --git a/src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs b/src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs index 53cffe5..f55858c 100644 --- a/src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs +++ b/src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs @@ -1,4 +1,5 @@ using AutoMapper; +using EventHub.Admin.Events; using EventHub.Admin.Organizations; namespace EventHub.Admin.Web @@ -8,6 +9,8 @@ namespace EventHub.Admin.Web public EventHubBlazorAutoMapperProfile() { CreateMap(); + + CreateMap(); } } } \ No newline at end of file diff --git a/src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs b/src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs index 1aa0481..0bd262e 100644 --- a/src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs +++ b/src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs @@ -48,6 +48,7 @@ namespace EventHub.Admin.Web.Menus ); await AddOrganizationMenu(context, l); + await AddEventMenu(context, l); } private Task ConfigureUserMenuAsync(MenuConfigurationContext context) @@ -87,5 +88,21 @@ namespace EventHub.Admin.Web.Menus return Task.CompletedTask; } + + private Task AddEventMenu(MenuConfigurationContext context, IStringLocalizer l) + { + var eventMenu = new ApplicationMenuItem( + EventHubMenus.EventManagement.GroupName, + l["Menu:EventManagement"], + icon: "fas fa-calendar-minus" + ); + + context.Menu.Items.Insert(3, eventMenu); + + eventMenu.AddItem(new ApplicationMenuItem(EventHubMenus.EventManagement.Events, displayName: l["Menu:Events"], url: "/events")) + .RequirePermissions(EventHubPermissions.Events.Default); + + return Task.CompletedTask; + } } } \ No newline at end of file diff --git a/src/EventHub.Admin.Web/Menus/EventHubMenus.cs b/src/EventHub.Admin.Web/Menus/EventHubMenus.cs index 66562a1..1e6f30b 100644 --- a/src/EventHub.Admin.Web/Menus/EventHubMenus.cs +++ b/src/EventHub.Admin.Web/Menus/EventHubMenus.cs @@ -11,5 +11,11 @@ public const string Organizations = GroupName + ".Organizations"; public const string OrganizationMemberships = GroupName + ".OrganizationMemberships"; } + + public static class EventManagement + { + public const string GroupName = Prefix + ".EventManagement"; + public const string Events = GroupName + ".Events"; + } } } \ No newline at end of file diff --git a/src/EventHub.Admin.Web/Pages/EventManagement.razor b/src/EventHub.Admin.Web/Pages/EventManagement.razor new file mode 100644 index 0000000..472aaf1 --- /dev/null +++ b/src/EventHub.Admin.Web/Pages/EventManagement.razor @@ -0,0 +1,138 @@ +@page "/events" +@using Microsoft.AspNetCore.Authorization +@using EventHub.Admin.Permissions +@using EventHub.Admin.Events +@inherits EventHubComponentBase +@attribute [Authorize(EventHubPermissions.Events.Default)] +@inject IEventAppService EventAppService + + + + @* ************************* PAGE HEADER ************************* *@ +

@L["Events"]

+
+ + + @* ************************* SEARCH ************************* *@ +
+
+ + + @L["Title"] + + + + + + @L["OrganizationDisplayName"] + + + + + + @L["StartTime"] + + + + + + @L["MinAttendeeCount"] + + + + + + @L["MaxAttendeeCount"] + + + +
+
+ + @* ************************* DATA GRID ************************* *@ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +@* ************************* EDIT MODAL ************************* *@ + + + +
+ + @L["UpdateEvent"] + + + + + + @L["EventInfo"] + @L["Timing"] + @L["CoverImage"] + + + + EventInfo + + + Timing + + + CoverImage + + + + + + + + +
+
+
\ No newline at end of file diff --git a/src/EventHub.Admin.Web/Pages/EventManagement.razor.cs b/src/EventHub.Admin.Web/Pages/EventManagement.razor.cs new file mode 100644 index 0000000..0c919bb --- /dev/null +++ b/src/EventHub.Admin.Web/Pages/EventManagement.razor.cs @@ -0,0 +1,110 @@ +using EventHub.Admin.Events; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Blazorise; +using Blazorise.DataGrid; +using Volo.Abp.Application.Dtos; +using System; +using System.ComponentModel; +using Microsoft.AspNetCore.Components.Web; + +namespace EventHub.Admin.Web.Pages +{ + public partial class EventManagement + { + private IReadOnlyList EventList { get; set; } + private EventListFilterDto Filter { get; set; } + private int CurrentPage { get; set; } + private string CurrentSorting { get; set; } + private int TotalCount { get; set; } + private int PageSize { get; } + private Guid EditingEventId { get; set; } + private EventDetailDto Event { get; set; } + private UpdateEventDto EditingEvent { get; set; } + private Modal EditEventModal { get; set; } + private string SelectedTabInEditModal { get; set; } + + public EventManagement() + { + Filter = new EventListFilterDto(); + PageSize = LimitedResultRequestDto.DefaultMaxResultCount; + SelectedTabInEditModal = EventEditTabs.EventInfo.ToString(); + } + + protected override async Task OnInitializedAsync() + { + await GetEventsAsync(); + } + + private async Task GetEventsAsync() + { + Filter.MaxResultCount = PageSize; + Filter.SkipCount = CurrentPage * PageSize; + Filter.Sorting = CurrentSorting; + + var result = await EventAppService.GetListAsync(Filter); + EventList = result.Items; + TotalCount = (int)result.TotalCount; + } + + private async Task OnDataGridReadAsync(DataGridReadDataEventArgs e) + { + CurrentSorting = e.Columns + .Where(c => c.Direction != SortDirection.None) + .Select(c => c.Field + (c.Direction == SortDirection.Descending ? " DESC" : "")) + .JoinAsString(","); + CurrentPage = e.Page - 1; + + await GetEventsAsync(); + await InvokeAsync(StateHasChanged); + } + + private async Task OpenEditEventModal(EventInListDto input) + { + EditingEventId = input.Id; + Event = await EventAppService.GetAsync(EditingEventId); + + EditingEvent = ObjectMapper.Map(Event); + EditEventModal.Show(); + } + + private void OnEditModalClosing(CancelEventArgs e) + { + SelectedTabInEditModal = EventEditTabs.EventInfo.ToString(); + } + + private void OnSelectedTabChangedInEditModal(string name) + { + SelectedTabInEditModal = name; + } + + private async Task UpdateEventAsync() + { + await EventAppService.UpdateAsync(EditingEventId, EditingEvent); + await GetEventsAsync(); + EditEventModal.Hide(); + } + + private async Task OnKeyPress(KeyboardEventArgs e) + { + if (e.Code is "Enter" or "NumpadEnter") + { + await GetEventsAsync(); + } + } + + private async Task OnSelectedDateChanged(DateTime? changedDate) + { + Filter.StartTime = changedDate; + await GetEventsAsync(); + } + } + + public enum EventEditTabs : byte + { + EventInfo, + Timing, + CoverImage + } +} diff --git a/src/EventHub.Domain.Shared/Events/EventConsts.cs b/src/EventHub.Domain.Shared/Events/EventConsts.cs index 78aea56..506d9cf 100644 --- a/src/EventHub.Domain.Shared/Events/EventConsts.cs +++ b/src/EventHub.Domain.Shared/Events/EventConsts.cs @@ -2,6 +2,8 @@ { public static class EventConsts { + public const string DefaultSorting = "Title desc"; + public const int UrlCodeLength = 8; public const int MaxTitleInUrlLength = 60; public const int MaxUrlLength = MaxTitleInUrlLength + 1 + UrlCodeLength; //Format: {Title}-{UrlCode} diff --git a/src/EventHub.Domain.Shared/Localization/EventHub/en.json b/src/EventHub.Domain.Shared/Localization/EventHub/en.json index 1bf060c..43adc30 100644 --- a/src/EventHub.Domain.Shared/Localization/EventHub/en.json +++ b/src/EventHub.Domain.Shared/Localization/EventHub/en.json @@ -115,6 +115,17 @@ "Loading": "Loading", "OrganizationInfo": "Organization Info", "ProfileImage": "Profile Image", - "OrganizationName": "Organization Name" + "OrganizationName": "Organization Name", + "Permission:EventManagement": "Event Management", + "Menu:EventManagement": "Event Management", + "Menu:Events": "Events", + "Title": "Title", + "OrganizationDisplayName": "Organization Display Name", + "StartTime": "Start Time", + "MinAttendeeCount": "Min Attendee Count", + "MaxAttendeeCount": "Max Attendee Count", + "UpdateEvent": "Update Event", + "EventInfo": "Event Info", + "Timing": "Timing" } }