mirror of https://github.com/abpframework/eventhub
39 changed files with 967 additions and 473 deletions
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
|
namespace EventHub.Events; |
||||
|
|
||||
|
public class AddSessionDto |
||||
|
{ |
||||
|
public Guid TrackId { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[StringLength(SessionConsts.MaxTitleLength, MinimumLength = SessionConsts.MinTitleLength)] |
||||
|
public string Title { get; set; } |
||||
|
|
||||
|
public DateTime StartTime { get; set; } |
||||
|
|
||||
|
public DateTime EndTime { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[StringLength(SessionConsts.MaxDescriptionLength, MinimumLength = SessionConsts.MinDescriptionLength)] |
||||
|
public string Description { get; set; } |
||||
|
|
||||
|
public string Language { get; set; } |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
|
namespace EventHub.Events; |
||||
|
|
||||
|
public class AddTractDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[StringLength(TrackConsts.MaxNameLength)] |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace EventHub.Events; |
||||
|
|
||||
|
public class TrackDto : EntityDto<Guid> |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
@ -1,104 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using EventHub.Events; |
|
||||
using Microsoft.AspNetCore.Mvc; |
|
||||
using Volo.Abp; |
|
||||
using Volo.Abp.Application.Dtos; |
|
||||
using Volo.Abp.AspNetCore.Mvc; |
|
||||
using Volo.Abp.Content; |
|
||||
using Volo.Abp.VirtualFileSystem; |
|
||||
|
|
||||
namespace EventHub.Controllers.Events |
|
||||
{ |
|
||||
[RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] |
|
||||
[Area("eventhub")] |
|
||||
[ControllerName("Event")] |
|
||||
[Route("api/eventhub/event")] |
|
||||
public class EventController : AbpController, IEventAppService |
|
||||
{ |
|
||||
private readonly IEventAppService _eventAppService; |
|
||||
private readonly IVirtualFileProvider _virtualFileProvider; |
|
||||
|
|
||||
public EventController( |
|
||||
IEventAppService eventAppService, |
|
||||
IVirtualFileProvider virtualFileProvider) |
|
||||
{ |
|
||||
_eventAppService = eventAppService; |
|
||||
_virtualFileProvider = virtualFileProvider; |
|
||||
} |
|
||||
|
|
||||
[HttpPost] |
|
||||
public async Task<EventDto> CreateAsync([FromForm] CreateEventDto input) |
|
||||
{ |
|
||||
return await _eventAppService.CreateAsync(input); |
|
||||
} |
|
||||
|
|
||||
[HttpGet] |
|
||||
public async Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input) |
|
||||
{ |
|
||||
return await _eventAppService.GetListAsync(input); |
|
||||
} |
|
||||
|
|
||||
[HttpGet] |
|
||||
[Route("by-url-code/{urlCode}")] |
|
||||
public async Task<EventDetailDto> GetByUrlCodeAsync(string urlCode) |
|
||||
{ |
|
||||
return await _eventAppService.GetByUrlCodeAsync(urlCode); |
|
||||
} |
|
||||
|
|
||||
[HttpGet] |
|
||||
[Route("location/{id}")] |
|
||||
public async Task<EventLocationDto> GetLocationAsync(Guid id) |
|
||||
{ |
|
||||
return await _eventAppService.GetLocationAsync(id); |
|
||||
} |
|
||||
|
|
||||
[HttpGet] |
|
||||
[Route("lookup/countries")] |
|
||||
public async Task<List<CountryLookupDto>> GetCountriesLookupAsync() |
|
||||
{ |
|
||||
return await _eventAppService.GetCountriesLookupAsync(); |
|
||||
} |
|
||||
|
|
||||
[HttpGet] |
|
||||
[Route("is-event-owner/{id}")] |
|
||||
public async Task<bool> IsEventOwnerAsync(Guid id) |
|
||||
{ |
|
||||
return await _eventAppService.IsEventOwnerAsync(id); |
|
||||
} |
|
||||
|
|
||||
[HttpPut] |
|
||||
[Route("{id}")] |
|
||||
public async Task UpdateAsync(Guid id, [FromForm] UpdateEventDto input) |
|
||||
{ |
|
||||
await _eventAppService.UpdateAsync(id, input); |
|
||||
} |
|
||||
|
|
||||
[HttpPost] |
|
||||
[Route("{id}/sessions")] |
|
||||
public async Task AddSessionAsync(Guid id, AddSessionDto input) |
|
||||
{ |
|
||||
await _eventAppService.AddSessionAsync(id, input); |
|
||||
} |
|
||||
|
|
||||
[HttpGet] |
|
||||
[Route("cover-image/{id}")] |
|
||||
public async Task<IRemoteStreamContent> GetCoverImageAsync(Guid id) |
|
||||
{ |
|
||||
var remoteStreamContent = await _eventAppService.GetCoverImageAsync(id); |
|
||||
|
|
||||
if (remoteStreamContent is null) |
|
||||
{ |
|
||||
var stream = _virtualFileProvider.GetFileInfo("/Images/eh-event.png").CreateReadStream(); |
|
||||
remoteStreamContent = new RemoteStreamContent(stream); |
|
||||
await stream.FlushAsync(); |
|
||||
} |
|
||||
|
|
||||
Response.Headers.Add("Accept-Ranges", "bytes"); |
|
||||
Response.ContentType = remoteStreamContent.ContentType; |
|
||||
|
|
||||
return remoteStreamContent; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,124 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using EventHub.Events; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Content; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace EventHub.Controllers.Events |
||||
|
{ |
||||
|
[RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Area("eventhubm")] |
||||
|
[ControllerName("Event")] |
||||
|
[Route("api/eventhub/event")] |
||||
|
public class EventController : EventHubController, IEventAppService |
||||
|
{ |
||||
|
private readonly IEventAppService _eventAppService; |
||||
|
private readonly IVirtualFileProvider _virtualFileProvider; |
||||
|
|
||||
|
public EventController( |
||||
|
IEventAppService eventAppService, |
||||
|
IVirtualFileProvider virtualFileProvider) |
||||
|
{ |
||||
|
_eventAppService = eventAppService; |
||||
|
_virtualFileProvider = virtualFileProvider; |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
public async Task<EventDto> CreateAsync([FromForm] CreateEventDto input) |
||||
|
{ |
||||
|
return await _eventAppService.CreateAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
public async Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input) |
||||
|
{ |
||||
|
return await _eventAppService.GetListAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("draft-events-by-user-id/{userId}")] |
||||
|
public async Task<List<EventInListDto>> GetDraftEventsByUserId(Guid userId) |
||||
|
{ |
||||
|
return await _eventAppService.GetDraftEventsByUserId(userId); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("by-url-code/{urlCode}")] |
||||
|
public async Task<EventDetailDto> GetByUrlCodeAsync(string urlCode) |
||||
|
{ |
||||
|
return await _eventAppService.GetByUrlCodeAsync(urlCode); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("location/{id}")] |
||||
|
public async Task<EventLocationDto> GetLocationAsync(Guid id) |
||||
|
{ |
||||
|
return await _eventAppService.GetLocationAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("lookup/countries")] |
||||
|
public async Task<List<CountryLookupDto>> GetCountriesLookupAsync() |
||||
|
{ |
||||
|
return await _eventAppService.GetCountriesLookupAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("is-event-owner/{id}")] |
||||
|
public async Task<bool> IsEventOwnerAsync(Guid id) |
||||
|
{ |
||||
|
return await _eventAppService.IsEventOwnerAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpPut] |
||||
|
[Route("{id}")] |
||||
|
public async Task UpdateAsync(Guid id, [FromForm] UpdateEventDto input) |
||||
|
{ |
||||
|
await _eventAppService.UpdateAsync(id, input); |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Route("{id}/tracks")] |
||||
|
public async Task AddTrackAsync(Guid id, AddTractDto input) |
||||
|
{ |
||||
|
await _eventAppService.AddTrackAsync(id, input); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("{id}/tracks")] |
||||
|
public async Task<List<TrackDto>> GetTracksAsync(Guid id) |
||||
|
{ |
||||
|
return await _eventAppService.GetTracksAsync(id); |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Route("{id}/sessions")] |
||||
|
public async Task AddSessionAsync(Guid id, AddSessionDto input) |
||||
|
{ |
||||
|
await _eventAppService.AddSessionAsync(id, input); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("cover-image/{id}")] |
||||
|
public async Task<IRemoteStreamContent> GetCoverImageAsync(Guid id) |
||||
|
{ |
||||
|
var remoteStreamContent = await _eventAppService.GetCoverImageAsync(id); |
||||
|
|
||||
|
if (remoteStreamContent is null) |
||||
|
{ |
||||
|
var stream = _virtualFileProvider.GetFileInfo("/Images/eh-event.png").CreateReadStream(); |
||||
|
remoteStreamContent = new RemoteStreamContent(stream); |
||||
|
await stream.FlushAsync(); |
||||
|
} |
||||
|
|
||||
|
Response.Headers.Add("Accept-Ranges", "bytes"); |
||||
|
Response.ContentType = remoteStreamContent.ContentType; |
||||
|
|
||||
|
return remoteStreamContent; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,175 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.ObjectModel; |
||||
|
using System.ComponentModel; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Globalization; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using EventHub.Events; |
||||
|
using EventHub.Organizations; |
||||
|
using EventHub.Web.Helpers; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Rendering; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
||||
|
using Volo.Abp.Users; |
||||
|
|
||||
|
namespace EventHub.Web.Pages.Events.Components.CreateEventArea; |
||||
|
|
||||
|
[Widget( |
||||
|
AutoInitialize = true, |
||||
|
RefreshUrl = "/Widgets/CreateEventArea", |
||||
|
ScriptFiles = new[] { "/Pages/Events/Components/CreateEventArea/create-event-area.js" } |
||||
|
)] |
||||
|
public class CreateEventAreaViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
private readonly IEventAppService _eventAppService; |
||||
|
private readonly IOrganizationAppService _organizationAppService; |
||||
|
|
||||
|
private readonly ICurrentUser _currentUser; |
||||
|
|
||||
|
public CreateEventAreaViewComponent( |
||||
|
IEventAppService eventAppService, |
||||
|
IOrganizationAppService organizationAppService, |
||||
|
ICurrentUser currentUser) |
||||
|
{ |
||||
|
_eventAppService = eventAppService; |
||||
|
_organizationAppService = organizationAppService; |
||||
|
_currentUser = currentUser; |
||||
|
} |
||||
|
|
||||
|
public async Task<IViewComponentResult> InvokeAsync(string eventUrlCode) |
||||
|
{ |
||||
|
NewEventViewModel model = null; |
||||
|
if (!eventUrlCode.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
var @event = await _eventAppService.GetByUrlCodeAsync(eventUrlCode); |
||||
|
model = ObjectMapper.Map<EventDetailDto, NewEventViewModel>(@event); |
||||
|
ViewData["EventId"] = @event.Id; |
||||
|
} |
||||
|
|
||||
|
model ??= new NewEventViewModel |
||||
|
{ |
||||
|
StartTime = DateTime.Now.ClearTime().AddDays(1).AddHours(19), |
||||
|
EndTime = DateTime.Now.ClearTime().AddDays(1).AddHours(21) |
||||
|
}; |
||||
|
|
||||
|
ViewData["Organizations"] = await GetOrganizationsSelectItemAsync(); |
||||
|
ViewData["Countries"] = await GetCountriesSelectItemAsync(); |
||||
|
ViewData["Languages"] = GetLanguagesSelectItem(); |
||||
|
|
||||
|
return View("~/Pages/Events/Components/CreateEventArea/Default.cshtml", model); |
||||
|
} |
||||
|
|
||||
|
private async Task<List<SelectListItem>> GetOrganizationsSelectItemAsync() |
||||
|
{ |
||||
|
var result = await _organizationAppService.GetOrganizationsByUserIdAsync(_currentUser.GetId()); |
||||
|
|
||||
|
return result.Items.Select( |
||||
|
organization => new SelectListItem |
||||
|
{ |
||||
|
Value = organization.Id.ToString(), |
||||
|
Text = organization.DisplayName |
||||
|
} |
||||
|
).ToList(); |
||||
|
} |
||||
|
|
||||
|
private async Task<List<SelectListItem>> GetCountriesSelectItemAsync() |
||||
|
{ |
||||
|
var result = await _eventAppService.GetCountriesLookupAsync(); |
||||
|
|
||||
|
return result.Select( |
||||
|
country => new SelectListItem |
||||
|
{ |
||||
|
Value = country.Id.ToString(), |
||||
|
Text = country.Name |
||||
|
} |
||||
|
).ToList(); |
||||
|
} |
||||
|
|
||||
|
private List<SelectListItem> GetLanguagesSelectItem() |
||||
|
{ |
||||
|
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
|
||||
|
|
||||
|
return result.Select( |
||||
|
cultureInfo => new SelectListItem |
||||
|
{ |
||||
|
Value = cultureInfo.TwoLetterISOLanguageName, |
||||
|
Text = cultureInfo.EnglishName |
||||
|
} |
||||
|
).ToList(); |
||||
|
} |
||||
|
|
||||
|
public class NewEventViewModel |
||||
|
{ |
||||
|
[SelectItems(nameof(Organizations))] |
||||
|
[DisplayName("Organization")] |
||||
|
public Guid OrganizationId { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)] |
||||
|
public string Title { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DataType(DataType.DateTime)] |
||||
|
public DateTime StartTime { get; set; } = DateTime.Now; |
||||
|
|
||||
|
[Required] |
||||
|
[DataType(DataType.DateTime)] |
||||
|
public DateTime EndTime { get; set; } = DateTime.Now; |
||||
|
|
||||
|
[Required] |
||||
|
[StringLength(EventConsts.MaxDescriptionLength, MinimumLength = EventConsts.MinDescriptionLength)] |
||||
|
[TextArea] |
||||
|
public string Description { get; set; } |
||||
|
|
||||
|
[CanBeNull] |
||||
|
[Display(Name = "Cover Image")] |
||||
|
[DataType(DataType.Upload)] |
||||
|
[MaxFileSize(EventConsts.MaxCoverImageFileSize)] |
||||
|
[AllowedExtensions(new string[] { ".jpg", ".png", ".jpeg" })] |
||||
|
public IFormFile CoverImageFile { get; set; } |
||||
|
|
||||
|
[Required] public bool? IsOnline { get; set; } |
||||
|
|
||||
|
[CanBeNull] |
||||
|
[StringLength(EventConsts.MaxOnlineLinkLength, MinimumLength = EventConsts.MinOnlineLinkLength)] |
||||
|
public string OnlineLink { get; set; } |
||||
|
|
||||
|
[SelectItems(nameof(Countries))] |
||||
|
[DisplayName("Country")] |
||||
|
public Guid? CountryId { get; set; } |
||||
|
|
||||
|
[CanBeNull] |
||||
|
[StringLength(EventConsts.MaxCityLength, MinimumLength = EventConsts.MinCityLength)] |
||||
|
public string City { get; set; } |
||||
|
|
||||
|
[DisplayName("Language")] |
||||
|
public string Language { get; set; } |
||||
|
|
||||
|
[Range(1, int.MaxValue)] |
||||
|
public int? Capacity { get; set; } |
||||
|
|
||||
|
public List<TrackDto> Tracks { get; set; } |
||||
|
|
||||
|
public NewEventViewModel() |
||||
|
{ |
||||
|
Tracks = new List<TrackDto>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public enum ProgressStepType : byte |
||||
|
{ |
||||
|
NewEvent = 0, |
||||
|
NewTrack, |
||||
|
NewSession |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
@using Microsoft.AspNetCore.Http |
||||
|
@model EventHub.Web.Pages.Events.Components.CreateEventArea.CreateEventAreaViewComponent.NewEventViewModel |
||||
|
|
||||
|
@{ |
||||
|
if (Model is null) |
||||
|
{ |
||||
|
throw new BadHttpRequestException(""); |
||||
|
} |
||||
|
|
||||
|
var eventId = ViewData["EventId"] as Guid?; |
||||
|
} |
||||
|
|
||||
|
<div id="CreateEventContainer"> |
||||
|
<partial name="_newEventSection.cshtml" model="@Model"/> |
||||
|
</div> |
||||
|
<div id="CreateTrackContainer" style="display: none"> |
||||
|
<partial name="_addTrackSection.cshtml" model="@Model.Tracks"/> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
<input type="hidden" id="EventId" value="@eventId"/> |
||||
@ -0,0 +1,71 @@ |
|||||
|
@using EventHub.Web.Pages.Events.Components.CreateEventArea |
||||
|
@model List<EventHub.Events.TrackDto> |
||||
|
|
||||
|
<partial name="_progressSection.cshtml" model="@CreateEventAreaViewComponent.ProgressStepType.NewTrack"/> |
||||
|
|
||||
|
@if (Model is not null) |
||||
|
{ |
||||
|
<div class="row"> |
||||
|
<div class="col-md-10 mx-auto"> |
||||
|
<div class="card"> |
||||
|
<div class="card-body profile-content p-5" style="min-height: 480px;"> |
||||
|
<h2 class="mb-4">Add Track</h2> |
||||
|
<div class="row"> |
||||
|
@foreach (var track in Model) |
||||
|
{ |
||||
|
<div class="col-md-3"> |
||||
|
<div class="track-item mb-4"> |
||||
|
<span class="track-name">@track.Name</span> |
||||
|
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#editTrackModal" class="mt-3 d-block text-info"><small><i class="fa fa-edit me-1"></i>Edit</small></a> |
||||
|
</div> |
||||
|
</div> |
||||
|
} |
||||
|
<div class="col-md-3"> |
||||
|
<div class="big-square-btn mb-4 text-center"> |
||||
|
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#AddTrackModal" class="btn btn-outline-secondary d-block px-2 py-5"><i class="fa fa-plus mb-3"></i><br>Add New Track</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="card-footer bg-white border-0 p-5 pt-1"> |
||||
|
<div class="row"> |
||||
|
<div class="col-6"> |
||||
|
<a href="#" class="btn btn-secondary"><i class="fa fa-arrow-left me-2"></i> Previous Step </a> |
||||
|
</div> |
||||
|
<div class="col-6 text-end"> |
||||
|
<a href="#" class="btn btn-link">Skip This Step</a> |
||||
|
<a href="#" class="btn btn-primary">Next Step <i class="fa fa-arrow-right ms-2"></i></a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
@* Add Track modal *@ |
||||
|
<div class="modal fade" id="AddTrackModal" tabindex="-1" aria-labelledby="AddTrackModal" aria-hidden="true"> |
||||
|
<div class="modal-dialog modal-dialog-centered"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header border-0 p-5 pb-2"> |
||||
|
<h5 class="modal-title">Add New Track</h5> |
||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
||||
|
</div> |
||||
|
<div class="modal-body profile-content px-5"> |
||||
|
<div class="form-floating"> |
||||
|
<input type="text" class="form-control" id="TrackName" placeholder=" "> |
||||
|
<label for="TrackName">Track Name</label> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="modal-footer border-0 p-5 pt-2"> |
||||
|
<button type="button" class="btn btn-secondary modal-close-button" data-bs-dismiss="modal">Close</button> |
||||
|
<button id="AddNewTrackButton" type="button" class="btn btn-primary" |
||||
|
data-url="@Url.Action("GetTracksSection", "Event", new { id = "eventIdPlaceholder" })"> |
||||
|
<i class="fa fa-plus me-2"></i> Add |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,143 @@ |
|||||
|
@using EventHub.Web.Pages.Events.Components.CreateEventArea |
||||
|
@using Microsoft.AspNetCore.Http |
||||
|
@model EventHub.Web.Pages.Events.Components.CreateEventArea.CreateEventAreaViewComponent.NewEventViewModel |
||||
|
|
||||
|
@{ |
||||
|
var organizations = ViewData["Organizations"] as IList<SelectListItem>; |
||||
|
if (organizations is null || !organizations.Any()) |
||||
|
{ |
||||
|
throw new BadHttpRequestException("There is no organization"); |
||||
|
} |
||||
|
|
||||
|
var countries = ViewData["Countries"] as IList<SelectListItem>; |
||||
|
if (countries is null || !countries.Any()) |
||||
|
{ |
||||
|
throw new BadHttpRequestException("There is no country"); |
||||
|
} |
||||
|
|
||||
|
var languages = ViewData["Languages"] as IList<SelectListItem>; |
||||
|
if (languages is null || !languages.Any()) |
||||
|
{ |
||||
|
throw new BadHttpRequestException("There is no language"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
<partial name="_progressSection.cshtml" model="@CreateEventAreaViewComponent.ProgressStepType.NewEvent"/> |
||||
|
|
||||
|
<form id="CreateEventForm" method="post" enctype="multipart/form-data"> |
||||
|
<div class="container pt-4"> |
||||
|
<div class="row py-4"> |
||||
|
<div class="col-md-9 mx-auto profile-content"> |
||||
|
<div class="card"> |
||||
|
<div class="card-body" style="min-height: 496px"> |
||||
|
<h3 class="mb-4">Create New Event</h3> |
||||
|
<div class="form"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="form-label-group"> |
||||
|
<select asp-for="@Model.OrganizationId" asp-items="organizations" class="form-select"> |
||||
|
@if (organizations.Count > 1) |
||||
|
{ |
||||
|
<option selected value="">Pick an organization</option> |
||||
|
} |
||||
|
</select> |
||||
|
<span asp-validation-for="@Model.OrganizationId" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="form-label-group"> |
||||
|
<input type="text" id="inputTitle" asp-for="@Model.Title" class="form-control" placeholder="Title"> |
||||
|
<label for="inputTitle">Title</label> |
||||
|
<span asp-validation-for="@Model.Title" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="form-label-group"> |
||||
|
<input id="inputStartdate" asp-for="@Model.StartTime" class="form-control" placeholder="Start Date"> |
||||
|
<label for="inputStartdate">Start Date</label> |
||||
|
<span asp-validation-for="@Model.StartTime" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="form-label-group"> |
||||
|
<input id="inputEnddate" asp-for="@Model.EndTime" class="form-control" placeholder="End Date"> |
||||
|
<label for="inputEnddate">End Date</label> |
||||
|
<span asp-validation-for="@Model.EndTime" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-12"> |
||||
|
<div class="form-label-group"> |
||||
|
<textarea type="text" id="inputDescription" asp-for="@Model.Description" class="form-control" placeholder="Description"></textarea> |
||||
|
<span asp-validation-for="@Model.Description" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-12"> |
||||
|
<h4 class="mb-4">Cover Image</h4> |
||||
|
<div class="row"> |
||||
|
<div class="col-6"> |
||||
|
<div class="image-area"><img id="imageResult" src="#" alt="" class="img-fluid rounded shadow-sm mx-auto d-block"></div> |
||||
|
</div> |
||||
|
<div class="col-6"> |
||||
|
<div> |
||||
|
<p class="mb-3">For horizontal format 600x400 sized upload images.</p> |
||||
|
<input type="file" id="Event_CoverImageFile" asp-for="@Model.CoverImageFile" class="form-control border-0" hidden> |
||||
|
<label id="upload-label" for="Event_CoverImageFile" class="btn btn-primary btn-lg text-white">Choose file</label> |
||||
|
<span asp-validation-for="@Model.CoverImageFile" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<select class="form-select" asp-for="@Model.IsOnline"> |
||||
|
<option selected="selected" value="">Event Type</option> |
||||
|
<option value="@Boolean.TrueString">Online</option> |
||||
|
<option value="@Boolean.FalseString">In Person</option> |
||||
|
</select> |
||||
|
<span asp-validation-for="@Model.IsOnline" class="text-danger"></span> |
||||
|
</div> |
||||
|
<div class="col-md-6 event-link-group" style="display: none"> |
||||
|
<div class="form-label-group"> |
||||
|
<input type="text" id="inputOnlineLink" asp-for="@Model.OnlineLink" class="form-control" placeholder="Online Link"> |
||||
|
<label for="inputOnlineLink">Online Link</label> |
||||
|
<span asp-validation-for="@Model.OnlineLink" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<div class="form-label-group"> |
||||
|
<input type="number" id="inputCapacity" asp-for="@Model.Capacity" class="form-control" placeholder="Capacity"> |
||||
|
<label for="inputCapacity">Capacity</label> |
||||
|
<span asp-validation-for="@Model.Capacity" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6 event-location-group"> |
||||
|
<select asp-for="@Model.CountryId" asp-items="countries" class="form-select"> |
||||
|
<option selected="" value="">Country</option> |
||||
|
</select> |
||||
|
<span asp-validation-for="@Model.CountryId" class="text-danger"></span> |
||||
|
</div> |
||||
|
<div class="col-md-6 event-location-group"> |
||||
|
<div class="form-label-group"> |
||||
|
<input type="text" id="inputCity" asp-for="@Model.City" class="form-control" placeholder="City"> |
||||
|
<label for="inputCity">City</label> |
||||
|
<span asp-validation-for="@Model.City" class="text-danger"></span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6"> |
||||
|
<select asp-for="@Model.Language" asp-items="languages" aria-label="en" class="form-select"> |
||||
|
<option selected value="">Language</option> |
||||
|
</select> |
||||
|
<span asp-validation-for="@Model.Language" class="text-danger"></span> |
||||
|
</div> |
||||
|
<div class="col-md-12 text-end"> |
||||
|
<button id="CreateEventButton" type="submit" class="btn btn-primary btn-lg"> |
||||
|
Next Step |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
@ -0,0 +1,15 @@ |
|||||
|
@using EventHub.Web.Pages.Events.Components.CreateEventArea |
||||
|
@model EventHub.Web.Pages.Events.Components.CreateEventArea.CreateEventAreaViewComponent.ProgressStepType |
||||
|
|
||||
|
<div class="px-4 py-1"> |
||||
|
<div class="progresses py-4"> |
||||
|
<ul class="d-flex align-items-center justify-content-between"> |
||||
|
<li id="step-1" class="create-steps @(Model == CreateEventAreaViewComponent.ProgressStepType.NewEvent ? "blue" : "")">1<span>New Event</span></li> |
||||
|
<li id="step-2" class="create-steps @(Model == CreateEventAreaViewComponent.ProgressStepType.NewTrack ? "blue" : "")">2<span>Add Track</span></li> |
||||
|
<li id="step-3" class="create-steps @(Model == CreateEventAreaViewComponent.ProgressStepType.NewSession ? "blue" : "")">3<span>Add Session</span></li> |
||||
|
</ul> |
||||
|
<div class="progress"> |
||||
|
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,69 @@ |
|||||
|
(function () { |
||||
|
abp.widgets.CreateEventArea = function ($wrapper) { |
||||
|
var eventApiService = eventHub.controllers.events.event; |
||||
|
var eventIdInput = $('#EventId'); |
||||
|
|
||||
|
if (eventIdInput.val().length === 36) { |
||||
|
// TODO: Add organization in UppdateEventDto
|
||||
|
$('#OrganizationId').prop("disabled", true).removeAttr('name'); |
||||
|
} |
||||
|
|
||||
|
$("#CreateEventForm").submit(function (e) { |
||||
|
e.preventDefault(); |
||||
|
if (!$(this).valid()) { |
||||
|
return false; |
||||
|
} |
||||
|
var input = $(this).serializeFormToObject(); |
||||
|
|
||||
|
if (eventIdInput.val().length === 36) { |
||||
|
eventApiService.update(eventIdInput.val(), input).then(function (eventUpdatedResponse) { |
||||
|
abp.notify.success('Updated event'); |
||||
|
SwitchToTrackCreation() |
||||
|
ScrollToWrapperBegin(); |
||||
|
}); |
||||
|
} else { |
||||
|
eventApiService.create(input).then(function (eventCreatedResponse) { |
||||
|
abp.notify.success('Created event as a draft'); |
||||
|
eventIdInput.val(eventCreatedResponse.id); |
||||
|
SwitchToTrackCreation() |
||||
|
ScrollToWrapperBegin(); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
function ScrollToWrapperBegin() { |
||||
|
$([document.documentElement, document.body]).animate({ |
||||
|
scrollTop: $wrapper.offset().top |
||||
|
}, 100); |
||||
|
} |
||||
|
|
||||
|
function SwitchToTrackCreation() { |
||||
|
$('#CreateEventContainer').css('display', 'none'); |
||||
|
$('#CreateTrackContainer').css('display', ''); |
||||
|
AddNewTrackButtonClickEventHandler(); |
||||
|
} |
||||
|
|
||||
|
function AddNewTrackButtonClickEventHandler() { |
||||
|
var addNewTrackButton = $('#AddNewTrackButton'); |
||||
|
addNewTrackButton.click(function (e) { |
||||
|
e.preventDefault(); |
||||
|
var trackName = $('#TrackName').val().trim(); |
||||
|
eventApiService.addTrack(eventIdInput.val(), {name: trackName}).then(function () { |
||||
|
var url = addNewTrackButton.attr('data-url').replace("eventIdPlaceholder", eventIdInput.val()); |
||||
|
abp.ajax({ |
||||
|
type: 'GET', dataType: 'html', contentType: 'text/html; charset=utf-8', url: url |
||||
|
}).then(function (getTracksResponse) { |
||||
|
$('#AddTrackModal').modal('hide'); |
||||
|
var createTrackContainer = $wrapper.find('#CreateTrackContainer'); |
||||
|
createTrackContainer.text(""); |
||||
|
createTrackContainer.append(getTracksResponse); |
||||
|
AddNewTrackButtonClickEventHandler(); |
||||
|
}); |
||||
|
|
||||
|
abp.notify.success('Added the track'); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
})(); |
||||
@ -1,180 +1,52 @@ |
|||||
using System; |
using System; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
using System.ComponentModel; |
|
||||
using System.ComponentModel.DataAnnotations; |
|
||||
using System.Globalization; |
|
||||
using System.IO; |
|
||||
using System.Linq; |
using System.Linq; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
using EventHub.Events; |
using EventHub.Events; |
||||
using EventHub.Organizations; |
using EventHub.Organizations; |
||||
using EventHub.Web.Helpers; |
|
||||
using JetBrains.Annotations; |
|
||||
using Microsoft.AspNetCore.Http; |
|
||||
using Microsoft.AspNetCore.Mvc; |
using Microsoft.AspNetCore.Mvc; |
||||
using Microsoft.AspNetCore.Mvc.Rendering; |
|
||||
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
|
||||
using Volo.Abp.Content; |
|
||||
using Volo.Abp.Users; |
using Volo.Abp.Users; |
||||
|
|
||||
namespace EventHub.Web.Pages.Events |
namespace EventHub.Web.Pages.Events |
||||
{ |
{ |
||||
public class NewPageModel : EventHubPageModel |
public class NewPageModel : EventHubPageModel |
||||
{ |
{ |
||||
[BindProperty] |
[BindProperty(SupportsGet = true)] |
||||
public NewEventViewModel Event { get; set; } |
public string EventUrlCode { get; set; } |
||||
|
|
||||
public List<SelectListItem> Organizations { get; private set; } |
[BindProperty(SupportsGet = true)] |
||||
public List<SelectListItem> Countries { get; private set; } |
public bool IsCreateNew { get; set; } |
||||
public List<SelectListItem> Languages { get; private set; } |
|
||||
|
public bool IsHasOrganizations { get; set; } |
||||
|
|
||||
|
public List<EventInListDto> DraftEventList { get; set; } |
||||
|
|
||||
private readonly IEventAppService _eventAppService; |
private readonly IEventAppService _eventAppService; |
||||
private readonly IOrganizationAppService _organizationAppService; |
private readonly IOrganizationAppService _organizationAppService; |
||||
|
|
||||
public NewPageModel( |
public NewPageModel( |
||||
IEventAppService eventAppService, |
IEventAppService eventAppService, |
||||
IOrganizationAppService organizationAppService) |
IOrganizationAppService organizationAppService) |
||||
{ |
{ |
||||
_eventAppService = eventAppService; |
_eventAppService = eventAppService; |
||||
_organizationAppService = organizationAppService; |
_organizationAppService = organizationAppService; |
||||
|
|
||||
|
DraftEventList = new List<EventInListDto>(); |
||||
} |
} |
||||
|
|
||||
public async Task OnGetAsync() |
public async Task OnGetAsync() |
||||
{ |
{ |
||||
Event = new NewEventViewModel |
IsHasOrganizations = (await _organizationAppService.GetOrganizationsByUserIdAsync(CurrentUser.GetId())).Items.Any(); |
||||
{ |
|
||||
StartTime = DateTime.Now.ClearTime().AddDays(1).AddHours(19), |
|
||||
EndTime = DateTime.Now.ClearTime().AddDays(1).AddHours(21) |
|
||||
}; |
|
||||
|
|
||||
await FillOrganizationsAsync(); |
if (!EventUrlCode.IsNullOrWhiteSpace()) |
||||
await FillCountriesAsync(); |
|
||||
FillLanguages(); |
|
||||
} |
|
||||
|
|
||||
public async Task<IActionResult> OnPostAsync() |
|
||||
{ |
|
||||
try |
|
||||
{ |
{ |
||||
ValidateModel(); |
return; |
||||
|
|
||||
var createEventDto = ObjectMapper.Map<NewEventViewModel, CreateEventDto>(Event); |
|
||||
|
|
||||
await using var memoryStream = new MemoryStream(); |
|
||||
if (Event.CoverImageFile != null && Event.CoverImageFile.Length > 0) |
|
||||
{ |
|
||||
await Event.CoverImageFile.CopyToAsync(memoryStream); |
|
||||
memoryStream.Position = 0; |
|
||||
|
|
||||
createEventDto.CoverImageStreamContent = new RemoteStreamContent(memoryStream, fileName: Event.CoverImageFile.FileName, contentType: Event.CoverImageFile.ContentType); |
|
||||
} |
|
||||
|
|
||||
var eventDto = await _eventAppService.CreateAsync(createEventDto); |
|
||||
await memoryStream.DisposeAsync(); |
|
||||
|
|
||||
return RedirectToPage("/Events/Detail", new {url = eventDto.UrlCode}); |
|
||||
} |
} |
||||
catch (Exception exception) |
|
||||
|
if (IsHasOrganizations && !IsCreateNew) |
||||
{ |
{ |
||||
ShowAlert(exception); |
DraftEventList = await _eventAppService.GetDraftEventsByUserId(CurrentUser.GetId()); |
||||
await FillOrganizationsAsync(); |
|
||||
await FillCountriesAsync(); |
|
||||
FillLanguages(); |
|
||||
return Page(); |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
private async Task FillOrganizationsAsync() |
|
||||
{ |
|
||||
var result = await _organizationAppService.GetOrganizationsByUserIdAsync(CurrentUser.GetId()); |
|
||||
Organizations = result.Items.Select( |
|
||||
organization => new SelectListItem |
|
||||
{ |
|
||||
Value = organization.Id.ToString(), |
|
||||
Text = organization.DisplayName |
|
||||
} |
|
||||
).ToList(); |
|
||||
} |
|
||||
|
|
||||
private async Task FillCountriesAsync() |
|
||||
{ |
|
||||
var result = await _eventAppService.GetCountriesLookupAsync(); |
|
||||
|
|
||||
Countries = result.Select( |
|
||||
country => new SelectListItem |
|
||||
{ |
|
||||
Value = country.Id.ToString(), |
|
||||
Text = country.Name |
|
||||
} |
|
||||
).ToList(); |
|
||||
} |
|
||||
|
|
||||
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 SelectListItem |
|
||||
{ |
|
||||
Value = cultureInfo.TwoLetterISOLanguageName, |
|
||||
Text = cultureInfo.EnglishName |
|
||||
} |
|
||||
).ToList(); |
|
||||
} |
|
||||
|
|
||||
public class NewEventViewModel |
|
||||
{ |
|
||||
[SelectItems(nameof(Organizations))] |
|
||||
[DisplayName("Organization")] |
|
||||
public Guid OrganizationId { get; set; } |
|
||||
|
|
||||
[Required] |
|
||||
[StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)] |
|
||||
public string Title { get; set; } |
|
||||
|
|
||||
[Required] |
|
||||
[DataType(DataType.DateTime)] |
|
||||
public DateTime StartTime { get; set; } = DateTime.Now; |
|
||||
|
|
||||
[Required] |
|
||||
[DataType(DataType.DateTime)] |
|
||||
public DateTime EndTime { get; set; } = DateTime.Now; |
|
||||
|
|
||||
[Required] |
|
||||
[StringLength(EventConsts.MaxDescriptionLength, MinimumLength = EventConsts.MinDescriptionLength)] |
|
||||
[TextArea] |
|
||||
public string Description { get; set; } |
|
||||
|
|
||||
[CanBeNull] |
|
||||
[Display(Name = "Cover Image")] |
|
||||
[DataType(DataType.Upload)] |
|
||||
[MaxFileSize(EventConsts.MaxCoverImageFileSize)] |
|
||||
[AllowedExtensions(new string[] {".jpg", ".png", ".jpeg"})] |
|
||||
public IFormFile CoverImageFile { get; set; } |
|
||||
|
|
||||
[Required] public bool? IsOnline { get; set; } |
|
||||
|
|
||||
[CanBeNull] |
|
||||
[StringLength(EventConsts.MaxOnlineLinkLength, MinimumLength = EventConsts.MinOnlineLinkLength)] |
|
||||
public string OnlineLink { get; set; } |
|
||||
|
|
||||
[SelectItems(nameof(Countries))] |
|
||||
[DisplayName("Country")] |
|
||||
public Guid? CountryId { get; set; } |
|
||||
|
|
||||
[CanBeNull] |
|
||||
[StringLength(EventConsts.MaxCityLength, MinimumLength = EventConsts.MinCityLength)] |
|
||||
public string City { get; set; } |
|
||||
|
|
||||
[SelectItems(nameof(Languages))] |
|
||||
[DisplayName("Language")] |
|
||||
public string Language { get; set; } |
|
||||
|
|
||||
[Range(1, int.MaxValue)] public int? Capacity { get; set; } |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue