Browse Source

Merge pull request #79 from volosoft/tracks-and-sessions

Add Track, Session and Speaker system
pull/84/head
Halil İbrahim Kalkan 5 years ago
committed by GitHub
parent
commit
78e7c057a5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      modules/payment/src/Payment.Web/Pages/Payment/PostCheckout.cshtml.cs
  2. 24
      modules/payment/src/Payment.Web/Pages/Payment/PreCheckout.cshtml.cs
  3. 6
      src/EventHub.Application.Contracts/EventHubApplicationContractsModule.cs
  4. 33
      src/EventHub.Application.Contracts/Events/AddSessionDto.cs
  5. 10
      src/EventHub.Application.Contracts/Events/AddTrackDto.cs
  6. 6
      src/EventHub.Application.Contracts/Events/DeleteTractDto.cs
  7. 9
      src/EventHub.Application.Contracts/Events/EventDetailDto.cs
  8. 7
      src/EventHub.Application.Contracts/Events/EventInListDto.cs
  9. 16
      src/EventHub.Application.Contracts/Events/IEventAppService.cs
  10. 27
      src/EventHub.Application.Contracts/Events/SessionDto.cs
  11. 13
      src/EventHub.Application.Contracts/Events/SpeakerDto.cs
  12. 17
      src/EventHub.Application.Contracts/Events/TrackDto.cs
  13. 2
      src/EventHub.Application.Contracts/Events/UpdateEventDto.cs
  14. 33
      src/EventHub.Application.Contracts/Events/UpdateSessionDto.cs
  15. 10
      src/EventHub.Application.Contracts/Events/UpdateTrackDto.cs
  16. 5
      src/EventHub.Application.Contracts/Users/IUserAppService.cs
  17. 6
      src/EventHub.Application.Contracts/Users/UserInListDto.cs
  18. 9
      src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs
  19. 255
      src/EventHub.Application/Events/EventAppService.cs
  20. 23
      src/EventHub.Application/Users/UserAppService.cs
  21. 6
      src/EventHub.BackgroundServices/Events/EventReminderNotifier.cs
  22. 4
      src/EventHub.BackgroundServices/Events/EventReminderWorker.cs
  23. 6
      src/EventHub.BackgroundServices/Events/EventTimingChangeNotifier.cs
  24. 4
      src/EventHub.BackgroundServices/Events/EventTimingChangeWorker.cs
  25. 4
      src/EventHub.BackgroundServices/Events/NewEventNotifier.cs
  26. 4
      src/EventHub.BackgroundServices/Events/NewEventWorker.cs
  27. 2
      src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs
  28. 12
      src/EventHub.Domain.Shared/EventHubErrorCodes.cs
  29. 8
      src/EventHub.Domain.Shared/Events/EventConsts.cs
  30. 14
      src/EventHub.Domain.Shared/Events/SessionConsts.cs
  31. 7
      src/EventHub.Domain.Shared/Events/TrackConsts.cs
  32. 11
      src/EventHub.Domain.Shared/Localization/EventHub/en.json
  33. 10
      src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs
  34. 7
      src/EventHub.Domain/Countries/Country.cs
  35. 400
      src/EventHub.Domain/Data/EventHubDataSeedContributor.cs
  36. 157
      src/EventHub.Domain/Events/Event.cs
  37. 2
      src/EventHub.Domain/Events/EventManager.cs
  38. 30
      src/EventHub.Domain/Events/EventTimeChangeLocalEventHandler.cs
  39. 18
      src/EventHub.Domain/Events/EventTimeChangingEventData.cs
  40. 13
      src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs
  41. 122
      src/EventHub.Domain/Events/Session.cs
  42. 28
      src/EventHub.Domain/Events/Speaker.cs
  43. 140
      src/EventHub.Domain/Events/Track.cs
  44. 10
      src/EventHub.Domain/Users/UserWithoutDetails.cs
  45. 37
      src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubDbContextModelCreatingExtensions.cs
  46. 67
      src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Events/EventRepository.cs
  47. 4
      src/EventHub.EntityFrameworkCore/EventHub.EntityFrameworkCore.csproj
  48. 2747
      src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.Designer.cs
  49. 115
      src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.cs
  50. 2831
      src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.Designer.cs
  51. 104
      src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.cs
  52. 130
      src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs
  53. 6
      src/EventHub.HttpApi.Client/EventHubHttpApiClientModule.cs
  54. 97
      src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs
  55. 8
      src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs
  56. 6
      src/EventHub.HttpApi/Controllers/EventHubController.cs
  57. 155
      src/EventHub.HttpApi/Controllers/Events/EventController.cs
  58. 4
      src/EventHub.HttpApi/Controllers/Events/Registrations/EventRegistrationController.cs
  59. 4
      src/EventHub.HttpApi/Controllers/Organizations/Memberships/OrganizationMembershipController.cs
  60. 4
      src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs
  61. 16
      src/EventHub.HttpApi/Controllers/Users/UserController.cs
  62. 6
      src/EventHub.Web.Theme/Themes/EventHub/Components/MainNavbar/Default.cshtml
  63. 3
      src/EventHub.Web.Theme/Themes/EventHub/Layouts/Application.cshtml
  64. 10
      src/EventHub.Web.Theme/wwwroot/themes/eventhub/style.css
  65. 4
      src/EventHub.Web/Controllers/EventController.cs
  66. 10
      src/EventHub.Web/Controllers/EventRegistrationController.cs
  67. 4
      src/EventHub.Web/Controllers/OrganizationController.cs
  68. 13
      src/EventHub.Web/Controllers/OrganizationMembershipController.cs
  69. 34
      src/EventHub.Web/Controllers/WidgetsController.cs
  70. 6
      src/EventHub.Web/EventHubWebAutoMapperProfile.cs
  71. 34
      src/EventHub.Web/EventHubWebModule.cs
  72. 853
      src/EventHub.Web/Pages/Empty.cshtml
  73. 10
      src/EventHub.Web/Pages/Empty.cshtml.cs
  74. 214
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/CreateOrEditEventAreaViewComponent.cs
  75. 35
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/Default.cshtml
  76. 170
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_eventSection.cshtml
  77. 3
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_modelNullErrorSection.cshtml
  78. 231
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_previewSection.cshtml
  79. 20
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_progressSection.cshtml
  80. 208
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_sessionSection.cshtml
  81. 105
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_trackSection.cshtml
  82. 3
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.css
  83. 512
      src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.js
  84. 4
      src/EventHub.Web/Pages/Events/Components/EventsArea/_eventListSection.cshtml
  85. 2
      src/EventHub.Web/Pages/Events/Components/RegistrationArea/RegistrationAreaViewComponent.cs
  86. 89
      src/EventHub.Web/Pages/Events/Detail.cshtml
  87. 140
      src/EventHub.Web/Pages/Events/Edit.cshtml
  88. 193
      src/EventHub.Web/Pages/Events/Edit.cshtml.cs
  89. 94
      src/EventHub.Web/Pages/Events/Edit.js
  90. 11
      src/EventHub.Web/Pages/Events/EventDateHelper.cs
  91. 214
      src/EventHub.Web/Pages/Events/New.cshtml
  92. 164
      src/EventHub.Web/Pages/Events/New.cshtml.cs
  93. 94
      src/EventHub.Web/Pages/Events/New.js
  94. 4
      src/EventHub.Web/Pages/Organizations/Components/JoinArea/JoinAreaViewComponent.cs
  95. 56
      src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml
  96. 44
      src/EventHub.Web/Pages/Organizations/Profile.cshtml
  97. 29
      src/EventHub.Web/Pages/Organizations/Profile.js
  98. 77
      src/EventHub.Web/Pages/Payment/CustomPostCheckout.cshtml.cs
  99. 23
      src/EventHub.Web/Pages/Payment/PostCheckout.cshtml
  100. 115
      src/EventHub.Web/Pages/Payment/PreCheckout.cshtml

26
modules/payment/src/Payment.Web/Pages/Payment/PostCheckout.cshtml.cs

@ -16,42 +16,42 @@ namespace Payment.Web.Pages.Payment
public string GoBackLink { get; set; } public string GoBackLink { get; set; }
private readonly IPaymentRequestAppService _paymentRequestAppService; protected IPaymentRequestAppService PaymentRequestAppService { get; }
private readonly PaymentWebOptions _paymentWebOptions; protected PaymentWebOptions PaymentWebOptions { get; }
private readonly IPaymentUrlBuilder _paymentUrlBuilder; protected IPaymentUrlBuilder PaymentUrlBuilder { get; }
public PostCheckoutPageModel( public PostCheckoutPageModel(
IPaymentRequestAppService appService, IPaymentRequestAppService appService,
IOptions<PaymentWebOptions> paymentWebOptions, IOptions<PaymentWebOptions> paymentWebOptions,
IPaymentUrlBuilder paymentUrlBuilder) IPaymentUrlBuilder paymentUrlBuilder)
{ {
_paymentRequestAppService = appService; PaymentRequestAppService = appService;
_paymentWebOptions = paymentWebOptions.Value; PaymentWebOptions = paymentWebOptions.Value;
_paymentUrlBuilder = paymentUrlBuilder; PaymentUrlBuilder = paymentUrlBuilder;
} }
public async Task<IActionResult> OnGetAsync() public virtual async Task<IActionResult> OnGetAsync()
{ {
if (Token.IsNullOrWhiteSpace()) if (Token.IsNullOrWhiteSpace())
{ {
return BadRequest(); return BadRequest();
} }
PaymentRequest = await _paymentRequestAppService.CompleteAsync(Token); PaymentRequest = await PaymentRequestAppService.CompleteAsync(Token);
GoBackLink = _paymentUrlBuilder.BuildCheckoutUrl(PaymentRequest.Id).AbsoluteUri; GoBackLink = PaymentUrlBuilder.BuildCheckoutUrl(PaymentRequest.Id).AbsoluteUri;
if (PaymentRequest.State == PaymentRequestState.Completed if (PaymentRequest.State == PaymentRequestState.Completed
&& !_paymentWebOptions.PaymentSuccessfulCallbackUrl.IsNullOrWhiteSpace()) && !PaymentWebOptions.PaymentSuccessfulCallbackUrl.IsNullOrWhiteSpace())
{ {
var callbackUrl = _paymentWebOptions.PaymentSuccessfulCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id; var callbackUrl = PaymentWebOptions.PaymentSuccessfulCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id;
Response.Redirect(callbackUrl); Response.Redirect(callbackUrl);
} }
if (PaymentRequest.State == PaymentRequestState.Failed if (PaymentRequest.State == PaymentRequestState.Failed
&& !_paymentWebOptions.PaymentFailureCallbackUrl.IsNullOrWhiteSpace()) && !PaymentWebOptions.PaymentFailureCallbackUrl.IsNullOrWhiteSpace())
{ {
var callbackUrl = _paymentWebOptions.PaymentFailureCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id; var callbackUrl = PaymentWebOptions.PaymentFailureCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id;
Response.Redirect(callbackUrl); Response.Redirect(callbackUrl);
} }

24
modules/payment/src/Payment.Web/Pages/Payment/PreCheckout.cshtml.cs

@ -14,36 +14,36 @@ namespace Payment.Web.Pages.Payment
public PaymentRequestDto PaymentRequest { get; set; } public PaymentRequestDto PaymentRequest { get; set; }
private readonly IPaymentRequestAppService _paymentRequestAppService; protected IPaymentRequestAppService PaymentRequestAppService { get; }
private readonly IPaymentUrlBuilder _paymentUrlBuilder; protected IPaymentUrlBuilder PaymentUrlBuilder { get; }
public PreCheckoutPageModel( public PreCheckoutPageModel(
IPaymentRequestAppService paymentRequestAppService, IPaymentRequestAppService paymentRequestAppService,
IPaymentUrlBuilder paymentUrlBuilder) IPaymentUrlBuilder paymentUrlBuilder)
{ {
_paymentRequestAppService = paymentRequestAppService; PaymentRequestAppService = paymentRequestAppService;
_paymentUrlBuilder = paymentUrlBuilder; PaymentUrlBuilder = paymentUrlBuilder;
} }
public async Task<IActionResult> OnGetAsync() public virtual async Task<IActionResult> OnGetAsync()
{ {
PaymentRequest = await _paymentRequestAppService.GetAsync(PaymentRequestId); PaymentRequest = await PaymentRequestAppService.GetAsync(PaymentRequestId);
return Page(); return Page();
} }
public async Task OnPostAsync() public virtual async Task OnPostAsync()
{ {
PaymentRequest = await _paymentRequestAppService.GetAsync(PaymentRequestId); PaymentRequest = await PaymentRequestAppService.GetAsync(PaymentRequestId);
var result = await _paymentRequestAppService.StartPaymentAsync(new StartPaymentDto var result = await PaymentRequestAppService.StartPaymentAsync(new StartPaymentDto
{ {
PaymentRequestId = PaymentRequest.Id, PaymentRequestId = PaymentRequest.Id,
ReturnUrl = _paymentUrlBuilder.BuildReturnUrl(PaymentRequestId).AbsoluteUri, ReturnUrl = PaymentUrlBuilder.BuildReturnUrl(PaymentRequestId).AbsoluteUri,
CancelUrl = _paymentUrlBuilder.BuildCheckoutUrl(PaymentRequestId).AbsoluteUri CancelUrl = PaymentUrlBuilder.BuildCheckoutUrl(PaymentRequestId).AbsoluteUri
}); });
Response.Redirect(result.CheckoutLink); Response.Redirect(result.CheckoutLink);
} }
} }
} }

6
src/EventHub.Application.Contracts/EventHubApplicationContractsModule.cs

@ -1,5 +1,6 @@
using Payment; using Payment;
using Volo.Abp.Application; using Volo.Abp.Application;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace EventHub namespace EventHub
@ -11,6 +12,9 @@ namespace EventHub
)] )]
public class EventHubApplicationContractsModule : AbpModule public class EventHubApplicationContractsModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context)
{
LimitedResultRequestDto.MaxMaxResultCount = 30;
}
} }
} }

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

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace EventHub.Events;
public class AddSessionDto
{
[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 AddSessionDto()
{
SpeakerUserNames = new List<string>();
}
}

10
src/EventHub.Application.Contracts/Events/AddTrackDto.cs

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace EventHub.Events;
public class AddTrackDto
{
[Required]
[StringLength(TrackConsts.MaxNameLength)]
public string Name { get; set; }
}

6
src/EventHub.Application.Contracts/Events/DeleteTractDto.cs

@ -0,0 +1,6 @@
namespace EventHub.Events;
public class DeleteTractDto
{
public string Name { get; set; }
}

9
src/EventHub.Application.Contracts/Events/EventDetailDto.cs

@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
namespace EventHub.Events namespace EventHub.Events
@ -40,5 +42,12 @@ namespace EventHub.Events
public string Language { get; set; } public string Language { get; set; }
public int? Capacity { get; set; } public int? Capacity { get; set; }
public List<TrackDto> Tracks { get; set; }
public EventDetailDto()
{
Tracks = new List<TrackDto>();
}
} }
} }

7
src/EventHub.Application.Contracts/Events/EventInListDto.cs

@ -1,9 +1,10 @@
using System; using System;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Auditing;
namespace EventHub.Events namespace EventHub.Events
{ {
public class EventInListDto : EntityDto<Guid> public class EventInListDto : EntityDto<Guid>, IHasCreationTime, IHasModificationTime
{ {
public string OrganizationName { get; set; } public string OrganizationName { get; set; }
@ -30,5 +31,9 @@ namespace EventHub.Events
public string UrlCode { get; set; } public string UrlCode { get; set; }
public string Url { get; set; } public string Url { get; set; }
public DateTime CreationTime { get; set; }
public DateTime? LastModificationTime { get; set; }
} }
} }

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

@ -13,6 +13,8 @@ namespace EventHub.Events
Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input); Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input);
Task<List<EventInListDto>> GetDraftEventsByCurrentUser();
Task<EventDetailDto> GetByUrlCodeAsync(string urlCode); Task<EventDetailDto> GetByUrlCodeAsync(string urlCode);
Task<EventLocationDto> GetLocationAsync(Guid id); Task<EventLocationDto> GetLocationAsync(Guid id);
@ -22,7 +24,21 @@ namespace EventHub.Events
Task<bool> IsEventOwnerAsync(Guid id); Task<bool> IsEventOwnerAsync(Guid id);
Task UpdateAsync(Guid id, UpdateEventDto input); Task UpdateAsync(Guid id, UpdateEventDto input);
Task<string> PublishAsync(Guid id);
Task AddTrackAsync(Guid id, AddTrackDto input);
Task UpdateTrackAsync(Guid id, Guid trackId, UpdateTrackDto input);
Task DeleteTrackAsync(Guid id, Guid trackId);
Task AddSessionAsync(Guid id, Guid trackId, AddSessionDto input);
Task UpdateSessionAsync(Guid id, Guid trackId, Guid sessionId, UpdateSessionDto input);
Task DeleteSessionAsync(Guid id, Guid trackId, Guid sessionId);
Task<IRemoteStreamContent> GetCoverImageAsync(Guid id); Task<IRemoteStreamContent> GetCoverImageAsync(Guid id);
} }
} }

27
src/EventHub.Application.Contracts/Events/SessionDto.cs

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace EventHub.Events;
public class SessionDto : EntityDto<Guid>
{
public Guid TrackId { get; set; }
public string Title { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string Description { get; set; }
public string Language { get; set; }
public List<SpeakerDto> Speakers { get; set; }
public SessionDto()
{
Speakers = new List<SpeakerDto>();
}
}

13
src/EventHub.Application.Contracts/Events/SpeakerDto.cs

@ -0,0 +1,13 @@
using System;
using Volo.Abp.Application.Dtos;
namespace EventHub.Events;
public class SpeakerDto : EntityDto<Guid>
{
public Guid SessionId { get; set; }
public Guid UserId { get; set; }
public string UserName { get; set; }
}

17
src/EventHub.Application.Contracts/Events/TrackDto.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace EventHub.Events;
public class TrackDto : EntityDto<Guid>
{
public string Name { get; set; }
public List<SessionDto> Sessions { get; set; }
public TrackDto()
{
Sessions = new List<SessionDto>();
}
}

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

@ -45,4 +45,4 @@ namespace EventHub.Events
[Range(1, int.MaxValue)] [Range(1, int.MaxValue)]
public int? Capacity { get; set; } public int? Capacity { get; set; }
} }
} }

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>();
}
}

10
src/EventHub.Application.Contracts/Events/UpdateTrackDto.cs

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace EventHub.Events;
public class UpdateTrackDto
{
[Required]
[StringLength(TrackConsts.MaxNameLength)]
public string Name { get; set; }
}

5
src/EventHub.Application.Contracts/Users/IUserAppService.cs

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Members; using EventHub.Members;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
@ -7,5 +8,7 @@ namespace EventHub.Users
public interface IUserAppService : IApplicationService public interface IUserAppService : IApplicationService
{ {
Task<UserDto> FindByUserNameAsync(string username); Task<UserDto> FindByUserNameAsync(string username);
Task<List<UserInListDto>> GetListByUserName(string username);
} }
} }

6
src/EventHub.Application.Contracts/Users/UserInListDto.cs

@ -0,0 +1,6 @@
namespace EventHub.Users;
public class UserInListDto
{
public string UserName { get; set; }
}

9
src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs

@ -31,6 +31,13 @@ namespace EventHub
.Ignore(x => x.OrganizationName) .Ignore(x => x.OrganizationName)
.Ignore(x => x.OrganizationDisplayName); .Ignore(x => x.OrganizationDisplayName);
CreateMap<Track, TrackDto>();
CreateMap<Session, SessionDto>();
CreateMap<Speaker, SpeakerDto>()
.Ignore(x => x.UserName);
CreateMap<IdentityUser, EventAttendeeDto>(); CreateMap<IdentityUser, EventAttendeeDto>();
CreateMap<Event, EventLocationDto>() CreateMap<Event, EventLocationDto>()
@ -39,6 +46,8 @@ namespace EventHub
CreateMap<Country, CountryLookupDto>(); CreateMap<Country, CountryLookupDto>();
CreateMap<IdentityUser, UserDto>(); CreateMap<IdentityUser, UserDto>();
CreateMap<UserWithoutDetails, UserInListDto>();
} }
} }
} }

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

@ -1,18 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Authentication;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Countries; using EventHub.Countries;
using EventHub.Events.Registrations; using EventHub.Events.Registrations;
using EventHub.Organizations; using EventHub.Organizations;
using EventHub.Users; using EventHub.Users;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Volo.Abp.BlobStoring; using Volo.Abp.BlobStoring;
using Volo.Abp.Content; using Volo.Abp.Content;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.Users; using Volo.Abp.Users;
namespace EventHub.Events namespace EventHub.Events
@ -64,8 +65,8 @@ namespace EventHub.Events
var @event = await _eventManager.CreateAsync( var @event = await _eventManager.CreateAsync(
organization, organization,
input.Title, input.Title,
input.StartTime, Clock.Normalize(input.StartTime),
input.EndTime, Clock.Normalize(input.EndTime),
input.Description input.Description
); );
@ -78,7 +79,7 @@ namespace EventHub.Events
{ {
await SaveCoverImageAsync(@event.Id, input.CoverImageStreamContent); await SaveCoverImageAsync(@event.Id, input.CoverImageStreamContent);
} }
await _eventRepository.InsertAsync(@event); await _eventRepository.InsertAsync(@event);
return ObjectMapper.Map<Event, EventDto>(@event); return ObjectMapper.Map<Event, EventDto>(@event);
@ -92,7 +93,8 @@ namespace EventHub.Events
var query = from @event in eventQueryable var query = from @event in eventQueryable
join organization in organizationQueryable on @event.OrganizationId equals organization.Id join organization in organizationQueryable on @event.OrganizationId equals organization.Id
select new {@event, organization}; where !@event.IsDraft
select new { @event, organization };
if (input.RegisteredUserId.HasValue) if (input.RegisteredUserId.HasValue)
{ {
@ -147,27 +149,43 @@ namespace EventHub.Events
query = query.OrderBy(x => x.@event.StartTime); query = query.OrderBy(x => x.@event.StartTime);
} }
var items = await AsyncExecuter.ToListAsync(query); var items = (await AsyncExecuter.ToListAsync(query)).Select(a => (a.@event, a.organization)).ToList();
var now = Clock.Now;
var events = items.Select( var events = GetEventInListDtoFromEventAndOrganizationTupleList(items);
i =>
{
var dto = ObjectMapper.Map<Event, EventInListDto>(i.@event);
dto.OrganizationName = i.organization.Name;
dto.OrganizationDisplayName = i.organization.DisplayName;
dto.IsLiveNow = now.IsBetween(i.@event.StartTime, i.@event.EndTime);
dto.Country = i.@event.CountryName;
return dto;
}
).ToList();
return new PagedResultDto<EventInListDto>(totalCount, events); return new PagedResultDto<EventInListDto>(totalCount, events);
} }
[Authorize]
public async Task<List<EventInListDto>> GetDraftEventsByCurrentUser()
{
var eventQueryable = await _eventRepository.GetQueryableAsync();
var organizationQueryable = await _organizationRepository.GetQueryableAsync();
var userId = CurrentUser.GetId();
var query = from @event in eventQueryable
join organization in organizationQueryable on @event.OrganizationId equals organization.Id
where organization.OwnerUserId == userId && @event.IsDraft
select new { @event, organization };
var items = (await AsyncExecuter.ToListAsync(query)).Select(a => (a.@event, a.organization)).ToList();
return GetEventInListDtoFromEventAndOrganizationTupleList(items);
}
public async Task<EventDetailDto> GetByUrlCodeAsync(string urlCode) public async Task<EventDetailDto> GetByUrlCodeAsync(string urlCode)
{ {
var @event = await _eventRepository.GetAsync(x => x.UrlCode == urlCode); var @event = await _eventRepository.GetAsync(x => x.UrlCode == urlCode, true);
if (@event.IsDraft)
{
if (!CurrentUser.Id.HasValue)
{
throw new AuthenticationException();
}
await CheckIfValidOwnerAsync(@event);
}
var organization = await _organizationRepository.GetAsync(@event.OrganizationId); var organization = await _organizationRepository.GetAsync(@event.OrganizationId);
var dto = ObjectMapper.Map<Event, EventDetailDto>(@event); var dto = ObjectMapper.Map<Event, EventDetailDto>(@event);
@ -175,11 +193,24 @@ namespace EventHub.Events
dto.OrganizationId = organization.Id; dto.OrganizationId = organization.Id;
dto.OrganizationName = organization.Name; dto.OrganizationName = organization.Name;
dto.OrganizationDisplayName = organization.DisplayName; dto.OrganizationDisplayName = organization.DisplayName;
var user = await _userRepository.GetAsync(organization.OwnerUserId); var user = await _userRepository.GetAsync(organization.OwnerUserId);
if (@event.IsDraft)
{
if (user.Id != CurrentUser.GetId())
{
throw new AbpAuthorizationException();
}
}
dto.OwnerUserName = user.UserName; dto.OwnerUserName = user.UserName;
dto.OwnerEmail = user.Email; dto.OwnerEmail = user.Email;
foreach (var speaker in from track in dto.Tracks from session in track.Sessions from speaker in session.Speakers select speaker)
{
speaker.UserName = (await _userRepository.GetAsync(speaker.UserId)).UserName;
}
return dto; return dto;
} }
@ -232,37 +263,132 @@ namespace EventHub.Events
public async Task UpdateAsync(Guid id, UpdateEventDto input) public async Task UpdateAsync(Guid id, UpdateEventDto input)
{ {
var @event = await _eventRepository.GetAsync(id); var @event = await _eventRepository.GetAsync(id);
var organization = await _organizationRepository.GetAsync(@event.OrganizationId); await CheckIfValidOwnerAsync(@event);
if (organization.OwnerUserId != CurrentUser.GetId())
{
throw new AbpAuthorizationException(
L["EventHub:NotAuthorizedToUpdateEvent", @event.Title],
EventHubErrorCodes.NotAuthorizedToUpdateEvent
);
}
await _eventManager.SetLocationAsync(@event, input.IsOnline, input.OnlineLink, input.CountryId, input.City); await _eventManager.SetLocationAsync(@event, input.IsOnline, input.OnlineLink, input.CountryId, input.City);
@event.SetTitle(input.Title); @event.SetTitle(input.Title);
@event.SetDescription(input.Description); @event.SetDescription(input.Description);
@event.Language = input.Language; @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); await _eventManager.SetCapacityAsync(@event, input.Capacity);
if (input.CoverImageStreamContent != null && input.CoverImageStreamContent.ContentLength > 0) if (input.CoverImageStreamContent != null && input.CoverImageStreamContent.ContentLength > 0)
{ {
await SaveCoverImageAsync(@event.Id, input.CoverImageStreamContent); await SaveCoverImageAsync(@event.Id, input.CoverImageStreamContent);
} }
await _eventRepository.UpdateAsync(@event);
}
[Authorize]
public async Task<string> PublishAsync(Guid id)
{
var @event = await _eventRepository.GetAsync(id);
await CheckIfValidOwnerAsync(@event);
@event.Publish();
await _eventRepository.UpdateAsync(@event, true);
return @event.UrlCode;
}
[Authorize]
public async Task AddTrackAsync(Guid id, AddTrackDto input)
{
var @event = await _eventRepository.GetAsync(id, true);
await CheckIfValidOwnerAsync(@event);
@event.AddTrack(
GuidGenerator.Create(),
input.Name
);
await _eventRepository.UpdateAsync(@event); await _eventRepository.UpdateAsync(@event);
} }
[Authorize]
public async Task UpdateTrackAsync(Guid id, Guid trackId, UpdateTrackDto input)
{
var @event = await _eventRepository.GetAsync(id, true);
await CheckIfValidOwnerAsync(@event);
@event.UpdateTrack(
trackId,
input.Name
);
await _eventRepository.UpdateAsync(@event);
}
[Authorize]
public async Task DeleteTrackAsync(Guid id, Guid trackId)
{
var @event = await _eventRepository.GetAsync(id, true);
await CheckIfValidOwnerAsync(@event);
@event.RemoveTrack(trackId);
await _eventRepository.UpdateAsync(@event);
}
[Authorize]
public async Task AddSessionAsync(Guid id, Guid trackId, AddSessionDto input)
{
var @event = await _eventRepository.GetAsync(id);
await CheckIfValidOwnerAsync(@event);
@event.AddSession(
trackId,
GuidGenerator.Create(),
input.Title,
input.Description,
Clock.Normalize(input.StartTime),
Clock.Normalize(input.EndTime),
input.Language,
await GetUserIdsByUserNamesAsync(input.SpeakerUserNames)
);
await _eventRepository.UpdateAsync(@event);
}
[Authorize]
public async Task UpdateSessionAsync(Guid id, Guid trackId, Guid sessionId, UpdateSessionDto input)
{
var @event = await _eventRepository.GetAsync(id);
await CheckIfValidOwnerAsync(@event);
@event.UpdateSession(
trackId,
sessionId,
input.Title,
input.Description,
Clock.Normalize(input.StartTime),
Clock.Normalize(input.EndTime),
input.Language,
await GetUserIdsByUserNamesAsync(input.SpeakerUserNames)
);
await _eventRepository.UpdateAsync(@event);
}
[Authorize]
public async Task DeleteSessionAsync(Guid id, Guid trackId, Guid sessionId)
{
var @event = await _eventRepository.GetAsync(id, true);
await CheckIfValidOwnerAsync(@event);
@event.RemoveSession(trackId, sessionId);
await _eventRepository.UpdateAsync(@event);
}
public async Task<IRemoteStreamContent> GetCoverImageAsync(Guid id) public async Task<IRemoteStreamContent> GetCoverImageAsync(Guid id)
{ {
var blobName = id.ToString(); var blobName = id.ToString();
var coverImageStream = await _eventBlobContainer.GetOrNullAsync(blobName); var coverImageStream = await _eventBlobContainer.GetOrNullAsync(blobName);
if (coverImageStream is null) if (coverImageStream is null)
{ {
return null; return null;
@ -277,5 +403,66 @@ namespace EventHub.Events
await _eventBlobContainer.SaveAsync(blobName, streamContent.GetStream(), overrideExisting: true); await _eventBlobContainer.SaveAsync(blobName, streamContent.GetStream(), overrideExisting: true);
} }
private async Task<List<Guid>> GetUserIdsByUserNamesAsync(List<string> userNames)
{
var userQueryable = await _userRepository.GetQueryableAsync();
var query = userQueryable
.Where(u => userNames.Contains(u.UserName))
.Select(x => x.Id);
var userIds = await AsyncExecuter.ToListAsync(query);
if (userIds.Count != userNames.Count)
{
await CheckIfValidUserNamesAsync(userNames);
}
return userIds;
}
private async Task CheckIfValidOwnerAsync(Event @event)
{
var organization = await _organizationRepository.GetAsync(@event.OrganizationId);
if (organization.OwnerUserId != CurrentUser.GetId())
{
throw new AbpAuthorizationException(
L["EventHub:NotAuthorizedToUpdateEvent", @event.Title],
EventHubErrorCodes.NotAuthorizedToUpdateEvent
);
}
}
private async Task CheckIfValidUserNamesAsync(List<string> speakerUserNames)
{
foreach (var userName in speakerUserNames)
{
var user = await _userRepository.AnyAsync(x => x.UserName == userName.Trim());
if (!user)
{
throw new BusinessException(EventHubErrorCodes.UserNotFound)
.WithData("UserName", userName);
}
}
}
private List<EventInListDto> GetEventInListDtoFromEventAndOrganizationTupleList(List<(Event @event, Organization organization)> items)
{
var now = Clock.Now;
var events = items.Select(
i =>
{
var dto = ObjectMapper.Map<Event, EventInListDto>(i.@event);
dto.OrganizationName = i.organization.Name;
dto.OrganizationDisplayName = i.organization.DisplayName;
dto.IsLiveNow = i.@event.IsLive(now);
dto.Country = i.@event.CountryName;
return dto;
}
).ToList();
return events;
}
} }
} }

23
src/EventHub.Application/Users/UserAppService.cs

@ -1,7 +1,8 @@
using System; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Members; using EventHub.Members;
using Volo.Abp.Domain.Repositories; using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Identity; using Volo.Abp.Identity;
namespace EventHub.Users namespace EventHub.Users
@ -21,5 +22,21 @@ namespace EventHub.Users
return ObjectMapper.Map<IdentityUser, UserDto>(user); return ObjectMapper.Map<IdentityUser, UserDto>(user);
} }
[Authorize]
public async Task<List<UserInListDto>> GetListByUserName(string username)
{
var userQueryable = await _userRepository.GetQueryableAsync();
var query = userQueryable
.Where(u => u.UserName.ToLower().Contains(username))
.Select(x => new UserWithoutDetails
{
Id = x.Id,
UserName = x.UserName
}).Take(10);
return ObjectMapper.Map<List<UserWithoutDetails>, List<UserInListDto>>(await AsyncExecuter.ToListAsync(query));
}
} }
} }

6
src/EventHub.BackgroundServices/Events/EventReminderNotifier.cs

@ -37,7 +37,7 @@ namespace EventHub.Events
public async Task NotifyAsync(Event @event) public async Task NotifyAsync(Event @event)
{ {
if (@event is null || @event.IsRemindingEmailSent) if (@event is null || @event.IsRemindingEmailSent || !@event.IsDraft)
{ {
return; return;
} }
@ -47,7 +47,7 @@ namespace EventHub.Events
var userQuery = from eventRegistration in registrationQueryable var userQuery = from eventRegistration in registrationQueryable
join user in userQueryable on eventRegistration.UserId equals user.Id join user in userQueryable on eventRegistration.UserId equals user.Id
where eventRegistration.EventId == @event.Id where eventRegistration.EventId == @event.Id && !@event.IsDraft
select user; select user;
var users = await _asyncExecuter.ToListAsync(userQuery); var users = await _asyncExecuter.ToListAsync(userQuery);
@ -71,4 +71,4 @@ namespace EventHub.Events
} }
} }
} }
} }

4
src/EventHub.BackgroundServices/Events/EventReminderWorker.cs

@ -32,7 +32,7 @@ namespace EventHub.Events
var queryable = await eventRepository.GetQueryableAsync(); var queryable = await eventRepository.GetQueryableAsync();
var query = queryable.Where(x => var query = queryable.Where(x =>
x.IsRemindingEmailSent == false && x.IsRemindingEmailSent == false &&
x.StartTime <= thirtyMinutesLater x.StartTime <= thirtyMinutesLater && !x.IsDraft
); );
var events = await asyncExecuter.ToListAsync(query); var events = await asyncExecuter.ToListAsync(query);
@ -56,4 +56,4 @@ namespace EventHub.Events
} }
} }
} }
} }

6
src/EventHub.BackgroundServices/Events/EventTimingChangeNotifier.cs

@ -37,7 +37,7 @@ namespace EventHub.Events
public async Task NotifyAsync(Event @event) public async Task NotifyAsync(Event @event)
{ {
if (@event is null || @event.IsTimingChangeEmailSent) if (@event is null || @event.IsTimingChangeEmailSent || !@event.IsDraft)
{ {
return; return;
} }
@ -47,7 +47,7 @@ namespace EventHub.Events
var userQuery = from eventRegistration in registrationQueryable var userQuery = from eventRegistration in registrationQueryable
join user in userQueryable on eventRegistration.UserId equals user.Id join user in userQueryable on eventRegistration.UserId equals user.Id
where eventRegistration.EventId == @event.Id where eventRegistration.EventId == @event.Id && !@event.IsDraft
select user; select user;
var users = await _asyncExecuter.ToListAsync(userQuery); var users = await _asyncExecuter.ToListAsync(userQuery);
@ -71,4 +71,4 @@ namespace EventHub.Events
} }
} }
} }
} }

4
src/EventHub.BackgroundServices/Events/EventTimingChangeWorker.cs

@ -31,7 +31,7 @@ namespace EventHub.Events
var asyncExecuter = workerContext.ServiceProvider.GetRequiredService<IAsyncQueryableExecuter>(); var asyncExecuter = workerContext.ServiceProvider.GetRequiredService<IAsyncQueryableExecuter>();
var queryable = await eventRepository.GetQueryableAsync(); var queryable = await eventRepository.GetQueryableAsync();
var query = queryable.Where(x => !x.IsTimingChangeEmailSent); var query = queryable.Where(x => !x.IsTimingChangeEmailSent && !x.IsDraft);
var events = await asyncExecuter.ToListAsync(query); var events = await asyncExecuter.ToListAsync(query);
@ -51,4 +51,4 @@ namespace EventHub.Events
} }
} }
} }
} }

4
src/EventHub.BackgroundServices/Events/NewEventNotifier.cs

@ -37,7 +37,7 @@ namespace EventHub.Events
public async Task NotifyAsync(Event @event) public async Task NotifyAsync(Event @event)
{ {
if (@event is null || @event.IsEmailSentToMembers) if (@event is null || @event.IsEmailSentToMembers || !@event.IsDraft)
{ {
return; return;
} }
@ -71,4 +71,4 @@ namespace EventHub.Events
} }
} }
} }
} }

4
src/EventHub.BackgroundServices/Events/NewEventWorker.cs

@ -34,7 +34,7 @@ namespace EventHub.Events
var queryable = await eventRepository.GetQueryableAsync(); var queryable = await eventRepository.GetQueryableAsync();
var newEvents = await asyncExecuter.ToListAsync( var newEvents = await asyncExecuter.ToListAsync(
queryable.Where(x => x.IsEmailSentToMembers == false) queryable.Where(x => x.IsEmailSentToMembers == false && !x.IsDraft)
); );
var fiveMinutesLater = clock.Now.AddMinutes(5); var fiveMinutesLater = clock.Now.AddMinutes(5);
@ -57,4 +57,4 @@ namespace EventHub.Events
} }
} }
} }
} }

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

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

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

@ -4,14 +4,22 @@
{ {
public const string OrganizationNameAlreadyExists = "EventHub:OrganizationNameAlreadyExists"; public const string OrganizationNameAlreadyExists = "EventHub:OrganizationNameAlreadyExists";
public const string NotAuthorizedToCreateEventInThisOrganization = "EventHub:NotAuthorizedToCreateEventInThisOrganization"; public const string NotAuthorizedToCreateEventInThisOrganization = "EventHub:NotAuthorizedToCreateEventInThisOrganization";
public const string EventEndTimeCantBeEarlierThanStartTime = "EventHub:EventEndTimeCantBeEarlierThanStartTime"; public const string EndTimeCantBeEarlierThanStartTime = "EventHub:EndTimeCantBeEarlierThanStartTime";
public const string SessionEndTimeCantBeEarlierThanStartTime = "EventHub:SessionEndTimeCantBeEarlierThanStartTime";
public const string CantRegisterOrUnregisterForAPastEvent = "EventHub:CantRegisterOrUnregisterForAPastEvent"; public const string CantRegisterOrUnregisterForAPastEvent = "EventHub:CantRegisterOrUnregisterForAPastEvent";
public const string NotAuthorizedToUpdateOrganizationProfile = "EventHub:NotAuthorizedToUpdateOrganizationProfile"; public const string NotAuthorizedToUpdateOrganizationProfile = "EventHub:NotAuthorizedToUpdateOrganizationProfile";
public const string CapacityOfEventFull = "EventHub:CapacityOfEventFull"; public const string CapacityOfEventFull = "EventHub:CapacityOfEventFull";
public const string CapacityCantBeLowerThanRegisteredUserCount = "EventHub:CapacityCantBeLowerThanRegisteredUserCount"; public const string CapacityCanNotBeLowerThanRegisteredUserCount = "EventHub:CapacityCantBeLowerThanRegisteredUserCount";
public const string NotAuthorizedToUpdateEvent = "EventHub:NotAuthorizedToUpdateEvent"; public const string NotAuthorizedToUpdateEvent = "EventHub:NotAuthorizedToUpdateEvent";
public const string CantChangeEventTiming = "EventHub:CantChangeEventTiming"; public const string CantChangeEventTiming = "EventHub:CantChangeEventTiming";
public const string SessionTimeShouldBeInTheEventTime = "EventHub:SessionTimeShouldBeInTheEventTime";
public const string SessionTimeConflictsWithAnExistingSession = "EventHub:SessionTimeConflictsWithAnExistingSession";
public const string UserNotFound = "EventHub:UserNotFound"; public const string UserNotFound = "EventHub:UserNotFound";
public const string OrganizationNotFound = "EventHub:OrganizationNotFound"; public const string OrganizationNotFound = "EventHub:OrganizationNotFound";
public const string TrackNameAlreadyExist = "EventHub:TrackNameAlreadyExist";
public const string TrackNotFound = "EventHub:TrackNotFound";
public const string SessionNotFound = "EventHub:SessionNotFound";
public const string SessionTitleAlreadyExist = "EventHub:SessionTitleAlreadyExist";
} }
} }

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

@ -13,18 +13,18 @@
public const int MinDescriptionLength = 50; public const int MinDescriptionLength = 50;
public const int MaxDescriptionLength = 2000; public const int MaxDescriptionLength = 2000;
public const int MinOnlineLinkLength = 4; public const int MinOnlineLinkLength = 4;
public const int MaxOnlineLinkLength = 2000; public const int MaxOnlineLinkLength = 2000;
public const int MinCityLength = 2; public const int MinCityLength = 2;
public const int MaxCityLength = 32; public const int MaxCityLength = 32;
public const int MinLanguageLength = 2; public const int MinLanguageLength = 2;
public const int MaxLanguageLength = 16; public const int MaxLanguageLength = 16;
public const int MaxTimingChangeCountForUser = 2; public const int MaxTimingChangeCountForUser = 2;
public const int MaxCoverImageFileSize = 5 * 1024 * 1024; public const int MaxCoverImageFileSize = 5 * 1024 * 1024;
public static string[] AllowedCoverImageExtensions = { ".jpg", ".png" }; public static string[] AllowedCoverImageExtensions = { ".jpg", ".png" };

14
src/EventHub.Domain.Shared/Events/SessionConsts.cs

@ -0,0 +1,14 @@
namespace EventHub.Events
{
public static class SessionConsts
{
public const int MinTitleLength = 8;
public const int MaxTitleLength = 128;
public const int MinDescriptionLength = 50;
public const int MaxDescriptionLength = 2000;
public const int MinLanguageLength = 2;
public const int MaxLanguageLength = 16;
}
}

7
src/EventHub.Domain.Shared/Events/TrackConsts.cs

@ -0,0 +1,7 @@
namespace EventHub.Events
{
public static class TrackConsts
{
public const int MaxNameLength = 64;
}
}

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

@ -18,7 +18,7 @@
"DisplayName:Capacity": "Capacity", "DisplayName:Capacity": "Capacity",
"EventHub:OrganizationNameAlreadyExists": "The organization {Name} already exists. Please use another name.", "EventHub:OrganizationNameAlreadyExists": "The organization {Name} already exists. Please use another name.",
"EventHub:NotAuthorizedToCreateEventInThisOrganization": "You are not authorized to create events for the organization {0}.", "EventHub:NotAuthorizedToCreateEventInThisOrganization": "You are not authorized to create events for the organization {0}.",
"EventHub:EventEndTimeCantBeEarlierThanStartTime": "Event end time can not be earlier than the start time.", "EventHub:EndTimeCantBeEarlierThanStartTime": "Event end time can not be earlier than the start time.",
"EventHub:CantRegisterOrUnregisterForAPastEvent": "Can not register to or unregister from an event in the past.", "EventHub:CantRegisterOrUnregisterForAPastEvent": "Can not register to or unregister from an event in the past.",
"Events": "Events", "Events": "Events",
"Members": "Members", "Members": "Members",
@ -83,6 +83,7 @@
"CoverImagePreview": "Cover Image Preview", "CoverImagePreview": "Cover Image Preview",
"EventTimingChangeSuccessMessage": "The time of the event has been changed successfully!", "EventTimingChangeSuccessMessage": "The time of the event has been changed successfully!",
"PastEvent": "Past Event", "PastEvent": "Past Event",
"EventHub:SessionEndTimeCantBeEarlierThanStartTime": "Session end time can not be earlier than the start time.",
"Menu:OrganizationManagement": "Organization Management", "Menu:OrganizationManagement": "Organization Management",
"Menu:OrganizationMemberships": "Memberships", "Menu:OrganizationMemberships": "Memberships",
"Permission:OrganizationManagement": "Organization Management", "Permission:OrganizationManagement": "Organization Management",
@ -151,6 +152,12 @@
"MaxStartTime": "Max Start Time", "MaxStartTime": "Max Start Time",
"Online": "Online", "Online": "Online",
"InPerson": "In Person", "InPerson": "In Person",
"UpgradeToPremium": "Upgrade To Premium" "UpgradeToPremium": "Upgrade To Premium",
"EventHub:TrackNameAlreadyExist": "The track {Name} already exists",
"EventHub:TrackNotFound": "The track not found!",
"EventHub:SessionNotFound": "The session not found!",
"EventHub:SessionTimeShouldBeInTheEventTime": "Session time should be in the event time!",
"EventHub:SessionTimeConflictsWithAnExistingSession": "Session time conflicts with an existing session!",
"EventHub:SessionTitleAlreadyExist": "The session {Title} already exists"
} }
} }

10
src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs

@ -23,12 +23,12 @@ namespace EventHub.Web
{ {
return GetConfigValue(configuration, nameof(Account), AccountDefaultValue); return GetConfigValue(configuration, nameof(Account), AccountDefaultValue);
} }
public static string GetWwwConfigValue(IConfiguration configuration) public static string GetWwwConfigValue(IConfiguration configuration)
{ {
return GetConfigValue(configuration, nameof(Www), WwwDefaultValue); return GetConfigValue(configuration, nameof(Www), WwwDefaultValue);
} }
public static string GetApiInternalConfigValue(IConfiguration configuration) public static string GetApiInternalConfigValue(IConfiguration configuration)
{ {
return GetConfigValue(configuration, nameof(ApiInternal), ApiInternalDefaultValue); return GetConfigValue(configuration, nameof(ApiInternal), ApiInternalDefaultValue);
@ -38,7 +38,7 @@ namespace EventHub.Web
{ {
return GetConfigValue(configuration, nameof(Api), ApiDefaultValue); return GetConfigValue(configuration, nameof(Api), ApiDefaultValue);
} }
public static string GetAdminConfigValue(IConfiguration configuration) public static string GetAdminConfigValue(IConfiguration configuration)
{ {
return GetConfigValue(configuration, nameof(Admin), AdminDefaultValue); return GetConfigValue(configuration, nameof(Admin), AdminDefaultValue);
@ -53,7 +53,7 @@ namespace EventHub.Web
{ {
return $"{ConfigurationName}:{appName}"; return $"{ConfigurationName}:{appName}";
} }
private static string GetConfigValue( private static string GetConfigValue(
IConfiguration configuration, IConfiguration configuration,
string appName, string appName,
@ -62,4 +62,4 @@ namespace EventHub.Web
return configuration[GetConfigKey(appName)] ?? defaultValue; return configuration[GetConfigKey(appName)] ?? defaultValue;
} }
} }
} }

7
src/EventHub.Domain/Countries/Country.cs

@ -10,15 +10,14 @@ namespace EventHub.Countries
private Country() private Country()
{ {
} }
internal Country( internal Country(
Guid id, Guid id,
string name) string name)
: base(id) : base(id)
{ {
Name = Check.NotNullOrWhiteSpace(name, nameof(name), CountryConsts.MaxNameLength); Name = Check.NotNullOrWhiteSpace(name, nameof(name), CountryConsts.MaxNameLength);
} }
} }
} }

400
src/EventHub.Domain/Data/EventHubDataSeedContributor.cs

@ -5,17 +5,21 @@ using EventHub.Countries;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
namespace EventHub.Data namespace EventHub.Data
{ {
public class EventHubDataSeedContributor : IDataSeedContributor, ITransientDependency public class EventHubDataSeedContributor : IDataSeedContributor, ITransientDependency
{ {
private readonly IRepository<Country, Guid> _countryRepository; private readonly IRepository<Country, Guid> _countryRepository;
private readonly IGuidGenerator _guidGenerator;
public EventHubDataSeedContributor( public EventHubDataSeedContributor(
IRepository<Country, Guid> countryRepository) IRepository<Country, Guid> countryRepository,
IGuidGenerator guidGenerator)
{ {
_countryRepository = countryRepository; _countryRepository = countryRepository;
_guidGenerator = guidGenerator;
} }
public async Task SeedAsync(DataSeedContext context) public async Task SeedAsync(DataSeedContext context)
@ -32,204 +36,204 @@ namespace EventHub.Data
var countries = new List<Country> var countries = new List<Country>
{ {
new Country(Guid.NewGuid(), "Afghanistan"), new Country(_guidGenerator.Create(), "Afghanistan"),
new Country(Guid.NewGuid(), "Albania"), new Country(_guidGenerator.Create(), "Albania"),
new Country(Guid.NewGuid(), "Algeria"), new Country(_guidGenerator.Create(), "Algeria"),
new Country(Guid.NewGuid(), "Andorra"), new Country(_guidGenerator.Create(), "Andorra"),
new Country(Guid.NewGuid(), "Angola"), new Country(_guidGenerator.Create(), "Angola"),
new Country(Guid.NewGuid(), "Antigua and Barbuda"), new Country(_guidGenerator.Create(), "Antigua and Barbuda"),
new Country(Guid.NewGuid(), "Argentina"), new Country(_guidGenerator.Create(), "Argentina"),
new Country(Guid.NewGuid(), "Armenia"), new Country(_guidGenerator.Create(), "Armenia"),
new Country(Guid.NewGuid(), "Australia"), new Country(_guidGenerator.Create(), "Australia"),
new Country(Guid.NewGuid(), "Austria"), new Country(_guidGenerator.Create(), "Austria"),
new Country(Guid.NewGuid(), "Azerbaijan"), new Country(_guidGenerator.Create(), "Azerbaijan"),
new Country(Guid.NewGuid(), "Bahamas"), new Country(_guidGenerator.Create(), "Bahamas"),
new Country(Guid.NewGuid(), "Bahrain"), new Country(_guidGenerator.Create(), "Bahrain"),
new Country(Guid.NewGuid(), "Bangladesh"), new Country(_guidGenerator.Create(), "Bangladesh"),
new Country(Guid.NewGuid(), "Barbados"), new Country(_guidGenerator.Create(), "Barbados"),
new Country(Guid.NewGuid(), "Belarus"), new Country(_guidGenerator.Create(), "Belarus"),
new Country(Guid.NewGuid(), "Belgium"), new Country(_guidGenerator.Create(), "Belgium"),
new Country(Guid.NewGuid(), "Belize"), new Country(_guidGenerator.Create(), "Belize"),
new Country(Guid.NewGuid(), "Benin"), new Country(_guidGenerator.Create(), "Benin"),
new Country(Guid.NewGuid(), "Bhutan"), new Country(_guidGenerator.Create(), "Bhutan"),
new Country(Guid.NewGuid(), "Bolivia"), new Country(_guidGenerator.Create(), "Bolivia"),
new Country(Guid.NewGuid(), "Bosnia and Herzegovina"), new Country(_guidGenerator.Create(), "Bosnia and Herzegovina"),
new Country(Guid.NewGuid(), "Botswana"), new Country(_guidGenerator.Create(), "Botswana"),
new Country(Guid.NewGuid(), "Brazil"), new Country(_guidGenerator.Create(), "Brazil"),
new Country(Guid.NewGuid(), "Brunei"), new Country(_guidGenerator.Create(), "Brunei"),
new Country(Guid.NewGuid(), "Bulgaria"), new Country(_guidGenerator.Create(), "Bulgaria"),
new Country(Guid.NewGuid(), "Burkina Faso"), new Country(_guidGenerator.Create(), "Burkina Faso"),
new Country(Guid.NewGuid(), "Burundi"), new Country(_guidGenerator.Create(), "Burundi"),
new Country(Guid.NewGuid(), "Côte d'Ivoire"), new Country(_guidGenerator.Create(), "Côte d'Ivoire"),
new Country(Guid.NewGuid(), "Cabo Verde"), new Country(_guidGenerator.Create(), "Cabo Verde"),
new Country(Guid.NewGuid(), "Cambodia"), new Country(_guidGenerator.Create(), "Cambodia"),
new Country(Guid.NewGuid(), "Cameroon"), new Country(_guidGenerator.Create(), "Cameroon"),
new Country(Guid.NewGuid(), "Canada"), new Country(_guidGenerator.Create(), "Canada"),
new Country(Guid.NewGuid(), "Central African Republic"), new Country(_guidGenerator.Create(), "Central African Republic"),
new Country(Guid.NewGuid(), "Chad"), new Country(_guidGenerator.Create(), "Chad"),
new Country(Guid.NewGuid(), "Chile"), new Country(_guidGenerator.Create(), "Chile"),
new Country(Guid.NewGuid(), "China"), new Country(_guidGenerator.Create(), "China"),
new Country(Guid.NewGuid(), "Colombia"), new Country(_guidGenerator.Create(), "Colombia"),
new Country(Guid.NewGuid(), "Comoros"), new Country(_guidGenerator.Create(), "Comoros"),
new Country(Guid.NewGuid(), "Congo (Congo-Brazzaville)"), new Country(_guidGenerator.Create(), "Congo (Congo-Brazzaville)"),
new Country(Guid.NewGuid(), "Costa Rica"), new Country(_guidGenerator.Create(), "Costa Rica"),
new Country(Guid.NewGuid(), "Croatia"), new Country(_guidGenerator.Create(), "Croatia"),
new Country(Guid.NewGuid(), "Cuba"), new Country(_guidGenerator.Create(), "Cuba"),
new Country(Guid.NewGuid(), "Cyprus"), new Country(_guidGenerator.Create(), "Cyprus"),
new Country(Guid.NewGuid(), "Czechia (Czech Republic)"), new Country(_guidGenerator.Create(), "Czechia (Czech Republic)"),
new Country(Guid.NewGuid(), "Democratic Republic of the Congo"), new Country(_guidGenerator.Create(), "Democratic Republic of the Congo"),
new Country(Guid.NewGuid(), "Denmark"), new Country(_guidGenerator.Create(), "Denmark"),
new Country(Guid.NewGuid(), "Djibouti"), new Country(_guidGenerator.Create(), "Djibouti"),
new Country(Guid.NewGuid(), "Dominica"), new Country(_guidGenerator.Create(), "Dominica"),
new Country(Guid.NewGuid(), "Dominican Republic"), new Country(_guidGenerator.Create(), "Dominican Republic"),
new Country(Guid.NewGuid(), "Ecuador"), new Country(_guidGenerator.Create(), "Ecuador"),
new Country(Guid.NewGuid(), "Egypt"), new Country(_guidGenerator.Create(), "Egypt"),
new Country(Guid.NewGuid(), "El Salvador"), new Country(_guidGenerator.Create(), "El Salvador"),
new Country(Guid.NewGuid(), "Equatorial Guinea"), new Country(_guidGenerator.Create(), "Equatorial Guinea"),
new Country(Guid.NewGuid(), "Eritrea"), new Country(_guidGenerator.Create(), "Eritrea"),
new Country(Guid.NewGuid(), "Estonia"), new Country(_guidGenerator.Create(), "Estonia"),
new Country(Guid.NewGuid(), "Eswatini (formerly Swaziland)"), new Country(_guidGenerator.Create(), "Eswatini (formerly Swaziland)"),
new Country(Guid.NewGuid(), "Ethiopia"), new Country(_guidGenerator.Create(), "Ethiopia"),
new Country(Guid.NewGuid(), "Fiji"), new Country(_guidGenerator.Create(), "Fiji"),
new Country(Guid.NewGuid(), "Finland"), new Country(_guidGenerator.Create(), "Finland"),
new Country(Guid.NewGuid(), "France"), new Country(_guidGenerator.Create(), "France"),
new Country(Guid.NewGuid(), "Gabon"), new Country(_guidGenerator.Create(), "Gabon"),
new Country(Guid.NewGuid(), "Gambia"), new Country(_guidGenerator.Create(), "Gambia"),
new Country(Guid.NewGuid(), "Georgia"), new Country(_guidGenerator.Create(), "Georgia"),
new Country(Guid.NewGuid(), "Germany"), new Country(_guidGenerator.Create(), "Germany"),
new Country(Guid.NewGuid(), "Ghana"), new Country(_guidGenerator.Create(), "Ghana"),
new Country(Guid.NewGuid(), "Greece"), new Country(_guidGenerator.Create(), "Greece"),
new Country(Guid.NewGuid(), "Grenada"), new Country(_guidGenerator.Create(), "Grenada"),
new Country(Guid.NewGuid(), "Guatemala"), new Country(_guidGenerator.Create(), "Guatemala"),
new Country(Guid.NewGuid(), "Guinea"), new Country(_guidGenerator.Create(), "Guinea"),
new Country(Guid.NewGuid(), "Guinea-Bissau"), new Country(_guidGenerator.Create(), "Guinea-Bissau"),
new Country(Guid.NewGuid(), "Guyana"), new Country(_guidGenerator.Create(), "Guyana"),
new Country(Guid.NewGuid(), "Haiti"), new Country(_guidGenerator.Create(), "Haiti"),
new Country(Guid.NewGuid(), "Holy See"), new Country(_guidGenerator.Create(), "Holy See"),
new Country(Guid.NewGuid(), "Honduras"), new Country(_guidGenerator.Create(), "Honduras"),
new Country(Guid.NewGuid(), "Hungary"), new Country(_guidGenerator.Create(), "Hungary"),
new Country(Guid.NewGuid(), "Iceland"), new Country(_guidGenerator.Create(), "Iceland"),
new Country(Guid.NewGuid(), "India"), new Country(_guidGenerator.Create(), "India"),
new Country(Guid.NewGuid(), "Indonesia"), new Country(_guidGenerator.Create(), "Indonesia"),
new Country(Guid.NewGuid(), "Iran"), new Country(_guidGenerator.Create(), "Iran"),
new Country(Guid.NewGuid(), "Iraq"), new Country(_guidGenerator.Create(), "Iraq"),
new Country(Guid.NewGuid(), "Ireland"), new Country(_guidGenerator.Create(), "Ireland"),
new Country(Guid.NewGuid(), "Israel"), new Country(_guidGenerator.Create(), "Israel"),
new Country(Guid.NewGuid(), "Italy"), new Country(_guidGenerator.Create(), "Italy"),
new Country(Guid.NewGuid(), "Jamaica"), new Country(_guidGenerator.Create(), "Jamaica"),
new Country(Guid.NewGuid(), "Japan"), new Country(_guidGenerator.Create(), "Japan"),
new Country(Guid.NewGuid(), "Jordan"), new Country(_guidGenerator.Create(), "Jordan"),
new Country(Guid.NewGuid(), "Kazakhstan"), new Country(_guidGenerator.Create(), "Kazakhstan"),
new Country(Guid.NewGuid(), "Kenya"), new Country(_guidGenerator.Create(), "Kenya"),
new Country(Guid.NewGuid(), "Kiribati"), new Country(_guidGenerator.Create(), "Kiribati"),
new Country(Guid.NewGuid(), "Kuwait"), new Country(_guidGenerator.Create(), "Kuwait"),
new Country(Guid.NewGuid(), "Kyrgyzstan"), new Country(_guidGenerator.Create(), "Kyrgyzstan"),
new Country(Guid.NewGuid(), "Laos"), new Country(_guidGenerator.Create(), "Laos"),
new Country(Guid.NewGuid(), "Latvia"), new Country(_guidGenerator.Create(), "Latvia"),
new Country(Guid.NewGuid(), "Lebanon"), new Country(_guidGenerator.Create(), "Lebanon"),
new Country(Guid.NewGuid(), "Lesotho"), new Country(_guidGenerator.Create(), "Lesotho"),
new Country(Guid.NewGuid(), "Liberia"), new Country(_guidGenerator.Create(), "Liberia"),
new Country(Guid.NewGuid(), "Libya"), new Country(_guidGenerator.Create(), "Libya"),
new Country(Guid.NewGuid(), "Liechtenstein"), new Country(_guidGenerator.Create(), "Liechtenstein"),
new Country(Guid.NewGuid(), "Lithuania"), new Country(_guidGenerator.Create(), "Lithuania"),
new Country(Guid.NewGuid(), "Luxembourg"), new Country(_guidGenerator.Create(), "Luxembourg"),
new Country(Guid.NewGuid(), "Madagascar"), new Country(_guidGenerator.Create(), "Madagascar"),
new Country(Guid.NewGuid(), "Malawi"), new Country(_guidGenerator.Create(), "Malawi"),
new Country(Guid.NewGuid(), "Malaysia"), new Country(_guidGenerator.Create(), "Malaysia"),
new Country(Guid.NewGuid(), "Maldives"), new Country(_guidGenerator.Create(), "Maldives"),
new Country(Guid.NewGuid(), "Mali"), new Country(_guidGenerator.Create(), "Mali"),
new Country(Guid.NewGuid(), "Malta"), new Country(_guidGenerator.Create(), "Malta"),
new Country(Guid.NewGuid(), "Marshall Islands"), new Country(_guidGenerator.Create(), "Marshall Islands"),
new Country(Guid.NewGuid(), "Mauritania"), new Country(_guidGenerator.Create(), "Mauritania"),
new Country(Guid.NewGuid(), "Mauritius"), new Country(_guidGenerator.Create(), "Mauritius"),
new Country(Guid.NewGuid(), "Mexico"), new Country(_guidGenerator.Create(), "Mexico"),
new Country(Guid.NewGuid(), "Micronesia"), new Country(_guidGenerator.Create(), "Micronesia"),
new Country(Guid.NewGuid(), "Moldova"), new Country(_guidGenerator.Create(), "Moldova"),
new Country(Guid.NewGuid(), "Monaco"), new Country(_guidGenerator.Create(), "Monaco"),
new Country(Guid.NewGuid(), "Mongolia"), new Country(_guidGenerator.Create(), "Mongolia"),
new Country(Guid.NewGuid(), "Montenegro"), new Country(_guidGenerator.Create(), "Montenegro"),
new Country(Guid.NewGuid(), "Morocco"), new Country(_guidGenerator.Create(), "Morocco"),
new Country(Guid.NewGuid(), "Mozambique"), new Country(_guidGenerator.Create(), "Mozambique"),
new Country(Guid.NewGuid(), "Myanmar (formerly Burma)"), new Country(_guidGenerator.Create(), "Myanmar (formerly Burma)"),
new Country(Guid.NewGuid(), "Namibia"), new Country(_guidGenerator.Create(), "Namibia"),
new Country(Guid.NewGuid(), "Nauru"), new Country(_guidGenerator.Create(), "Nauru"),
new Country(Guid.NewGuid(), "Nepal"), new Country(_guidGenerator.Create(), "Nepal"),
new Country(Guid.NewGuid(), "Netherlands"), new Country(_guidGenerator.Create(), "Netherlands"),
new Country(Guid.NewGuid(), "New Zealand"), new Country(_guidGenerator.Create(), "New Zealand"),
new Country(Guid.NewGuid(), "Nicaragua"), new Country(_guidGenerator.Create(), "Nicaragua"),
new Country(Guid.NewGuid(), "Niger"), new Country(_guidGenerator.Create(), "Niger"),
new Country(Guid.NewGuid(), "Nigeria"), new Country(_guidGenerator.Create(), "Nigeria"),
new Country(Guid.NewGuid(), "North Korea"), new Country(_guidGenerator.Create(), "North Korea"),
new Country(Guid.NewGuid(), "North Macedonia"), new Country(_guidGenerator.Create(), "North Macedonia"),
new Country(Guid.NewGuid(), "Norway"), new Country(_guidGenerator.Create(), "Norway"),
new Country(Guid.NewGuid(), "Oman"), new Country(_guidGenerator.Create(), "Oman"),
new Country(Guid.NewGuid(), "Pakistan"), new Country(_guidGenerator.Create(), "Pakistan"),
new Country(Guid.NewGuid(), "Palau"), new Country(_guidGenerator.Create(), "Palau"),
new Country(Guid.NewGuid(), "Palestine State"), new Country(_guidGenerator.Create(), "Palestine State"),
new Country(Guid.NewGuid(), "Panama"), new Country(_guidGenerator.Create(), "Panama"),
new Country(Guid.NewGuid(), "Papua New Guinea"), new Country(_guidGenerator.Create(), "Papua New Guinea"),
new Country(Guid.NewGuid(), "Paraguay"), new Country(_guidGenerator.Create(), "Paraguay"),
new Country(Guid.NewGuid(), "Peru"), new Country(_guidGenerator.Create(), "Peru"),
new Country(Guid.NewGuid(), "Philippines"), new Country(_guidGenerator.Create(), "Philippines"),
new Country(Guid.NewGuid(), "Poland"), new Country(_guidGenerator.Create(), "Poland"),
new Country(Guid.NewGuid(), "Portugal"), new Country(_guidGenerator.Create(), "Portugal"),
new Country(Guid.NewGuid(), "Qatar"), new Country(_guidGenerator.Create(), "Qatar"),
new Country(Guid.NewGuid(), "Romania"), new Country(_guidGenerator.Create(), "Romania"),
new Country(Guid.NewGuid(), "Russia"), new Country(_guidGenerator.Create(), "Russia"),
new Country(Guid.NewGuid(), "Rwanda"), new Country(_guidGenerator.Create(), "Rwanda"),
new Country(Guid.NewGuid(), "Saint Kitts and Nevis"), new Country(_guidGenerator.Create(), "Saint Kitts and Nevis"),
new Country(Guid.NewGuid(), "Saint Lucia"), new Country(_guidGenerator.Create(), "Saint Lucia"),
new Country(Guid.NewGuid(), "Saint Vincent and the Grenadines"), new Country(_guidGenerator.Create(), "Saint Vincent and the Grenadines"),
new Country(Guid.NewGuid(), "Samoa"), new Country(_guidGenerator.Create(), "Samoa"),
new Country(Guid.NewGuid(), "San Marino"), new Country(_guidGenerator.Create(), "San Marino"),
new Country(Guid.NewGuid(), "Sao Tome and Principe"), new Country(_guidGenerator.Create(), "Sao Tome and Principe"),
new Country(Guid.NewGuid(), "Saudi Arabia"), new Country(_guidGenerator.Create(), "Saudi Arabia"),
new Country(Guid.NewGuid(), "Senegal"), new Country(_guidGenerator.Create(), "Senegal"),
new Country(Guid.NewGuid(), "Serbia"), new Country(_guidGenerator.Create(), "Serbia"),
new Country(Guid.NewGuid(), "Seychelles"), new Country(_guidGenerator.Create(), "Seychelles"),
new Country(Guid.NewGuid(), "Sierra Leone"), new Country(_guidGenerator.Create(), "Sierra Leone"),
new Country(Guid.NewGuid(), "Singapore"), new Country(_guidGenerator.Create(), "Singapore"),
new Country(Guid.NewGuid(), "Slovakia"), new Country(_guidGenerator.Create(), "Slovakia"),
new Country(Guid.NewGuid(), "Slovenia"), new Country(_guidGenerator.Create(), "Slovenia"),
new Country(Guid.NewGuid(), "Solomon Islands"), new Country(_guidGenerator.Create(), "Solomon Islands"),
new Country(Guid.NewGuid(), "Somalia"), new Country(_guidGenerator.Create(), "Somalia"),
new Country(Guid.NewGuid(), "South Africa"), new Country(_guidGenerator.Create(), "South Africa"),
new Country(Guid.NewGuid(), "South Korea"), new Country(_guidGenerator.Create(), "South Korea"),
new Country(Guid.NewGuid(), "South Sudan"), new Country(_guidGenerator.Create(), "South Sudan"),
new Country(Guid.NewGuid(), "Spain"), new Country(_guidGenerator.Create(), "Spain"),
new Country(Guid.NewGuid(), "Sri Lanka"), new Country(_guidGenerator.Create(), "Sri Lanka"),
new Country(Guid.NewGuid(), "Sudan"), new Country(_guidGenerator.Create(), "Sudan"),
new Country(Guid.NewGuid(), "Suriname"), new Country(_guidGenerator.Create(), "Suriname"),
new Country(Guid.NewGuid(), "Sweden"), new Country(_guidGenerator.Create(), "Sweden"),
new Country(Guid.NewGuid(), "Switzerland"), new Country(_guidGenerator.Create(), "Switzerland"),
new Country(Guid.NewGuid(), "Syria"), new Country(_guidGenerator.Create(), "Syria"),
new Country(Guid.NewGuid(), "Tajikistan"), new Country(_guidGenerator.Create(), "Tajikistan"),
new Country(Guid.NewGuid(), "Tanzania"), new Country(_guidGenerator.Create(), "Tanzania"),
new Country(Guid.NewGuid(), "Thailand"), new Country(_guidGenerator.Create(), "Thailand"),
new Country(Guid.NewGuid(), "Timor-Leste"), new Country(_guidGenerator.Create(), "Timor-Leste"),
new Country(Guid.NewGuid(), "Togo"), new Country(_guidGenerator.Create(), "Togo"),
new Country(Guid.NewGuid(), "Tonga"), new Country(_guidGenerator.Create(), "Tonga"),
new Country(Guid.NewGuid(), "Trinidad and Tobago"), new Country(_guidGenerator.Create(), "Trinidad and Tobago"),
new Country(Guid.NewGuid(), "Tunisia"), new Country(_guidGenerator.Create(), "Tunisia"),
new Country(Guid.NewGuid(), "Turkey"), new Country(_guidGenerator.Create(), "Turkey"),
new Country(Guid.NewGuid(), "Turkmenistan"), new Country(_guidGenerator.Create(), "Turkmenistan"),
new Country(Guid.NewGuid(), "Tuvalu"), new Country(_guidGenerator.Create(), "Tuvalu"),
new Country(Guid.NewGuid(), "Uganda"), new Country(_guidGenerator.Create(), "Uganda"),
new Country(Guid.NewGuid(), "Ukraine"), new Country(_guidGenerator.Create(), "Ukraine"),
new Country(Guid.NewGuid(), "United Arab Emirates"), new Country(_guidGenerator.Create(), "United Arab Emirates"),
new Country(Guid.NewGuid(), "United Kingdom"), new Country(_guidGenerator.Create(), "United Kingdom"),
new Country(Guid.NewGuid(), "United States of America"), new Country(_guidGenerator.Create(), "United States of America"),
new Country(Guid.NewGuid(), "Uruguay"), new Country(_guidGenerator.Create(), "Uruguay"),
new Country(Guid.NewGuid(), "Uzbekistan"), new Country(_guidGenerator.Create(), "Uzbekistan"),
new Country(Guid.NewGuid(), "Vanuatu"), new Country(_guidGenerator.Create(), "Vanuatu"),
new Country(Guid.NewGuid(), "Venezuela"), new Country(_guidGenerator.Create(), "Venezuela"),
new Country(Guid.NewGuid(), "Vietnam"), new Country(_guidGenerator.Create(), "Vietnam"),
new Country(Guid.NewGuid(), "Yemen"), new Country(_guidGenerator.Create(), "Yemen"),
new Country(Guid.NewGuid(), "Zambia"), new Country(_guidGenerator.Create(), "Zambia"),
new Country(Guid.NewGuid(), "Zimbabwe") new Country(_guidGenerator.Create(), "Zimbabwe")
}; };
await _countryRepository.InsertManyAsync(countries, autoSave: true); await _countryRepository.InsertManyAsync(countries, autoSave: true);
} }
} }
} }

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

@ -1,5 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.Domain.Entities.Auditing;
namespace EventHub.Events namespace EventHub.Events
@ -41,7 +45,11 @@ namespace EventHub.Events
public int TimingChangeCount { get; set; } public int TimingChangeCount { get; set; }
public bool IsTimingChangeEmailSent { get; set; } public bool IsTimingChangeEmailSent { get; set; }
public bool IsDraft { get; private set; }
public ICollection<Track> Tracks { get; private set; }
private Event() private Event()
{ {
@ -59,9 +67,14 @@ namespace EventHub.Events
{ {
OrganizationId = organizationId; OrganizationId = organizationId;
UrlCode = Check.NotNullOrWhiteSpace(urlCode, urlCode, EventConsts.UrlCodeLength, EventConsts.UrlCodeLength); UrlCode = Check.NotNullOrWhiteSpace(urlCode, urlCode, EventConsts.UrlCodeLength, EventConsts.UrlCodeLength);
SetTitle(title); SetTitle(title);
SetDescription(description); SetDescription(description);
SetTimeInternal(startTime, endTime); SetTimeInternal(startTime, endTime);
Publish(false);
Tracks = new Collection<Track>();
} }
public Event SetTitle(string title) public Event SetTitle(string title)
@ -83,29 +96,155 @@ namespace EventHub.Events
{ {
return this; return this;
} }
if (TimingChangeCount >= EventConsts.MaxTimingChangeCountForUser) if (!IsDraft)
{ {
throw new BusinessException(EventHubErrorCodes.CantChangeEventTiming) if (TimingChangeCount >= EventConsts.MaxTimingChangeCountForUser)
.WithData("MaxTimingChangeLimit", EventConsts.MaxTimingChangeCountForUser); {
throw new BusinessException(EventHubErrorCodes.CantChangeEventTiming)
.WithData("MaxTimingChangeLimit", EventConsts.MaxTimingChangeCountForUser);
}
} }
AddLocalEvent(new EventTimeChangingEventData(this, StartTime, EndTime)); SetTimeInternal(startTime, endTime);
return SetTimeInternal(startTime, endTime); if (!IsDraft)
{
TimingChangeCount++;
IsTimingChangeEmailSent = false;
}
return this;
} }
private Event SetTimeInternal(DateTime startTime, DateTime endTime) private Event SetTimeInternal(DateTime startTime, DateTime endTime)
{ {
if (startTime > endTime) if (startTime > endTime)
{ {
throw new BusinessException(EventHubErrorCodes.EventEndTimeCantBeEarlierThanStartTime); throw new BusinessException(EventHubErrorCodes.EndTimeCantBeEarlierThanStartTime);
} }
StartTime = startTime; StartTime = startTime;
EndTime = endTime; EndTime = endTime;
TimingChangeCount++;
return this; return this;
} }
public Event AddTrack(Guid trackId, string name)
{
if (Tracks.Any(x => x.Name == name))
{
throw new BusinessException(EventHubErrorCodes.TrackNameAlreadyExist)
.WithData("Name", name);
}
Tracks.Add(new Track(trackId, this.Id, name));
return this;
}
public Event UpdateTrack(Guid trackId, string name)
{
if (Tracks.Any(x => x.Name == name))
{
throw new BusinessException(EventHubErrorCodes.TrackNameAlreadyExist)
.WithData("Name", name);
}
var track = Tracks.SingleOrDefault(x => x.Id == trackId);
if (track is null)
{
throw new BusinessException(EventHubErrorCodes.TrackNotFound);
}
track.SetName(name);
return this;
}
public Event RemoveTrack(Guid trackId)
{
var track = Tracks.SingleOrDefault(x => x.Id == trackId);
if (track is null)
{
throw new BusinessException(EventHubErrorCodes.TrackNotFound);
}
Tracks.Remove(track);
return this;
}
public Event AddSession(
Guid trackId,
Guid sessionId,
string title,
string description,
DateTime startTime,
DateTime endTime,
string language,
ICollection<Guid> speakerUserIds)
{
CheckIfValidSessionTime(startTime, endTime);
var track = GetTrack(trackId);
track.AddSession(sessionId, title, description,startTime, endTime, language, speakerUserIds);
return this;
}
public Event UpdateSession(
Guid trackId,
Guid sessionId,
string title,
string description,
DateTime startTime,
DateTime endTime,
string language,
ICollection<Guid> speakerUserIds)
{
CheckIfValidSessionTime(startTime, endTime);
var track = GetTrack(trackId);
track.UpdateSession(sessionId, title, description, startTime, endTime, language, speakerUserIds);
return this;
}
public Event RemoveSession(Guid trackId, Guid sessionId)
{
var track = Tracks.SingleOrDefault(x => x.Id == trackId);
if (track is null)
{
throw new BusinessException(EventHubErrorCodes.TrackNotFound);
}
track.RemoveSession(sessionId);
return this;
}
public Event Publish(bool isPublish = true)
{
IsDraft = !isPublish;
return this;
}
public bool IsLive(DateTime now)
{
return now.IsBetween(StartTime, EndTime);
}
private Track GetTrack(Guid trackId)
{
return Tracks.FirstOrDefault(t => t.Id == trackId) ??
throw new EntityNotFoundException(typeof(Track), trackId);
}
private void CheckIfValidSessionTime(DateTime startTime, DateTime endTime)
{
if (startTime < this.StartTime || this.EndTime < endTime)
{
throw new BusinessException(EventHubErrorCodes.SessionTimeShouldBeInTheEventTime);
}
}
} }
} }

2
src/EventHub.Domain/Events/EventManager.cs

@ -53,7 +53,7 @@ namespace EventHub.Events
var registeredUserCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id); var registeredUserCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id);
if (capacity.Value < registeredUserCount) if (capacity.Value < registeredUserCount)
{ {
throw new BusinessException(EventHubErrorCodes.CapacityCantBeLowerThanRegisteredUserCount); throw new BusinessException(EventHubErrorCodes.CapacityCanNotBeLowerThanRegisteredUserCount);
} }
} }

30
src/EventHub.Domain/Events/EventTimeChangeLocalEventHandler.cs

@ -1,30 +0,0 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus;
namespace EventHub.Events
{
public class EventTimeChangeLocalEventHandler : ILocalEventHandler<EventTimeChangingEventData>, ITransientDependency
{
private readonly IRepository<Event, Guid> _eventRepository;
public EventTimeChangeLocalEventHandler(IRepository<Event, Guid> eventRepository)
{
_eventRepository = eventRepository;
}
public async Task HandleEventAsync(EventTimeChangingEventData eventData)
{
await UpdateEventForTimingChangeAsync(eventData.Event);
}
private async Task UpdateEventForTimingChangeAsync(Event @event)
{
@event.IsTimingChangeEmailSent = false;
await _eventRepository.UpdateAsync(@event, true);
}
}
}

18
src/EventHub.Domain/Events/EventTimeChangingEventData.cs

@ -1,18 +0,0 @@
using System;
namespace EventHub.Events
{
public class EventTimeChangingEventData
{
public Event Event { get; }
public DateTime OldStartTime { get; }
public DateTime OldEndTime { get; }
public EventTimeChangingEventData(Event @event, DateTime oldStartTime, DateTime oldEndTime)
{
Event = @event;
OldStartTime = oldStartTime;
OldEndTime = oldEndTime;
}
}
}

13
src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs

@ -1,5 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Identity; using Volo.Abp.Identity;
@ -37,12 +38,14 @@ namespace EventHub.Events.Registrations
return; return;
} }
var registrationCount = await _eventRegistrationRepository.GetCountAsync(@event.Id); if (@event.Capacity != null)
if (@event.Capacity != null && registrationCount >= @event.Capacity)
{ {
throw new BusinessException(EventHubErrorCodes.CapacityOfEventFull) var registrationCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id);
.WithData("EventTitle", @event.Title); if (registrationCount >= @event.Capacity)
{
throw new BusinessException(EventHubErrorCodes.CapacityOfEventFull)
.WithData("EventTitle", @event.Title);
}
} }
await _eventRegistrationRepository.InsertAsync( await _eventRegistrationRepository.InsertAsync(

122
src/EventHub.Domain/Events/Session.cs

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
namespace EventHub.Events
{
public class Session : Entity<Guid>
{
public Guid TrackId { get; private set; }
public string Title { get; private set; }
public DateTime StartTime { get; private set; }
public DateTime EndTime { get; private set; }
public string Description { get; private set; }
public string Language { get; set; }
public ICollection<Speaker> Speakers { get; private set; }
private Session()
{
}
internal Session(
Guid id,
Guid trackId,
string title,
DateTime startTime,
DateTime endTime,
string description,
string language,
ICollection<Guid> speakerUserIds)
: base(id)
{
TrackId = trackId;
SetTitle(title);
SetDescription(description);
SetTime(startTime, endTime);
SetLanguage(language);
Speakers = new Collection<Speaker>();
AddSpeakers(speakerUserIds);
}
public Session SetTitle(string title)
{
Title = Check.NotNullOrWhiteSpace(title, nameof(title), SessionConsts.MaxTitleLength, SessionConsts.MinTitleLength);
return this;
}
public Session SetDescription(string description)
{
Description = Check.NotNullOrWhiteSpace(description, nameof(description), SessionConsts.MaxDescriptionLength, SessionConsts.MinDescriptionLength);
return this;
}
internal Session SetTime(DateTime startTime, DateTime endTime)
{
if (startTime > endTime)
{
throw new BusinessException(EventHubErrorCodes.SessionEndTimeCantBeEarlierThanStartTime);
}
StartTime = startTime;
EndTime = endTime;
return this;
}
internal Session SetLanguage(string language)
{
Language = Check.NotNullOrWhiteSpace(language, nameof(language), SessionConsts.MaxLanguageLength);
return this;
}
internal Session AddSpeakers(ICollection<Guid> userIds)
{
foreach (var userId in userIds)
{
AddSpeaker(userId);
}
return this;
}
internal Session AddSpeaker(Guid userId)
{
if (Speakers.Any(x => x.UserId == userId))
{
return this;
}
Speakers.Add(new Speaker(Id, userId));
return this;
}
internal Session RemoveSpeakers(ICollection<Guid> userIds)
{
foreach (var userId in userIds)
{
RemoveSpeaker(userId);
}
return this;
}
internal Session RemoveSpeaker(Guid userId)
{
Speakers.RemoveAll(t => t.UserId == userId);
return this;
}
}
}

28
src/EventHub.Domain/Events/Speaker.cs

@ -0,0 +1,28 @@
using System;
using Volo.Abp.Domain.Entities;
namespace EventHub.Events
{
public class Speaker : Entity
{
public Guid SessionId { get; private set; }
public Guid UserId { get; private set; }
private Speaker()
{
}
public Speaker(Guid sessionId, Guid userId)
{
SessionId = sessionId;
UserId = userId;
}
public override object[] GetKeys()
{
return new object[] {SessionId, UserId};
}
}
}

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

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
namespace EventHub.Events
{
public class Track : Entity<Guid>
{
public Guid EventId { get; private set; }
public string Name { get; private set; }
public ICollection<Session> Sessions { get; private set; }
private Track()
{
}
internal Track(
Guid id,
Guid eventId,
string name)
: base(id)
{
EventId = eventId;
SetName(name);
Sessions = new Collection<Session>();
}
public Track SetName(string name)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), TrackConsts.MaxNameLength);
return this;
}
internal Track AddSession(
Guid sessionId,
string title,
string description,
DateTime startTime,
DateTime endTime,
string language,
ICollection<Guid> speakerUserIds)
{
if (Sessions.Any(s => s.Title == title))
{
throw new BusinessException(EventHubErrorCodes.SessionTitleAlreadyExist)
.WithData("Title", title);
}
if (startTime > endTime)
{
throw new BusinessException(EventHubErrorCodes.SessionEndTimeCantBeEarlierThanStartTime);
}
CheckIfValidSessionTime(startTime);
CheckIfValidSessionTime(endTime);
Sessions.Add(new Session(sessionId, Id, title, startTime, endTime, description, language, speakerUserIds));
return this;
}
internal Track UpdateSession(
Guid sessionId,
string title,
string description,
DateTime startTime,
DateTime endTime,
string language,
ICollection<Guid> speakerUserIds)
{
if (startTime > endTime)
{
throw new BusinessException(EventHubErrorCodes.SessionEndTimeCantBeEarlierThanStartTime);
}
var session = Sessions.Single(x => x.Id == sessionId);
if (session.StartTime != startTime)
{
CheckIfValidSessionTime(endTime, sessionId);
}
if (session.EndTime != endTime)
{
CheckIfValidSessionTime(endTime, sessionId);
}
session.SetTitle(title);
session.SetDescription(description);
session.SetTime(startTime, endTime);
session.SetLanguage(language);
if (!speakerUserIds.Any())
{
session.RemoveSpeakers(session.Speakers.Select(x => x.UserId).ToList());
return this;
}
var speakersToBeRemoved = session.Speakers.Where(x => speakerUserIds.Any(s => s != x.UserId)).Select(x => x.UserId).ToList();
session.RemoveSpeakers(speakersToBeRemoved);
session.AddSpeakers(speakerUserIds);
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;
}
private void CheckIfValidSessionTime(DateTime date, Guid? sessionId = null)
{
foreach (var session in Sessions)
{
if (sessionId.HasValue && sessionId!.Value == session.Id)
{
continue;
}
if (date.IsBetween(session.StartTime, session.EndTime))
{
throw new BusinessException(EventHubErrorCodes.SessionTimeConflictsWithAnExistingSession);
}
}
}
}
}

10
src/EventHub.Domain/Users/UserWithoutDetails.cs

@ -0,0 +1,10 @@
using System;
namespace EventHub.Users;
public class UserWithoutDetails
{
public Guid Id { get; set; }
public string UserName { get; set; }
}

37
src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubDbContextModelCreatingExtensions.cs

@ -71,6 +71,43 @@ namespace EventHub.EntityFrameworkCore
b.HasIndex(x => x.UrlCode); b.HasIndex(x => x.UrlCode);
b.HasIndex(x => new {x.IsRemindingEmailSent, x.StartTime}); b.HasIndex(x => new {x.IsRemindingEmailSent, x.StartTime});
b.HasIndex(x => x.IsEmailSentToMembers); b.HasIndex(x => x.IsEmailSentToMembers);
b.HasMany(x => x.Tracks).WithOne().IsRequired().HasForeignKey(x => x.EventId);
});
builder.Entity<Track>(b =>
{
b.ToTable(EventHubConsts.DbTablePrefix + "EventTracks", EventHubConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name).IsRequired().HasMaxLength(TrackConsts.MaxNameLength);
b.HasMany(x => x.Sessions).WithOne().IsRequired().HasForeignKey(x => x.TrackId);
});
builder.Entity<Session>(b =>
{
b.ToTable(EventHubConsts.DbTablePrefix + "EventSessions", EventHubConsts.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Title).IsRequired().HasMaxLength(SessionConsts.MaxTitleLength);
b.Property(x => x.Description).IsRequired().HasMaxLength(SessionConsts.MaxDescriptionLength);
b.Property(x => x.Language).IsRequired().HasMaxLength(SessionConsts.MaxLanguageLength);
b.HasMany(x => x.Speakers).WithOne().IsRequired().HasForeignKey(x => x.SessionId);
});
builder.Entity<Speaker>(b =>
{
b.ToTable(EventHubConsts.DbTablePrefix + "EventSpeakers", EventHubConsts.DbSchema);
b.ConfigureByConvention();
b.HasKey(x => new {x.SessionId, x.UserId});
b.HasOne<IdentityUser>().WithMany().HasForeignKey(x => x.UserId).IsRequired();
}); });
builder.Entity<EventRegistration>(b => builder.Entity<EventRegistration>(b =>

67
src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Events/EventRepository.cs

@ -20,12 +20,12 @@ namespace EventHub.EntityFrameworkCore.Events
} }
public async Task<int> GetCountAsync( public async Task<int> GetCountAsync(
string title = null, string title = null,
string organizationDisplayName = null, string organizationDisplayName = null,
int? minAttendeeCount = null, int? minAttendeeCount = null,
int? maxAttendeeCount = null, int? maxAttendeeCount = null,
DateTime? minStartTime = null, DateTime? minStartTime = null,
DateTime? maxStartTime = null, DateTime? maxStartTime = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) )
{ {
@ -36,35 +36,36 @@ namespace EventHub.EntityFrameworkCore.Events
var organizationQueryable = dbContext.Set<Organization>().AsQueryable(); var organizationQueryable = dbContext.Set<Organization>().AsQueryable();
var query = (from @event in eventQueryable var query = (from @event in eventQueryable
join organization in organizationQueryable on @event.OrganizationId equals organization.Id join organization in organizationQueryable on @event.OrganizationId equals organization.Id
select new select new
{ {
Event = @event, Event = @event,
Organization = organization, Organization = organization,
AttendeeCount = (from eventRegistration in eventRegistrationQueryable AttendeeCount = (from eventRegistration in eventRegistrationQueryable
where eventRegistration.EventId == @event.Id where eventRegistration.EventId == @event.Id
select @eventRegistration).Count() select @eventRegistration).Count()
}) })
.WhereIf(!string.IsNullOrWhiteSpace(title), x => x.Event.Title.ToLower().Contains(title.ToLower())) .WhereIf(!string.IsNullOrWhiteSpace(title), x => x.Event.Title.ToLower().Contains(title.ToLower()))
.WhereIf(!string.IsNullOrWhiteSpace(organizationDisplayName), x => x.Organization.DisplayName.ToLower().Contains(organizationDisplayName.ToLower())) .WhereIf(!string.IsNullOrWhiteSpace(organizationDisplayName),
.WhereIf(minStartTime.HasValue, x => x.Event.StartTime >= minStartTime) x => x.Organization.DisplayName.ToLower().Contains(organizationDisplayName.ToLower()))
.WhereIf(maxStartTime.HasValue, x => x.Event.StartTime <= maxStartTime) .WhereIf(minStartTime.HasValue, x => x.Event.StartTime >= minStartTime)
.WhereIf(minAttendeeCount.HasValue, x => x.AttendeeCount >= minAttendeeCount) .WhereIf(maxStartTime.HasValue, x => x.Event.StartTime <= maxStartTime)
.WhereIf(maxAttendeeCount.HasValue, x => x.AttendeeCount <= maxAttendeeCount); .WhereIf(minAttendeeCount.HasValue, x => x.AttendeeCount >= minAttendeeCount)
.WhereIf(maxAttendeeCount.HasValue, x => x.AttendeeCount <= maxAttendeeCount);
return await query.CountAsync(GetCancellationToken(cancellationToken)); return await query.CountAsync(GetCancellationToken(cancellationToken));
} }
public async Task<List<EventWithDetails>> GetListAsync( public async Task<List<EventWithDetails>> GetListAsync(
string sorting = null, string sorting = null,
int skipCount = 0, int skipCount = 0,
int maxResultCount = int.MaxValue, int maxResultCount = int.MaxValue,
string title = null, string title = null,
string organizationDisplayName = null, string organizationDisplayName = null,
int? minAttendeeCount = null, int? minAttendeeCount = null,
int? maxAttendeeCount = null, int? maxAttendeeCount = null,
DateTime? minStartTime = null, DateTime? minStartTime = null,
DateTime? maxStartTime = null, DateTime? maxStartTime = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) )
{ {
@ -98,5 +99,13 @@ namespace EventHub.EntityFrameworkCore.Events
return await query.ToListAsync(GetCancellationToken(cancellationToken)); return await query.ToListAsync(GetCancellationToken(cancellationToken));
} }
public override async Task<IQueryable<Event>> WithDetailsAsync()
{
return (await GetQueryableAsync())
.Include(e => e.Tracks)
.ThenInclude(t => t.Sessions)
.ThenInclude(s => s.Speakers);
}
} }
} }

4
src/EventHub.EntityFrameworkCore/EventHub.EntityFrameworkCore.csproj

@ -19,8 +19,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Volo.Abp.BlobStoring.Database.EntityFrameworkCore" Version="5.0.0-rc.1" /> <PackageReference Include="Volo.Abp.BlobStoring.Database.EntityFrameworkCore" Version="5.0.0-rc.1" />
<ProjectReference Include="..\EventHub.Domain\EventHub.Domain.csproj" /> <ProjectReference Include="..\EventHub.Domain\EventHub.Domain.csproj" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.PostgreSql" Version="5.0.0-rc.1" /> <PackageReference Include="Volo.Abp.EntityFrameworkCore.PostgreSql" Version="5.0.0-rc.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.0-rc.*" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.0" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="5.0.0-rc.1" /> <PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="5.0.0-rc.1" />
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" Version="5.0.0-rc.1" /> <PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" Version="5.0.0-rc.1" />
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="5.0.0-rc.1" /> <PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="5.0.0-rc.1" />

2747
src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.Designer.cs

File diff suppressed because it is too large

115
src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.cs

@ -0,0 +1,115 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EventHub.Migrations
{
public partial class Added_Tracks_And_Sessions : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsDraft",
table: "EhEvents",
type: "boolean",
nullable: false,
defaultValue: false);
migrationBuilder.CreateTable(
name: "EhEventTracks",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
EventId = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_EhEventTracks", x => x.Id);
table.ForeignKey(
name: "FK_EhEventTracks_EhEvents_EventId",
column: x => x.EventId,
principalTable: "EhEvents",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "EhEventSessions",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
TrackId = table.Column<Guid>(type: "uuid", nullable: false),
Title = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
StartTime = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
EndTime = table.Column<DateTime>(type: "timestamp without time zone", nullable: false),
Description = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: false),
Language = table.Column<string>(type: "character varying(16)", maxLength: 16, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_EhEventSessions", x => x.Id);
table.ForeignKey(
name: "FK_EhEventSessions_EhEventTracks_TrackId",
column: x => x.TrackId,
principalTable: "EhEventTracks",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "EhEventSpeakers",
columns: table => new
{
SessionId = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_EhEventSpeakers", x => new { x.SessionId, x.UserId });
table.ForeignKey(
name: "FK_EhEventSpeakers_AbpUsers_UserId",
column: x => x.UserId,
principalTable: "AbpUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_EhEventSpeakers_EhEventSessions_SessionId",
column: x => x.SessionId,
principalTable: "EhEventSessions",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_EhEventSessions_TrackId",
table: "EhEventSessions",
column: "TrackId");
migrationBuilder.CreateIndex(
name: "IX_EhEventSpeakers_UserId_SessionId",
table: "EhEventSpeakers",
columns: new[] { "UserId", "SessionId" });
migrationBuilder.CreateIndex(
name: "IX_EhEventTracks_EventId",
table: "EhEventTracks",
column: "EventId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "EhEventSpeakers");
migrationBuilder.DropTable(
name: "EhEventSessions");
migrationBuilder.DropTable(
name: "EhEventTracks");
migrationBuilder.DropColumn(
name: "IsDraft",
table: "EhEvents");
}
}
}

2831
src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.Designer.cs

File diff suppressed because it is too large

104
src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.cs

@ -0,0 +1,104 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EventHub.Migrations
{
public partial class Removed_Unnecessary_Index : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_EhEventSpeakers_UserId_SessionId",
table: "EhEventSpeakers");
migrationBuilder.DropIndex(
name: "IX_AbpSettings_Name_ProviderName_ProviderKey",
table: "AbpSettings");
migrationBuilder.DropIndex(
name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey",
table: "AbpPermissionGrants");
migrationBuilder.AlterColumn<DateTime>(
name: "StartTime",
table: "EhEventSessions",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "EndTime",
table: "EhEventSessions",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.CreateIndex(
name: "IX_EhEventSpeakers_UserId",
table: "EhEventSpeakers",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AbpSettings_Name_ProviderName_ProviderKey",
table: "AbpSettings",
columns: new[] { "Name", "ProviderName", "ProviderKey" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AbpPermissionGrants_TenantId_Name_ProviderName_ProviderKey",
table: "AbpPermissionGrants",
columns: new[] { "TenantId", "Name", "ProviderName", "ProviderKey" },
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_EhEventSpeakers_UserId",
table: "EhEventSpeakers");
migrationBuilder.DropIndex(
name: "IX_AbpSettings_Name_ProviderName_ProviderKey",
table: "AbpSettings");
migrationBuilder.DropIndex(
name: "IX_AbpPermissionGrants_TenantId_Name_ProviderName_ProviderKey",
table: "AbpPermissionGrants");
migrationBuilder.AlterColumn<DateTime>(
name: "StartTime",
table: "EhEventSessions",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "EndTime",
table: "EhEventSessions",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.CreateIndex(
name: "IX_EhEventSpeakers_UserId_SessionId",
table: "EhEventSpeakers",
columns: new[] { "UserId", "SessionId" });
migrationBuilder.CreateIndex(
name: "IX_AbpSettings_Name_ProviderName_ProviderKey",
table: "AbpSettings",
columns: new[] { "Name", "ProviderName", "ProviderKey" });
migrationBuilder.CreateIndex(
name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey",
table: "AbpPermissionGrants",
columns: new[] { "Name", "ProviderName", "ProviderKey" });
}
}
}

130
src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs

@ -19,7 +19,7 @@ namespace EventHub.Migrations
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
.HasAnnotation("ProductVersion", "6.0.0-rc.2.21480.5") .HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63); .HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -99,6 +99,9 @@ namespace EventHub.Migrations
.HasDefaultValue(false) .HasDefaultValue(false)
.HasColumnName("IsDeleted"); .HasColumnName("IsDeleted");
b.Property<bool>("IsDraft")
.HasColumnType("boolean");
b.Property<bool>("IsEmailSentToMembers") b.Property<bool>("IsEmailSentToMembers")
.HasColumnType("boolean"); .HasColumnType("boolean");
@ -208,6 +211,77 @@ namespace EventHub.Migrations
b.ToTable("EhEventRegistrations", (string)null); b.ToTable("EhEventRegistrations", (string)null);
}); });
modelBuilder.Entity("EventHub.Events.Session", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("character varying(2000)");
b.Property<DateTime>("EndTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Language")
.IsRequired()
.HasMaxLength(16)
.HasColumnType("character varying(16)");
b.Property<DateTime>("StartTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<Guid>("TrackId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("TrackId");
b.ToTable("EhEventSessions", (string)null);
});
modelBuilder.Entity("EventHub.Events.Speaker", b =>
{
b.Property<Guid>("SessionId")
.HasColumnType("uuid");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("SessionId", "UserId");
b.HasIndex("UserId");
b.ToTable("EhEventSpeakers", (string)null);
});
modelBuilder.Entity("EventHub.Events.Track", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<Guid>("EventId")
.HasColumnType("uuid");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.HasKey("Id");
b.HasIndex("EventId");
b.ToTable("EhEventTracks", (string)null);
});
modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b => modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -2253,7 +2327,8 @@ namespace EventHub.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("Name", "ProviderName", "ProviderKey"); b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey")
.IsUnique();
b.ToTable("AbpPermissionGrants", (string)null); b.ToTable("AbpPermissionGrants", (string)null);
}); });
@ -2284,7 +2359,8 @@ namespace EventHub.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("Name", "ProviderName", "ProviderKey"); b.HasIndex("Name", "ProviderName", "ProviderKey")
.IsUnique();
b.ToTable("AbpSettings", (string)null); b.ToTable("AbpSettings", (string)null);
}); });
@ -2318,6 +2394,39 @@ namespace EventHub.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("EventHub.Events.Session", b =>
{
b.HasOne("EventHub.Events.Track", null)
.WithMany("Sessions")
.HasForeignKey("TrackId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EventHub.Events.Speaker", b =>
{
b.HasOne("EventHub.Events.Session", null)
.WithMany("Speakers")
.HasForeignKey("SessionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Volo.Abp.Identity.IdentityUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EventHub.Events.Track", b =>
{
b.HasOne("EventHub.Events.Event", null)
.WithMany("Tracks")
.HasForeignKey("EventId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b => modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b =>
{ {
b.HasOne("EventHub.Organizations.Organization", null) b.HasOne("EventHub.Organizations.Organization", null)
@ -2619,6 +2728,21 @@ namespace EventHub.Migrations
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("EventHub.Events.Event", b =>
{
b.Navigation("Tracks");
});
modelBuilder.Entity("EventHub.Events.Session", b =>
{
b.Navigation("Speakers");
});
modelBuilder.Entity("EventHub.Events.Track", b =>
{
b.Navigation("Sessions");
});
modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b =>
{ {
b.Navigation("Actions"); b.Navigation("Actions");

6
src/EventHub.HttpApi.Client/EventHubHttpApiClientModule.cs

@ -2,6 +2,7 @@
using Volo.Abp.Http.Client; using Volo.Abp.Http.Client;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Payment; using Payment;
using Volo.Abp.VirtualFileSystem;
namespace EventHub namespace EventHub
{ {
@ -20,6 +21,11 @@ namespace EventHub
typeof(EventHubApplicationContractsModule).Assembly, typeof(EventHubApplicationContractsModule).Assembly,
RemoteServiceName RemoteServiceName
); );
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EventHubHttpApiClientModule>();
});
} }
} }
} }

97
src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs

@ -1,97 +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);
}
[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;
}
}
}

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

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

6
src/EventHub.HttpApi/Controllers/EventHubController.cs

@ -3,13 +3,11 @@ using Volo.Abp.AspNetCore.Mvc;
namespace EventHub.Controllers namespace EventHub.Controllers
{ {
/* Inherit your controllers from this class. public abstract class EventHubController : AbpControllerBase
*/
public abstract class EventHubController : AbpController
{ {
protected EventHubController() protected EventHubController()
{ {
LocalizationResource = typeof(EventHubResource); LocalizationResource = typeof(EventHubResource);
} }
} }
} }

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

@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
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("eventhub")]
[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")]
public async Task<List<EventInListDto>> GetDraftEventsByCurrentUser()
{
return await _eventAppService.GetDraftEventsByCurrentUser();
}
[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);
}
[HttpPut]
[Route("/publish/{id}")]
public async Task<string> PublishAsync(Guid id)
{
return await _eventAppService.PublishAsync(id);
}
[HttpPost]
[Route("{id}/tracks")]
public async Task AddTrackAsync(Guid id, AddTrackDto input)
{
await _eventAppService.AddTrackAsync(id, input);
}
[HttpPut]
[Route("{id}/tracks/{trackId}")]
public async Task UpdateTrackAsync(Guid id, Guid trackId, UpdateTrackDto input)
{
await _eventAppService.UpdateTrackAsync(id, trackId, input);
}
[HttpDelete]
[Route("{id}/tracks/{trackId}")]
public async Task DeleteTrackAsync(Guid id, Guid trackId)
{
await _eventAppService.DeleteTrackAsync(id, trackId);
}
[HttpPost]
[Route("{id}/tracks/{trackId}/sessions")]
public async Task AddSessionAsync(Guid id, Guid trackId, AddSessionDto input)
{
input.SpeakerUserNames = input.SpeakerUserNames.Where(x => !x.IsNullOrWhiteSpace()).ToList();
await _eventAppService.AddSessionAsync(id, trackId, input);
}
[HttpPut]
[Route("{id}/tracks/{trackId}/sessions/{sessionId}")]
public async Task UpdateSessionAsync(Guid id, Guid trackId, Guid sessionId, UpdateSessionDto input)
{
input.SpeakerUserNames = input.SpeakerUserNames.Where(x => !x.IsNullOrWhiteSpace()).ToList();
await _eventAppService.UpdateSessionAsync(id, trackId, sessionId, input);
}
[HttpDelete]
[Route("{id}/tracks/{trackId}/sessions/{sessionId}")]
public async Task DeleteSessionAsync(Guid id, Guid trackId, Guid sessionId)
{
await _eventAppService.DeleteSessionAsync(id, trackId, sessionId);
}
[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;
}
}
}

4
src/EventHub.HttpApi.Host/Controllers/Events/Registrations/EventRegistrationController.cs → src/EventHub.HttpApi/Controllers/Events/Registrations/EventRegistrationController.cs

@ -12,7 +12,7 @@ namespace EventHub.Controllers.Events.Registrations
[Area("eventhub")] [Area("eventhub")]
[ControllerName("EventRegistration")] [ControllerName("EventRegistration")]
[Route("api/eventhub/event-registration")] [Route("api/eventhub/event-registration")]
public class EventRegistrationController : AbpController, IEventRegistrationAppService public class EventRegistrationController : EventHubController, IEventRegistrationAppService
{ {
private readonly IEventRegistrationAppService _eventRegistrationAppService; private readonly IEventRegistrationAppService _eventRegistrationAppService;
@ -55,4 +55,4 @@ namespace EventHub.Controllers.Events.Registrations
return await _eventRegistrationAppService.IsPastEventAsync(eventId); return await _eventRegistrationAppService.IsPastEventAsync(eventId);
} }
} }
} }

4
src/EventHub.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs → src/EventHub.HttpApi/Controllers/Organizations/Memberships/OrganizationMembershipController.cs

@ -12,7 +12,7 @@ namespace EventHub.Controllers.Organizations.Memberships
[Area("eventhub")] [Area("eventhub")]
[ControllerName("OrganizationMembership")] [ControllerName("OrganizationMembership")]
[Route("api/eventhub/organization-membership")] [Route("api/eventhub/organization-membership")]
public class OrganizationMembershipController : AbpController, IOrganizationMembershipAppService public class OrganizationMembershipController : EventHubController, IOrganizationMembershipAppService
{ {
private readonly IOrganizationMembershipAppService _organizationMembershipAppService; private readonly IOrganizationMembershipAppService _organizationMembershipAppService;
@ -49,4 +49,4 @@ namespace EventHub.Controllers.Organizations.Memberships
return await _organizationMembershipAppService.GetMembersAsync(input); return await _organizationMembershipAppService.GetMembersAsync(input);
} }
} }
} }

4
src/EventHub.HttpApi.Host/Controllers/Organizations/OrganizationController.cs → src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs

@ -14,7 +14,7 @@ namespace EventHub.Controllers.Organizations
[Area("eventhub")] [Area("eventhub")]
[ControllerName("Organization")] [ControllerName("Organization")]
[Route("api/eventhub/organization")] [Route("api/eventhub/organization")]
public class OrganizationController : AbpController, IOrganizationAppService public class OrganizationController : EventHubController, IOrganizationAppService
{ {
private readonly IOrganizationAppService _organizationAppService; private readonly IOrganizationAppService _organizationAppService;
private readonly IVirtualFileProvider _virtualFileProvider; private readonly IVirtualFileProvider _virtualFileProvider;
@ -86,4 +86,4 @@ namespace EventHub.Controllers.Organizations
return remoteStreamContent; return remoteStreamContent;
} }
} }
} }

16
src/EventHub.HttpApi.Host/Controllers/Users/UserController.cs → src/EventHub.HttpApi/Controllers/Users/UserController.cs

@ -1,17 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Members; using EventHub.Members;
using EventHub.Users; using EventHub.Users;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
namespace EventHub.Controllers.Users namespace EventHub.Controllers.Users
{ {
[RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] [RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)]
[Area("eventhub")] [Area("eventhub")]
[ControllerName("User")] [ControllerName("User")]
[Route("/api/eventhub/user")] [Route("/api/eventhub/users")]
public class UserController : AbpController, IUserAppService public class UserController : EventHubController, IUserAppService
{ {
private readonly IUserAppService _userAppService; private readonly IUserAppService _userAppService;
@ -26,5 +26,13 @@ namespace EventHub.Controllers.Users
{ {
return await _userAppService.FindByUserNameAsync(username); return await _userAppService.FindByUserNameAsync(username);
} }
[HttpGet]
[Route("contains/{username}")]
public async Task<List<UserInListDto>> GetListByUserName(string username)
{
username = username.Trim().ToLower();
return await _userAppService.GetListByUserName(username);
}
} }
} }

6
src/EventHub.Web.Theme/Themes/EventHub/Components/MainNavbar/Default.cshtml

@ -48,9 +48,9 @@
+ Create + Create
</a> </a>
<div class="dropdown-menu m-2" aria-labelledby="dropdownMenuButton"> <div class="dropdown-menu m-2" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="@UrlOptions.Value.Www@Url.Page("/organizations/new")">Organization</a> <a class="dropdown-item" href="@UrlOptions.Value.Www.EnsureEndsWith('/')organizations/new">Organization</a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item" href="@UrlOptions.Value.Www@Url.Page("/events/new")">Event</a> <a class="dropdown-item" href="@UrlOptions.Value.Www.EnsureEndsWith('/')events/new">Event</a>
</div> </div>
</div> </div>
</li> </li>
@ -142,4 +142,4 @@
return route.Split("/")[1].EnsureStartsWith('/').EnsureEndsWith('/'); return route.Split("/")[1].EnsureStartsWith('/').EnsureEndsWith('/');
} }
} }

3
src/EventHub.Web.Theme/Themes/EventHub/Layouts/Application.cshtml

@ -44,7 +44,6 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>@pageTitle</title> <title>@pageTitle</title>
@* TODO(berkansasmaz): Get SEO content from the marketing team *@
<meta name="description" content="@(!string.IsNullOrEmpty(ViewBag.Description) ? ViewBag.Description : "EventHub Description")"/> <meta name="description" content="@(!string.IsNullOrEmpty(ViewBag.Description) ? ViewBag.Description : "EventHub Description")"/>
<meta property="og:title" content="@(!string.IsNullOrWhiteSpace(ViewBag.OgTitle) ? ViewBag.OgTitle : "EventHub Title")"/> <meta property="og:title" content="@(!string.IsNullOrWhiteSpace(ViewBag.OgTitle) ? ViewBag.OgTitle : "EventHub Title")"/>
<meta property="og:url" content="@Context.Request.GetDisplayUrl()"/> <meta property="og:url" content="@Context.Request.GetDisplayUrl()"/>
@ -88,4 +87,4 @@
@await RenderSectionAsync("scripts", false) @await RenderSectionAsync("scripts", false)
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.Last, StandardLayouts.Application) @await Component.InvokeLayoutHookAsync(LayoutHooks.Body.Last, StandardLayouts.Application)
</body> </body>
</html> </html>

10
src/EventHub.Web.Theme/wwwroot/themes/eventhub/style.css

@ -57,6 +57,10 @@ p {
word-wrap: break-word; word-wrap: break-word;
} }
textarea {
height: 90px!important;
}
/*NAVBAR*/ /*NAVBAR*/
.navbar { .navbar {
box-shadow: 0 5px 10px rgba(0, 4, 74, 0.05) !important; box-shadow: 0 5px 10px rgba(0, 4, 74, 0.05) !important;
@ -845,7 +849,7 @@ p .text-primary {
} }
} }
.nav-tabs.profile-nav .nav-item .nav-link { .nav-tabs.profile-nav .nav-item .nav-link, .detail-tabs .nav-tabs .nav-item .nav-link {
border: 0; border: 0;
color: #00044A; color: #00044A;
font-size: 16px; font-size: 16px;
@ -853,7 +857,7 @@ p .text-primary {
} }
.nav-tabs.profile-nav .nav-item.show .nav-link, .nav-tabs.profile-nav .nav-item.show .nav-link,
.nav-tabs.profile-nav .nav-link.active { .nav-tabs.profile-nav .nav-link.active, .detail-tabs .nav-tabs .nav-item .nav-link.active, .detail-tabs .nav-tabs .nav-item.show .nav-link {
color: #00044A; color: #00044A;
background: none; background: none;
border: 0; border: 0;
@ -1092,4 +1096,4 @@ div.checkbox.switcher label input:checked + span small, div.radio.switcher label
} }
} }
/*This area was made to be able to navigate between pages. It should be removed from the project*/ /*This area was made to be able to navigate between pages. It should be removed from the project*/

4
src/EventHub.Web/Controllers/EventController.cs

@ -6,7 +6,6 @@ using Volo.Abp.AspNetCore.Mvc;
namespace EventHub.Web.Controllers namespace EventHub.Web.Controllers
{ {
[Route("api/event")]
public class EventController : AbpController public class EventController : AbpController
{ {
private readonly IEventAppService _eventAppService; private readonly IEventAppService _eventAppService;
@ -17,7 +16,6 @@ namespace EventHub.Web.Controllers
} }
[HttpGet] [HttpGet]
[Route("get-list")]
public async Task<IActionResult> GetList(EventListFilterDto input) public async Task<IActionResult> GetList(EventListFilterDto input)
{ {
ViewData.Model = (await _eventAppService.GetListAsync(input)).Items.ToList(); ViewData.Model = (await _eventAppService.GetListAsync(input)).Items.ToList();
@ -29,4 +27,4 @@ namespace EventHub.Web.Controllers
}; };
} }
} }
} }

10
src/EventHub.Web/Controllers/EventRegistrationController.cs

@ -1,7 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Events.Registrations; using EventHub.Events.Registrations;
using EventHub.Web.Pages.Events.Components.RegistrationArea;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc;
@ -29,14 +28,5 @@ namespace EventHub.Web.Controllers
await _eventRegistrationAppService.UnregisterAsync(eventId); await _eventRegistrationAppService.UnregisterAsync(eventId);
return NoContent(); return NoContent();
} }
[HttpGet]
public IActionResult Widget(Guid eventId)
{
return ViewComponent(
typeof(RegistrationAreaViewComponent),
new {eventId}
);
}
} }
} }

4
src/EventHub.Web/Controllers/OrganizationController.cs

@ -6,7 +6,6 @@ using Volo.Abp.AspNetCore.Mvc;
namespace EventHub.Web.Controllers namespace EventHub.Web.Controllers
{ {
[Route("api/organization")]
public class OrganizationController : AbpController public class OrganizationController : AbpController
{ {
private readonly IOrganizationAppService _organizationAppService; private readonly IOrganizationAppService _organizationAppService;
@ -17,7 +16,6 @@ namespace EventHub.Web.Controllers
} }
[HttpGet] [HttpGet]
[Route("get-list")]
public async Task<IActionResult> GetList(OrganizationListFilterDto input) public async Task<IActionResult> GetList(OrganizationListFilterDto input)
{ {
ViewData.Model = (await _organizationAppService.GetListAsync(input)).Items.ToList(); ViewData.Model = (await _organizationAppService.GetListAsync(input)).Items.ToList();
@ -29,4 +27,4 @@ namespace EventHub.Web.Controllers
}; };
} }
} }
} }

13
src/EventHub.Web/Controllers/OrganizationMembershipController.cs

@ -2,7 +2,6 @@ using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Organizations.Memberships; using EventHub.Organizations.Memberships;
using EventHub.Web.Pages.Organizations.Components.JoinArea;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc;
@ -18,7 +17,6 @@ namespace EventHub.Web.Controllers
} }
[HttpGet] [HttpGet]
[Route("get-list")]
public async Task<IActionResult> GetList(OrganizationMemberListFilterDto input) public async Task<IActionResult> GetList(OrganizationMemberListFilterDto input)
{ {
ViewData.Model = (await _organizationMembershipAppService.GetMembersAsync(input)).Items.ToList(); ViewData.Model = (await _organizationMembershipAppService.GetMembersAsync(input)).Items.ToList();
@ -43,14 +41,5 @@ namespace EventHub.Web.Controllers
await _organizationMembershipAppService.LeaveAsync(organizationId); await _organizationMembershipAppService.LeaveAsync(organizationId);
return NoContent(); return NoContent();
} }
[HttpGet]
public IActionResult Widget(Guid organizationId)
{
return ViewComponent(
typeof(JoinAreaViewComponent),
new {organizationId}
);
}
} }
} }

34
src/EventHub.Web/Controllers/WidgetsController.cs

@ -1,6 +1,9 @@
using System; using System;
using EventHub.Web.Pages.Events.Components.AttendeesArea; using EventHub.Web.Pages.Events.Components.AttendeesArea;
using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea;
using EventHub.Web.Pages.Events.Components.LocationArea; using EventHub.Web.Pages.Events.Components.LocationArea;
using EventHub.Web.Pages.Events.Components.RegistrationArea;
using EventHub.Web.Pages.Organizations.Components.JoinArea;
using EventHub.Web.Pages.Organizations.Components.MembersArea; using EventHub.Web.Pages.Organizations.Components.MembersArea;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc;
@ -47,5 +50,36 @@ namespace EventHub.Web.Controllers
new {eventId} new {eventId}
); );
} }
[HttpGet]
public IActionResult RegistrationArea(Guid eventId)
{
return ViewComponent(
typeof(RegistrationAreaViewComponent),
new {eventId}
);
}
[HttpGet]
public IActionResult JoinArea(Guid organizationId)
{
return ViewComponent(
typeof(JoinAreaViewComponent),
new {organizationId}
);
}
[HttpGet]
public IActionResult CreateOrEditEventArea(string eventUrlCode, CreateOrEditEventAreaViewComponent.ProgressStepType stepType)
{
return ViewComponent(
typeof(CreateOrEditEventAreaViewComponent),
new
{
eventUrlCode,
stepType
}
);
}
} }
} }

6
src/EventHub.Web/EventHubWebAutoMapperProfile.cs

@ -1,6 +1,7 @@
using AutoMapper; using AutoMapper;
using EventHub.Events; using EventHub.Events;
using EventHub.Organizations; using EventHub.Organizations;
using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea;
using EventHub.Web.Pages.Organizations; using EventHub.Web.Pages.Organizations;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
using EditPageModel = EventHub.Web.Pages.Events.EditPageModel; using EditPageModel = EventHub.Web.Pages.Events.EditPageModel;
@ -12,12 +13,11 @@ namespace EventHub.Web
public EventHubWebAutoMapperProfile() public EventHubWebAutoMapperProfile()
{ {
CreateMap<NewPageModel.CreateOrganizationViewModel, CreateOrganizationDto>(); CreateMap<NewPageModel.CreateOrganizationViewModel, CreateOrganizationDto>();
CreateMap<Pages.Events.NewPageModel.NewEventViewModel, CreateEventDto>() CreateMap<CreateOrEditEventAreaViewComponent.NewEventViewModel, CreateEventDto>()
.Ignore(x => x.CoverImageStreamContent); .Ignore(x => x.CoverImageStreamContent);
CreateMap<OrganizationProfileDto, EditOrganizationViewModel>(); CreateMap<OrganizationProfileDto, EditOrganizationViewModel>();
CreateMap<EditOrganizationViewModel, UpdateOrganizationDto>(); CreateMap<EditOrganizationViewModel, UpdateOrganizationDto>();
CreateMap<EventDetailDto, EditPageModel.EditEventViewModel>(); CreateMap<EventDetailDto, CreateOrEditEventAreaViewComponent.NewEventViewModel>();
CreateMap<EditPageModel.EditEventViewModel, UpdateEventDto>();
} }
} }
} }

34
src/EventHub.Web/EventHubWebModule.cs

@ -89,23 +89,7 @@ namespace EventHub.Web
ConfigureRazorPageOptions(); ConfigureRazorPageOptions();
ConfigurePremiumPlanInfo(context, configuration); ConfigurePremiumPlanInfo(context, configuration);
} }
private void ConfigurePremiumPlanInfo(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddOptions<PremiumPlanInfoOptions>()
.Bind(configuration.GetSection(PremiumPlanInfoOptions.OrganizationPlanInfo))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.IsActive)
{
return config.OnePremiumPeriodAsMonth > config.CanBeExtendedAfterHowManyMonths;
}
return true;
}, "OnePremiumPeriodAsMonth must be greater than CanBeExtendedAfterHowManyMonths.");
}
private void ConfigureBundles() private void ConfigureBundles()
{ {
Configure<AbpBundlingOptions>(options => Configure<AbpBundlingOptions>(options =>
@ -250,6 +234,22 @@ namespace EventHub.Web
.PersistKeysToStackExchangeRedis(redis, "EventHub-Protection-Keys"); .PersistKeysToStackExchangeRedis(redis, "EventHub-Protection-Keys");
} }
private void ConfigurePremiumPlanInfo(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddOptions<PremiumPlanInfoOptions>()
.Bind(configuration.GetSection(PremiumPlanInfoOptions.OrganizationPlanInfo))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.IsActive)
{
return config.OnePremiumPeriodAsMonth > config.CanBeExtendedAfterHowManyMonths;
}
return true;
}, "OnePremiumPeriodAsMonth must be greater than CanBeExtendedAfterHowManyMonths.");
}
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnApplicationInitialization(ApplicationInitializationContext context)
{ {
var app = context.GetApplicationBuilder(); var app = context.GetApplicationBuilder();

853
src/EventHub.Web/Pages/Empty.cshtml

@ -1,853 +0,0 @@
@page "/test"
@using EventHub.Web.Pages
@model EventHub.Web.Pages.Empty
<main class="static-page container mt-5">
<h1 class="text-center mb-3">Create an Event</h1>
<p class="text-center mb-5">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam ut nulla consequat, tempus sapien in,
pellentesque nunc. Sed at nunc pellentesque, fermentum mi et, fermentum massa. </p>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-body px-4 py-5 text-center">
<svg width="50" height="58" viewBox="0 0 50 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M33.0445 28.1094H9.31281C8.68249 28.1094 8.17188 28.6205 8.17188 29.2515C8.17188 29.8825 8.68249 30.394 9.31281 30.394H33.0445C33.6748 30.394 34.1854 29.8825 34.1854 29.2515C34.1854 28.6209 33.6748 28.1094 33.0445 28.1094Z" fill="#E8345D"/>
<path d="M33.0445 34.8032H9.31281C8.68249 34.8032 8.17188 35.3143 8.17188 35.9453C8.17188 36.5763 8.68249 37.0879 9.31281 37.0879H33.0445C33.6748 37.0879 34.1854 36.5763 34.1854 35.9453C34.1854 35.3143 33.6748 34.8032 33.0445 34.8032Z" fill="#E8345D"/>
<path d="M33.0445 41.4976H9.31281C8.68249 41.4976 8.17188 42.0087 8.17188 42.6397C8.17188 43.2707 8.68249 43.7822 9.31281 43.7822H33.0445C33.6748 43.7822 34.1854 43.2707 34.1854 42.6397C34.1854 42.0087 33.6748 41.4976 33.0445 41.4976Z" fill="#E8345D"/>
<path d="M48.0044 5.61362H44.3885V1.99791C44.3885 0.896072 43.4919 0 42.3894 0H37.5942C36.4935 0 35.5981 0.896072 35.5981 1.99791V5.61317H31.9827C31.1161 5.61317 30.3785 6.16896 30.1019 6.94246H12.6346C12.332 6.94246 12.0418 7.06282 11.8281 7.27699L0.333931 18.791C0.120145 19.0051 0 19.2959 0 19.599V54.9016C0 56.6101 1.38917 58 3.09637 58H39.7387C41.4459 58 42.8351 56.6101 42.8351 54.9016V39.4692C42.8351 38.8382 42.324 38.3267 41.6941 38.3267C41.0643 38.3267 40.5536 38.8382 40.5536 39.4692V54.9016C40.5536 55.3428 40.1804 55.7153 39.7387 55.7153H3.09637C2.65466 55.7153 2.28142 55.3428 2.28142 54.9016V20.7411H10.3103C12.2212 20.7411 13.7755 19.1857 13.7755 17.2736V9.22711H29.9831V12.4114C29.9831 13.5132 30.8802 14.4093 31.9827 14.4093H35.5981V18.0246C35.5981 19.1282 36.4935 20.026 37.5942 20.026H40.5532V29.1438C40.5532 29.7744 41.0643 30.2859 41.6941 30.2859C42.324 30.2859 42.8346 29.7744 42.8346 29.1438V19.9742C43.7233 19.7707 44.3885 18.9751 44.3885 18.025V14.4093H48.0039C49.1047 14.4093 50 13.5132 50 12.4114V7.61462C50 6.51102 49.1047 5.61362 48.0044 5.61362ZM10.3103 18.4564H3.8941L11.4937 10.8431V17.2736C11.4937 17.9259 10.9632 18.4564 10.3103 18.4564ZM47.7186 12.1246H43.2481C42.6177 12.1246 42.1071 12.6362 42.1071 13.2672V17.7413H37.8795V13.2672C37.8795 12.6362 37.3689 12.1246 36.739 12.1246H32.265V7.89827H36.739C37.3689 7.89827 37.8795 7.38673 37.8795 6.75572V2.28465H42.1071V6.75572C42.1071 7.38673 42.6177 7.89827 43.2481 7.89827H47.7186V12.1246Z" fill="#E8345D"/>
<path d="M41.6937 33.1411C41.0633 33.1411 40.5527 33.6526 40.5527 34.2837V34.3067C40.5527 34.9372 41.0638 35.4488 41.6937 35.4488C42.3235 35.4488 42.8342 34.9372 42.8342 34.3067V34.2837C42.8346 33.6526 42.3235 33.1411 41.6937 33.1411Z" fill="#E8345D"/>
</svg>
<h2 class="mt-4">Create a New Event</h2>
<p class="my-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam utnulla consequat, tempus sapien in.</p>
<a href="#" class="btn btn-primary">Create</a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body px-4 py-5 text-center">
<svg width="42" height="56" viewBox="0 0 42 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.9071 0H12.456C12.4436 0 12.4313 0.00128174 12.4193 0.00170898C12.4069 0.00213622 12.3946 0.00299073 12.3822 0.00384521C12.3361 0.00683594 12.29 0.0119629 12.2452 0.020935C12.2439 0.020935 12.2431 0.020935 12.2422 0.0213622C12.1965 0.0303345 12.1517 0.0435792 12.1074 0.0585327C12.0958 0.0623779 12.0839 0.0666504 12.0724 0.0709229C12.0314 0.0863037 11.9913 0.103821 11.952 0.123901C11.949 0.12561 11.9456 0.126892 11.9422 0.128601C11.9008 0.150818 11.8616 0.176453 11.8231 0.203369C11.8133 0.210632 11.8031 0.218323 11.7933 0.226013C11.7549 0.255066 11.7182 0.285828 11.6836 0.320435L0.320063 11.6971C0.285497 11.7318 0.254344 11.7693 0.225325 11.8074C0.217643 11.8172 0.210388 11.827 0.20356 11.8369C0.176248 11.8757 0.150216 11.9155 0.128025 11.9569C0.126745 11.9595 0.125892 11.9616 0.125038 11.9642C0.104127 12.0039 0.0862037 12.0453 0.0708408 12.0876C0.0665732 12.0988 0.0623056 12.1099 0.0584649 12.121C0.0435287 12.1658 0.0302994 12.2111 0.0209108 12.2573V12.2581C0.011949 12.3038 0.00682802 12.3508 0.00384076 12.3978C0.00298726 12.4098 0.00213375 12.4218 0.001707 12.4337C0.00128026 12.4461 0 12.4585 0 12.4709V54.9062C0 55.5104 0.489057 56 1.09248 56H40.9075C41.5105 56 42 55.5104 42 54.9062V1.09375C41.9996 0.489624 41.5105 0 40.9071 0ZM11.3635 3.73456V11.3771H3.72981L11.3635 3.73456ZM39.8146 53.8125H2.18497V13.5646H12.456C13.0594 13.5646 13.5485 13.0746 13.5485 12.4709V2.1875H39.815V53.8125H39.8146Z" fill="#E8345D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.3835 20.3442C6.78007 20.3442 6.29102 20.8339 6.29102 21.438V37.2427C6.29102 37.8468 6.78007 38.3364 7.3835 38.3364C7.98693 38.3364 8.47598 37.8468 8.47598 37.2427V22.5317H26.4473V28.1146C26.1631 28.0936 25.8759 28.0825 25.5866 28.0825C19.2088 28.0825 14.0199 33.277 14.0199 39.6626C14.0199 39.9514 14.031 40.239 14.0523 40.5239H11.7534C11.15 40.5239 10.6609 41.0135 10.6609 41.6177C10.6609 42.2218 11.15 42.7114 11.7534 42.7114H14.4309C15.3313 46.0046 17.6605 48.8001 20.8654 50.2365C21.4164 50.4835 22.0625 50.2365 22.3091 49.6849C22.5558 49.1338 22.3091 48.4865 21.7582 48.24C18.3847 46.728 16.2049 43.3613 16.2049 39.6626C16.2049 34.4835 20.4135 30.27 25.5866 30.27C30.7596 30.27 34.9683 34.4835 34.9683 39.6626C34.9683 43.3613 32.7889 46.728 29.4154 48.24C28.8644 48.4865 28.6178 49.1338 28.8644 49.6849C29.0462 50.0912 29.4448 50.3322 29.8622 50.3322C30.0116 50.3322 30.1631 50.3015 30.3081 50.2365C34.4664 48.3729 37.1532 44.2222 37.1532 39.6626C37.1532 34.3323 33.5374 29.8325 28.6323 28.4905V21.438C28.6323 20.8339 28.1428 20.3442 27.5398 20.3442H7.3835Z" fill="#E8345D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.5886 49.0552C24.9869 49.0552 24.4961 49.5461 24.4961 50.1489C24.4961 50.7513 24.9869 51.2427 25.5886 51.2427C26.1903 51.2427 26.6811 50.7513 26.6811 50.1489C26.6811 49.5461 26.1903 49.0552 25.5886 49.0552Z" fill="#E8345D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.3835 40.5239C6.78178 40.5239 6.29102 41.0153 6.29102 41.6177C6.29102 42.2201 6.78178 42.7114 7.3835 42.7114C7.98522 42.7114 8.47598 42.2201 8.47598 41.6177C8.47598 41.0153 7.98522 40.5239 7.3835 40.5239Z" fill="#E8345D"/>
</svg>
<h2 class="my-4">Drafts</h2>
<div class="card card-body border-light mb-0 mt-3">
<div class="row">
<div class="col-7 text-start">
<a href="#" class="text-info stretched-link text-decoration-none">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</a>
</div>
<div class="col-5 text-end">
<span>Last edit: 01/01/2020</span>
</div>
</div>
</div>
<div class="card card-body border-light mb-0 mt-3">
<div class="row">
<div class="col-7 text-start">
<a href="#" class="text-info stretched-link text-decoration-none">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</a>
</div>
<div class="col-5 text-end">
<span>Last edit: 01/01/2020</span>
</div>
</div>
</div>
<div class="card card-body border-light mb-0 mt-3">
<div class="row">
<div class="col-7 text-start">
<a href="#" class="text-info stretched-link text-decoration-none">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</a>
</div>
<div class="col-5 text-end">
<span>Last edit: 01/01/2020</span>
</div>
</div>
</div>
<div class="card card-body border-light mb-0 mt-3">
<div class="row">
<div class="col-7 text-start">
<a href="#" class="text-info stretched-link text-decoration-none">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</a>
</div>
<div class="col-5 text-end">
<span>Last edit: 01/01/2020</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Progress Steps -->
<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 blue">1<span>New Event</span></li>
<li id="step-2" class="create-steps">2<span>Add Track</span></li>
<li id="step-3" class="create-steps">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>
<!-- Create a New Event -->
<div class="row">
<div class="col-md-10 mx-auto">
<div class="card">
<div class="card-body profile-content p-5">
<h2 class="mb-4">Create a New Event</h2>
<div class="row">
<div class="col-md-6">
<div class="form-floating mb-4">
<input type="text" class="form-control" id="OrganizationName" placeholder=" ">
<label for="OrganizationName">Email address</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-4">
<input type="text" id="Title" placeholder=" " class="form-control">
<label for="Title">Title</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-4">
<input type="text" id="StartDate" placeholder=" " class="form-control">
<label for="StartDate">Start Date</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-4">
<input type="text" id="EndDate" placeholder=" " class="form-control">
<label for="EndDate">End Date</label>
</div>
</div>
<div class="col-md-12">
<div class="form-floating mb-4">
<textarea class="form-control" placeholder="Leave a comment here" id="EventsExplanation" style="height: 150px"></textarea>
<label for="EventsExplanation">Event’s Explanation</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3 class="mb-3">Cover Image</h3>
<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">Photo size should be <span class="text-primary">1050x596</span> and <span class="text-primary">max 5 MB.</span></p>
<input type="file" id="Organization_ProfilePictureFile" class="form-control border-0" hidden="" name="Organization.ProfilePictureFile">
<label id="upload-label" for="Organization_ProfilePictureFile" class="btn btn-primary btn-lg text-white">Upload</label>
<span class="Microsoft.AspNetCore.Mvc.TagHelpers.TagHelperOutputExtensions+ClassAttributeHtmlContent text-danger" data-valmsg-for="Organization.ProfilePictureFile" data-valmsg-replace="true"></span>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating mb-4">
<select class="form-select" id="Country">
<option selected>Select Country</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<label for="Country">Country</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-4">
<select class="form-select" id="City">
<option selected>Select City</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<label for="City">City</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-4">
<select class="form-select" id="Language">
<option selected>Select Language</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<label for="Language">Country</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-4">
<input type="text" id="Capacity" placeholder=" " class="form-control">
<label for="Capacity">Capacity</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-4">
<select class="form-select" id="TypeOfEvent">
<option selected>Select Type of Event</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<label for="TypeOfEvent">Type of Event</label>
</div>
</div>
</div>
<div class="row">
<div class="col-6"></div>
<div class="col-6 text-end">
<a href="#" class="btn btn-primary btn-lg">Next Step <i class="fa fa-arrow-right ms-2"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Progress Steps -->
<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 blue">1<span>New Event</span></li>
<li id="step-2" class="create-steps blue">2<span>Add Track</span></li>
<li id="step-3" class="create-steps">3<span>Add Session</span></li>
</ul>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: 50%;" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
<!-- Add Track -->
<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">
<div class="col-md-3">
<div class="track-item mb-4">
<span class="track-name">There is a track name here</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="track-item mb-4">
<span class="track-name">There is a track name here</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="track-item mb-4">
<span class="track-name">There is a track name here</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>
<!-- Progress Steps -->
<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 blue">1<span>New Event</span></li>
<li id="step-2" class="create-steps blue">2<span>Add Track</span></li>
<li id="step-3" class="create-steps blue">3<span>Add Session</span></li>
</ul>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: 100%;" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
<!-- Add Session -->
<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 Session</h2>
<table class="table">
<thead>
<tr>
<th class="text-center" width="25%">Track 1</th>
<th class="text-center" width="25%">Track 1</th>
<th class="text-center" width="25%">Track 1</th>
<th class="text-center" width="25%">Track 1</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#editSessionModal" class="mt-3 d-block text-info"><small><i class="fa fa-edit me-1"></i>Edit</small></a>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#editSessionModal" class="mt-3 d-block text-info"><small><i class="fa fa-edit me-1"></i>Edit</small></a>
</div>
<div class="big-square-btn text-center">
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#addSessionModal" class="btn btn-outline-secondary d-block px-2 py-5"><i class="fa fa-plus mb-3"></i><br>Add New Session</a>
</div>
</td>
<td>
<div class="big-square-btn text-center">
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#addSessionModal" class="btn btn-outline-secondary d-block px-2 py-5"><i class="fa fa-plus mb-3"></i><br>Add New Session</a>
</div>
</td>
<td>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Berkan Şaşmaz</span>
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#editSessionModal" class="mt-3 d-block text-info"><small><i class="fa fa-edit me-1"></i>Edit</small></a>
</div>
<div class="big-square-btn text-center">
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#addSessionModal" class="btn btn-outline-secondary d-block px-2 py-5"><i class="fa fa-plus mb-3"></i><br>Add New Session</a>
</div>
</td>
<td>
<div class="big-square-btn text-center">
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#addSessionModal" class="btn btn-outline-secondary d-block px-2 py-5"><i class="fa fa-plus mb-3"></i><br>Add New Session</a>
</div>
</td>
</tr>
</tbody>
</table>
</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-outline-secondary me-2">Save as Draft</a>
<a href="#" class="btn btn-success">Publish <i class="fa fa-arrow-right ms-2"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Event Preview -->
<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-5">Event Preview</h2>
<div class="row mb-4">
<div class="col-md-4">
<span>Organization Name:</span>
</div>
<div class="col-md-8">
<span class="preview-item">Volosoft</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Title:</span>
</div>
<div class="col-md-8">
<span class="preview-item">My pretty cool event</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Start Date:</span>
</div>
<div class="col-md-8">
<span class="preview-item">10/08/2025 - 09:00</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>End Date:</span>
</div>
<div class="col-md-8">
<span class="preview-item">10/08/2025 - 10:00</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Event’s Explanation:</span>
</div>
<div class="col-md-8">
<p class="preview-item">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum.</p>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Cover Image:</span>
</div>
<div class="col-md-8">
<img src="https://api.openeventhub.com/api/eventhub/event/cover-image/3a003fb9-611a-db0c-82b2-7299ff8e8ce7" class="w-100 rounded-3">
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Country:</span>
</div>
<div class="col-md-8">
<span class="preview-item">Turkey</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>City:</span>
</div>
<div class="col-md-8">
<span class="preview-item">Istanbul</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Language:</span>
</div>
<div class="col-md-8">
<span class="preview-item">English</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Capacity:</span>
</div>
<div class="col-md-8">
<span class="preview-item">100+</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Event Type:</span>
</div>
<div class="col-md-8">
<span class="preview-item">Online</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Tracks:</span>
</div>
<div class="col-md-8">
<span class="preview-item">4</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Sessions:</span>
</div>
</div>
<table class="table">
<thead>
<tr>
<th class="text-center" width="25%">Track 1</th>
<th class="text-center" width="25%">Track 1</th>
<th class="text-center" width="25%">Track 1</th>
<th class="text-center" width="25%">Track 1</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
</td>
<td>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
</td>
<td>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
</td>
<td>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
<div class="track-item">
<span class="track-name">Session Title</span>
<small class="text-primary d-block my-2">09:00 - 10:00 | English</small>
<span class="d-block mb-2">Halil İbrahim Kalkan</span>
</div>
</td>
</tr>
</tbody>
</table>
</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-outline-secondary me-2">Save as Draft</a>
<a href="#" class="btn btn-success">Publish <i class="fa fa-arrow-right ms-2"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- 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" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary"><i class="fa fa-plus me-2"></i>Add</button>
</div>
</div>
</div>
</div>
<!-- Edit Track Modal -->
<div class="modal fade" id="editTrackModal" tabindex="-1" aria-labelledby="editTrackModal" 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 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">
<label for="TrackName" class="mb-2">Edit Track Name</label>
<input type="text" class="form-control" id="TrackName" placeholder="There is a track name here">
</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>
</div>
</div>
</div>
<!-- Add Session Modal -->
<div class="modal fade" id="addSessionModal" tabindex="-1" aria-labelledby="addSessionModal" 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 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-floating mb-3">
<input type="text" class="form-control" id="Title2" placeholder=" ">
<label for="Title2">Title</label>
</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="StartTime2" placeholder=" ">
<label for="StartTime2">Start Time</label>
</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="EndTime2" placeholder=" ">
<label for="EndTime2">End Time</label>
</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="Language2" placeholder=" ">
<label for="Language2">Language</label>
</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="Speakers" placeholder=" ">
<label for="Speakers">Speakers</label>
</div>
</div>
<div class="modal-footer border-0 p-5 pt-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary"><i class="fa fa-plus me-2"></i>Add</button>
</div>
</div>
</div>
</div>
<!-- Edit Session Modal -->
<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>
<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">
</div>
<div class="form mb-3">
<label for="Speakers">Speakers</label>
<input type="text" class="form-control" id="Speakers" placeholder="@@hikalkan">
</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>
</div>
</div>
</div>
<!-- Pricing -->
<main class="static-page container mt-5">
<h1 class="text-center mb-3">Eventhub Pricing List</h1>
<p class="text-center mb-5">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam ut nulla consequat, tempus sapien in,
pellentesque nunc. Sed at nunc pellentesque, fermentum mi et, fermentum massa. </p>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-body px-4 py-4 text-center">
<h2 class="mt-4">Normal Account</h2>
<p class="my-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam utnulla consequat, tempus sapien in.</p>
<div class="price mb-4">
<small>$</small>
<span>0</span>
</div>
<ul class="list-check-icon mb-4 text-start">
<li><i class="fa fa-check-circle"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </li>
<li><i class="fa fa-check-circle"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </li>
<li><i class="fa fa-check-circle"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </li>
</ul>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body px-4 py-4 text-center">
<h2 class="mt-4">Premium Account</h2>
<p class="my-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam utnulla consequat, tempus sapien in.</p>
<div class="price mb-4">
<small>$</small>
<span>60</span>
</div>
<ul class="list-check-icon mb-4 text-start">
<li><i class="fa fa-check-circle"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </li>
<li><i class="fa fa-check-circle"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </li>
<li><i class="fa fa-check-circle"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit adipiscing elit. </li>
<li><i class="fa fa-check-circle"></i> Lorem ipsum adipiscing elit dolor sit amet, consectetur adipiscing elit. </li>
<li><i class="fa fa-check-circle"></i> Lorem adipiscing elit adipiscing elitipsum dolor sit amet, consectetur adipiscing elit. </li>
<li><i class="fa fa-check-circle"></i> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </li>
</ul>
<a href="#" class="btn btn-info my-4"><i class="fas fa-crown me-2"></i> Upgrade to Premium</a>
</div>
</div>
</div>
<h2 class="text-center mb-3">Frequently Asked Questions</h2>
<div id="accordion" class="mb-5 pb-5 faq-container">
<div class="faq shown" data-anchor-id="accordion1">
<div class="faq-header">
<h5><button class="btn btn-faq" data-bs-toggle="collapse" data-bs-target="#accordion1" aria-expanded="false" aria-controls="accordion1">Lorem ipsum dolor sit amet, consectetur adipiscing elit?<i class="fas fa-chevron-up"></i> </button></h5>
</div>
<div id="accordion1" class="collapse show" data-bs-parent="#accordion" style="">
<div class="faq-body">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
</div>
</div>
</div>
<div class="faq" data-anchor-id="accordion12">
<div class="faq-header">
<h5><button class="btn btn-faq collapsed" data-bs-toggle="collapse" data-bs-target="#accordion12" aria-expanded="false" aria-controls="accordion12">What onsectetur adipiscinglor sit amet?<i class="fas fa-chevron-up"></i> </button></h5>
</div>
<div id="accordion12" class="collapse" data-bs-parent="#accordion" style="">
<div class="faq-body">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
</div>
</div>
</div>
<div class="faq" data-anchor-id="accordion123">
<div class="faq-header">
<h5><button class="btn btn-faq collapsed" data-bs-toggle="collapse" data-bs-target="#accordion123" aria-expanded="false" aria-controls="accordion123">What onsectetur adipiscing elit. Lorem ipsum dolor sit amet?<i class="fas fa-chevron-up"></i> </button></h5>
</div>
<div id="accordion123" class="collapse" data-bs-parent="#accordion" style="">
<div class="faq-body">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
</div>
</div>
</div>
<div class="faq" data-anchor-id="accordion124">
<div class="faq-header">
<h5><button class="btn btn-faq collapsed" data-bs-toggle="collapse" data-bs-target="#accordion124" aria-expanded="false" aria-controls="accordion124">Asectetur adipiscing elorem ipsum dolor sit amet?<i class="fas fa-chevron-up"></i> </button></h5>
</div>
<div id="accordion124" class="collapse" data-bs-parent="#accordion" style="">
<div class="faq-body">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Cart -->
<main class="static-page container mt-5">
<h1 class="text-center mb-3">Title Cart</h1>
<div class="row">
<div class="col-md-7">
<div class="card">
<div class="card-body px-5 py-4">
<h2 class="my-4">Product Detail</h2>
<h3 class="my-4">Premium Account: <span class="text-primary">Volosoft Company</span></h2>
<p>Start Date: <span class="text-dark">02/11/2020</span></p>
<p>End Date: <span class="text-dark">02/11/2020</span></p>
</div>
</div>
</div>
<div class="col-md-5">
<div class="card">
<div class="card-body px-5 py-4">
<h2 class="mt-4">Summary</h2>
<div class="cart-summary pt-3 pb-2">
<p class="my-4">Premium Licence <span class="text-dark float-end">02/11/2020</span></p>
<hr>
<p class="my-4">TOTAL <span class="text-dark float-end">02/11/2020</span></p>
</div>
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#cardModal" class="btn btn-info d-block my-4">Checkout</a>
</div>
</div>
</div>
</div>
</main>
<!-- Cart Modal -->
<div class="modal fade" id="cardModal" tabindex="-1" aria-labelledby="cardModal" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header border-0 p-5 pb-2">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body px-5">
<i class="fa fa-check-circle modal-icon"></i>
<h5 class="modal-title text-center">Thank you for your perchase!</h5>
<p class="text-center">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam utnulla consequat.</p>
</div>
<div class="modal-footer border-0 p-5 pt-2">
<button type="button" class="btn btn-info w-100" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

10
src/EventHub.Web/Pages/Empty.cshtml.cs

@ -1,10 +0,0 @@
namespace EventHub.Web.Pages
{
public class Empty : EventHubPageModel
{
public void OnGet()
{
}
}
}

214
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/CreateOrEditEventAreaViewComponent.cs

@ -0,0 +1,214 @@
using System;
using System.Collections.Generic;
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.CreateOrEditEventArea;
[Widget(
AutoInitialize = true,
RefreshUrl = "/Widgets/CreateOrEditEventArea",
ScriptFiles = new[] { "/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.js" },
StyleFiles = new[] { "/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.css" }
)]
public class CreateOrEditEventAreaViewComponent : AbpViewComponent
{
private readonly IEventAppService _eventAppService;
private readonly IOrganizationAppService _organizationAppService;
private readonly ICurrentUser _currentUser;
public CreateOrEditEventAreaViewComponent(
IEventAppService eventAppService,
IOrganizationAppService organizationAppService,
ICurrentUser currentUser)
{
_eventAppService = eventAppService;
_organizationAppService = organizationAppService;
_currentUser = currentUser;
}
public async Task<IViewComponentResult> InvokeAsync(
string eventUrlCode,
ProgressStepType stepType = ProgressStepType.Event)
{
NewEventViewModel model = null;
if (!eventUrlCode.IsNullOrWhiteSpace())
{
var @event = await _eventAppService.GetByUrlCodeAsync(eventUrlCode);
model = ObjectMapper.Map<EventDetailDto, NewEventViewModel>(@event);
ViewData["EventId"] = @event.Id;
ViewData["EventUrlCode"] = @event.UrlCode;
}
model ??= new NewEventViewModel
{
StartTime = DateTime.Now.ClearTime().AddDays(1).AddHours(19),
EndTime = DateTime.Now.ClearTime().AddDays(2).AddHours(21)
};
await FillViewDataAsync(stepType, model);
return View("~/Pages/Events/Components/CreateOrEditEventArea/Default.cshtml", model);
}
private async Task<string> GetOrganizationNameAsync(Guid organizationId)
{
var organizationList = (await _organizationAppService.GetOrganizationsByUserIdAsync(_currentUser.GetId())).Items;
return organizationList.Single(x => x.Id == organizationId).Name;
}
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<string> GetCountryNameAsync(Guid countryId)
{
var countries = await _eventAppService.GetCountriesLookupAsync();
return countries.Single(x => x.Id == countryId).Name;
}
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();
}
private async Task FillViewDataAsync(ProgressStepType stepType, NewEventViewModel model)
{
ViewData["StepType"] = stepType.ToString();
switch (stepType)
{
case ProgressStepType.Event:
ViewData["Organizations"] = await GetOrganizationsSelectItemAsync();
ViewData["Countries"] = await GetCountriesSelectItemAsync();
ViewData["Languages"] = GetLanguagesSelectItem();
break;
case ProgressStepType.Session:
ViewData["Languages"] = GetLanguagesSelectItem();
break;
case ProgressStepType.Preview:
ViewData["OrganizationName"] = await GetOrganizationNameAsync(model.OrganizationId);
if (model.CountryId is not null)
{
ViewData["CountryName"] = await GetCountryNameAsync(model.CountryId!.Value);
}
break;
}
}
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
{
Event = 0,
Track,
Session,
Preview
}
}

35
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/Default.cshtml

@ -0,0 +1,35 @@
@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea
@using Microsoft.AspNetCore.Http
@model EventHub.Web.Pages.Events.Components.CreateOrEditEventArea.CreateOrEditEventAreaViewComponent.NewEventViewModel
@{
if (Model is null)
{
throw new BadHttpRequestException("");
}
var eventId = ViewData["EventId"] as Guid?;
var eventUrlCode = ViewData["EventUrlCode"]?.ToString();
var result = Enum.TryParse(ViewData["StepType"]!.ToString(), out CreateOrEditEventAreaViewComponent.ProgressStepType stepType);
if (!result)
{
throw new BadHttpRequestException("");
}
}
<div id="EventContainer" style="@(stepType != CreateOrEditEventAreaViewComponent.ProgressStepType.Event ? "display: none" : "")">
<partial name="_eventSection.cshtml" model="@Model"/>
</div>
<div id="TrackContainer" style="@(stepType != CreateOrEditEventAreaViewComponent.ProgressStepType.Track ? "display: none" : "")">
<partial name="_trackSection.cshtml" model="@Model.Tracks"/>
</div>
<div id="SessionContainer" style="@(stepType != CreateOrEditEventAreaViewComponent.ProgressStepType.Session ? "display: none" : "")">
<partial name="_sessionSection.cshtml" model="@Model.Tracks"/>
</div>
<div id="PreviewContainer" style="@(stepType != CreateOrEditEventAreaViewComponent.ProgressStepType.Preview ? "display: none" : "")">
<partial name="_previewSection.cshtml" model="@Model"/>
</div>
<input type="hidden" id="EventId" value="@eventId"/>
<input type="hidden" id="EventUrlCode" value="@eventUrlCode"/>

170
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_eventSection.cshtml

@ -0,0 +1,170 @@
@using EventHub.Web
@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea
@using Microsoft.AspNetCore.Http
@using Microsoft.Extensions.Options
@model EventHub.Web.Pages.Events.Components.CreateOrEditEventArea.CreateOrEditEventAreaViewComponent.NewEventViewModel
@inject IOptions<EventHubUrlOptions> UrlOptions
@{
var organizations = new List<SelectListItem>();
var countries = new List<SelectListItem>();
var languages = new List<SelectListItem>();
var eventId = ViewData["EventId"] as Guid?;
@if (ViewData["StepType"]!.ToString() == CreateOrEditEventAreaViewComponent.ProgressStepType.Event.ToString())
{
organizations = ViewData["Organizations"] as List<SelectListItem>;
if (organizations is null || !organizations.Any())
{
throw new BadHttpRequestException("There is no organization");
}
countries = ViewData["Countries"] as List<SelectListItem>;
if (countries is null || !countries.Any())
{
throw new BadHttpRequestException("There is no country");
}
languages = ViewData["Languages"] as List<SelectListItem>;
if (languages is null || !languages.Any())
{
throw new BadHttpRequestException("There is no language");
}
}
}
<partial name="_progressSection.cshtml" model="@CreateOrEditEventAreaViewComponent.ProgressStepType.Event" />
@if (Model is not null)
{
<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">@(eventId.HasValue ? "Edit" : "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">
@if (eventId.HasValue)
{
<div class="image-area event-thumbnail img-fluid rounded shadow-sm mx-auto d-block" style="background-image: url(@(UrlOptions.Value.Api.EnsureEndsWith('/') + $"api/eventhub/event/cover-image/{eventId}")" alt="@Model.Title"></div>
<div class="image-area result-area" style="display: none"><img id="imageResult" src="#" alt="" class="img-fluid rounded shadow-sm mx-auto d-block"></div>
}
else
{
<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" disabled type="submit" class="btn btn-primary btn-lg"
data-url="@(eventId.HasValue ? Url.ActionLink("Update", "Event", new {Area = "eventhub", id = eventId!.Value}, null) : Url.ActionLink("Create", "Event", new {Area = "eventhub"}, null))">
Next Step <i class="fa fa-arrow-right ms-2"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
}
else
{
<partial name="_modelNullErrorSection.cshtml" />
}

3
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_modelNullErrorSection.cshtml

@ -0,0 +1,3 @@
<div class="text-center mt-5">
<h3><i class="fas fa-exclamation-circle"></i> An unknown error has occurred!</h3>
</div>

231
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_previewSection.cshtml

@ -0,0 +1,231 @@
@using EventHub.Web
@using EventHub.Web.Pages.Events
@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea
@using Microsoft.AspNetCore.Http
@using Microsoft.Extensions.Options
@model EventHub.Web.Pages.Events.Components.CreateOrEditEventArea.CreateOrEditEventAreaViewComponent.NewEventViewModel
@inject IOptions<EventHubUrlOptions> UrlOptions
@{
var organizationName = "";
var countryName = "";
Guid? eventId = null;
@if (ViewData["StepType"]!.ToString() == CreateOrEditEventAreaViewComponent.ProgressStepType.Preview.ToString())
{
eventId = ViewData["EventId"] as Guid?;
organizationName = ViewData["OrganizationName"] as string;
if (organizationName.IsNullOrWhiteSpace())
{
throw new BadHttpRequestException("Organization name cannot be empty!");
}
countryName = ViewData["CountryName"] as string;
if (Model is {CountryId: { } } && countryName.IsNullOrWhiteSpace())
{
throw new BadHttpRequestException("Country name cannot be empty when CountryId exists!");
}
}
}
@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-5">Event Preview</h2>
<div class="row mb-4">
<div class="col-md-4">
<span>Organization Name:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@organizationName</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Title:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@Model.Title</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Start Date:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@Model.StartTime.ToShortDateString() - @Model.StartTime.ToShortTimeString()</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>End Date:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@Model.EndTime.ToShortDateString() - @Model.EndTime.ToShortTimeString()</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Event’s Explanation:</span>
</div>
<div class="col-md-8">
<p class="preview-item">@Model.Description</p>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Cover Image:</span>
</div>
<div class="col-md-8">
<div class="w-100 rounded-3 event-thumbnail" style="background-image: url(@(UrlOptions.Value.Api.EnsureEndsWith('/') + $"api/eventhub/event/cover-image/{eventId}")" alt="@Model.Title"></div>
</div>
</div>
@if (Model.IsOnline != null && Model.IsOnline!.Value)
{
<div class="row mb-4">
<div class="col-md-4">
<span>Event Type:</span>
</div>
<div class="col-md-8">
<span class="preview-item">Online</span>
</div>
</div>
@if (!Model.OnlineLink.IsNullOrWhiteSpace())
{
<div class="row mb-4">
<div class="col-md-4">
<span>Url:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@Model.OnlineLink</span>
</div>
</div>
}
}
else if (Model.CountryId.HasValue && !Model.City.IsNullOrEmpty())
{
<div class="row mb-4">
<div class="col-md-4">
<span>Country:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@countryName</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>City:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@Model.City</span>
</div>
</div>
}
@if (!Model.Language.IsNullOrWhiteSpace())
{
<div class="row mb-4">
<div class="col-md-4">
<span>Language:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@Model.Language</span>
</div>
</div>
}
@if (Model.Capacity.HasValue)
{
<div class="row mb-4">
<div class="col-md-4">
<span>Capacity:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@Model.Capacity</span>
</div>
</div>
}
@if (Model.Tracks.Any())
{
<div class="row mb-4">
<div class="col-md-4">
<span>Tracks:</span>
</div>
<div class="col-md-8">
<span class="preview-item">@Model.Tracks.Count</span>
</div>
</div>
<div class="row mb-4">
<div class="col-md-4">
<span>Sessions:</span>
</div>
</div>
<table class="table">
<thead>
<tr>
@foreach (var track in Model.Tracks)
{
<th class="text-center" width="25%">@track.Name</th>
}
</tr>
</thead>
<tbody>
<tr>
@foreach (var track in Model.Tracks)
{
<td>
@if (track.Sessions.Any())
{
@foreach (var session in track.Sessions)
{
<div class="track-item">
<span class="track-name">@session.Title.TruncateWithPostfix(55)</span>
<small class="text-primary d-block my-2">@EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(session.StartTime), TimeOnly.FromDateTime(session.EndTime)) | @session.Language</small>
@foreach (var speaker in session.Speakers)
{
<span class="d-block mb-2">@speaker.UserName</span>
}
</div>
}
}
else
{
<div class="track-item text-center">
<span class="track-name text-primary"><i class="fas fa-exclamation-circle"></i> There is no session!</span>
<span class="d-block mb-2 mt-3">Tracks without Session are not visible after the event is published.</span>
</div>
}
</td>
}
</tr>
</tbody>
</table>
}
</div>
<div class="card-footer bg-white border-0 p-5 pt-1">
<div class="row">
<div class="col-6">
@if (Model.Tracks.Any() && Model.Tracks.First().Sessions.Any())
{
<button id="PreviousStepButtonInPreviewSection" data-target-step="@CreateOrEditEventAreaViewComponent.ProgressStepType.Session" class="btn btn-secondary step-transition"><i class="fa fa-arrow-left me-2"></i> Previous Step </button>
}
else
{
<button id="PreviousStepButtonInPreviewSection" data-target-step="@CreateOrEditEventAreaViewComponent.ProgressStepType.Track" class="btn btn-secondary step-transition"><i class="fa fa-arrow-left me-2"></i> Previous Step </button>
}
</div>
<div class="col-6 text-end">
<button id="PublishButton" class="btn btn-success">Publish <i class="fa fa-arrow-right ms-2"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
}
else
{
<partial name="_modelNullErrorSection.cshtml" />
}

20
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_progressSection.cshtml

@ -0,0 +1,20 @@
@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea
@model EventHub.Web.Pages.Events.Components.CreateOrEditEventArea.CreateOrEditEventAreaViewComponent.ProgressStepType
@{
var eventId = ViewData["EventId"] as Guid?;
var prefixOfStep = eventId.HasValue ? "Edit" : "New";
}
<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 == CreateOrEditEventAreaViewComponent.ProgressStepType.Event ? "blue" : "")">1<span>@prefixOfStep Event</span></li>
<li id="step-2" class="create-steps @(Model == CreateOrEditEventAreaViewComponent.ProgressStepType.Track ? "blue" : "")">2<span>@prefixOfStep Track</span></li>
<li id="step-3" class="create-steps @(Model == CreateOrEditEventAreaViewComponent.ProgressStepType.Session ? "blue" : "")">3<span>@prefixOfStep 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>

208
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_sessionSection.cshtml

@ -0,0 +1,208 @@
@using EventHub.Web.Pages.Events
@using EventHub.Events
@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea
@using Microsoft.AspNetCore.Http
@model List<EventHub.Events.TrackDto>
@{
var languages = new List<SelectListItem>();
@if (ViewData["StepType"]!.ToString() == CreateOrEditEventAreaViewComponent.ProgressStepType.Session.ToString())
{
languages = ViewData["Languages"] as List<SelectListItem>;
if (languages is null || !languages.Any())
{
throw new BadHttpRequestException("There is no language");
}
languages = languages.Select(l =>
{
if (l.Value == "en")
{
l.Selected = true;
}
return l;
}).ToList();
}
}
<partial name="_progressSection.cshtml" model="@CreateOrEditEventAreaViewComponent.ProgressStepType.Session" />
@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 Session</h2>
<table class="table">
<thead>
<tr>
@foreach (var track in Model)
{
<th class="text-center" width="25%">
@track.Name.TruncateWithPostfix(20)
</th>
}
</tr>
</thead>
<tbody>
<tr>
@foreach (var track in Model)
{
<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">@session.Title.TruncateWithPostfix(25)</span>
<small class="text-primary d-block my-2">@EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(session.StartTime), TimeOnly.FromDateTime(session.EndTime)) | @session.Language</small>
@foreach (var speaker in session.Speakers)
{
<span class="d-block mb-2">@speaker.UserName</span>
}
<abp-row>
<abp-column size="_5">
<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>
</abp-column>
<abp-column size="_6">
<a id="@session.Id" data-track-id="@track.Id" href="javascript:;" class="mt-3 d-block text-info delete-session-button">
<small><i class="fa fa-trash me-1"></i>Delete</small>
</a>
</abp-column>
</abp-row>
</div>
}
<div class="big-square-btn text-center">
<a id="@track.Id-@track.Sessions.GetHashCode()" data-track-id="@track.Id" data-bs-toggle="modal" data-bs-target="#AddSessionModal" class="btn btn-outline-secondary d-block px-2 py-5 add-session-button">
<i class="fa fa-plus mb-3"></i>
<br>Add New Session
</a>
</div>
</td>
}
</tr>
</tbody>
</table>
</div>
<div class="card-footer bg-white border-0 p-5 pt-1">
<div class="row">
<div class="col-6">
<button id="PreviousStepButtonInSessionSection"
data-target-step="@CreateOrEditEventAreaViewComponent.ProgressStepType.Track"
class="btn btn-secondary step-transition"><i class="fa fa-arrow-left me-2"></i> Previous
Step </button>
</div>
<div class="col-6 text-end">
<button id="PreviewButtonInSessionSection" data-target-step="@CreateOrEditEventAreaViewComponent.ProgressStepType.Preview" class="btn btn-primary step-transition">Preview <i class="fa fa-arrow-right ms-2"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Add Session Modal -->
<div class="modal fade" id="AddSessionModal" tabindex="-1" aria-labelledby="AddSessionModal" 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 Session</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="AddNewSessionForm">
<input disabled type="hidden" id="TrackId" name="TrackId" value="TrackIdPlaceHolder" />
<div class="modal-body profile-content px-5">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="TitleInNewSessionModal" name="Title" placeholder=" " required minLength=@SessionConsts.MinTitleLength maxLength=@SessionConsts.MaxTitleLength />
<label for="TitleInNewSessionModal">Title</label>
</div>
<div class="form-floating mb-3">
<textarea type="text" class="form-control" id="DescriptionInNewSessionModal" name="Description" placeholder=" " required minLength=@SessionConsts.MinDescriptionLength maxLength=@SessionConsts.MaxDescriptionLength></textarea>
<label for="DescriptionInNewSessionModal">Description</label>
</div>
<div class="form-floating mb-3">
<input type="datetime-local" class="form-control" id="StartTimeInNewSessionModal" name="StartTime" placeholder=" " required />
<label for="StartTimeInNewSessionModal">Start Time</label>
</div>
<div class="form-floating mb-3">
<input type="datetime-local" class="form-control" id="EndTimeInNewSessionModal" name="EndTime"placeholder=" " required />
<label for="EndTimeInNewSessionModal">End Time</label>
</div>
<div class="form-floating mb-3">
<select id="LanguageInNewSessionModal" name="Language" asp-items="languages" aria-label="en" class="form-select" required>
</select>
<label for="LanguageInNewSessionModal">Language</label>
</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="SpeakersInNewSessionModal" placeholder="Speakers" data-multiple />
</div>
</div>
<div class="modal-footer border-0 p-5 pt-2">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary"><i class="fa fa-plus me-2"></i>Add</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit Session Modal -->
<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 id="EditModalTitle" class="modal-title">Edit Session</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<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="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>
</form>
</div>
</div>
</div>
}
else
{
<partial name="_modelNullErrorSection.cshtml" />
}

105
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_trackSection.cshtml

@ -0,0 +1,105 @@
@using EventHub.Events
@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea
@model List<EventHub.Events.TrackDto>
<partial name="_progressSection.cshtml" model="@CreateOrEditEventAreaViewComponent.ProgressStepType.Track"/>
@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 truncate">@track.Name</span>
<abp-row>
<abp-column>
<a id="@track.Id" data-track-name="@track.Name" href="javascript:;" data-bs-toggle="modal" data-bs-target="#EditTrackModal" class="mt-3 d-block text-info edit-track-button"><small><i class="fa fa-edit me-1"></i>Edit</small></a>
</abp-column>
<abp-column>
<a id="@track.Id" href="javascript:;" class="mt-3 d-block text-info delete-track-button"><small><i class="fa fa-trash me-1"></i>Delete</small></a>
</abp-column>
</abp-row>
</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">
<button id="PreviousStepButtonInTrackSection" data-target-step="@CreateOrEditEventAreaViewComponent.ProgressStepType.Event" class="btn btn-secondary step-transition"><i class="fa fa-arrow-left me-2"></i> Previous Step </button>
</div>
<div class="col-6 text-end">
<button id="SkipThisStepButtonInTrackSection" data-target-step="@CreateOrEditEventAreaViewComponent.ProgressStepType.Preview" class="btn btn-link step-transition text-decoration-none">Skip This Step</button>
<button id="NextStepButtonInTrackSection" data-target-step="@CreateOrEditEventAreaViewComponent.ProgressStepType.Session" class="btn btn-primary step-transition" @(Model.Any() ? "" : "disabled")>Next Step <i class="fa fa-arrow-right ms-2"></i></button>
</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>
<form id="AddNewTrackForm" method="post">
<div class="modal-body profile-content px-5">
<div class="form-floating">
<input type="text" class="form-control" id="TrackNameInput" placeholder=" " required data-val-length-max="@TrackConsts.MaxNameLength">
<label for="TrackNameInput">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 type="submit" class="btn btn-primary">
<i class="fa fa-plus me-2"></i> Add
</button>
</div>
</form>
</div>
</div>
</div>
@* Edit Track Modal *@
<div class="modal fade" id="EditTrackModal" tabindex="-1" aria-labelledby="EditTrackModal" 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 Track</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="EditTrackForm">
<div class="modal-body profile-content px-5">
<input type="hidden" id="TrackId" value="TrackIdPlaceHolder"/>
<label for="TrackNameInput" class="mb-2">Edit Track Name</label>
<input type="text" class="form-control" id="TrackNameInput" placeholder="There is a track name here" required data-val-length-max="@TrackConsts.MaxNameLength">
</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="submit" class="btn btn-primary"><i class="fa fa-check-circle me-2"></i>Save</button>
</div>
</form>
</div>
</div>
</div>
}else
{
<partial name="_modelNullErrorSection.cshtml"/>
}

3
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.css

@ -0,0 +1,3 @@
.awesomplete {
display: inline!important;
}

512
src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.js

@ -0,0 +1,512 @@
(function () {
abp.widgets.CreateOrEditEventArea = function ($wrapper) {
var l = abp.localization.getResource('EventHub');
toastr.options.timeOut = 500;
toastr.options.preventDuplicates = true;
var stepType = {
"Event": "Event",
"Track": "Track",
"Session": "Session",
"Preview": "Preview"
}
var widgetManager = $wrapper.data('abp-widget-manager');
var eventApiService = eventHub.controllers.events.event;
var eventIdInput = $('#EventId');
var eventUrlCodeInput = $('#EventUrlCode');
var filter = {
eventUrlCode: "",
stepType: stepType.Event
}
var getFilters = function () {
return filter;
}
var init = function () {
$('#CreateEventButton').removeAttr('disabled')
FocusTrackNameInputInModalEventHandler("AddTrackModal")
FocusTrackNameInputInModalEventHandler("EditTrackModal")
NewEventFormInputAreaChangeHandler();
FilFileInputChangeEventHandler();
if (eventIdInput.val().length === 36) {
$('#OrganizationId').prop("disabled", true); // Once the Organization is selected, it cannot be changed again.
}
$("#CreateEventForm").submit(function (e) {
e.preventDefault();
if (!$(this).valid()) {
return false;
}
var formData = new FormData();
formData = FillNewEventFormData(formData);
var httpMetod = 'POST'
if (eventIdInput.val().length === 36) {
httpMetod = 'PUT'
}
$.ajax({
xhr: function() {
var xhr = new window.XMLHttpRequest();
xhr.upload.addEventListener("progress", function(evt) {
if (evt.lengthComputable) {
var percentComplete = evt.loaded / evt.total;
percentComplete = parseInt(percentComplete * 100);
if(percentComplete !== 100){
$('#CreateEventButton').prop( "disabled", true);
}
}
}, false);
return xhr;
},
url: $('#CreateEventButton').data('url'),
data: formData,
type: httpMetod,
contentType: false,
processData: false,
success: function(response){
if (eventIdInput.val().length === 36) {
abp.notify.success('Updated event');
}else{
abp.notify.success('Created event as a draft' , '', toastr.options.timeOut = 2500);
eventIdInput.val(response.id);
eventUrlCodeInput.val(response.urlCode);
}
SwitchToTrackCreation()
},
error: function (errorRaw) {
abp.notify.error(errorRaw.responseJSON.error.message, 'Error',
toastr.options = {
timeOut: 2500,
progressBar: true,
positionClass: "toast-bottom-right"
}
);
}
});
});
function SwitchToTrackCreation() {
$('#EventContainer').css('display', 'none');
$('#TrackContainer').css('display', '');
ScrollToWrapperBegin();
}
AddNewTrackSubmitFormEventHandler();
OpenEditTrackModalClickEventHandler();
EditTrackSubmitFormEventHandler();
DeleteTrackButtonClickEventHandler();
OpenAddNewSessionModalClickEventHandler();
AddNewSessionEventHandler();
OpenEditSessionModalClickEventHandler();
EditSessionEventHandler();
DeleteSessionButtonClickEventHandler();
PublishEventButtonClickEventHandler();
PrevieousOrNextStepTransitionEventHandler()
};
function FillNewEventFormData(formData) {
var fileInput = document.getElementById('Event_CoverImageFile');
formData.append("OrganizationId", $('#OrganizationId').val().trim());
formData.append("Title", $('#inputTitle').val().trim());
formData.append("StartTime", $('#inputStartdate').val().trim());
formData.append("EndTime", $('#inputEnddate').val().trim());
formData.append("Description", $('#inputDescription').val().trim());
formData.append("CoverImageStreamContent", fileInput.files[0]);
formData.append("IsOnline", $('#IsOnline').val().trim());
formData.append("OnlineLink", $('#inputOnlineLink').val().trim());
formData.append("CountryId", $('#CountryId').val().trim());
formData.append("City", $('#inputCity').val().trim());
formData.append("Language", $('#Language').val().trim());
formData.append("Capacity", $('#inputCapacity').val().trim());
return formData;
}
function AddNewTrackSubmitFormEventHandler() {
var addNewTrackForm = $('#AddNewTrackForm');
addNewTrackForm.submit(function (e) {
e.preventDefault();
if (!addNewTrackForm.valid()) {
return false;
}
var trackName = addNewTrackForm.find('#TrackNameInput').val().trim();
eventApiService.addTrack(eventIdInput.val(), {name: trackName}).then(function () {
$('#AddTrackModal').modal('hide');
abp.notify.success('Added the track');
FillFilter(stepType.Track);
widgetManager.refresh();
});
});
}
function FocusTrackNameInputInModalEventHandler(elementId) {
$('#' + elementId).on('shown.bs.modal', function () {
$(this).find('input[type=text]').trigger('focus')
});
}
function OpenEditTrackModalClickEventHandler() {
$('.edit-track-button').click(function (e) {
var editTrackModal = $('#EditTrackModal');
var clickedTrackId = this.id;
var trackName = $('#' + clickedTrackId).data('track-name');
editTrackModal.find('#TrackNameInput').val(trackName);
editTrackModal.find('#TrackId').val(clickedTrackId);
});
}
function EditTrackSubmitFormEventHandler() {
var editTrackForm = $('#EditTrackForm');
editTrackForm.submit(function (e) {
e.preventDefault();
if (!editTrackForm.valid()) {
return false;
}
var trackId = editTrackForm.find('#TrackId').val();
var oldTrackName = $('#' + trackId).data('track-name');
var trackName = editTrackForm.find('#TrackNameInput').val().trim();
if (trackName === oldTrackName) {
abp.notify.error('Track name must be different from the previous name.');
return;
}
eventApiService.updateTrack(eventIdInput.val(), trackId, {name: trackName}).then(function () {
$('#EditTrackModal').modal('hide');
abp.notify.success('Updated the track');
FillFilter(stepType.Track)
widgetManager.refresh();
});
});
}
function DeleteTrackButtonClickEventHandler() {
$('.delete-track-button').click(function (e) {
e.preventDefault();
eventApiService.deleteTrack(eventIdInput.val(), this.id).then(function () {
abp.notify.success('Successfully deleted');
FillFilter(stepType.Track)
widgetManager.refresh();
});
});
}
function OpenAddNewSessionModalClickEventHandler() {
$('.add-session-button').click(function (e) {
var addSessionModal = $('#AddSessionModal');
var clickedSessionId = this.id;
var trackId = $('#' + clickedSessionId).data('track-id');
addSessionModal.find('#TrackId').val(trackId);
$('#StartTimeInNewSessionModal').val($('#inputStartdate').val());
$('#EndTimeInNewSessionModal').val($('#inputEnddate').val());
CreateAutoCompleteByElement("SpeakersInNewSessionModal");
});
}
function AddNewSessionEventHandler() {
var addNewSessionForm = $('#AddNewSessionForm');
addNewSessionForm.submit(function (e) {
e.preventDefault();
if (!addNewSessionForm.valid()) {
return false;
}
var addSessionModal = $('#AddSessionModal');
var trackId = addSessionModal.find('#TrackId').val();
var userNameList = $('#SpeakersInNewSessionModal').val().split(/[ , ]+/);
userNameList = userNameList.filter(v => v !== '');
var input = $(this).serializeFormToObject();
input.speakerUserNames = userNameList;
eventApiService.addSession(eventIdInput.val(), trackId, input).then(function () {
$('#AddSessionModal').modal('hide');
abp.notify.success('Added the session');
FillFilter(stepType.Session);
widgetManager.refresh();
});
});
}
function OpenEditSessionModalClickEventHandler() {
$('.edit-session-button').click(function (e) {
e.preventDefault();
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.Session);
widgetManager.refresh();
});
});
}
function DeleteSessionButtonClickEventHandler() {
$('.delete-session-button').click(function (e) {
e.preventDefault();
var clickedSessionId = this.id;
var trackId = $('#' + clickedSessionId).data('track-id');
eventApiService.deleteSession(eventIdInput.val(), trackId, clickedSessionId).then(function () {
abp.notify.success('Successfully deleted');
FillFilter(stepType.Session)
widgetManager.refresh();
});
});
}
function PublishEventButtonClickEventHandler() {
$('#PublishButton').click(function (e) {
e.preventDefault();
eventApiService.publish(eventIdInput.val()).then(function (res) {
e.prefe
abp.notify.success('Event Published');
window.location.replace('https://' + window.location.host + '/events/' + res);
});
});
}
function PrevieousOrNextStepTransitionEventHandler() {
$('.step-transition').click(function (e) {
var clickedButtonId = this.id;
var targetStep = $('#' + clickedButtonId).data('target-step');
FillFilter(targetStep);
widgetManager.refresh();
ScrollToWrapperBegin();
});
}
function CreateAutoCompleteByElement(id) {
var element = document.getElementById(id);
var awesomplete = new Awesomplete(element, {
filter: function (text, input) {
return Awesomplete.FILTER_CONTAINS(text, input.match(/[^,]*$/)[0]);
},
item: function (text, input) {
return Awesomplete.ITEM(text, input.match(/[^,]*$/)[0]);
},
replace: function (text) {
var before = this.input.value.match(/^.+,\s*|/)[0];
this.input.value = before + text + " , ";
},
list: []
});
function delay(callback, ms) {
var timer = 0;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
callback.apply(context, args);
}, ms || 0);
};
}
$('#' + id).keyup(delay(function (e) {
var filterText = $(this).val().split(/[ ,]+/);
eventHub.controllers.users.user.getListByUserName(filterText[filterText.length-1]).then(function (res) {
var userNames = []
$.each(res, function (i, v) {
if (!$('#' + id).val().includes(v.userName)){
userNames.push(v.userName);
}
});
awesomplete.list = userNames
awesomplete.evaluate();
});
}, 500));
}
function FillFilter(stepType) {
filter.stepType = stepType;
filter.eventUrlCode = eventUrlCodeInput.val();
}
function ScrollToWrapperBegin() {
$([document.documentElement, document.body]).animate({
scrollTop: $wrapper.offset().top
}, 100);
}
$(document).ajaxSend(function (event, jqxhr, settings, exception) {
ButtonsBusy(true)
});
$(document).ajaxComplete(function (event, jqxhr, settings, exception) {
ButtonsBusy(false)
});
abp.ajax.showError = function (error) {
abp.notify.error(
error.message,
'Error',
toastr.options = {
timeOut: 2500,
progressBar: true,
positionClass: "toast-bottom-right"
}
);
}
function ButtonsBusy(isBusy) {
if (isBusy) {
$('button[type=submit]').attr('disabled', 'disabled')
} else {
$('button[type=submit]').removeAttr('disabled')
}
}
function NewEventFormInputAreaChangeHandler() {
var isOnline = $("#IsOnline option:selected").val()
if (isOnline === "True") {
$("#CountryId").attr("required", false);
$("#inputCity").attr("required", false);
$(".event-link-group").show();
$(".event-location-group").hide();
} else {
$("#CountryId").attr("required", true);
$("#inputCity").attr("required", true);
$(".event-link-group").hide();
$(".event-location-group").show();
}
$('#IsOnline').on('change', '', function () {
var isOnline = $("#IsOnline option:selected").val()
if (isOnline === "True") {
$("#CountryId").attr("required", false);
$("#inputCity").attr("required", false);
$(".event-link-group").show();
$(".event-location-group").hide();
} else {
$("#CountryId").attr("required", true);
$("#inputCity").attr("required", true);
$(".event-link-group").hide();
$(".event-location-group").show();
}
});
}
function FilFileInputChangeEventHandler() {
var file;
var infoArea = document.getElementById('upload-label');
var fileInput = document.getElementById('Event_CoverImageFile');
if (fileInput !== null) {
fileInput.addEventListener('change', function () {
var showFile = true;
file = fileInput.files[0];
if (file === undefined) {
$('#Event_CoverImageFile').val('');
$('#imageResult').attr('src', '#');
infoArea.textContent = 'Choose file'
return;
}
var permittedExtensions = ["jpg", "jpeg", "png"]
var fileExtension = $(this).val().split('.').pop();
if (permittedExtensions.indexOf(fileExtension) === -1) {
showFile = false;
abp.message.error(l('EventCoverImageExtensionNotAllowed'))
.then(() => {
$('#Event_CoverImageFile').val('');
file = null;
});
} else if (file.size > 1024 * 1024) {
showFile = false;
abp.message.error(l('EventCoverImageSizeExceedMessage'))
.then(() => {
$('#Event_CoverImageFile').val('');
file = null;
});
}
var img = new Image();
img.onload = function () {
var imageError = true;
var sizes = {
width: this.width,
height: this.height
};
URL.revokeObjectURL(this.src);
if (showFile && imageError) {
readURL(file);
}
}
var objectURL = URL.createObjectURL(file);
img.src = objectURL;
});
function readURL(input) {
if (input) {
var reader = new FileReader();
reader.onload = function (e) {
if (eventIdInput.val().length === 36){
$('.image-area').css('display', 'none').removeClass('d-block');
$('.result-area').css('display', '');
}
$('#imageResult').attr('src', e.target.result);
infoArea.textContent = 'File name: ' + input.name;
}
reader.readAsDataURL(input);
}
}
}
}
return {
getFilters: getFilters,
init: init,
};
}
})();

4
src/EventHub.Web/Pages/Events/Components/EventsArea/_eventListSection.cshtml

@ -23,7 +23,7 @@
<div class="col-9"> <div class="col-9">
<div class="event-info"> <div class="event-info">
<h5>@eventDto.Title</h5> <h5>@eventDto.Title</h5>
<small>@eventDto.StartTime.ToString("hh tt", CultureInfo.InvariantCulture) - @eventDto.EndTime.ToString("hh tt", CultureInfo.InvariantCulture) | @EventDateHelper.GetLocationInfo(eventDto)</small> <small>@EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(eventDto.StartTime), TimeOnly.FromDateTime(eventDto.EndTime)) | @EventDateHelper.GetLocationInfo(eventDto)</small>
<p>@eventDto.Description</p> <p>@eventDto.Description</p>
</div> </div>
<a href="@Url.Page("/Events/Detail", new { url = eventDto.Url })" class="btn btn-link p-0">Learn More <img src="/assets/more-btn.svg"></a> <a href="@Url.Page("/Events/Detail", new { url = eventDto.Url })" class="btn btn-link p-0">Learn More <img src="/assets/more-btn.svg"></a>
@ -33,4 +33,4 @@
</div> </div>
</div> </div>
</div> </div>
} }

2
src/EventHub.Web/Pages/Events/Components/RegistrationArea/RegistrationAreaViewComponent.cs

@ -10,7 +10,7 @@ namespace EventHub.Web.Pages.Events.Components.RegistrationArea
{ {
[Widget( [Widget(
AutoInitialize = true, AutoInitialize = true,
RefreshUrl = "/EventRegistration/Widget", RefreshUrl = "/Widgets/RegistrationArea",
ScriptFiles = new[] {"/Pages/Events/Components/RegistrationArea/registration-area.js"} ScriptFiles = new[] {"/Pages/Events/Components/RegistrationArea/registration-area.js"}
)] )]
public class RegistrationAreaViewComponent : AbpViewComponent public class RegistrationAreaViewComponent : AbpViewComponent

89
src/EventHub.Web/Pages/Events/Detail.cshtml

@ -2,7 +2,6 @@
@inject IHtmlLocalizer<EventHubResource> L @inject IHtmlLocalizer<EventHubResource> L
@using EventHub.Localization @using EventHub.Localization
@using EventHub.Web @using EventHub.Web
@using EventHub.Web.Helpers
@using EventHub.Web.Pages.Events @using EventHub.Web.Pages.Events
@using EventHub.Web.Pages.Events.Components.AttendeesArea @using EventHub.Web.Pages.Events.Components.AttendeesArea
@using EventHub.Web.Pages.Events.Components.EventsArea @using EventHub.Web.Pages.Events.Components.EventsArea
@ -37,7 +36,7 @@
ViewBag.OgImage = coverImageUrl; ViewBag.OgImage = coverImageUrl;
} }
<div class="bg-white py-5"> <div class="py-5">
<div class="container detail-page pb-4"> <div class="container detail-page pb-4">
<div class="row"> <div class="row">
<div class="col-md-7"> <div class="col-md-7">
@ -72,17 +71,79 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-7"> <div class="col-12 mt-4">
<h5 class="detail-head mt-5 font-weight-bold">Event Description</h5> <div class="card">
<p id="EventDescription"> <div class="card-body p-5">
@Model.Event.Description @if (Model.Event.Tracks.Any())
</p> {
<abp-tabs class="detail-tabs">
<h5 class="detail-head mt-5 font-weight-bold">Date</h5> <abp-tab class="tab" title="Description">
<p> <h5 class="detail-head mt-5 font-weight-bold">Event Description</h5>
@EventDateHelper.GetDateRangeText(Model.Event.StartTime, Model.Event.EndTime) <p id="EventDescription">
</p> @Model.Event.Description
@await Component.InvokeAsync(typeof(AttendeesAreaViewComponent), new {eventId = Model.Event.Id}) </p>
<h5 class="detail-head mt-5 font-weight-bold">Date</h5>
<p>
@EventDateHelper.GetDateRangeText(Model.Event.StartTime, Model.Event.EndTime)
</p>
@await Component.InvokeAsync(typeof(AttendeesAreaViewComponent), new { eventId = Model.Event.Id })
</abp-tab>
<abp-tab class="tab" title=" Track / Session">
<table class="table mt-3">
<thead>
<tr>
@foreach (var track in Model.Event.Tracks)
{
@if (track.Sessions.Any())
{
<th class="text-center" width="25%">@track.Name</th>
}
}
</tr>
</thead>
<tbody>
<tr>
@foreach (var track in Model.Event.Tracks)
{
@if (track.Sessions.Any())
{
<td>
@foreach (var session in track.Sessions)
{
<div class="track-item">
<span class="track-name">@session.Title.TruncateWithPostfix(55)</span>
<small class="text-primary d-block my-2">@EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(session.StartTime), TimeOnly.FromDateTime(session.EndTime)) | @session.Language</small>
@foreach (var speaker in session.Speakers)
{
<span class="d-block mb-2">@speaker.UserName</span>
}
</div>
}
</td>
}
}
</tr>
</tbody>
</table>
</abp-tab>
</abp-tabs>
}
else
{
<h5 class="detail-head mt-5 font-weight-bold">Event Description</h5>
<p id="EventDescription">
@Model.Event.Description
</p>
<h5 class="detail-head mt-5 font-weight-bold">Date</h5>
<p>
@EventDateHelper.GetDateRangeText(Model.Event.StartTime, Model.Event.EndTime)
</p>
@await Component.InvokeAsync(typeof(AttendeesAreaViewComponent), new { eventId = Model.Event.Id })
}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -102,4 +163,4 @@
MaxResultCount = 6, MaxResultCount = 6,
isPagination = false isPagination = false
}) })
</div> </div>

140
src/EventHub.Web/Pages/Events/Edit.cshtml

@ -1,135 +1,19 @@
@page "/event/edit/{url}" @page "/event/edit/{url}"
@inject IHtmlLocalizer<EventHubResource> L @using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea
@using EventHub.Localization
@using EventHub.Web
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options
@model EventHub.Web.Pages.Events.EditPageModel @model EventHub.Web.Pages.Events.EditPageModel
@inject IOptions<EventHubUrlOptions> UrlOptions
@section scripts { @section scripts {
<abp-script src="/Pages/Events/Edit.js"/> <abp-script src="/libs/awesomplete/awesomplete.min.js" />
} }
<form method="post" enctype="multipart/form-data"> @section styles {
<div class="container pt-4"> <abp-style src="/libs/awesomplete/awesomplete.css" />
<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">Edit Event</h3>
<div class="form">
<div class="row">
<input type="hidden" value="@Model.Url" id="event-detail-url"/>
<abp-input asp-for="Event.Id"/>
<div class="col-md-6">
<div class="form-label-group">
<select asp-for="Event.OrganizationId" asp-items="Model.Organizations" class="form-select">
<option selected value="">Organization</option>
</select>
<span asp-validation-for="Event.OrganizationId" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input type="text" id="inputTitle" asp-for="Event.Title" class="form-control" placeholder="Title">
<label for="inputTitle">Title</label>
<span asp-validation-for="Event.Title" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input id="inputStartdate" asp-for="Event.StartTime" class="form-control" placeholder="Start Date">
<label for="inputStartdate">Start Date</label>
<span asp-validation-for="Event.StartTime" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input id="inputEnddate" asp-for="Event.EndTime" class="form-control" placeholder="End Date">
<label for="inputEnddate">End Date</label>
<span asp-validation-for="Event.EndTime" class="text-danger"></span>
</div>
</div>
<div class="col-md-12">
<div class="form-label-group form-floating">
<textarea rows="5" type="text" id="inputDescription" asp-for="Event.Description" class="form-control" placeholder="Description"></textarea>
<span asp-validation-for="Event.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-5">
<div class="image-area">
<img id="imageResult" src="@(UrlOptions.Value.Api.EnsureEndsWith('/') + $"api/eventhub/event/cover-image/{Model.Event.Id}")" alt="" class="img-fluid rounded shadow-sm mx-auto d-block">
</div>
</div>
<div class="col-7">
<div>
<p class="mb-3">For horizontal format 600x300 sized upload images.</p>
<input type="file" id="Event_CoverImageFile" asp-for="Event.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="Event.CoverImageFile" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<select class="form-select" asp-for="Event.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="Event.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="Event.OnlineLink" class="form-control" placeholder="Online Link">
<label for="inputOnlineLink">Online Link</label>
<span asp-validation-for="Event.OnlineLink" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input type="number" id="inputCapacity" asp-for="Event.Capacity" class="form-control" placeholder="Capacity">
<label for="inputCapacity">Capacity</label>
<span asp-validation-for="Event.Capacity" class="text-danger"></span>
</div>
</div>
<div class="col-md-6 event-location-group">
<select asp-for="Event.CountryId" asp-items="Model.Countries" class="form-select">
<option selected="" value="">Country</option>
</select>
<span asp-validation-for="Event.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="Event.City" class="form-control" placeholder="City">
<label for="inputCity">City</label>
<span asp-validation-for="Event.City" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<select asp-for="Event.Language" asp-items="Model.Languages" aria-label="en" class="form-select">
<option selected value="">Language</option>
</select>
<span asp-validation-for="Event.Language" class="text-danger"></span>
</div>
<div class="col-md-12 text-end"> <div class="container mt-5">
<button type="submit" class="btn btn-primary btn-lg"> @await Component.InvokeAsync(typeof(CreateOrEditEventAreaViewComponent), new
@L["Submit"] {
</button> eventUrlCode = Model.UrlCode,
</div> stepType = CreateOrEditEventAreaViewComponent.ProgressStepType.Event
</div> })
</div> </div>
</div>
</div>
</div>
</div>
</div>
</form>

193
src/EventHub.Web/Pages/Events/Edit.cshtml.cs

@ -1,202 +1,19 @@
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.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 namespace EventHub.Web.Pages.Events
{ {
public class EditPageModel : EventHubPageModel public class EditPageModel : EventHubPageModel
{ {
[BindProperty(SupportsGet = true)] public string UrlCode { get; set; }
public new string Url { get; set; }
[BindProperty] public EditPageModel()
public EditEventViewModel Event { get; set; }
public List<SelectListItem> Organizations { get; private set; }
public List<SelectListItem> Countries { get; private set; }
public List<SelectListItem> Languages { get; private set; }
private readonly IEventAppService _eventAppService;
private readonly IOrganizationAppService _organizationAppService;
public EditPageModel(
IEventAppService eventAppService,
IOrganizationAppService organizationAppService)
{
_eventAppService = eventAppService;
_organizationAppService = organizationAppService;
}
public async Task OnGetAsync()
{ {
var urlCode = EventUrlCodeHelper.GetCodeFromUrl(Url);
var eventDetailDto = await _eventAppService.GetByUrlCodeAsync(urlCode);
Event = ObjectMapper.Map<EventDetailDto, EditEventViewModel>(eventDetailDto);
await FillOrganizationsAsync();
await FillCountriesAsync();
FillLanguages();
} }
public async Task<IActionResult> OnPostAsync() public void OnGet(string url)
{ {
try UrlCode = EventUrlCodeHelper.GetCodeFromUrl(url);
{
ValidateModel();
var updateEventDto = ObjectMapper.Map<EditEventViewModel, UpdateEventDto>(Event);
await using var memoryStream = new MemoryStream();
if (Event.CoverImageFile != null && Event.CoverImageFile.Length > 0)
{
await Event.CoverImageFile.CopyToAsync(memoryStream);
memoryStream.Position = 0;
updateEventDto.CoverImageStreamContent = new RemoteStreamContent(memoryStream, fileName: Event.CoverImageFile.FileName, contentType: Event.CoverImageFile.ContentType);
}
await _eventAppService.UpdateAsync(Event.Id, updateEventDto);
await memoryStream.DisposeAsync();
return RedirectToPage("./Detail", new { url = Url });
}
catch (Exception exception)
{
ShowAlert(exception);
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();
if (Event.OrganizationId != Guid.Empty)
{
Organizations.Single(x => x.Value == Event.OrganizationId.ToString()).Selected = true;
}
}
private async Task FillCountriesAsync()
{
var result = await _eventAppService.GetCountriesLookupAsync();
Countries = result.Select(
country => new SelectListItem
{
Value = country.Id.ToString(),
Text = country.Name
}
).ToList();
if (Event.CountryId.HasValue)
{
Countries.Single(x => x.Value == Event.CountryId.ToString()).Selected = true;
}
}
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();
if (!string.IsNullOrWhiteSpace(Event.Language))
{
Languages.Single(x => x.Value == Event.Language).Selected = true;
}
}
public class EditEventViewModel
{
[HiddenInput]
public Guid Id { get; set; }
[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; }
} }
} }
} }

94
src/EventHub.Web/Pages/Events/Edit.js

@ -1,94 +0,0 @@
$(function () {
var l = abp.localization.getResource('EventHub');
var isOnline = $("#Event_IsOnline option:selected").val()
if (isOnline === "True") {
$(".event-link-group").show();
$(".event-location-group").hide();
} else {
$(".event-link-group").hide();
$(".event-location-group").show();
}
$('#Event_IsOnline').on('change', '', function () {
var isOnline = $("#Event_IsOnline option:selected").val()
if (isOnline === "True") {
$("#Event_CountryId").attr("required", false);
$("#inputCity").attr("required", false);
$(".event-link-group").show();
$(".event-location-group").hide();
} else {
$("#Event_CountryId").attr("required", true);
$("#inputCity").attr("required", true);
$(".event-link-group").hide();
$(".event-location-group").show();
}
});
var infoArea = document.getElementById('upload-label');
var fileInput = document.getElementById('Event_CoverImageFile');
var file;
if (fileInput !== null) {
fileInput.addEventListener('change', function () {
var showFile = true;
file = fileInput.files[0];
if (file === undefined) {
$('#Event_CoverImageFile').val('');
$('#imageResult').attr('src', '#');
infoArea.textContent = 'Choose file'
return;
}
var permittedExtensions = ["jpg", "jpeg", "png"]
var fileExtension = $(this).val().split('.').pop();
if (permittedExtensions.indexOf(fileExtension) === -1) {
showFile = false;
abp.message.error(l('EventCoverImageExtensionNotAllowed'))
.then(() => {
$('#Event_CoverImageFile').val('');
file = null;
});
} else if (file.size > 1024 * 1024) {
showFile = false;
abp.message.error(l('EventCoverImageSizeExceedMessage'))
.then(() => {
$('#Event_CoverImageFile').val('');
file = null;
});
}
var img = new Image();
img.onload = function () {
var imageError = true;
var sizes = {
width: this.width,
height: this.height
};
URL.revokeObjectURL(this.src);
if (showFile && imageError) {
readURL(file);
}
}
var objectURL = URL.createObjectURL(file);
img.src = objectURL;
});
function readURL(input) {
if (input) {
var reader = new FileReader();
reader.onload = function (e) {
$('#imageResult').attr('src', e.target.result);
infoArea.textContent = 'File name: ' + input.name;
}
reader.readAsDataURL(input);
}
}
}
});

11
src/EventHub.Web/Pages/Events/EventDateHelper.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Globalization;
using System.Text; using System.Text;
using EventHub.Events; using EventHub.Events;
@ -24,6 +25,16 @@ namespace EventHub.Web.Pages.Events
return sb.ToString(); return sb.ToString();
} }
public static string GetTimeRangeText(TimeOnly startTime, TimeOnly endTime)
{
var sb = new StringBuilder();
sb.Append(startTime.ToString("hh tt", CultureInfo.InvariantCulture));
sb.Append(" - ");
sb.Append(endTime.ToString("hh tt", CultureInfo.InvariantCulture));
return sb.ToString();
}
public static string GetLocationInfo(EventInListDto eventItem) public static string GetLocationInfo(EventInListDto eventItem)
{ {
if (eventItem.IsOnline) if (eventItem.IsOnline)

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

@ -1,145 +1,99 @@
@page "/events/new" @page "/events/new/{eventUrlCode?}"
@inject IHtmlLocalizer<EventHubResource> L @inject IHtmlLocalizer<EventHubResource> L
@using EventHub.Localization @using EventHub.Localization
@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea
@using Microsoft.AspNetCore.Mvc.Localization @using Microsoft.AspNetCore.Mvc.Localization
@model EventHub.Web.Pages.Events.NewPageModel @model EventHub.Web.Pages.Events.NewPageModel
@section scripts { @section scripts {
<abp-script src="/Pages/Events/New.js"/> <abp-script src="/libs/awesomplete/awesomplete.min.js" />
} }
@if (!Model.Organizations.Any()) @section styles {
{ <abp-style src="/libs/awesomplete/awesomplete.css" />
<div class="container mt-5"> }
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto "> <div class="container mt-5">
<div class="card" > @if (!Model.IsHasOrganizations)
<div class="card-body text-center p-lg-5"> {
<h4 class="mb-4">@L["NewEventYouHaveNoOrganizationYetMessage"]</h4> <div class="row">
<a abp-button="Primary" href="@Url.Page("/Organizations/New")">@L["CreateAnOrganization"]</a> <div class="col-lg-8 col-md-10 mx-auto ">
<div class="card">
<div class="card-body text-center p-lg-5">
<h4 class="mb-4">@L["NewEventYouHaveNoOrganizationYetMessage"]</h4>
<a abp-button="Primary" href="@Url.Page("/Organizations/New")">@L["CreateAnOrganization"]</a>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> }
</div> else if (Model.DraftEventList.Any())
} {
else <h1 class="text-center mb-3">Create an Event</h1>
{ <p class="text-center mb-5">
<form method="post" enctype="multipart/form-data"> You can create a new event or proceed through the draft events you have created before.
<div class="container pt-4"> </p>
<div class="row py-4"> <div class="row">
<div class="col-md-9 mx-auto profile-content"> <div class="col-md-6">
<div class="card"> <div class="card">
<div class="card-body" style="min-height: 496px"> <div class="card-body px-4 py-5 text-center">
<h3 class="mb-4">Create New Event</h3> <svg width="50" height="58" viewBox="0 0 50 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<div class="form"> <path d="M33.0445 28.1094H9.31281C8.68249 28.1094 8.17188 28.6205 8.17188 29.2515C8.17188 29.8825 8.68249 30.394 9.31281 30.394H33.0445C33.6748 30.394 34.1854 29.8825 34.1854 29.2515C34.1854 28.6209 33.6748 28.1094 33.0445 28.1094Z" fill="#E8345D"/>
<div class="row"> <path d="M33.0445 34.8032H9.31281C8.68249 34.8032 8.17188 35.3143 8.17188 35.9453C8.17188 36.5763 8.68249 37.0879 9.31281 37.0879H33.0445C33.6748 37.0879 34.1854 36.5763 34.1854 35.9453C34.1854 35.3143 33.6748 34.8032 33.0445 34.8032Z" fill="#E8345D"/>
<div class="col-md-6"> <path d="M33.0445 41.4976H9.31281C8.68249 41.4976 8.17188 42.0087 8.17188 42.6397C8.17188 43.2707 8.68249 43.7822 9.31281 43.7822H33.0445C33.6748 43.7822 34.1854 43.2707 34.1854 42.6397C34.1854 42.0087 33.6748 41.4976 33.0445 41.4976Z" fill="#E8345D"/>
<div class="form-label-group"> <path d="M48.0044 5.61362H44.3885V1.99791C44.3885 0.896072 43.4919 0 42.3894 0H37.5942C36.4935 0 35.5981 0.896072 35.5981 1.99791V5.61317H31.9827C31.1161 5.61317 30.3785 6.16896 30.1019 6.94246H12.6346C12.332 6.94246 12.0418 7.06282 11.8281 7.27699L0.333931 18.791C0.120145 19.0051 0 19.2959 0 19.599V54.9016C0 56.6101 1.38917 58 3.09637 58H39.7387C41.4459 58 42.8351 56.6101 42.8351 54.9016V39.4692C42.8351 38.8382 42.324 38.3267 41.6941 38.3267C41.0643 38.3267 40.5536 38.8382 40.5536 39.4692V54.9016C40.5536 55.3428 40.1804 55.7153 39.7387 55.7153H3.09637C2.65466 55.7153 2.28142 55.3428 2.28142 54.9016V20.7411H10.3103C12.2212 20.7411 13.7755 19.1857 13.7755 17.2736V9.22711H29.9831V12.4114C29.9831 13.5132 30.8802 14.4093 31.9827 14.4093H35.5981V18.0246C35.5981 19.1282 36.4935 20.026 37.5942 20.026H40.5532V29.1438C40.5532 29.7744 41.0643 30.2859 41.6941 30.2859C42.324 30.2859 42.8346 29.7744 42.8346 29.1438V19.9742C43.7233 19.7707 44.3885 18.9751 44.3885 18.025V14.4093H48.0039C49.1047 14.4093 50 13.5132 50 12.4114V7.61462C50 6.51102 49.1047 5.61362 48.0044 5.61362ZM10.3103 18.4564H3.8941L11.4937 10.8431V17.2736C11.4937 17.9259 10.9632 18.4564 10.3103 18.4564ZM47.7186 12.1246H43.2481C42.6177 12.1246 42.1071 12.6362 42.1071 13.2672V17.7413H37.8795V13.2672C37.8795 12.6362 37.3689 12.1246 36.739 12.1246H32.265V7.89827H36.739C37.3689 7.89827 37.8795 7.38673 37.8795 6.75572V2.28465H42.1071V6.75572C42.1071 7.38673 42.6177 7.89827 43.2481 7.89827H47.7186V12.1246Z" fill="#E8345D"/>
<select asp-for="Event.OrganizationId" asp-items="Model.Organizations" class="form-select"> <path d="M41.6937 33.1411C41.0633 33.1411 40.5527 33.6526 40.5527 34.2837V34.3067C40.5527 34.9372 41.0638 35.4488 41.6937 35.4488C42.3235 35.4488 42.8342 34.9372 42.8342 34.3067V34.2837C42.8346 33.6526 42.3235 33.1411 41.6937 33.1411Z" fill="#E8345D"/>
@if (Model.Organizations.Count > 1) </svg>
<h2 class="mt-4">Create a New Event</h2>
<p class="my-4">Create your own Eventhub group and benefit from a community of millions.</p>
<a href="@Url.Page("/events/new", new {isCreateNew = true})" class="btn btn-primary">Create</a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body px-4 py-5 text-center">
<svg width="42" height="56" viewBox="0 0 42 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.9071 0H12.456C12.4436 0 12.4313 0.00128174 12.4193 0.00170898C12.4069 0.00213622 12.3946 0.00299073 12.3822 0.00384521C12.3361 0.00683594 12.29 0.0119629 12.2452 0.020935C12.2439 0.020935 12.2431 0.020935 12.2422 0.0213622C12.1965 0.0303345 12.1517 0.0435792 12.1074 0.0585327C12.0958 0.0623779 12.0839 0.0666504 12.0724 0.0709229C12.0314 0.0863037 11.9913 0.103821 11.952 0.123901C11.949 0.12561 11.9456 0.126892 11.9422 0.128601C11.9008 0.150818 11.8616 0.176453 11.8231 0.203369C11.8133 0.210632 11.8031 0.218323 11.7933 0.226013C11.7549 0.255066 11.7182 0.285828 11.6836 0.320435L0.320063 11.6971C0.285497 11.7318 0.254344 11.7693 0.225325 11.8074C0.217643 11.8172 0.210388 11.827 0.20356 11.8369C0.176248 11.8757 0.150216 11.9155 0.128025 11.9569C0.126745 11.9595 0.125892 11.9616 0.125038 11.9642C0.104127 12.0039 0.0862037 12.0453 0.0708408 12.0876C0.0665732 12.0988 0.0623056 12.1099 0.0584649 12.121C0.0435287 12.1658 0.0302994 12.2111 0.0209108 12.2573V12.2581C0.011949 12.3038 0.00682802 12.3508 0.00384076 12.3978C0.00298726 12.4098 0.00213375 12.4218 0.001707 12.4337C0.00128026 12.4461 0 12.4585 0 12.4709V54.9062C0 55.5104 0.489057 56 1.09248 56H40.9075C41.5105 56 42 55.5104 42 54.9062V1.09375C41.9996 0.489624 41.5105 0 40.9071 0ZM11.3635 3.73456V11.3771H3.72981L11.3635 3.73456ZM39.8146 53.8125H2.18497V13.5646H12.456C13.0594 13.5646 13.5485 13.0746 13.5485 12.4709V2.1875H39.815V53.8125H39.8146Z" fill="#E8345D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.3835 20.3442C6.78007 20.3442 6.29102 20.8339 6.29102 21.438V37.2427C6.29102 37.8468 6.78007 38.3364 7.3835 38.3364C7.98693 38.3364 8.47598 37.8468 8.47598 37.2427V22.5317H26.4473V28.1146C26.1631 28.0936 25.8759 28.0825 25.5866 28.0825C19.2088 28.0825 14.0199 33.277 14.0199 39.6626C14.0199 39.9514 14.031 40.239 14.0523 40.5239H11.7534C11.15 40.5239 10.6609 41.0135 10.6609 41.6177C10.6609 42.2218 11.15 42.7114 11.7534 42.7114H14.4309C15.3313 46.0046 17.6605 48.8001 20.8654 50.2365C21.4164 50.4835 22.0625 50.2365 22.3091 49.6849C22.5558 49.1338 22.3091 48.4865 21.7582 48.24C18.3847 46.728 16.2049 43.3613 16.2049 39.6626C16.2049 34.4835 20.4135 30.27 25.5866 30.27C30.7596 30.27 34.9683 34.4835 34.9683 39.6626C34.9683 43.3613 32.7889 46.728 29.4154 48.24C28.8644 48.4865 28.6178 49.1338 28.8644 49.6849C29.0462 50.0912 29.4448 50.3322 29.8622 50.3322C30.0116 50.3322 30.1631 50.3015 30.3081 50.2365C34.4664 48.3729 37.1532 44.2222 37.1532 39.6626C37.1532 34.3323 33.5374 29.8325 28.6323 28.4905V21.438C28.6323 20.8339 28.1428 20.3442 27.5398 20.3442H7.3835Z" fill="#E8345D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.5886 49.0552C24.9869 49.0552 24.4961 49.5461 24.4961 50.1489C24.4961 50.7513 24.9869 51.2427 25.5886 51.2427C26.1903 51.2427 26.6811 50.7513 26.6811 50.1489C26.6811 49.5461 26.1903 49.0552 25.5886 49.0552Z" fill="#E8345D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.3835 40.5239C6.78178 40.5239 6.29102 41.0153 6.29102 41.6177C6.29102 42.2201 6.78178 42.7114 7.3835 42.7114C7.98522 42.7114 8.47598 42.2201 8.47598 41.6177C8.47598 41.0153 7.98522 40.5239 7.3835 40.5239Z" fill="#E8345D"/>
</svg>
<h2 class="my-4">Drafts</h2>
@foreach (var draftEvent in Model.DraftEventList)
{ {
<option selected value="">Pick an organization</option> <div class="card card-body border-light mb-0 mt-3">
<div class="row">
<div class="col-7 text-start">
<a href="@Url.Page("/events/new", new {eventUrlCode = draftEvent.UrlCode})" class="text-info stretched-link text-decoration-none">@draftEvent.Title</a>
</div>
<div class="col-5 text-end">
<span>
Last edit:
@if (!draftEvent.LastModificationTime.HasValue)
{
<span>@draftEvent.CreationTime.ToShortDateString()</span>
}
else
{
<span>@draftEvent.LastModificationTime!.Value.ToShortDateString()</span>
}
</span>
</div>
</div>
</div>
} }
</select>
<span asp-validation-for="Event.OrganizationId" class="text-danger"></span>
</div> </div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input type="text" id="inputTitle" asp-for="Event.Title" class="form-control" placeholder="Title">
<label for="inputTitle">Title</label>
<span asp-validation-for="Event.Title" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input id="inputStartdate" asp-for="Event.StartTime" class="form-control" placeholder="Start Date">
<label for="inputStartdate">Start Date</label>
<span asp-validation-for="Event.StartTime" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input id="inputEnddate" asp-for="Event.EndTime" class="form-control" placeholder="End Date">
<label for="inputEnddate">End Date</label>
<span asp-validation-for="Event.EndTime" class="text-danger"></span>
</div>
</div>
<div class="col-md-12">
<div class="form-label-group">
<textarea rows="5" type="text" id="inputDescription" asp-for="Event.Description" class="form-control" placeholder="Description"></textarea>
<span asp-validation-for="Event.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="Event.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="Event.CoverImageFile" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<select class="form-select" asp-for="Event.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="Event.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="Event.OnlineLink" class="form-control" placeholder="Online Link">
<label for="inputOnlineLink">Online Link</label>
<span asp-validation-for="Event.OnlineLink" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input type="number" id="inputCapacity" asp-for="Event.Capacity" class="form-control" placeholder="Capacity">
<label for="inputCapacity">Capacity</label>
<span asp-validation-for="Event.Capacity" class="text-danger"></span>
</div>
</div>
<div class="col-md-6 event-location-group">
<select asp-for="Event.CountryId" asp-items="Model.Countries" class="form-select">
<option selected="" value="">Country</option>
</select>
<span asp-validation-for="Event.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="Event.City" class="form-control" placeholder="City">
<label for="inputCity">City</label>
<span asp-validation-for="Event.City" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<select asp-for="Event.Language" asp-items="Model.Languages" aria-label="en" class="form-select">
<option selected value="">Language</option>
</select>
<span asp-validation-for="Event.Language" class="text-danger"></span>
</div>
<div class="col-md-12 text-end">
<button type="submit" class="btn btn-primary btn-lg">
Publish New Event
</button>
</div>
</div> </div>
</div>
</div> </div>
</div> </div>
</div> }
</div> else
</div> {
</form> @await Component.InvokeAsync(typeof(CreateOrEditEventAreaViewComponent), new
} {
eventUrlCode = Model.EventUrlCode,
stepType = CreateOrEditEventAreaViewComponent.ProgressStepType.Event
})
}
</div>

164
src/EventHub.Web/Pages/Events/New.cshtml.cs

@ -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.GetDraftEventsByCurrentUser();
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; }
}
} }
} }

94
src/EventHub.Web/Pages/Events/New.js

@ -1,94 +0,0 @@
$(function () {
var l = abp.localization.getResource('EventHub');
var isOnline = $("#Event_IsOnline option:selected").val()
if (isOnline === "True") {
$(".event-link-group").show();
$(".event-location-group").hide();
} else {
$(".event-link-group").hide();
$(".event-location-group").show();
}
$('#Event_IsOnline').on('change', '', function () {
var isOnline = $("#Event_IsOnline option:selected").val()
if (isOnline === "True") {
$("#Event_CountryId").attr("required", false);
$("#inputCity").attr("required", false);
$(".event-link-group").show();
$(".event-location-group").hide();
} else {
$("#Event_CountryId").attr("required", true);
$("#inputCity").attr("required", true);
$(".event-link-group").hide();
$(".event-location-group").show();
}
});
var infoArea = document.getElementById('upload-label');
var fileInput = document.getElementById('Event_CoverImageFile');
var file;
if (fileInput !== null) {
fileInput.addEventListener('change', function () {
var showFile = true;
file = fileInput.files[0];
if (file === undefined) {
$('#Event_CoverImageFile').val('');
$('#imageResult').attr('src', '#');
infoArea.textContent = 'Choose file'
return;
}
var permittedExtensions = ["jpg", "jpeg", "png"]
var fileExtension = $(this).val().split('.').pop();
if (permittedExtensions.indexOf(fileExtension) === -1) {
showFile = false;
abp.message.error(l('EventCoverImageExtensionNotAllowed'))
.then(() => {
$('#Event_CoverImageFile').val('');
file = null;
});
} else if (file.size > 1024 * 1024) {
showFile = false;
abp.message.error(l('EventCoverImageSizeExceedMessage'))
.then(() => {
$('#Event_CoverImageFile').val('');
file = null;
});
}
var img = new Image();
img.onload = function () {
var imageError = true;
var sizes = {
width: this.width,
height: this.height
};
URL.revokeObjectURL(this.src);
if (showFile && imageError) {
readURL(file);
}
}
var objectURL = URL.createObjectURL(file);
img.src = objectURL;
});
function readURL(input) {
if (input) {
var reader = new FileReader();
reader.onload = function (e) {
$('#imageResult').attr('src', e.target.result);
infoArea.textContent = 'File name: ' + input.name;
}
reader.readAsDataURL(input);
}
}
}
});

4
src/EventHub.Web/Pages/Organizations/Components/JoinArea/JoinAreaViewComponent.cs

@ -10,7 +10,7 @@ namespace EventHub.Web.Pages.Organizations.Components.JoinArea
{ {
[Widget( [Widget(
AutoInitialize = true, AutoInitialize = true,
RefreshUrl = "/OrganizationMembership/Widget", RefreshUrl = "/Widgets/JoinArea",
ScriptFiles = new[] {"/Pages/Organizations/Components/JoinArea/join-area.js"} ScriptFiles = new[] {"/Pages/Organizations/Components/JoinArea/join-area.js"}
)] )]
public class JoinAreaViewComponent : AbpViewComponent public class JoinAreaViewComponent : AbpViewComponent
@ -49,4 +49,4 @@ namespace EventHub.Web.Pages.Organizations.Components.JoinArea
public bool IsJoined { get; set; } public bool IsJoined { get; set; }
} }
} }
} }

56
src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml

@ -0,0 +1,56 @@
@using EventHub.Web.PaymentRequests
@using Microsoft.Extensions.Options
@model EventHub.Organizations.OrganizationProfileDto
@inject IOptionsSnapshot<PremiumPlanInfoOptions> PremiumPlanInfoOptionsSnapshot
<abp-container class="mt-4">
<abp-row>
@if (Model.IsPremium && PremiumPlanInfoOptionsSnapshot.Value.IsActive && PremiumPlanInfoOptionsSnapshot.Value.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths)))
{
<span class="mb-2 ms-5 text-center strong">Premium end date: @Model.PremiumEndDate!.Value.ToShortDateString()</span>
}
<abp-column>
<a href="/organization/edit/@Model.Name" class="btn btn-success text-white">Edit</a>
</abp-column>
<abp-column size="_9">
@if (PremiumPlanInfoOptionsSnapshot.Value.IsActive)
{
@if (Model.IsPremium)
{
@if (PremiumPlanInfoOptionsSnapshot.Value.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths)))
{
<div class="d-grid gap-2">
<a href="@Url.Page("/Pricing", new { organizationName = Model.Name })" class="btn btn-info">
<i class="fas fa-crown me-2"></i> Extend Now
</a>
</div>
}
else
{
<div class="d-grid gap-2">
<button class="btn btn-info">
<span class="mt-2">Premium end date: @Model.PremiumEndDate!.Value.ToShortDateString()</span>
</button>
</div>
}
}
else
{
<div class="d-grid gap-2">
<a href="@Url.Page("/Pricing", new { organizationName = Model.Name })" class="btn btn-info">
<i class="fas fa-crown me-2"></i> Upgrade To @PremiumPlanInfoOptionsSnapshot.Value.PlanType
</a>
</div>
}
}
else
{
<abp-card class="text-center">
<abp-card-body>
<span>Premium end date: @Model.PremiumEndDate!.Value.ToShortDateString()</span>
</abp-card-body>
</abp-card>
}
</abp-column>
</abp-row>
</abp-container>

44
src/EventHub.Web/Pages/Organizations/Profile.cshtml

@ -8,13 +8,14 @@
@using EventHub.Web.PaymentRequests @using EventHub.Web.PaymentRequests
@using Microsoft.AspNetCore.Mvc.Localization @using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options @using Microsoft.Extensions.Options
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.SweetAlert2
@using Volo.Abp.Timing @using Volo.Abp.Timing
@model EventHub.Web.Pages.Organizations.ProfilePageModel @model EventHub.Web.Pages.Organizations.ProfilePageModel
@inject IClock Clock @inject IClock Clock
@inject IOptions<EventHubUrlOptions> UrlOptions @inject IOptions<EventHubUrlOptions> UrlOptions
@inject IOptionsSnapshot<PremiumPlanInfoOptions> PremiumPlanInfoOptionsSnapshot
@section scripts { @section scripts {
<abp-script type="@typeof(Sweetalert2ScriptContributor)"/>
<abp-script src="/Pages/Organizations/Profile.js"/> <abp-script src="/Pages/Organizations/Profile.js"/>
} }
@ -66,44 +67,7 @@
@if (Model.IsOrganizationOwner) @if (Model.IsOrganizationOwner)
{ {
<a href="/organization/edit/@Model.Organization.Name" class="btn btn-primary mt-4 text-white"> <partial name="Components/_upgradeOrExtendOrganizationSection.cshtml" model="@Model.Organization"/>
<i class="far fa-edit"></i> Edit
</a>
@if (PremiumPlanInfoOptionsSnapshot.Value.IsActive)
{
<abp-card class="detail-head mt-4 font-weight-bold text-center">
<abp-card-body>
@if (Model.Organization.IsPremium)
{
<span>Premium end date: @Model.Organization.PremiumEndDate!.Value.ToShortDateString()</span>
@if (PremiumPlanInfoOptionsSnapshot.Value.IsExtendable && Model.Organization.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths)))
{
<div class="d-grid gap-2">
<a href="@Url.Page("/Pricing", new { organizationName = Model.Organization.Name })" class="btn btn-primary mt-3">
<i class="fas fa-handshake"></i> @L["ExtendNow"].Value
</a>
</div>
}
}
else
{
<div class="d-grid gap-2">
<a href="@Url.Page("/Pricing", new { organizationName = Model.Organization.Name })" class="btn btn-primary">
<i class="fas fa-handshake"></i> @L["UpgradeToPremium"]
</a>
</div>
}
</abp-card-body>
</abp-card>
}else if (Model.Organization.IsPremium)
{
<abp-card class="detail-head mt-4 font-weight-bold text-center">
<abp-card-body>
<span>Premium end date: @Model.Organization.PremiumEndDate!.Value.ToShortDateString()</span>
</abp-card-body>
</abp-card>
}
} }
@await Component.InvokeAsync(typeof(JoinAreaViewComponent), new {organizationId = Model.Organization.Id}) @await Component.InvokeAsync(typeof(JoinAreaViewComponent), new {organizationId = Model.Organization.Id})
@await Component.InvokeAsync(typeof(MembersAreaViewComponent), @await Component.InvokeAsync(typeof(MembersAreaViewComponent),
@ -212,4 +176,4 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>

29
src/EventHub.Web/Pages/Organizations/Profile.js

@ -1,13 +1,36 @@
$(function () { $(function () {
$('.events > .nav-item > a[data-toggle="tab"]').on('shown.bs.tab', function (e) { $('.events > .nav-item > a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href") var target = $(e.target).attr("href")
if (target === "#UpcomingEvent"){ if (target === "#UpcomingEvent") {
$("a[href='#PastEvent']").removeClass('selected'); $("a[href='#PastEvent']").removeClass('selected');
$("a[href='#UpcomingEvent']").addClass('selected'); $("a[href='#UpcomingEvent']").addClass('selected');
}else{ } else {
$("a[href='#UpcomingEvent']").removeClass('selected'); $("a[href='#UpcomingEvent']").removeClass('selected');
$("a[href='#PastEvent']").addClass('selected'); $("a[href='#PastEvent']").addClass('selected');
} }
}); });
});
var searchParams = new URLSearchParams(window.location.search);
var paymentRequestId = searchParams.get('paymentRequestId');
if (paymentRequestId.length === 36) {
payment.paymentRequests.paymentRequest
.get(paymentRequestId)
.then(function (r) {
if (r.extraProperties.Status === "COMPLETED") {
Swal.fire({
title: 'Thank you for your purchase!',
text: 'Your Organization has been upgraded to Premium.',
imageUrl: '/assets/success.svg',
imageHeight: "70",
imageWidth: "70",
showCloseButton: true,
confirmButtonText: 'Close'
}).then(() => {
window.location.replace(window.location.pathname)
});
}
});
}
});

77
src/EventHub.Web/Pages/Payment/CustomPostCheckout.cshtml.cs

@ -0,0 +1,77 @@
using System;
using System.Threading.Tasks;
using EventHub.Organizations;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Payment.PaymentRequests;
using Payment.Web;
using Payment.Web.Pages.Payment;
using Payment.Web.PaymentRequest;
namespace EventHub.Web.Pages.Payment;
public class CustomPostCheckoutPageModel : PostCheckoutPageModel
{
public string OrganizationName { get; set; }
public CustomPostCheckoutPageModel(
IPaymentRequestAppService appService,
IOptions<PaymentWebOptions> paymentWebOptions,
IPaymentUrlBuilder paymentUrlBuilder)
: base(
appService,
paymentWebOptions,
paymentUrlBuilder)
{
}
public override async Task<IActionResult> OnGetAsync()
{
if (Token.IsNullOrWhiteSpace())
{
return BadRequest();
}
PaymentRequest = await PaymentRequestAppService.CompleteAsync(Token);
FillOrganizationName();
PaymentWebOptions.PaymentSuccessfulCallbackUrl = Url.Page("/Organizations/Profile", new { name = OrganizationName });
GoBackLink = PaymentUrlBuilder.BuildCheckoutUrl(PaymentRequest.Id).AbsoluteUri;
if (PaymentRequest.State == PaymentRequestState.Completed
&& !PaymentWebOptions.PaymentSuccessfulCallbackUrl.IsNullOrWhiteSpace())
{
var callbackUrl = PaymentWebOptions.PaymentSuccessfulCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id;
Response.Redirect(callbackUrl);
}
if (PaymentRequest.State == PaymentRequestState.Failed
&& !PaymentWebOptions.PaymentFailureCallbackUrl.IsNullOrWhiteSpace())
{
var callbackUrl = PaymentWebOptions.PaymentFailureCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id;
Response.Redirect(callbackUrl);
}
return Page();
}
private void FillOrganizationName()
{
var isExistExtraProperties = PaymentRequest.ExtraProperties.TryGetValue(nameof(OrganizationPaymentRequestExtraParameterConfiguration), out var ExtraProperties);
if (!isExistExtraProperties)
{
throw new BadHttpRequestException("");
}
var organizationPaymentExtraParameterConfiguration = JsonConvert.DeserializeObject<OrganizationPaymentRequestExtraParameterConfiguration>(ExtraProperties.ToString());
if (organizationPaymentExtraParameterConfiguration is null)
{
throw new BadHttpRequestException("");
}
OrganizationName = organizationPaymentExtraParameterConfiguration.OrganizationName;
}
}

23
src/EventHub.Web/Pages/Payment/PostCheckout.cshtml

@ -1,32 +1,15 @@
@page @page
@using EventHub.Organizations
@using EventHub.Web @using EventHub.Web
@using Microsoft.AspNetCore.Http
@using Microsoft.Extensions.Options @using Microsoft.Extensions.Options
@using Newtonsoft.Json
@using Payment.Web.Pages.Payment
@using Payment.PaymentRequests @using Payment.PaymentRequests
@using Volo.Abp.Users @using Volo.Abp.Users
@inherits Payment.Web.Pages.PaymentPageBase @inherits Payment.Web.Pages.PaymentPageBase
@model Payment.Web.Pages.Payment.PostCheckoutPageModel @model EventHub.Web.Pages.Payment.CustomPostCheckoutPageModel
@inject IOptions<EventHubUrlOptions> UrlOptions @inject IOptions<EventHubUrlOptions> UrlOptions
@inject ICurrentUser CurrentUser @inject ICurrentUser CurrentUser
@{ @{
PageLayout.Content.Title = L["PaymentResult"] + L["Enum:PaymentRequestState:" + Model.PaymentRequest.State]; PageLayout.Content.Title = L["PaymentResult"] + L["Enum:PaymentRequestState:" + Model.PaymentRequest.State];
var isExistExtraProperties = Model.PaymentRequest.ExtraProperties.TryGetValue(nameof(OrganizationPaymentRequestExtraParameterConfiguration), out var ExtraProperties);
if (!isExistExtraProperties)
{
throw new BadHttpRequestException("");
}
var organizationPaymentExtraParameterConfiguration = JsonConvert.DeserializeObject<OrganizationPaymentRequestExtraParameterConfiguration>(ExtraProperties.ToString());
if (organizationPaymentExtraParameterConfiguration is null)
{
throw new BadHttpRequestException("");
}
var organizationName = organizationPaymentExtraParameterConfiguration.OrganizationName;
} }
<main class="static-page container mt-5 text-center"> <main class="static-page container mt-5 text-center">
@ -69,8 +52,8 @@
<div class="col-md-8 mx-auto text-center"> <div class="col-md-8 mx-auto text-center">
<hr class="mt-4 mb-5" /> <hr class="mt-4 mb-5" />
<a href="/" class="btn btn-sm btn-outline-primary">Home page</a> <a href="/" class="btn btn-sm btn-outline-primary">Home page</a>
<a href="@UrlOptions.Value.Www/organizations/@organizationName" class="btn btn-sm btn-primary">My Organization</a> <a href="@UrlOptions.Value.Www/organizations/@Model.OrganizationName" class="btn btn-sm btn-primary">My Organization</a>
</div> </div>
</div> </div>
</div> </div>
</main> </main>

115
src/EventHub.Web/Pages/Payment/PreCheckout.cshtml

@ -31,79 +31,42 @@
<abp-script src="/client-proxies/Payment-proxy.js" /> <abp-script src="/client-proxies/Payment-proxy.js" />
</abp-script-bundle> </abp-script-bundle>
} }
<main class="static-page container mt-5">
<div class="container px-3 my-5 clearfix"> <h1 class="text-center mb-3">Shopping Cart</h1>
<abp-card> <div class="row">
<abp-card-header><h5>Shopping Cart</h5></abp-card-header> <div class="col-md-7">
<abp-card-body> <div class="card">
<div class="card-body px-5 py-4">
<div class="table-responsive"> <h2 class="my-4">Product Detail</h2>
<table class="table table-bordered m-0"> <h3 class="my-4">@OrganizationPlanType.Premium Account: <span class="text-primary">@organizationName.ToUpperInvariant()</span></h3>
<thead> @if (!organizationPaymentRequestExtraParameterConfiguration.IsExtend)
<tr> {
<th class="text-center py-3 px-4" style="min-width: 400px;">Product Name &amp; Details</th> <p>Start Date: <span class="text-dark">@DateTime.Now.ToShortDateString()</span></p>
<th class="text-center py-3 px-4" style="width: 100px;">Total</th> <p>End Date: <span class="text-dark">@DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth).ToShortDateString()</span></p>
</tr> }
</thead> else
<tbody> {
<p>Your license will be extended for <span class="text-dark">@planInfo.OnePremiumPeriodAsMonth</span> months.</p>
<tr> }
<td class="p-4"> </div>
<div class="media align-items-center"> </div>
<div class="media-body"> </div>
<p class="font-weight-bolder">@Model.PaymentRequest.ProductName</p> <div class="col-md-5">
<small> <div class="card">
@if (!organizationPaymentRequestExtraParameterConfiguration.IsExtend) <div class="card-body px-5 py-4">
{ <h2 class="mt-4">Summary</h2>
<span class="text-muted">Target Plan: @OrganizationPlanType.Premium</span> <div class="cart-summary pt-3 pb-2">
<span class="text-muted ms-1">End date: @DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth).ToShortDateString()</span> <p class="my-4">Premium Licence <span class="text-dark float-end">@Model.PaymentRequest.Price @Model.PaymentRequest.Currency</span></p>
} <hr>
else <p class="my-4">TOTAL <span class="text-dark float-end">@Model.PaymentRequest.Price @Model.PaymentRequest.Currency</span></p>
{ </div>
<span class="text-muted">Plan: @OrganizationPlanType.Premium</span> <form method="post">
} <div class="d-grid gap-2">
<span class="ui-product-color ui-product-color-sm align-text-bottom" style="background:#e81e2c;"></span> &nbsp; <button type="submit" class="btn btn-info my-4">@L["Checkout"]</button>
</small> </div>
</div> </form>
</div> </div>
</td> </div>
<td class="text-center font-weight-semibold align-middle">@Model.PaymentRequest.Price @Model.PaymentRequest.Currency</td> </div>
</tr> </div>
</tbody> </main>
</table>
</div>
<div class="d-flex flex-wrap justify-content-between align-items-center pb-4">
<abp-container>
<abp-row class="float-end">
<abp-column>
<div class="text-end mt-4">
<label class="text-muted font-weight-normal m-0">Total price</label>
<div class="text-large"><strong>@Model.PaymentRequest.Price @Model.PaymentRequest.Currency</strong></div>
</div>
</abp-column>
</abp-row>
</abp-container>
</div>
<abp-container class="float-end">
<abp-row>
<abp-column class="float-end">
<form method="post">
@if (Model.PaymentRequest.State != PaymentRequestState.Waiting)
{
<abp-alert alert-type="Info">Current State: @Model.PaymentRequest.State</abp-alert>
<abp-alert alert-type="Warning">@L["PaymentRequestIsNotWaiting"]</abp-alert>
}
else
{
<abp-button class="mt-2 float-end" offset="_10" size="Large" type="submit" button-type="Primary">@L["Checkout"]</abp-button>
<a href="@Url.Page("/Pricing", new { organizationName = organizationName })" class="btn btn-lg btn-default md-btn-flat mt-2 me-3 float-end">Back to pricing</a>
}
</form>
</abp-column>
</abp-row>
</abp-container>
</abp-card-body>
</abp-card>
</div>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save