Browse Source

Admin: Edit event details

pull/53/head
Engincan VESKE 5 years ago
parent
commit
613e0861fc
  1. 10
      src/EventHub.Admin.Application.Contracts/Events/CountryLookupDto.cs
  2. 18
      src/EventHub.Admin.Application.Contracts/Events/EventDetailDto.cs
  3. 5
      src/EventHub.Admin.Application.Contracts/Events/IEventAppService.cs
  4. 30
      src/EventHub.Admin.Application.Contracts/Events/UpdateEventDto.cs
  5. 6
      src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs
  6. 55
      src/EventHub.Admin.Application/Events/EventAppService.cs
  7. 12
      src/EventHub.Admin.HttpApi.Host/Controllers/Events/EventController.cs
  8. 159
      src/EventHub.Admin.Web/Pages/EventManagement.razor
  9. 82
      src/EventHub.Admin.Web/Pages/EventManagement.razor.cs
  10. 1
      src/EventHub.Application/Events/EventAppService.cs
  11. 2
      src/EventHub.Domain.Shared/Events/EventConsts.cs
  12. 10
      src/EventHub.Domain.Shared/Localization/EventHub/en.json

10
src/EventHub.Admin.Application.Contracts/Events/CountryLookupDto.cs

@ -0,0 +1,10 @@
using System;
using Volo.Abp.Application.Dtos;
namespace EventHub.Admin.Events
{
public class CountryLookupDto : EntityDto<Guid>
{
public string Name { get; set; }
}
}

18
src/EventHub.Admin.Application.Contracts/Events/EventDetailDto.cs

@ -7,6 +7,24 @@ namespace EventHub.Admin.Events
{
public string Title { get; set; }
public string Description { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public byte[] CoverImageContent { get; set; }
public bool IsOnline { get; set; }
public string OnlineLink { get; set; }
public Guid? CountryId { get; set; }
public string City { get; set; }
public string Language { get; set; }
public int? Capacity { get; set; }
}
}

5
src/EventHub.Admin.Application.Contracts/Events/IEventAppService.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
@ -12,5 +13,9 @@ namespace EventHub.Admin.Events
Task<EventDetailDto> GetAsync(Guid id);
Task UpdateAsync(Guid id, UpdateEventDto input);
Task<List<CountryLookupDto>> GetCountriesLookupAsync();
Task<byte[]> GetCoverImageAsync(Guid id);
}
}

30
src/EventHub.Admin.Application.Contracts/Events/UpdateEventDto.cs

@ -1,6 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using EventHub.Events;
using JetBrains.Annotations;
namespace EventHub.Admin.Events
{
@ -10,8 +11,37 @@ namespace EventHub.Admin.Events
[StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)]
public string Title { get; set; }
[Required]
[StringLength(EventConsts.MaxDescriptionLength, MinimumLength = EventConsts.MinDescriptionLength)]
public string Description { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime StartTime { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime EndTime { get; set; }
[CanBeNull]
public byte[] CoverImageContent { get; set; }
public bool IsOnline { get; set; }
[CanBeNull]
[StringLength(EventConsts.MaxOnlineLinkLength, MinimumLength = EventConsts.MinOnlineLinkLength)]
public string OnlineLink { get; set; }
public Guid? CountryId { get; set; }
[CanBeNull]
[StringLength(EventConsts.MaxCityLength, MinimumLength = EventConsts.MinCityLength)]
public string City { get; set; }
[CanBeNull]
[StringLength(EventConsts.MaxLanguageLength, MinimumLength = EventConsts.MinLanguageLength)]
public string Language { get; set; }
[Range(1, int.MaxValue)]
public int? Capacity { get; set; }
}
}

6
src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs

@ -2,6 +2,7 @@ using AutoMapper;
using EventHub.Admin.Events;
using EventHub.Admin.Organizations;
using EventHub.Admin.Organizations.Memberships;
using EventHub.Countries;
using EventHub.Events;
using EventHub.Organizations;
using EventHub.Organizations.Memberships;
@ -22,7 +23,10 @@ namespace EventHub.Admin
CreateMap<OrganizationMemberWithDetails, OrganizationMemberDto>();
CreateMap<Event, EventDetailDto>();
CreateMap<Event, EventDetailDto>()
.Ignore(x => x.CoverImageContent);
CreateMap<Country, CountryLookupDto>();
}
}
}

55
src/EventHub.Admin.Application/Events/EventAppService.cs

@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using EventHub.Countries;
using EventHub.Events;
using EventHub.Events.Registrations;
using EventHub.Organizations;
using Volo.Abp.Application.Dtos;
using Volo.Abp.BlobStoring;
using Volo.Abp.Domain.Repositories;
namespace EventHub.Admin.Events
@ -16,23 +19,35 @@ namespace EventHub.Admin.Events
private readonly IRepository<Event, Guid> _eventRepository;
private readonly IRepository<EventRegistration, Guid> _eventRegistrationRepository;
private readonly IRepository<Organization, Guid> _organizationRepository;
private readonly IBlobContainer<EventCoverImageContainer> _eventBlobContainer;
private readonly EventManager _eventManager;
private readonly IRepository<Country, Guid> _countryRepository;
public EventAppService(
IRepository<Event, Guid> eventRepository,
IRepository<EventRegistration, Guid> eventRegistrationRepository,
IRepository<Organization, Guid> organizationRepository
)
IRepository<Organization, Guid> organizationRepository,
IBlobContainer<EventCoverImageContainer> eventBlobContainer,
EventManager eventManager,
IRepository<Country, Guid> countryRepository)
{
_eventRepository = eventRepository;
_eventRegistrationRepository = eventRegistrationRepository;
_organizationRepository = organizationRepository;
_eventBlobContainer = eventBlobContainer;
_eventManager = eventManager;
_countryRepository = countryRepository;
}
public async Task<EventDetailDto> GetAsync(Guid id)
{
var @event = await _eventRepository.GetAsync(id);
return ObjectMapper.Map<Event, EventDetailDto>(@event);
var eventDetailDto = ObjectMapper.Map<Event, EventDetailDto>(@event);
eventDetailDto.CoverImageContent = await GetCoverImageAsync(id);
return eventDetailDto;
}
public async Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input)
@ -74,10 +89,44 @@ namespace EventHub.Admin.Events
{
var @event = await _eventRepository.GetAsync(id);
await _eventManager.SetLocationAsync(@event, input.IsOnline, input.OnlineLink, input.CountryId, input.City);
@event.SetTitle(input.Title);
@event.SetDescription(input.Description);
@event.Language = input.Language;
@event.SetTime(input.StartTime, @event.EndTime);
await _eventManager.SetCapacityAsync(@event, input.Capacity);
var blobName = id.ToString();
if (input.CoverImageContent.IsNullOrEmpty())
{
await _eventBlobContainer.DeleteAsync(blobName);
}
else
{
await _eventBlobContainer.SaveAsync(blobName, input.CoverImageContent, overrideExisting: true);
}
await _eventRepository.UpdateAsync(@event);
}
public async Task<byte[]> GetCoverImageAsync(Guid id)
{
var blobName = id.ToString();
return await _eventBlobContainer.GetAllBytesOrNullAsync(blobName);
}
public async Task<List<CountryLookupDto>> GetCountriesLookupAsync()
{
var countriesQueryable = await _countryRepository.GetQueryableAsync();
var query = from country in countriesQueryable
orderby country.Name
select country;
var countries = await AsyncExecuter.ToListAsync(query);
return ObjectMapper.Map<List<Country>, List<CountryLookupDto>>(countries);
}
}
}

12
src/EventHub.Admin.HttpApi.Host/Controllers/Events/EventController.cs

@ -30,6 +30,18 @@ namespace EventHub.Admin.Controllers.Events
return _eventAppService.GetAsync(id);
}
[HttpGet("countries")]
public Task<List<CountryLookupDto>> GetCountriesLookupAsync()
{
return _eventAppService.GetCountriesLookupAsync();
}
[HttpGet("cover-image/{id}")]
public Task<byte[]> GetCoverImageAsync(Guid id)
{
return _eventAppService.GetCoverImageAsync(id);
}
[HttpGet]
public Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input)
{

159
src/EventHub.Admin.Web/Pages/EventManagement.razor

@ -2,6 +2,7 @@
@using Microsoft.AspNetCore.Authorization
@using EventHub.Admin.Permissions
@using EventHub.Admin.Events
@using EventHub.Events
@inherits EventHubComponentBase
@attribute [Authorize(EventHubPermissions.Events.Default)]
@inject IEventAppService EventAppService
@ -111,14 +112,160 @@
<Tab Name="@EventEditTabs.CoverImage.ToString()">@L["CoverImage"]</Tab>
</Items>
<Content>
<TabPanel Name="@EventEditTabs.EventInfo.ToString()">
EventInfo
<TabPanel Name="@EventEditTabs.EventInfo.ToString()" Class="mt-3">
<Validations Mode="ValidationMode.Auto" Model="@EditEventModal" ValidateOnLoad="false">
<Validation>
<Field>
<FieldLabel>@L["Title"] *</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingEvent.Title">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["Description"] *</FieldLabel>
<MemoEdit @bind-Text="@EditingEvent.Description">
<Feedback>
<ValidationError/>
</Feedback>
</MemoEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["IsOnline"]</FieldLabel>
<Select @bind-SelectedValue="@EditingEvent.IsOnline">
<SelectItem Value="@Boolean.TrueString">Online</SelectItem>
<SelectItem Value="@Boolean.FalseString">In Person</SelectItem>
</Select>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["Language"]</FieldLabel>
<Select @bind-SelectedValue="@EditingEvent.Language">
@if (Languages != null && Languages.Any())
{
foreach (var language in Languages)
{
<SelectItem Value="@language.Value">@language.Text</SelectItem>
}
}
</Select>
</Field>
</Validation>
@if (EditingEvent.IsOnline)
{
<Validation>
<Field>
<FieldLabel>@L["OnlineLink"]</FieldLabel>
<MemoEdit @bind-Text="@EditingEvent.OnlineLink">
<Feedback>
<ValidationError/>
</Feedback>
</MemoEdit>
</Field>
</Validation>
}
else
{
<Validation>
<Field>
<FieldLabel>@L["Country"]</FieldLabel>
<Select @bind-SelectedValue="@EditingEvent.CountryId">
@if (Countries != null && Countries.Any())
{
@foreach (var country in Countries)
{
<SelectItem Value="@country.Id">@country.Name</SelectItem>
}
}
</Select>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["City"]</FieldLabel>
<TextEdit TValue="string" @bind-Text="@EditingEvent.City">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Field>
</Validation>
}
<Validation>
<Field>
<FieldLabel>@L["Capacity"]</FieldLabel>
<NumericEdit TValue="int?" @bind-Value="@EditingEvent.Capacity">
<Feedback>
<ValidationError/>
</Feedback>
</NumericEdit>
</Field>
</Validation>
</Validations>
</TabPanel>
<TabPanel Name="@EventEditTabs.Timing.ToString()">
Timing
<TabPanel Name="@EventEditTabs.Timing.ToString()" Class="mt-3">
<Validations Mode="ValidationMode.Auto" Model="@EditEventModal" ValidateOnLoad="false">
<Validation>
<Field>
<FieldLabel>@L["StartTime"] *</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="@EditingEvent.StartTime">
<Feedback>
<ValidationError/>
</Feedback>
</DateEdit>
</Field>
</Validation>
<Validation>
<Field>
<FieldLabel>@L["EndTime"] *</FieldLabel>
<DateEdit TValue="DateTime" @bind-Date="@EditingEvent.EndTime">
<Feedback>
<ValidationError/>
</Feedback>
</DateEdit>
</Field>
</Validation>
</Validations>
</TabPanel>
<TabPanel Name="@EventEditTabs.CoverImage.ToString()">
CoverImage
<TabPanel Name="@EventEditTabs.CoverImage.ToString()" Class="mt-3">
@if (!string.IsNullOrEmpty(CoverImageUrl))
{
<div class="text-center mt-4">
<img src="@CoverImageUrl" class="event-cover" width="600" height="340" alt="cover-image"/>
</div>
}
<div class="mt-3 mb-2">
<Field Horizontal="true">
<FieldLabel ColumnSize="ColumnSize.Is3">@L["ChooseCoverImage"]</FieldLabel>
<FieldBody ColumnSize="ColumnSize.Is6">
<FileEdit MaxMessageSize="@EventConsts.MaxCoverImageFileSize" Filter="@string.Join(", ", EventConsts.AllowedCoverImageExtensions)" Changed="@OnCoverImageFileChanged" AutoReset="true"/>
</FieldBody>
<Column ColumnSize="ColumnSize.Is3">
@if (!string.IsNullOrEmpty(CoverImageUrl))
{
<Button Color="Color.Warning" Clicked="@OnDeleteCoverImageButtonClicked">@L["DeleteCoverImage"]</Button>
}
else
{
<Button disabled Color="Color.Secondary" Clicked="@OnDeleteCoverImageButtonClicked">@L["DeleteCoverImage"]</Button>
}
</Column>
</Field>
</div>
</TabPanel>
</Content>
</Tabs>

82
src/EventHub.Admin.Web/Pages/EventManagement.razor.cs

@ -8,6 +8,9 @@ using Volo.Abp.Application.Dtos;
using System;
using System.ComponentModel;
using Microsoft.AspNetCore.Components.Web;
using System.IO;
using System.Globalization;
using NUglify.Helpers;
namespace EventHub.Admin.Web.Pages
{
@ -23,18 +26,42 @@ namespace EventHub.Admin.Web.Pages
private EventDetailDto Event { get; set; }
private UpdateEventDto EditingEvent { get; set; }
private Modal EditEventModal { get; set; }
private string SelectedTabInEditModal { get; set; }
private string SelectedTabInEditModal { get; set; }
private string CoverImageUrl { get; set; }
private IFileEntry FileEntry { get; set; }
private List<CountryLookupDto> Countries { get; set; }
private List<Language> Languages { get; set; }
public EventManagement()
{
Filter = new EventListFilterDto();
PageSize = LimitedResultRequestDto.DefaultMaxResultCount;
SelectedTabInEditModal = EventEditTabs.EventInfo.ToString();
EditingEvent = new UpdateEventDto();
Countries = new List<CountryLookupDto>();
Languages = new List<Language>();
}
protected override async Task OnInitializedAsync()
{
await GetEventsAsync();
await FillCountriesAsync();
FillLanguages();
}
private void FillLanguages()
{
var result = CultureInfo.GetCultures(CultureTypes.NeutralCultures)
.DistinctBy(x => x.EnglishName)
.OrderBy(x => x.EnglishName)
.ToList();
result.Remove(result.Single(x => x.TwoLetterISOLanguageName == "iv")); // Invariant Language
Languages = result.Select(cultureInfo => new Language
{
Value = cultureInfo.TwoLetterISOLanguageName,
Text = cultureInfo.EnglishName
}).ToList();
}
private async Task GetEventsAsync()
@ -66,6 +93,8 @@ namespace EventHub.Admin.Web.Pages
Event = await EventAppService.GetAsync(EditingEventId);
EditingEvent = ObjectMapper.Map<EventDetailDto, UpdateEventDto>(Event);
FillCoverImageUrl(EditingEvent.CoverImageContent);
EditEventModal.Show();
}
@ -83,6 +112,8 @@ namespace EventHub.Admin.Web.Pages
{
await EventAppService.UpdateAsync(EditingEventId, EditingEvent);
await GetEventsAsync();
CoverImageUrl = string.Empty;
EditEventModal.Hide();
}
@ -99,6 +130,49 @@ namespace EventHub.Admin.Web.Pages
Filter.StartTime = changedDate;
await GetEventsAsync();
}
private void FillCoverImageUrl(byte[] content)
{
if (content.IsNullOrEmpty())
{
return;
}
var imageBase64Data = Convert.ToBase64String(content);
var imageDataUrl = $"data:image/png;base64,{imageBase64Data}";
CoverImageUrl = imageDataUrl;
}
private async Task OnCoverImageFileChanged(FileChangedEventArgs e)
{
FileEntry = e.Files.FirstOrDefault();
if (FileEntry is null)
{
return;
}
using (var stream = new MemoryStream())
{
await FileEntry.WriteToStreamAsync(stream);
stream.Seek(0, SeekOrigin.Begin);
EditingEvent.CoverImageContent = stream.ToArray();
FillCoverImageUrl(EditingEvent.CoverImageContent);
await InvokeAsync(StateHasChanged);
}
}
private void OnDeleteCoverImageButtonClicked()
{
EditingEvent.CoverImageContent = null;
FileEntry = new FileEntry();
CoverImageUrl = null;
}
private async Task FillCountriesAsync()
{
Countries = await EventAppService.GetCountriesLookupAsync();
}
}
public enum EventEditTabs : byte
@ -107,4 +181,10 @@ namespace EventHub.Admin.Web.Pages
Timing,
CoverImage
}
public class Language
{
public string Value { get; set; }
public string Text { get; set; }
}
}

1
src/EventHub.Application/Events/EventAppService.cs

@ -5,7 +5,6 @@ using System.Threading.Tasks;
using EventHub.Countries;
using EventHub.Events.Registrations;
using EventHub.Organizations;
using EventHub.Users;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Authorization;

2
src/EventHub.Domain.Shared/Events/EventConsts.cs

@ -26,5 +26,7 @@
public const int MaxTimingChangeCountForUser = 2;
public const int MaxCoverImageFileSize = 5 * 1024 * 1024;
public static string[] AllowedCoverImageExtensions = { ".jpg", ".png" };
}
}

10
src/EventHub.Domain.Shared/Localization/EventHub/en.json

@ -126,6 +126,14 @@
"MaxAttendeeCount": "Max Attendee Count",
"UpdateEvent": "Update Event",
"EventInfo": "Event Info",
"Timing": "Timing"
"Timing": "Timing",
"IsOnline": "Is Online?",
"OnlineLink": "Online Link",
"Country": "Country",
"City": "City",
"Capacity": "Capacity",
"EndTime": "End Time",
"ChooseCoverImage": "Choose a cover image",
}
}

Loading…
Cancel
Save