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.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
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.UI.Bootstrap.TagHelpers.Form; |
|||
using Volo.Abp.Content; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace EventHub.Web.Pages.Events |
|||
{ |
|||
public class NewPageModel : EventHubPageModel |
|||
{ |
|||
[BindProperty] |
|||
public NewEventViewModel Event { get; set; } |
|||
[BindProperty(SupportsGet = true)] |
|||
public string EventUrlCode { get; set; } |
|||
|
|||
public List<SelectListItem> Organizations { get; private set; } |
|||
public List<SelectListItem> Countries { get; private set; } |
|||
public List<SelectListItem> Languages { get; private set; } |
|||
[BindProperty(SupportsGet = true)] |
|||
public bool IsCreateNew { get; set; } |
|||
|
|||
public bool IsHasOrganizations { get; set; } |
|||
|
|||
public List<EventInListDto> DraftEventList { get; set; } |
|||
|
|||
private readonly IEventAppService _eventAppService; |
|||
private readonly IOrganizationAppService _organizationAppService; |
|||
|
|||
public NewPageModel( |
|||
IEventAppService eventAppService, |
|||
IEventAppService eventAppService, |
|||
IOrganizationAppService organizationAppService) |
|||
{ |
|||
_eventAppService = eventAppService; |
|||
_organizationAppService = organizationAppService; |
|||
|
|||
DraftEventList = new List<EventInListDto>(); |
|||
} |
|||
|
|||
public async Task OnGetAsync() |
|||
{ |
|||
Event = new NewEventViewModel |
|||
{ |
|||
StartTime = DateTime.Now.ClearTime().AddDays(1).AddHours(19), |
|||
EndTime = DateTime.Now.ClearTime().AddDays(1).AddHours(21) |
|||
}; |
|||
IsHasOrganizations = (await _organizationAppService.GetOrganizationsByUserIdAsync(CurrentUser.GetId())).Items.Any(); |
|||
|
|||
await FillOrganizationsAsync(); |
|||
await FillCountriesAsync(); |
|||
FillLanguages(); |
|||
} |
|||
|
|||
public async Task<IActionResult> OnPostAsync() |
|||
{ |
|||
try |
|||
if (!EventUrlCode.IsNullOrWhiteSpace()) |
|||
{ |
|||
ValidateModel(); |
|||
|
|||
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}); |
|||
return; |
|||
} |
|||
catch (Exception exception) |
|||
|
|||
if (IsHasOrganizations && !IsCreateNew) |
|||
{ |
|||
ShowAlert(exception); |
|||
await FillOrganizationsAsync(); |
|||
await FillCountriesAsync(); |
|||
FillLanguages(); |
|||
return Page(); |
|||
DraftEventList = await _eventAppService.GetDraftEventsByUserId(CurrentUser.GetId()); |
|||
} |
|||
} |
|||
|
|||
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