Browse Source

feat: add ability to edit speakers with Session

pull/79/head
berkansasmaz 5 years ago
parent
commit
6eac5bcc36
No known key found for this signature in database GPG Key ID: 884D815C3F32BE00
  1. 2
      src/EventHub.Application.Contracts/Events/IEventAppService.cs
  2. 33
      src/EventHub.Application.Contracts/Events/UpdateSessionDto.cs
  3. 51
      src/EventHub.Application/Events/EventAppService.cs
  4. 2
      src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs
  5. 1
      src/EventHub.Domain.Shared/EventHubErrorCodes.cs
  6. 4
      src/EventHub.Domain.Shared/Localization/EventHub/en.json
  7. 17
      src/EventHub.Domain/Events/Event.cs
  8. 14
      src/EventHub.Domain/Events/Track.cs
  9. 7
      src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs
  10. 7
      src/EventHub.HttpApi/Controllers/Events/EventController.cs
  11. 2
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/CreateEventAreaViewComponent.cs
  12. 71
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/_addSessionSection.cshtml
  13. 2
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/_newEventSection.cshtml
  14. 64
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/create-event-area.js
  15. 8
      src/EventHub.Web/Pages/Events/New.cshtml

2
src/EventHub.Application.Contracts/Events/IEventAppService.cs

@ -33,6 +33,8 @@ namespace EventHub.Events
Task AddSessionAsync(Guid id, AddSessionDto input);
Task UpdateSessionAsync(Guid id, Guid trackId, Guid sessionId, UpdateSessionDto input);
Task<IRemoteStreamContent> GetCoverImageAsync(Guid id);
}
}

33
src/EventHub.Application.Contracts/Events/UpdateSessionDto.cs

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EventHub.Events;
public class UpdateSessionDto
{
[Required]
[StringLength(SessionConsts.MaxTitleLength, MinimumLength = SessionConsts.MinTitleLength)]
public string Title { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime StartTime { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime EndTime { get; set; }
[Required]
[StringLength(SessionConsts.MaxDescriptionLength, MinimumLength = SessionConsts.MinDescriptionLength)]
public string Description { get; set; }
public string Language { get; set; }
public List<string> SpeakerUserNames { get; set; }
public UpdateSessionDto()
{
SpeakerUserNames = new List<string>();
}
}

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

@ -13,6 +13,7 @@ using Volo.Abp.Authorization;
using Volo.Abp.BlobStoring;
using Volo.Abp.Content;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.Users;
namespace EventHub.Events
@ -64,8 +65,8 @@ namespace EventHub.Events
var @event = await _eventManager.CreateAsync(
organization,
input.Title,
input.StartTime,
input.EndTime,
Clock.Normalize(input.StartTime),
Clock.Normalize(input.EndTime),
input.Description
);
@ -259,7 +260,7 @@ namespace EventHub.Events
@event.SetTitle(input.Title);
@event.SetDescription(input.Description);
@event.Language = input.Language;
@event.SetTime(input.StartTime, input.EndTime);
@event.SetTime(Clock.Normalize(input.StartTime), Clock.Normalize(input.EndTime));
await _eventManager.SetCapacityAsync(@event, input.Capacity);
if (input.CoverImageStreamContent != null && input.CoverImageStreamContent.ContentLength > 0)
@ -311,13 +312,13 @@ namespace EventHub.Events
public async Task AddSessionAsync(Guid id, AddSessionDto input)
{
var @event = await _eventRepository.GetAsync(id);
@event.AddSession(
input.TrackId,
GuidGenerator.Create(),
input.Title,
input.StartTime,
input.EndTime,
Clock.Normalize(input.StartTime),
Clock.Normalize(input.EndTime),
input.Description,
input.Language,
await GetUserIdsByUserNamesAsync(input.SpeakerUserNames)
@ -326,6 +327,25 @@ namespace EventHub.Events
await _eventRepository.UpdateAsync(@event);
}
[Authorize]
public async Task UpdateSessionAsync(Guid id, Guid trackId, Guid sessionId, UpdateSessionDto input)
{
var @event = await _eventRepository.GetAsync(id);
@event.UpdateSession(
trackId,
sessionId,
input.Title,
Clock.Normalize(input.StartTime),
Clock.Normalize(input.EndTime),
input.Description,
input.Language,
await GetUserIdsByUserNamesAsync(input.SpeakerUserNames)
);
await _eventRepository.UpdateAsync(@event, true);
}
public async Task<IRemoteStreamContent> GetCoverImageAsync(Guid id)
{
var blobName = id.ToString();
@ -349,20 +369,13 @@ namespace EventHub.Events
private async Task<List<Guid>> GetUserIdsByUserNamesAsync(List<string> userNames)
{
var speakerUserIDs = new List<Guid>();
foreach (var userName in userNames.Where(userName => !userName.IsNullOrWhiteSpace()))
{
var user = await _userRepository.FindAsync(x => x.UserName == userName.Trim());
if (user is null)
{
throw new BusinessException(EventHubErrorCodes.UserNotFound)
.WithData("UserName", userName);
}
var userQueryable = await _userRepository.GetQueryableAsync();
speakerUserIDs.Add(user.Id);
}
return speakerUserIDs;
var query = userQueryable
.Where(u => userNames.Any(p => p == u.UserName))
.Select(x => x.Id);
return await AsyncExecuter.ToListAsync(query);
}
private async Task CheckOwnerControlAsync(Event @event)

2
src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs

@ -62,4 +62,4 @@ namespace EventHub
Configure<EventHubUrlOptions>(configuration.GetSection("AppUrls"));
}
}
}
}

1
src/EventHub.Domain.Shared/EventHubErrorCodes.cs

@ -18,5 +18,6 @@
public const string OrganizationNotFound = "EventHub:OrganizationNotFound";
public const string TrackNameAlreadyExist = "EventHub:TrackNameAlreadyExist";
public const string TrackNotFound = "EventHub:TrackNotFound";
public const string SessionNotFound = "EventHub:SessionNotFound";
}
}

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

@ -154,6 +154,8 @@
"InPerson": "In Person",
"UpgradeToPremium": "Upgrade To Premium",
"EventHub:TrackNameAlreadyExist": "The track {Name} already exists",
"EventHub:TrackNotFound": "The track not found!"
"EventHub:TrackNotFound": "The track not found!",
"EventHub:SessionNotFound": "The session not found!",
"EventHub:SessionTimeShouldBeInTheEventTime": "Session time should be in the event time!"
}
}

17
src/EventHub.Domain/Events/Event.cs

@ -196,6 +196,23 @@ namespace EventHub.Events
track.AddSession(sessionId, title, startTime, endTime, description, language, speakerUserIds);
return this;
}
public Event UpdateSession(
Guid trackId,
Guid sessionId,
string title,
DateTime startTime,
DateTime endTime,
string description,
string language,
ICollection<Guid> speakerUserIds)
{
var track = GetTrack(trackId);
track.RemoveSession(sessionId);
AddSession(trackId, sessionId, title, startTime, endTime, description, language, speakerUserIds);
return this;
}
public Event Publish(bool isPublish = true)
{

14
src/EventHub.Domain/Events/Track.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
@ -63,5 +64,18 @@ namespace EventHub.Events
return this;
}
internal Track RemoveSession(Guid sessionId)
{
var session = Sessions.SingleOrDefault(x => x.Id == sessionId);
if (session is null)
{
throw new BusinessException(EventHubErrorCodes.SessionNotFound);
}
Sessions.Remove(session);
return this;
}
}
}

7
src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs

@ -30,6 +30,7 @@ using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
using Volo.Abp.Timing;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
@ -63,6 +64,7 @@ namespace EventHub
ConfigureSwaggerServices(context, configuration);
ConfigureBackgroundJobs();
ConfigureAutoApiControllers();
ConfigureTiming();
}
private void ConfigureAutoApiControllers()
@ -185,6 +187,11 @@ namespace EventHub
{
context.Services.AddSameSiteCookiePolicy();
}
private void ConfigureTiming()
{
Configure<AbpClockOptions>(options => { options.Kind = DateTimeKind.Utc; });
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{

7
src/EventHub.HttpApi/Controllers/Events/EventController.cs

@ -109,6 +109,13 @@ namespace EventHub.Controllers.Events
await _eventAppService.AddSessionAsync(id, input);
}
[HttpPut]
[Route("{id}/tracks/{trackId}/sessions/{sessionId}")]
public async Task UpdateSessionAsync(Guid id, Guid trackId, Guid sessionId, UpdateSessionDto input)
{
await _eventAppService.UpdateSessionAsync(id, trackId, sessionId, input);
}
[HttpGet]
[Route("cover-image/{id}")]
public async Task<IRemoteStreamContent> GetCoverImageAsync(Guid id)

2
src/EventHub.Web/Pages/Events/Components/CreateEventArea/CreateEventAreaViewComponent.cs

@ -59,7 +59,7 @@ public class CreateEventAreaViewComponent : AbpViewComponent
model ??= new NewEventViewModel
{
StartTime = DateTime.Now.ClearTime().AddDays(1).AddHours(19),
EndTime = DateTime.Now.ClearTime().AddDays(1).AddHours(21)
EndTime = DateTime.Now.ClearTime().AddDays(2).AddHours(21)
};
ViewData["StepType"] = stepType.ToString();

71
src/EventHub.Web/Pages/Events/Components/CreateEventArea/_addSessionSection.cshtml

@ -47,6 +47,13 @@
<td>
@foreach (var session in track.Sessions)
{
<input type="hidden" id="TitleOfSession-@session.Id" value="@session.Title"/>
<input type="hidden" id="DescriptionOfSession-@session.Id" value="@session.Description"/>
<input type="datetime-local" id="StartTimeOfSession-@session.Id" value="@session.StartTime.ToString("s")" style="display: none"/>
<input type="datetime-local" id="EndTimeOfSession-@session.Id" value="@session.EndTime.ToString("s")" style="display: none"/>
<input type="hidden" id="LanguageOfSession-@session.Id" value="@session.Language"/>
<input type="hidden" id="SpeakersOfSession-@session.Id" value="@(session.Speakers.Any() ? string.Join(", ", session.Speakers.Select(x => x.UserName)).EnsureEndsWith(',').EnsureEndsWith(' ') : "")"/>
<div class="track-item">
<span class="track-name truncate">@session.Title</span>
<small class="text-primary d-block my-2">@EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(session.StartTime), TimeOnly.FromDateTime(session.EndTime)) | @session.Language</small>
@ -54,7 +61,7 @@
{
<span class="d-block mb-2">@speaker.UserName</span>
}
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#editSessionModal" class="mt-3 d-block text-info">
<a id="@session.Id" data-track-id="@track.Id" href="javascript:;" data-bs-toggle="modal" data-bs-target="#EditSessionModal" class="mt-3 d-block text-info edit-session-button">
<small><i class="fa fa-edit me-1"></i>Edit</small>
</a>
</div>
@ -135,39 +142,49 @@
</div>
<!-- Edit Session Modal -->
<div class="modal fade" id="editSessionModal" tabindex="-1" aria-labelledby="editSessionModal" aria-hidden="true">
<div class="modal fade" id="EditSessionModal" tabindex="-1" aria-labelledby="EditSessionModal" 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">Edit This Session</h5>
<h5 id="EditModalTitle" class="modal-title">Edit Session</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 mb-3">
<label for="Title2">Title</label>
<input type="text" class="form-control" id="Title2" placeholder="Lorem Upsym Title">
</div>
<div class="form mb-3">
<label for="StartTime2">Start Time</label>
<input type="text" class="form-control" id="StartTime2" placeholder="09:00">
</div>
<div class="form mb-3">
<label for="EndTime2">End Time</label>
<input type="text" class="form-control" id="EndTime2" placeholder="10:00">
</div>
<div class="form mb-3">
<label for="Language2">Language</label>
<input type="text" class="form-control" id="Language2" placeholder="English">
<form id="EditSessionForm">
<input type="hidden" id="TrackId" value="TrackIdPlaceHolder"/>
<input type="hidden" id="SessionId" value="SessionIdPlaceHolder"/>
<div class="modal-body profile-content px-5">
<div class="form mb-3">
<label for="TitleInEditSessionModal">Title</label>
<input type="text" class="form-control" id="TitleInEditSessionModal" name="Title" placeholder=" " required minLength=@SessionConsts.MinTitleLength maxLength=@SessionConsts.MaxTitleLength />
</div>
<div class="form mb-3">
<label for="DescriptionInEditSessionModal">Description</label>
<textarea type="text" class="form-control" id="DescriptionInEditSessionModal" name="Description" placeholder=" " required minLength=@SessionConsts.MinDescriptionLength maxLength=@SessionConsts.MaxDescriptionLength></textarea>
</div>
<div class="form mb-3">
<label for="StartTimeInEditSessionModal">Start Time</label>
<input type="datetime-local" class="form-control" id="StartTimeInEditSessionModal" name="StartTime" placeholder=" " required />
</div>
<div class="form mb-3">
<label for="EndTimeInEditSessionModal">End Time</label>
<input type="datetime-local" class="form-control" id="EndTimeInEditSessionModal" name="EndTime" placeholder=" " required />
</div>
<div class="form mb-3">
<label for="LanguageInEditSessionModal">Language</label>
<select id="LanguageInEditSessionModal" name="Language" asp-items="languages" aria-label="en" class="form-select" required>
</select>
</div>
<div class="form mb-3">
<label for="SpeakersInEditSessionModal">Speakers</label>
<input type="text" class="form-control" id="SpeakersInEditSessionModal" placeholder="Speakers" data-multiple />
</div>
</div>
<div class="form mb-3">
<label for="Speakers">Speakers</label>
<input type="text" class="form-control" id="Speakers" placeholder="@@hikalkan">
<div class="modal-footer border-0 p-5 pt-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary"><i class="fa fa-check-circle me-2"></i>Save</button>
</div>
</div>
<div class="modal-footer border-0 p-5 pt-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary"><i class="fa fa-check-circle me-2"></i>Save</button>
</div>
</form>
</div>
</div>
</div>

2
src/EventHub.Web/Pages/Events/Components/CreateEventArea/_newEventSection.cshtml

@ -130,7 +130,7 @@
<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">
<button id="CreateEventButton" disabled type="submit" class="btn btn-primary btn-lg">
Next Step
</button>
</div>

64
src/EventHub.Web/Pages/Events/Components/CreateEventArea/create-event-area.js

@ -1,8 +1,9 @@
(function () {
abp.widgets.CreateEventArea = function ($wrapper) {
var l = abp.localization.getResource('EventHub');
toastr.options.timeOut = 500;
toastr.options.positionClass = "toast-top-right";
toastr.options.preventDuplicates = true;
var stepType = {
"NewEvent": "NewEvent",
"NewTrack": "NewTrack",
@ -24,6 +25,7 @@
}
var init = function () {
$('#CreateEventButton').removeAttr('disabled')
FocusTrackNameInputInModalEventHandler("AddTrackModal")
FocusTrackNameInputInModalEventHandler("EditTrackModal")
@ -67,6 +69,8 @@
OpenAddNewSessionModalClickEventHandler();
AddNewSessionEventHandler();
OpenEditSessionModalClickEventHandler();
EditSessionEventHandler();
PrevieousOrNextStepTransitionEventHandler()
};
@ -172,6 +176,51 @@
});
}
function OpenEditSessionModalClickEventHandler() {
$('.edit-session-button').click(function (e) {
var editSessionModal = $('#EditSessionModal');
var clickedSessionId = this.id;
var trackId = $('#' + clickedSessionId).data('track-id');
editSessionModal.find('#TrackId').val(trackId);
editSessionModal.find('#SessionId').val(clickedSessionId);
var titleOfSession = $('#TitleOfSession-' + clickedSessionId).val();
$('#EditModalTitle').html('Edit ' + abp.utils.truncateStringWithPostfix(titleOfSession, 10) + ' Session');
editSessionModal.find('#TitleInEditSessionModal').val(titleOfSession);
editSessionModal.find('#DescriptionInEditSessionModal').val($('#DescriptionOfSession-' + clickedSessionId).val());
editSessionModal.find('#StartTimeInEditSessionModal').val($('#StartTimeOfSession-' + clickedSessionId).val());
editSessionModal.find('#EndTimeInEditSessionModal').val($('#EndTimeOfSession-' + clickedSessionId).val());
editSessionModal.find('#LanguageInEditSessionModal').val($('#LanguageOfSession-' + clickedSessionId).val());
editSessionModal.find('#SpeakersInEditSessionModal').val($('#SpeakersOfSession-' + clickedSessionId).val());
CreateAutoCompleteByElement("SpeakersInEditSessionModal");
});
}
function EditSessionEventHandler() {
var editSessionForm = $('#EditSessionForm');
editSessionForm.submit(function (e) {
e.preventDefault();
if (!editSessionForm.valid()) {
return false;
}
var userNameList = $('#SpeakersInEditSessionModal').val().split(/[ , ]+/);
userNameList = userNameList.filter(v => v !== '');
var input = editSessionForm.serializeFormToObject();
input.speakerUserNames = userNameList;
eventApiService.updateSession(eventIdInput.val(), editSessionForm.find('#TrackId').val(), editSessionForm.find('#SessionId').val(), input).then(function () {
$('#EditSessionModal').modal('hide');
abp.notify.success('Updated the session');
FillFilter(stepType.NewSession);
widgetManager.refresh();
});
});
}
function PrevieousOrNextStepTransitionEventHandler() {
$('.step-transition').click(function (e) {
var clickedButtonId = this.id;
@ -246,6 +295,17 @@
ButtonsBusy(false)
});
abp.ajax.showError = function (error) {
abp.notify.error(
l(error.code),
'Error',
toastr.options = {
timeOut: 2000,
progressBar: true
}
);
}
function ButtonsBusy(isBusy) {
if (isBusy) {
$('button[type=submit]').attr('disabled', 'disabled')

8
src/EventHub.Web/Pages/Events/New.cshtml

@ -6,8 +6,10 @@
@model EventHub.Web.Pages.Events.NewPageModel
@section scripts {
<abp-script src="/Pages/Events/New.js"/>
<abp-script src="/libs/awesomplete/awesomplete.min.js" />
<abp-script-bundle>
<abp-script src="/Pages/Events/New.js"/>
<abp-script src="/libs/awesomplete/awesomplete.min.js" />
</abp-script-bundle>
}
@section styles {
@ -71,7 +73,7 @@
<div class="col-5 text-end">
<span>
Last edit:
@if (draftEvent.LastModificationTime is null)
@if (!draftEvent.LastModificationTime.HasValue)
{
<span>@draftEvent.CreationTime.ToShortDateString()</span>
}

Loading…
Cancel
Save