diff --git a/modules/payment/src/Payment.Web/Pages/Payment/PostCheckout.cshtml.cs b/modules/payment/src/Payment.Web/Pages/Payment/PostCheckout.cshtml.cs index 26e5c54..377b247 100644 --- a/modules/payment/src/Payment.Web/Pages/Payment/PostCheckout.cshtml.cs +++ b/modules/payment/src/Payment.Web/Pages/Payment/PostCheckout.cshtml.cs @@ -16,42 +16,42 @@ namespace Payment.Web.Pages.Payment public string GoBackLink { get; set; } - private readonly IPaymentRequestAppService _paymentRequestAppService; - private readonly PaymentWebOptions _paymentWebOptions; - private readonly IPaymentUrlBuilder _paymentUrlBuilder; + protected IPaymentRequestAppService PaymentRequestAppService { get; } + protected PaymentWebOptions PaymentWebOptions { get; } + protected IPaymentUrlBuilder PaymentUrlBuilder { get; } public PostCheckoutPageModel( IPaymentRequestAppService appService, IOptions paymentWebOptions, IPaymentUrlBuilder paymentUrlBuilder) { - _paymentRequestAppService = appService; - _paymentWebOptions = paymentWebOptions.Value; - _paymentUrlBuilder = paymentUrlBuilder; + PaymentRequestAppService = appService; + PaymentWebOptions = paymentWebOptions.Value; + PaymentUrlBuilder = paymentUrlBuilder; } - public async Task OnGetAsync() + public virtual async Task OnGetAsync() { if (Token.IsNullOrWhiteSpace()) { 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 - && !_paymentWebOptions.PaymentSuccessfulCallbackUrl.IsNullOrWhiteSpace()) + && !PaymentWebOptions.PaymentSuccessfulCallbackUrl.IsNullOrWhiteSpace()) { - var callbackUrl = _paymentWebOptions.PaymentSuccessfulCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id; + var callbackUrl = PaymentWebOptions.PaymentSuccessfulCallbackUrl + "?paymentRequestId=" + PaymentRequest.Id; Response.Redirect(callbackUrl); } 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); } diff --git a/modules/payment/src/Payment.Web/Pages/Payment/PreCheckout.cshtml.cs b/modules/payment/src/Payment.Web/Pages/Payment/PreCheckout.cshtml.cs index 0902d77..3e8f2f2 100644 --- a/modules/payment/src/Payment.Web/Pages/Payment/PreCheckout.cshtml.cs +++ b/modules/payment/src/Payment.Web/Pages/Payment/PreCheckout.cshtml.cs @@ -14,36 +14,36 @@ namespace Payment.Web.Pages.Payment public PaymentRequestDto PaymentRequest { get; set; } - private readonly IPaymentRequestAppService _paymentRequestAppService; - private readonly IPaymentUrlBuilder _paymentUrlBuilder; + protected IPaymentRequestAppService PaymentRequestAppService { get; } + protected IPaymentUrlBuilder PaymentUrlBuilder { get; } public PreCheckoutPageModel( IPaymentRequestAppService paymentRequestAppService, IPaymentUrlBuilder paymentUrlBuilder) { - _paymentRequestAppService = paymentRequestAppService; - _paymentUrlBuilder = paymentUrlBuilder; + PaymentRequestAppService = paymentRequestAppService; + PaymentUrlBuilder = paymentUrlBuilder; } - public async Task OnGetAsync() + public virtual async Task OnGetAsync() { - PaymentRequest = await _paymentRequestAppService.GetAsync(PaymentRequestId); + PaymentRequest = await PaymentRequestAppService.GetAsync(PaymentRequestId); 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, - ReturnUrl = _paymentUrlBuilder.BuildReturnUrl(PaymentRequestId).AbsoluteUri, - CancelUrl = _paymentUrlBuilder.BuildCheckoutUrl(PaymentRequestId).AbsoluteUri + ReturnUrl = PaymentUrlBuilder.BuildReturnUrl(PaymentRequestId).AbsoluteUri, + CancelUrl = PaymentUrlBuilder.BuildCheckoutUrl(PaymentRequestId).AbsoluteUri }); Response.Redirect(result.CheckoutLink); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Application.Contracts/EventHubApplicationContractsModule.cs b/src/EventHub.Application.Contracts/EventHubApplicationContractsModule.cs index 56fc60a..7367a20 100644 --- a/src/EventHub.Application.Contracts/EventHubApplicationContractsModule.cs +++ b/src/EventHub.Application.Contracts/EventHubApplicationContractsModule.cs @@ -1,5 +1,6 @@ using Payment; using Volo.Abp.Application; +using Volo.Abp.Application.Dtos; using Volo.Abp.Modularity; namespace EventHub @@ -11,6 +12,9 @@ namespace EventHub )] public class EventHubApplicationContractsModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + LimitedResultRequestDto.MaxMaxResultCount = 30; + } } } diff --git a/src/EventHub.Application.Contracts/Events/AddSessionDto.cs b/src/EventHub.Application.Contracts/Events/AddSessionDto.cs new file mode 100644 index 0000000..c74ae79 --- /dev/null +++ b/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 SpeakerUserNames { get; set; } + + public AddSessionDto() + { + SpeakerUserNames = new List(); + } +} diff --git a/src/EventHub.Application.Contracts/Events/AddTrackDto.cs b/src/EventHub.Application.Contracts/Events/AddTrackDto.cs new file mode 100644 index 0000000..cbed7ab --- /dev/null +++ b/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; } +} diff --git a/src/EventHub.Application.Contracts/Events/DeleteTractDto.cs b/src/EventHub.Application.Contracts/Events/DeleteTractDto.cs new file mode 100644 index 0000000..fd9d396 --- /dev/null +++ b/src/EventHub.Application.Contracts/Events/DeleteTractDto.cs @@ -0,0 +1,6 @@ +namespace EventHub.Events; + +public class DeleteTractDto +{ + public string Name { get; set; } +} diff --git a/src/EventHub.Application.Contracts/Events/EventDetailDto.cs b/src/EventHub.Application.Contracts/Events/EventDetailDto.cs index ddb3eec..9adea00 100644 --- a/src/EventHub.Application.Contracts/Events/EventDetailDto.cs +++ b/src/EventHub.Application.Contracts/Events/EventDetailDto.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using Volo.Abp.Application.Dtos; namespace EventHub.Events @@ -40,5 +42,12 @@ namespace EventHub.Events public string Language { get; set; } public int? Capacity { get; set; } + + public List Tracks { get; set; } + + public EventDetailDto() + { + Tracks = new List(); + } } } diff --git a/src/EventHub.Application.Contracts/Events/EventInListDto.cs b/src/EventHub.Application.Contracts/Events/EventInListDto.cs index a2235ac..b919bd0 100644 --- a/src/EventHub.Application.Contracts/Events/EventInListDto.cs +++ b/src/EventHub.Application.Contracts/Events/EventInListDto.cs @@ -1,9 +1,10 @@ using System; using Volo.Abp.Application.Dtos; +using Volo.Abp.Auditing; namespace EventHub.Events { - public class EventInListDto : EntityDto + public class EventInListDto : EntityDto, IHasCreationTime, IHasModificationTime { public string OrganizationName { get; set; } @@ -30,5 +31,9 @@ namespace EventHub.Events public string UrlCode { get; set; } public string Url { get; set; } + + public DateTime CreationTime { get; set; } + + public DateTime? LastModificationTime { get; set; } } } diff --git a/src/EventHub.Application.Contracts/Events/IEventAppService.cs b/src/EventHub.Application.Contracts/Events/IEventAppService.cs index bd1238f..9f151da 100644 --- a/src/EventHub.Application.Contracts/Events/IEventAppService.cs +++ b/src/EventHub.Application.Contracts/Events/IEventAppService.cs @@ -13,6 +13,8 @@ namespace EventHub.Events Task> GetListAsync(EventListFilterDto input); + Task> GetDraftEventsByCurrentUser(); + Task GetByUrlCodeAsync(string urlCode); Task GetLocationAsync(Guid id); @@ -22,7 +24,21 @@ namespace EventHub.Events Task IsEventOwnerAsync(Guid id); Task UpdateAsync(Guid id, UpdateEventDto input); + + Task 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 GetCoverImageAsync(Guid id); } } diff --git a/src/EventHub.Application.Contracts/Events/SessionDto.cs b/src/EventHub.Application.Contracts/Events/SessionDto.cs new file mode 100644 index 0000000..42bf3ae --- /dev/null +++ b/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 +{ + 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 Speakers { get; set; } + + public SessionDto() + { + Speakers = new List(); + } +} diff --git a/src/EventHub.Application.Contracts/Events/SpeakerDto.cs b/src/EventHub.Application.Contracts/Events/SpeakerDto.cs new file mode 100644 index 0000000..2f00a3b --- /dev/null +++ b/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 +{ + public Guid SessionId { get; set; } + + public Guid UserId { get; set; } + + public string UserName { get; set; } +} diff --git a/src/EventHub.Application.Contracts/Events/TrackDto.cs b/src/EventHub.Application.Contracts/Events/TrackDto.cs new file mode 100644 index 0000000..5b39693 --- /dev/null +++ b/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 +{ + public string Name { get; set; } + + public List Sessions { get; set; } + + public TrackDto() + { + Sessions = new List(); + } +} diff --git a/src/EventHub.Application.Contracts/Events/UpdateEventDto.cs b/src/EventHub.Application.Contracts/Events/UpdateEventDto.cs index 0e1edf6..233b9e1 100644 --- a/src/EventHub.Application.Contracts/Events/UpdateEventDto.cs +++ b/src/EventHub.Application.Contracts/Events/UpdateEventDto.cs @@ -45,4 +45,4 @@ namespace EventHub.Events [Range(1, int.MaxValue)] public int? Capacity { get; set; } } -} \ No newline at end of file +} diff --git a/src/EventHub.Application.Contracts/Events/UpdateSessionDto.cs b/src/EventHub.Application.Contracts/Events/UpdateSessionDto.cs new file mode 100644 index 0000000..f32acf5 --- /dev/null +++ b/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 SpeakerUserNames { get; set; } + + public UpdateSessionDto() + { + SpeakerUserNames = new List(); + } +} diff --git a/src/EventHub.Application.Contracts/Events/UpdateTrackDto.cs b/src/EventHub.Application.Contracts/Events/UpdateTrackDto.cs new file mode 100644 index 0000000..6434578 --- /dev/null +++ b/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; } +} diff --git a/src/EventHub.Application.Contracts/Users/IUserAppService.cs b/src/EventHub.Application.Contracts/Users/IUserAppService.cs index 6b71d6f..951b8f6 100644 --- a/src/EventHub.Application.Contracts/Users/IUserAppService.cs +++ b/src/EventHub.Application.Contracts/Users/IUserAppService.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using EventHub.Members; using Volo.Abp.Application.Services; @@ -7,5 +8,7 @@ namespace EventHub.Users public interface IUserAppService : IApplicationService { Task FindByUserNameAsync(string username); + + Task> GetListByUserName(string username); } -} \ No newline at end of file +} diff --git a/src/EventHub.Application.Contracts/Users/UserInListDto.cs b/src/EventHub.Application.Contracts/Users/UserInListDto.cs new file mode 100644 index 0000000..52af0a8 --- /dev/null +++ b/src/EventHub.Application.Contracts/Users/UserInListDto.cs @@ -0,0 +1,6 @@ +namespace EventHub.Users; + +public class UserInListDto +{ + public string UserName { get; set; } +} diff --git a/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs b/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs index 72e16f2..f16f205 100644 --- a/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs +++ b/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs @@ -31,6 +31,13 @@ namespace EventHub .Ignore(x => x.OrganizationName) .Ignore(x => x.OrganizationDisplayName); + CreateMap(); + + CreateMap(); + + CreateMap() + .Ignore(x => x.UserName); + CreateMap(); CreateMap() @@ -39,6 +46,8 @@ namespace EventHub CreateMap(); CreateMap(); + + CreateMap(); } } } diff --git a/src/EventHub.Application/Events/EventAppService.cs b/src/EventHub.Application/Events/EventAppService.cs index 17bf152..e82aa9c 100644 --- a/src/EventHub.Application/Events/EventAppService.cs +++ b/src/EventHub.Application/Events/EventAppService.cs @@ -1,18 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Authentication; using System.Threading.Tasks; using EventHub.Countries; using EventHub.Events.Registrations; using EventHub.Organizations; using EventHub.Users; using Microsoft.AspNetCore.Authorization; +using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.Authorization; using Volo.Abp.BlobStoring; using Volo.Abp.Content; using Volo.Abp.Domain.Repositories; -using Volo.Abp.Identity; using Volo.Abp.Users; namespace EventHub.Events @@ -64,8 +65,8 @@ namespace EventHub.Events var @event = await _eventManager.CreateAsync( organization, input.Title, - input.StartTime, - input.EndTime, + Clock.Normalize(input.StartTime), + Clock.Normalize(input.EndTime), input.Description ); @@ -78,7 +79,7 @@ namespace EventHub.Events { await SaveCoverImageAsync(@event.Id, input.CoverImageStreamContent); } - + await _eventRepository.InsertAsync(@event); return ObjectMapper.Map(@event); @@ -92,7 +93,8 @@ namespace EventHub.Events var query = from @event in eventQueryable 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) { @@ -147,27 +149,43 @@ namespace EventHub.Events query = query.OrderBy(x => x.@event.StartTime); } - var items = await AsyncExecuter.ToListAsync(query); - var now = Clock.Now; + var items = (await AsyncExecuter.ToListAsync(query)).Select(a => (a.@event, a.organization)).ToList(); - var events = items.Select( - i => - { - var dto = ObjectMapper.Map(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(); + var events = GetEventInListDtoFromEventAndOrganizationTupleList(items); return new PagedResultDto(totalCount, events); } + [Authorize] + public async Task> 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 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 dto = ObjectMapper.Map(@event); @@ -175,11 +193,24 @@ namespace EventHub.Events dto.OrganizationId = organization.Id; dto.OrganizationName = organization.Name; dto.OrganizationDisplayName = organization.DisplayName; - + var user = await _userRepository.GetAsync(organization.OwnerUserId); + if (@event.IsDraft) + { + if (user.Id != CurrentUser.GetId()) + { + throw new AbpAuthorizationException(); + } + } + dto.OwnerUserName = user.UserName; 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; } @@ -232,37 +263,132 @@ namespace EventHub.Events public async Task UpdateAsync(Guid id, UpdateEventDto input) { var @event = await _eventRepository.GetAsync(id); - var organization = await _organizationRepository.GetAsync(@event.OrganizationId); - - if (organization.OwnerUserId != CurrentUser.GetId()) - { - throw new AbpAuthorizationException( - L["EventHub:NotAuthorizedToUpdateEvent", @event.Title], - EventHubErrorCodes.NotAuthorizedToUpdateEvent - ); - } + await CheckIfValidOwnerAsync(@event); await _eventManager.SetLocationAsync(@event, input.IsOnline, input.OnlineLink, input.CountryId, input.City); @event.SetTitle(input.Title); @event.SetDescription(input.Description); @event.Language = input.Language; - @event.SetTime(input.StartTime, input.EndTime); + @event.SetTime(Clock.Normalize(input.StartTime), Clock.Normalize(input.EndTime)); await _eventManager.SetCapacityAsync(@event, input.Capacity); - + if (input.CoverImageStreamContent != null && input.CoverImageStreamContent.ContentLength > 0) { await SaveCoverImageAsync(@event.Id, input.CoverImageStreamContent); } + + await _eventRepository.UpdateAsync(@event); + } + + [Authorize] + public async Task 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); } - + + [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 GetCoverImageAsync(Guid id) { var blobName = id.ToString(); var coverImageStream = await _eventBlobContainer.GetOrNullAsync(blobName); - + if (coverImageStream is null) { return null; @@ -277,5 +403,66 @@ namespace EventHub.Events await _eventBlobContainer.SaveAsync(blobName, streamContent.GetStream(), overrideExisting: true); } + + private async Task> GetUserIdsByUserNamesAsync(List 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 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 GetEventInListDtoFromEventAndOrganizationTupleList(List<(Event @event, Organization organization)> items) + { + var now = Clock.Now; + var events = items.Select( + i => + { + var dto = ObjectMapper.Map(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; + } } -} \ No newline at end of file +} diff --git a/src/EventHub.Application/Users/UserAppService.cs b/src/EventHub.Application/Users/UserAppService.cs index 42ed495..4cf6a52 100644 --- a/src/EventHub.Application/Users/UserAppService.cs +++ b/src/EventHub.Application/Users/UserAppService.cs @@ -1,7 +1,8 @@ -using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using EventHub.Members; -using Volo.Abp.Domain.Repositories; +using Microsoft.AspNetCore.Authorization; using Volo.Abp.Identity; namespace EventHub.Users @@ -21,5 +22,21 @@ namespace EventHub.Users return ObjectMapper.Map(user); } + + [Authorize] + public async Task> 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>(await AsyncExecuter.ToListAsync(query)); + } } -} \ No newline at end of file +} diff --git a/src/EventHub.BackgroundServices/Events/EventReminderNotifier.cs b/src/EventHub.BackgroundServices/Events/EventReminderNotifier.cs index 7ef1995..d2b3944 100644 --- a/src/EventHub.BackgroundServices/Events/EventReminderNotifier.cs +++ b/src/EventHub.BackgroundServices/Events/EventReminderNotifier.cs @@ -37,7 +37,7 @@ namespace EventHub.Events public async Task NotifyAsync(Event @event) { - if (@event is null || @event.IsRemindingEmailSent) + if (@event is null || @event.IsRemindingEmailSent || !@event.IsDraft) { return; } @@ -47,7 +47,7 @@ namespace EventHub.Events var userQuery = from eventRegistration in registrationQueryable join user in userQueryable on eventRegistration.UserId equals user.Id - where eventRegistration.EventId == @event.Id + where eventRegistration.EventId == @event.Id && !@event.IsDraft select user; var users = await _asyncExecuter.ToListAsync(userQuery); @@ -71,4 +71,4 @@ namespace EventHub.Events } } } -} \ No newline at end of file +} diff --git a/src/EventHub.BackgroundServices/Events/EventReminderWorker.cs b/src/EventHub.BackgroundServices/Events/EventReminderWorker.cs index f8da636..ac18a93 100644 --- a/src/EventHub.BackgroundServices/Events/EventReminderWorker.cs +++ b/src/EventHub.BackgroundServices/Events/EventReminderWorker.cs @@ -32,7 +32,7 @@ namespace EventHub.Events var queryable = await eventRepository.GetQueryableAsync(); var query = queryable.Where(x => x.IsRemindingEmailSent == false && - x.StartTime <= thirtyMinutesLater + x.StartTime <= thirtyMinutesLater && !x.IsDraft ); var events = await asyncExecuter.ToListAsync(query); @@ -56,4 +56,4 @@ namespace EventHub.Events } } } -} \ No newline at end of file +} diff --git a/src/EventHub.BackgroundServices/Events/EventTimingChangeNotifier.cs b/src/EventHub.BackgroundServices/Events/EventTimingChangeNotifier.cs index e60b9f0..9f9fc30 100644 --- a/src/EventHub.BackgroundServices/Events/EventTimingChangeNotifier.cs +++ b/src/EventHub.BackgroundServices/Events/EventTimingChangeNotifier.cs @@ -37,7 +37,7 @@ namespace EventHub.Events public async Task NotifyAsync(Event @event) { - if (@event is null || @event.IsTimingChangeEmailSent) + if (@event is null || @event.IsTimingChangeEmailSent || !@event.IsDraft) { return; } @@ -47,7 +47,7 @@ namespace EventHub.Events var userQuery = from eventRegistration in registrationQueryable join user in userQueryable on eventRegistration.UserId equals user.Id - where eventRegistration.EventId == @event.Id + where eventRegistration.EventId == @event.Id && !@event.IsDraft select user; var users = await _asyncExecuter.ToListAsync(userQuery); @@ -71,4 +71,4 @@ namespace EventHub.Events } } } -} \ No newline at end of file +} diff --git a/src/EventHub.BackgroundServices/Events/EventTimingChangeWorker.cs b/src/EventHub.BackgroundServices/Events/EventTimingChangeWorker.cs index aa6587d..faac3b4 100644 --- a/src/EventHub.BackgroundServices/Events/EventTimingChangeWorker.cs +++ b/src/EventHub.BackgroundServices/Events/EventTimingChangeWorker.cs @@ -31,7 +31,7 @@ namespace EventHub.Events var asyncExecuter = workerContext.ServiceProvider.GetRequiredService(); 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); @@ -51,4 +51,4 @@ namespace EventHub.Events } } } -} \ No newline at end of file +} diff --git a/src/EventHub.BackgroundServices/Events/NewEventNotifier.cs b/src/EventHub.BackgroundServices/Events/NewEventNotifier.cs index 64dcf02..486fb51 100644 --- a/src/EventHub.BackgroundServices/Events/NewEventNotifier.cs +++ b/src/EventHub.BackgroundServices/Events/NewEventNotifier.cs @@ -37,7 +37,7 @@ namespace EventHub.Events public async Task NotifyAsync(Event @event) { - if (@event is null || @event.IsEmailSentToMembers) + if (@event is null || @event.IsEmailSentToMembers || !@event.IsDraft) { return; } @@ -71,4 +71,4 @@ namespace EventHub.Events } } } -} \ No newline at end of file +} diff --git a/src/EventHub.BackgroundServices/Events/NewEventWorker.cs b/src/EventHub.BackgroundServices/Events/NewEventWorker.cs index 4dd9a97..372744f 100644 --- a/src/EventHub.BackgroundServices/Events/NewEventWorker.cs +++ b/src/EventHub.BackgroundServices/Events/NewEventWorker.cs @@ -34,7 +34,7 @@ namespace EventHub.Events var queryable = await eventRepository.GetQueryableAsync(); 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); @@ -57,4 +57,4 @@ namespace EventHub.Events } } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs b/src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs index 25829cb..8d03059 100644 --- a/src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs +++ b/src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs @@ -62,4 +62,4 @@ namespace EventHub Configure(configuration.GetSection("AppUrls")); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain.Shared/EventHubErrorCodes.cs b/src/EventHub.Domain.Shared/EventHubErrorCodes.cs index d35d621..fb7a99c 100644 --- a/src/EventHub.Domain.Shared/EventHubErrorCodes.cs +++ b/src/EventHub.Domain.Shared/EventHubErrorCodes.cs @@ -4,14 +4,22 @@ { public const string OrganizationNameAlreadyExists = "EventHub:OrganizationNameAlreadyExists"; 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 NotAuthorizedToUpdateOrganizationProfile = "EventHub:NotAuthorizedToUpdateOrganizationProfile"; 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 CantChangeEventTiming = "EventHub:CantChangeEventTiming"; + public const string SessionTimeShouldBeInTheEventTime = "EventHub:SessionTimeShouldBeInTheEventTime"; + public const string SessionTimeConflictsWithAnExistingSession = "EventHub:SessionTimeConflictsWithAnExistingSession"; public const string UserNotFound = "EventHub:UserNotFound"; 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"; + } } diff --git a/src/EventHub.Domain.Shared/Events/EventConsts.cs b/src/EventHub.Domain.Shared/Events/EventConsts.cs index 6e27a39..5ee9254 100644 --- a/src/EventHub.Domain.Shared/Events/EventConsts.cs +++ b/src/EventHub.Domain.Shared/Events/EventConsts.cs @@ -13,18 +13,18 @@ public const int MinDescriptionLength = 50; public const int MaxDescriptionLength = 2000; - + public const int MinOnlineLinkLength = 4; public const int MaxOnlineLinkLength = 2000; - + public const int MinCityLength = 2; public const int MaxCityLength = 32; - + public const int MinLanguageLength = 2; public const int MaxLanguageLength = 16; public const int MaxTimingChangeCountForUser = 2; - + public const int MaxCoverImageFileSize = 5 * 1024 * 1024; public static string[] AllowedCoverImageExtensions = { ".jpg", ".png" }; diff --git a/src/EventHub.Domain.Shared/Events/SessionConsts.cs b/src/EventHub.Domain.Shared/Events/SessionConsts.cs new file mode 100644 index 0000000..1ee8817 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/src/EventHub.Domain.Shared/Events/TrackConsts.cs b/src/EventHub.Domain.Shared/Events/TrackConsts.cs new file mode 100644 index 0000000..b3fb99d --- /dev/null +++ b/src/EventHub.Domain.Shared/Events/TrackConsts.cs @@ -0,0 +1,7 @@ +namespace EventHub.Events +{ + public static class TrackConsts + { + public const int MaxNameLength = 64; + } +} \ No newline at end of file diff --git a/src/EventHub.Domain.Shared/Localization/EventHub/en.json b/src/EventHub.Domain.Shared/Localization/EventHub/en.json index b8ef443..838a9e5 100644 --- a/src/EventHub.Domain.Shared/Localization/EventHub/en.json +++ b/src/EventHub.Domain.Shared/Localization/EventHub/en.json @@ -18,7 +18,7 @@ "DisplayName:Capacity": "Capacity", "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: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.", "Events": "Events", "Members": "Members", @@ -83,6 +83,7 @@ "CoverImagePreview": "Cover Image Preview", "EventTimingChangeSuccessMessage": "The time of the event has been changed successfully!", "PastEvent": "Past Event", + "EventHub:SessionEndTimeCantBeEarlierThanStartTime": "Session end time can not be earlier than the start time.", "Menu:OrganizationManagement": "Organization Management", "Menu:OrganizationMemberships": "Memberships", "Permission:OrganizationManagement": "Organization Management", @@ -151,6 +152,12 @@ "MaxStartTime": "Max Start Time", "Online": "Online", "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" } } diff --git a/src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs b/src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs index 9446c4d..72037e6 100644 --- a/src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs +++ b/src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs @@ -23,12 +23,12 @@ namespace EventHub.Web { return GetConfigValue(configuration, nameof(Account), AccountDefaultValue); } - + public static string GetWwwConfigValue(IConfiguration configuration) { return GetConfigValue(configuration, nameof(Www), WwwDefaultValue); } - + public static string GetApiInternalConfigValue(IConfiguration configuration) { return GetConfigValue(configuration, nameof(ApiInternal), ApiInternalDefaultValue); @@ -38,7 +38,7 @@ namespace EventHub.Web { return GetConfigValue(configuration, nameof(Api), ApiDefaultValue); } - + public static string GetAdminConfigValue(IConfiguration configuration) { return GetConfigValue(configuration, nameof(Admin), AdminDefaultValue); @@ -53,7 +53,7 @@ namespace EventHub.Web { return $"{ConfigurationName}:{appName}"; } - + private static string GetConfigValue( IConfiguration configuration, string appName, @@ -62,4 +62,4 @@ namespace EventHub.Web return configuration[GetConfigKey(appName)] ?? defaultValue; } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain/Countries/Country.cs b/src/EventHub.Domain/Countries/Country.cs index 1c05c25..1e1ae19 100644 --- a/src/EventHub.Domain/Countries/Country.cs +++ b/src/EventHub.Domain/Countries/Country.cs @@ -10,15 +10,14 @@ namespace EventHub.Countries private Country() { - } internal Country( - Guid id, + Guid id, string name) - : base(id) + : base(id) { Name = Check.NotNullOrWhiteSpace(name, nameof(name), CountryConsts.MaxNameLength); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain/Data/EventHubDataSeedContributor.cs b/src/EventHub.Domain/Data/EventHubDataSeedContributor.cs index 29e9daf..0ce6c03 100644 --- a/src/EventHub.Domain/Data/EventHubDataSeedContributor.cs +++ b/src/EventHub.Domain/Data/EventHubDataSeedContributor.cs @@ -5,17 +5,21 @@ using EventHub.Countries; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; namespace EventHub.Data { public class EventHubDataSeedContributor : IDataSeedContributor, ITransientDependency { private readonly IRepository _countryRepository; - + private readonly IGuidGenerator _guidGenerator; + public EventHubDataSeedContributor( - IRepository countryRepository) + IRepository countryRepository, + IGuidGenerator guidGenerator) { _countryRepository = countryRepository; + _guidGenerator = guidGenerator; } public async Task SeedAsync(DataSeedContext context) @@ -32,204 +36,204 @@ namespace EventHub.Data var countries = new List { - new Country(Guid.NewGuid(), "Afghanistan"), - new Country(Guid.NewGuid(), "Albania"), - new Country(Guid.NewGuid(), "Algeria"), - new Country(Guid.NewGuid(), "Andorra"), - new Country(Guid.NewGuid(), "Angola"), - new Country(Guid.NewGuid(), "Antigua and Barbuda"), - new Country(Guid.NewGuid(), "Argentina"), - new Country(Guid.NewGuid(), "Armenia"), - new Country(Guid.NewGuid(), "Australia"), - new Country(Guid.NewGuid(), "Austria"), - new Country(Guid.NewGuid(), "Azerbaijan"), - new Country(Guid.NewGuid(), "Bahamas"), - new Country(Guid.NewGuid(), "Bahrain"), - new Country(Guid.NewGuid(), "Bangladesh"), - new Country(Guid.NewGuid(), "Barbados"), - new Country(Guid.NewGuid(), "Belarus"), - new Country(Guid.NewGuid(), "Belgium"), - new Country(Guid.NewGuid(), "Belize"), - new Country(Guid.NewGuid(), "Benin"), - new Country(Guid.NewGuid(), "Bhutan"), - new Country(Guid.NewGuid(), "Bolivia"), - new Country(Guid.NewGuid(), "Bosnia and Herzegovina"), - new Country(Guid.NewGuid(), "Botswana"), - new Country(Guid.NewGuid(), "Brazil"), - new Country(Guid.NewGuid(), "Brunei"), - new Country(Guid.NewGuid(), "Bulgaria"), - new Country(Guid.NewGuid(), "Burkina Faso"), - new Country(Guid.NewGuid(), "Burundi"), - new Country(Guid.NewGuid(), "Côte d'Ivoire"), - new Country(Guid.NewGuid(), "Cabo Verde"), - new Country(Guid.NewGuid(), "Cambodia"), - new Country(Guid.NewGuid(), "Cameroon"), - new Country(Guid.NewGuid(), "Canada"), - new Country(Guid.NewGuid(), "Central African Republic"), - new Country(Guid.NewGuid(), "Chad"), - new Country(Guid.NewGuid(), "Chile"), - new Country(Guid.NewGuid(), "China"), - new Country(Guid.NewGuid(), "Colombia"), - new Country(Guid.NewGuid(), "Comoros"), - new Country(Guid.NewGuid(), "Congo (Congo-Brazzaville)"), - new Country(Guid.NewGuid(), "Costa Rica"), - new Country(Guid.NewGuid(), "Croatia"), - new Country(Guid.NewGuid(), "Cuba"), - new Country(Guid.NewGuid(), "Cyprus"), - new Country(Guid.NewGuid(), "Czechia (Czech Republic)"), - new Country(Guid.NewGuid(), "Democratic Republic of the Congo"), - new Country(Guid.NewGuid(), "Denmark"), - new Country(Guid.NewGuid(), "Djibouti"), - new Country(Guid.NewGuid(), "Dominica"), - new Country(Guid.NewGuid(), "Dominican Republic"), - new Country(Guid.NewGuid(), "Ecuador"), - new Country(Guid.NewGuid(), "Egypt"), - new Country(Guid.NewGuid(), "El Salvador"), - new Country(Guid.NewGuid(), "Equatorial Guinea"), - new Country(Guid.NewGuid(), "Eritrea"), - new Country(Guid.NewGuid(), "Estonia"), - new Country(Guid.NewGuid(), "Eswatini (formerly Swaziland)"), - new Country(Guid.NewGuid(), "Ethiopia"), - new Country(Guid.NewGuid(), "Fiji"), - new Country(Guid.NewGuid(), "Finland"), - new Country(Guid.NewGuid(), "France"), - new Country(Guid.NewGuid(), "Gabon"), - new Country(Guid.NewGuid(), "Gambia"), - new Country(Guid.NewGuid(), "Georgia"), - new Country(Guid.NewGuid(), "Germany"), - new Country(Guid.NewGuid(), "Ghana"), - new Country(Guid.NewGuid(), "Greece"), - new Country(Guid.NewGuid(), "Grenada"), - new Country(Guid.NewGuid(), "Guatemala"), - new Country(Guid.NewGuid(), "Guinea"), - new Country(Guid.NewGuid(), "Guinea-Bissau"), - new Country(Guid.NewGuid(), "Guyana"), - new Country(Guid.NewGuid(), "Haiti"), - new Country(Guid.NewGuid(), "Holy See"), - new Country(Guid.NewGuid(), "Honduras"), - new Country(Guid.NewGuid(), "Hungary"), - new Country(Guid.NewGuid(), "Iceland"), - new Country(Guid.NewGuid(), "India"), - new Country(Guid.NewGuid(), "Indonesia"), - new Country(Guid.NewGuid(), "Iran"), - new Country(Guid.NewGuid(), "Iraq"), - new Country(Guid.NewGuid(), "Ireland"), - new Country(Guid.NewGuid(), "Israel"), - new Country(Guid.NewGuid(), "Italy"), - new Country(Guid.NewGuid(), "Jamaica"), - new Country(Guid.NewGuid(), "Japan"), - new Country(Guid.NewGuid(), "Jordan"), - new Country(Guid.NewGuid(), "Kazakhstan"), - new Country(Guid.NewGuid(), "Kenya"), - new Country(Guid.NewGuid(), "Kiribati"), - new Country(Guid.NewGuid(), "Kuwait"), - new Country(Guid.NewGuid(), "Kyrgyzstan"), - new Country(Guid.NewGuid(), "Laos"), - new Country(Guid.NewGuid(), "Latvia"), - new Country(Guid.NewGuid(), "Lebanon"), - new Country(Guid.NewGuid(), "Lesotho"), - new Country(Guid.NewGuid(), "Liberia"), - new Country(Guid.NewGuid(), "Libya"), - new Country(Guid.NewGuid(), "Liechtenstein"), - new Country(Guid.NewGuid(), "Lithuania"), - new Country(Guid.NewGuid(), "Luxembourg"), - new Country(Guid.NewGuid(), "Madagascar"), - new Country(Guid.NewGuid(), "Malawi"), - new Country(Guid.NewGuid(), "Malaysia"), - new Country(Guid.NewGuid(), "Maldives"), - new Country(Guid.NewGuid(), "Mali"), - new Country(Guid.NewGuid(), "Malta"), - new Country(Guid.NewGuid(), "Marshall Islands"), - new Country(Guid.NewGuid(), "Mauritania"), - new Country(Guid.NewGuid(), "Mauritius"), - new Country(Guid.NewGuid(), "Mexico"), - new Country(Guid.NewGuid(), "Micronesia"), - new Country(Guid.NewGuid(), "Moldova"), - new Country(Guid.NewGuid(), "Monaco"), - new Country(Guid.NewGuid(), "Mongolia"), - new Country(Guid.NewGuid(), "Montenegro"), - new Country(Guid.NewGuid(), "Morocco"), - new Country(Guid.NewGuid(), "Mozambique"), - new Country(Guid.NewGuid(), "Myanmar (formerly Burma)"), - new Country(Guid.NewGuid(), "Namibia"), - new Country(Guid.NewGuid(), "Nauru"), - new Country(Guid.NewGuid(), "Nepal"), - new Country(Guid.NewGuid(), "Netherlands"), - new Country(Guid.NewGuid(), "New Zealand"), - new Country(Guid.NewGuid(), "Nicaragua"), - new Country(Guid.NewGuid(), "Niger"), - new Country(Guid.NewGuid(), "Nigeria"), - new Country(Guid.NewGuid(), "North Korea"), - new Country(Guid.NewGuid(), "North Macedonia"), - new Country(Guid.NewGuid(), "Norway"), - new Country(Guid.NewGuid(), "Oman"), - new Country(Guid.NewGuid(), "Pakistan"), - new Country(Guid.NewGuid(), "Palau"), - new Country(Guid.NewGuid(), "Palestine State"), - new Country(Guid.NewGuid(), "Panama"), - new Country(Guid.NewGuid(), "Papua New Guinea"), - new Country(Guid.NewGuid(), "Paraguay"), - new Country(Guid.NewGuid(), "Peru"), - new Country(Guid.NewGuid(), "Philippines"), - new Country(Guid.NewGuid(), "Poland"), - new Country(Guid.NewGuid(), "Portugal"), - new Country(Guid.NewGuid(), "Qatar"), - new Country(Guid.NewGuid(), "Romania"), - new Country(Guid.NewGuid(), "Russia"), - new Country(Guid.NewGuid(), "Rwanda"), - new Country(Guid.NewGuid(), "Saint Kitts and Nevis"), - new Country(Guid.NewGuid(), "Saint Lucia"), - new Country(Guid.NewGuid(), "Saint Vincent and the Grenadines"), - new Country(Guid.NewGuid(), "Samoa"), - new Country(Guid.NewGuid(), "San Marino"), - new Country(Guid.NewGuid(), "Sao Tome and Principe"), - new Country(Guid.NewGuid(), "Saudi Arabia"), - new Country(Guid.NewGuid(), "Senegal"), - new Country(Guid.NewGuid(), "Serbia"), - new Country(Guid.NewGuid(), "Seychelles"), - new Country(Guid.NewGuid(), "Sierra Leone"), - new Country(Guid.NewGuid(), "Singapore"), - new Country(Guid.NewGuid(), "Slovakia"), - new Country(Guid.NewGuid(), "Slovenia"), - new Country(Guid.NewGuid(), "Solomon Islands"), - new Country(Guid.NewGuid(), "Somalia"), - new Country(Guid.NewGuid(), "South Africa"), - new Country(Guid.NewGuid(), "South Korea"), - new Country(Guid.NewGuid(), "South Sudan"), - new Country(Guid.NewGuid(), "Spain"), - new Country(Guid.NewGuid(), "Sri Lanka"), - new Country(Guid.NewGuid(), "Sudan"), - new Country(Guid.NewGuid(), "Suriname"), - new Country(Guid.NewGuid(), "Sweden"), - new Country(Guid.NewGuid(), "Switzerland"), - new Country(Guid.NewGuid(), "Syria"), - new Country(Guid.NewGuid(), "Tajikistan"), - new Country(Guid.NewGuid(), "Tanzania"), - new Country(Guid.NewGuid(), "Thailand"), - new Country(Guid.NewGuid(), "Timor-Leste"), - new Country(Guid.NewGuid(), "Togo"), - new Country(Guid.NewGuid(), "Tonga"), - new Country(Guid.NewGuid(), "Trinidad and Tobago"), - new Country(Guid.NewGuid(), "Tunisia"), - new Country(Guid.NewGuid(), "Turkey"), - new Country(Guid.NewGuid(), "Turkmenistan"), - new Country(Guid.NewGuid(), "Tuvalu"), - new Country(Guid.NewGuid(), "Uganda"), - new Country(Guid.NewGuid(), "Ukraine"), - new Country(Guid.NewGuid(), "United Arab Emirates"), - new Country(Guid.NewGuid(), "United Kingdom"), - new Country(Guid.NewGuid(), "United States of America"), - new Country(Guid.NewGuid(), "Uruguay"), - new Country(Guid.NewGuid(), "Uzbekistan"), - new Country(Guid.NewGuid(), "Vanuatu"), - new Country(Guid.NewGuid(), "Venezuela"), - new Country(Guid.NewGuid(), "Vietnam"), - new Country(Guid.NewGuid(), "Yemen"), - new Country(Guid.NewGuid(), "Zambia"), - new Country(Guid.NewGuid(), "Zimbabwe") + new Country(_guidGenerator.Create(), "Afghanistan"), + new Country(_guidGenerator.Create(), "Albania"), + new Country(_guidGenerator.Create(), "Algeria"), + new Country(_guidGenerator.Create(), "Andorra"), + new Country(_guidGenerator.Create(), "Angola"), + new Country(_guidGenerator.Create(), "Antigua and Barbuda"), + new Country(_guidGenerator.Create(), "Argentina"), + new Country(_guidGenerator.Create(), "Armenia"), + new Country(_guidGenerator.Create(), "Australia"), + new Country(_guidGenerator.Create(), "Austria"), + new Country(_guidGenerator.Create(), "Azerbaijan"), + new Country(_guidGenerator.Create(), "Bahamas"), + new Country(_guidGenerator.Create(), "Bahrain"), + new Country(_guidGenerator.Create(), "Bangladesh"), + new Country(_guidGenerator.Create(), "Barbados"), + new Country(_guidGenerator.Create(), "Belarus"), + new Country(_guidGenerator.Create(), "Belgium"), + new Country(_guidGenerator.Create(), "Belize"), + new Country(_guidGenerator.Create(), "Benin"), + new Country(_guidGenerator.Create(), "Bhutan"), + new Country(_guidGenerator.Create(), "Bolivia"), + new Country(_guidGenerator.Create(), "Bosnia and Herzegovina"), + new Country(_guidGenerator.Create(), "Botswana"), + new Country(_guidGenerator.Create(), "Brazil"), + new Country(_guidGenerator.Create(), "Brunei"), + new Country(_guidGenerator.Create(), "Bulgaria"), + new Country(_guidGenerator.Create(), "Burkina Faso"), + new Country(_guidGenerator.Create(), "Burundi"), + new Country(_guidGenerator.Create(), "Côte d'Ivoire"), + new Country(_guidGenerator.Create(), "Cabo Verde"), + new Country(_guidGenerator.Create(), "Cambodia"), + new Country(_guidGenerator.Create(), "Cameroon"), + new Country(_guidGenerator.Create(), "Canada"), + new Country(_guidGenerator.Create(), "Central African Republic"), + new Country(_guidGenerator.Create(), "Chad"), + new Country(_guidGenerator.Create(), "Chile"), + new Country(_guidGenerator.Create(), "China"), + new Country(_guidGenerator.Create(), "Colombia"), + new Country(_guidGenerator.Create(), "Comoros"), + new Country(_guidGenerator.Create(), "Congo (Congo-Brazzaville)"), + new Country(_guidGenerator.Create(), "Costa Rica"), + new Country(_guidGenerator.Create(), "Croatia"), + new Country(_guidGenerator.Create(), "Cuba"), + new Country(_guidGenerator.Create(), "Cyprus"), + new Country(_guidGenerator.Create(), "Czechia (Czech Republic)"), + new Country(_guidGenerator.Create(), "Democratic Republic of the Congo"), + new Country(_guidGenerator.Create(), "Denmark"), + new Country(_guidGenerator.Create(), "Djibouti"), + new Country(_guidGenerator.Create(), "Dominica"), + new Country(_guidGenerator.Create(), "Dominican Republic"), + new Country(_guidGenerator.Create(), "Ecuador"), + new Country(_guidGenerator.Create(), "Egypt"), + new Country(_guidGenerator.Create(), "El Salvador"), + new Country(_guidGenerator.Create(), "Equatorial Guinea"), + new Country(_guidGenerator.Create(), "Eritrea"), + new Country(_guidGenerator.Create(), "Estonia"), + new Country(_guidGenerator.Create(), "Eswatini (formerly Swaziland)"), + new Country(_guidGenerator.Create(), "Ethiopia"), + new Country(_guidGenerator.Create(), "Fiji"), + new Country(_guidGenerator.Create(), "Finland"), + new Country(_guidGenerator.Create(), "France"), + new Country(_guidGenerator.Create(), "Gabon"), + new Country(_guidGenerator.Create(), "Gambia"), + new Country(_guidGenerator.Create(), "Georgia"), + new Country(_guidGenerator.Create(), "Germany"), + new Country(_guidGenerator.Create(), "Ghana"), + new Country(_guidGenerator.Create(), "Greece"), + new Country(_guidGenerator.Create(), "Grenada"), + new Country(_guidGenerator.Create(), "Guatemala"), + new Country(_guidGenerator.Create(), "Guinea"), + new Country(_guidGenerator.Create(), "Guinea-Bissau"), + new Country(_guidGenerator.Create(), "Guyana"), + new Country(_guidGenerator.Create(), "Haiti"), + new Country(_guidGenerator.Create(), "Holy See"), + new Country(_guidGenerator.Create(), "Honduras"), + new Country(_guidGenerator.Create(), "Hungary"), + new Country(_guidGenerator.Create(), "Iceland"), + new Country(_guidGenerator.Create(), "India"), + new Country(_guidGenerator.Create(), "Indonesia"), + new Country(_guidGenerator.Create(), "Iran"), + new Country(_guidGenerator.Create(), "Iraq"), + new Country(_guidGenerator.Create(), "Ireland"), + new Country(_guidGenerator.Create(), "Israel"), + new Country(_guidGenerator.Create(), "Italy"), + new Country(_guidGenerator.Create(), "Jamaica"), + new Country(_guidGenerator.Create(), "Japan"), + new Country(_guidGenerator.Create(), "Jordan"), + new Country(_guidGenerator.Create(), "Kazakhstan"), + new Country(_guidGenerator.Create(), "Kenya"), + new Country(_guidGenerator.Create(), "Kiribati"), + new Country(_guidGenerator.Create(), "Kuwait"), + new Country(_guidGenerator.Create(), "Kyrgyzstan"), + new Country(_guidGenerator.Create(), "Laos"), + new Country(_guidGenerator.Create(), "Latvia"), + new Country(_guidGenerator.Create(), "Lebanon"), + new Country(_guidGenerator.Create(), "Lesotho"), + new Country(_guidGenerator.Create(), "Liberia"), + new Country(_guidGenerator.Create(), "Libya"), + new Country(_guidGenerator.Create(), "Liechtenstein"), + new Country(_guidGenerator.Create(), "Lithuania"), + new Country(_guidGenerator.Create(), "Luxembourg"), + new Country(_guidGenerator.Create(), "Madagascar"), + new Country(_guidGenerator.Create(), "Malawi"), + new Country(_guidGenerator.Create(), "Malaysia"), + new Country(_guidGenerator.Create(), "Maldives"), + new Country(_guidGenerator.Create(), "Mali"), + new Country(_guidGenerator.Create(), "Malta"), + new Country(_guidGenerator.Create(), "Marshall Islands"), + new Country(_guidGenerator.Create(), "Mauritania"), + new Country(_guidGenerator.Create(), "Mauritius"), + new Country(_guidGenerator.Create(), "Mexico"), + new Country(_guidGenerator.Create(), "Micronesia"), + new Country(_guidGenerator.Create(), "Moldova"), + new Country(_guidGenerator.Create(), "Monaco"), + new Country(_guidGenerator.Create(), "Mongolia"), + new Country(_guidGenerator.Create(), "Montenegro"), + new Country(_guidGenerator.Create(), "Morocco"), + new Country(_guidGenerator.Create(), "Mozambique"), + new Country(_guidGenerator.Create(), "Myanmar (formerly Burma)"), + new Country(_guidGenerator.Create(), "Namibia"), + new Country(_guidGenerator.Create(), "Nauru"), + new Country(_guidGenerator.Create(), "Nepal"), + new Country(_guidGenerator.Create(), "Netherlands"), + new Country(_guidGenerator.Create(), "New Zealand"), + new Country(_guidGenerator.Create(), "Nicaragua"), + new Country(_guidGenerator.Create(), "Niger"), + new Country(_guidGenerator.Create(), "Nigeria"), + new Country(_guidGenerator.Create(), "North Korea"), + new Country(_guidGenerator.Create(), "North Macedonia"), + new Country(_guidGenerator.Create(), "Norway"), + new Country(_guidGenerator.Create(), "Oman"), + new Country(_guidGenerator.Create(), "Pakistan"), + new Country(_guidGenerator.Create(), "Palau"), + new Country(_guidGenerator.Create(), "Palestine State"), + new Country(_guidGenerator.Create(), "Panama"), + new Country(_guidGenerator.Create(), "Papua New Guinea"), + new Country(_guidGenerator.Create(), "Paraguay"), + new Country(_guidGenerator.Create(), "Peru"), + new Country(_guidGenerator.Create(), "Philippines"), + new Country(_guidGenerator.Create(), "Poland"), + new Country(_guidGenerator.Create(), "Portugal"), + new Country(_guidGenerator.Create(), "Qatar"), + new Country(_guidGenerator.Create(), "Romania"), + new Country(_guidGenerator.Create(), "Russia"), + new Country(_guidGenerator.Create(), "Rwanda"), + new Country(_guidGenerator.Create(), "Saint Kitts and Nevis"), + new Country(_guidGenerator.Create(), "Saint Lucia"), + new Country(_guidGenerator.Create(), "Saint Vincent and the Grenadines"), + new Country(_guidGenerator.Create(), "Samoa"), + new Country(_guidGenerator.Create(), "San Marino"), + new Country(_guidGenerator.Create(), "Sao Tome and Principe"), + new Country(_guidGenerator.Create(), "Saudi Arabia"), + new Country(_guidGenerator.Create(), "Senegal"), + new Country(_guidGenerator.Create(), "Serbia"), + new Country(_guidGenerator.Create(), "Seychelles"), + new Country(_guidGenerator.Create(), "Sierra Leone"), + new Country(_guidGenerator.Create(), "Singapore"), + new Country(_guidGenerator.Create(), "Slovakia"), + new Country(_guidGenerator.Create(), "Slovenia"), + new Country(_guidGenerator.Create(), "Solomon Islands"), + new Country(_guidGenerator.Create(), "Somalia"), + new Country(_guidGenerator.Create(), "South Africa"), + new Country(_guidGenerator.Create(), "South Korea"), + new Country(_guidGenerator.Create(), "South Sudan"), + new Country(_guidGenerator.Create(), "Spain"), + new Country(_guidGenerator.Create(), "Sri Lanka"), + new Country(_guidGenerator.Create(), "Sudan"), + new Country(_guidGenerator.Create(), "Suriname"), + new Country(_guidGenerator.Create(), "Sweden"), + new Country(_guidGenerator.Create(), "Switzerland"), + new Country(_guidGenerator.Create(), "Syria"), + new Country(_guidGenerator.Create(), "Tajikistan"), + new Country(_guidGenerator.Create(), "Tanzania"), + new Country(_guidGenerator.Create(), "Thailand"), + new Country(_guidGenerator.Create(), "Timor-Leste"), + new Country(_guidGenerator.Create(), "Togo"), + new Country(_guidGenerator.Create(), "Tonga"), + new Country(_guidGenerator.Create(), "Trinidad and Tobago"), + new Country(_guidGenerator.Create(), "Tunisia"), + new Country(_guidGenerator.Create(), "Turkey"), + new Country(_guidGenerator.Create(), "Turkmenistan"), + new Country(_guidGenerator.Create(), "Tuvalu"), + new Country(_guidGenerator.Create(), "Uganda"), + new Country(_guidGenerator.Create(), "Ukraine"), + new Country(_guidGenerator.Create(), "United Arab Emirates"), + new Country(_guidGenerator.Create(), "United Kingdom"), + new Country(_guidGenerator.Create(), "United States of America"), + new Country(_guidGenerator.Create(), "Uruguay"), + new Country(_guidGenerator.Create(), "Uzbekistan"), + new Country(_guidGenerator.Create(), "Vanuatu"), + new Country(_guidGenerator.Create(), "Venezuela"), + new Country(_guidGenerator.Create(), "Vietnam"), + new Country(_guidGenerator.Create(), "Yemen"), + new Country(_guidGenerator.Create(), "Zambia"), + new Country(_guidGenerator.Create(), "Zimbabwe") }; await _countryRepository.InsertManyAsync(countries, autoSave: true); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain/Events/Event.cs b/src/EventHub.Domain/Events/Event.cs index eb6a386..7a57616 100644 --- a/src/EventHub.Domain/Events/Event.cs +++ b/src/EventHub.Domain/Events/Event.cs @@ -1,5 +1,9 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; using Volo.Abp; +using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities.Auditing; namespace EventHub.Events @@ -41,7 +45,11 @@ namespace EventHub.Events public int TimingChangeCount { get; set; } public bool IsTimingChangeEmailSent { get; set; } - + + public bool IsDraft { get; private set; } + + public ICollection Tracks { get; private set; } + private Event() { @@ -59,9 +67,14 @@ namespace EventHub.Events { OrganizationId = organizationId; UrlCode = Check.NotNullOrWhiteSpace(urlCode, urlCode, EventConsts.UrlCodeLength, EventConsts.UrlCodeLength); + SetTitle(title); SetDescription(description); SetTimeInternal(startTime, endTime); + + Publish(false); + + Tracks = new Collection(); } public Event SetTitle(string title) @@ -83,29 +96,155 @@ namespace EventHub.Events { return this; } - - if (TimingChangeCount >= EventConsts.MaxTimingChangeCountForUser) + + if (!IsDraft) { - throw new BusinessException(EventHubErrorCodes.CantChangeEventTiming) - .WithData("MaxTimingChangeLimit", EventConsts.MaxTimingChangeCountForUser); + if (TimingChangeCount >= 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) { if (startTime > endTime) { - throw new BusinessException(EventHubErrorCodes.EventEndTimeCantBeEarlierThanStartTime); + throw new BusinessException(EventHubErrorCodes.EndTimeCantBeEarlierThanStartTime); } StartTime = startTime; EndTime = endTime; - TimingChangeCount++; 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 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 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); + } + } } } diff --git a/src/EventHub.Domain/Events/EventManager.cs b/src/EventHub.Domain/Events/EventManager.cs index 3f87ac5..69171b1 100644 --- a/src/EventHub.Domain/Events/EventManager.cs +++ b/src/EventHub.Domain/Events/EventManager.cs @@ -53,7 +53,7 @@ namespace EventHub.Events var registeredUserCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id); if (capacity.Value < registeredUserCount) { - throw new BusinessException(EventHubErrorCodes.CapacityCantBeLowerThanRegisteredUserCount); + throw new BusinessException(EventHubErrorCodes.CapacityCanNotBeLowerThanRegisteredUserCount); } } diff --git a/src/EventHub.Domain/Events/EventTimeChangeLocalEventHandler.cs b/src/EventHub.Domain/Events/EventTimeChangeLocalEventHandler.cs deleted file mode 100644 index 492a64d..0000000 --- a/src/EventHub.Domain/Events/EventTimeChangeLocalEventHandler.cs +++ /dev/null @@ -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, ITransientDependency - { - private readonly IRepository _eventRepository; - - public EventTimeChangeLocalEventHandler(IRepository 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); - } - } -} \ No newline at end of file diff --git a/src/EventHub.Domain/Events/EventTimeChangingEventData.cs b/src/EventHub.Domain/Events/EventTimeChangingEventData.cs deleted file mode 100644 index cf57a1a..0000000 --- a/src/EventHub.Domain/Events/EventTimeChangingEventData.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs b/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs index 85c34d2..4f46edb 100644 --- a/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs +++ b/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Volo.Abp; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Services; using Volo.Abp.Guids; using Volo.Abp.Identity; @@ -37,12 +38,14 @@ namespace EventHub.Events.Registrations return; } - var registrationCount = await _eventRegistrationRepository.GetCountAsync(@event.Id); - - if (@event.Capacity != null && registrationCount >= @event.Capacity) + if (@event.Capacity != null) { - throw new BusinessException(EventHubErrorCodes.CapacityOfEventFull) - .WithData("EventTitle", @event.Title); + var registrationCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id); + if (registrationCount >= @event.Capacity) + { + throw new BusinessException(EventHubErrorCodes.CapacityOfEventFull) + .WithData("EventTitle", @event.Title); + } } await _eventRegistrationRepository.InsertAsync( diff --git a/src/EventHub.Domain/Events/Session.cs b/src/EventHub.Domain/Events/Session.cs new file mode 100644 index 0000000..de40c15 --- /dev/null +++ b/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 + { + 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 Speakers { get; private set; } + + private Session() + { + + } + + internal Session( + Guid id, + Guid trackId, + string title, + DateTime startTime, + DateTime endTime, + string description, + string language, + ICollection speakerUserIds) + : base(id) + { + TrackId = trackId; + SetTitle(title); + SetDescription(description); + SetTime(startTime, endTime); + SetLanguage(language); + + Speakers = new Collection(); + 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 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 userIds) + { + foreach (var userId in userIds) + { + RemoveSpeaker(userId); + } + + return this; + } + + internal Session RemoveSpeaker(Guid userId) + { + Speakers.RemoveAll(t => t.UserId == userId); + + return this; + } + } +} diff --git a/src/EventHub.Domain/Events/Speaker.cs b/src/EventHub.Domain/Events/Speaker.cs new file mode 100644 index 0000000..766fb73 --- /dev/null +++ b/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}; + } + } +} diff --git a/src/EventHub.Domain/Events/Track.cs b/src/EventHub.Domain/Events/Track.cs new file mode 100644 index 0000000..ce9a823 --- /dev/null +++ b/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 + { + public Guid EventId { get; private set; } + + public string Name { get; private set; } + + public ICollection Sessions { get; private set; } + + private Track() + { + + } + + internal Track( + Guid id, + Guid eventId, + string name) + : base(id) + { + EventId = eventId; + SetName(name); + Sessions = new Collection(); + } + + 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 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 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); + } + } + } + } +} diff --git a/src/EventHub.Domain/Users/UserWithoutDetails.cs b/src/EventHub.Domain/Users/UserWithoutDetails.cs new file mode 100644 index 0000000..b02395f --- /dev/null +++ b/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; } +} diff --git a/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubDbContextModelCreatingExtensions.cs b/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubDbContextModelCreatingExtensions.cs index c9313ba..d3fa308 100644 --- a/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubDbContextModelCreatingExtensions.cs +++ b/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubDbContextModelCreatingExtensions.cs @@ -71,6 +71,43 @@ namespace EventHub.EntityFrameworkCore b.HasIndex(x => x.UrlCode); b.HasIndex(x => new {x.IsRemindingEmailSent, x.StartTime}); b.HasIndex(x => x.IsEmailSentToMembers); + + b.HasMany(x => x.Tracks).WithOne().IsRequired().HasForeignKey(x => x.EventId); + }); + + builder.Entity(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(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(b => + { + b.ToTable(EventHubConsts.DbTablePrefix + "EventSpeakers", EventHubConsts.DbSchema); + + b.ConfigureByConvention(); + + b.HasKey(x => new {x.SessionId, x.UserId}); + + b.HasOne().WithMany().HasForeignKey(x => x.UserId).IsRequired(); }); builder.Entity(b => diff --git a/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Events/EventRepository.cs b/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Events/EventRepository.cs index 603824c..46977ea 100644 --- a/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Events/EventRepository.cs +++ b/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Events/EventRepository.cs @@ -20,12 +20,12 @@ namespace EventHub.EntityFrameworkCore.Events } public async Task GetCountAsync( - string title = null, - string organizationDisplayName = null, - int? minAttendeeCount = null, - int? maxAttendeeCount = null, - DateTime? minStartTime = null, - DateTime? maxStartTime = null, + string title = null, + string organizationDisplayName = null, + int? minAttendeeCount = null, + int? maxAttendeeCount = null, + DateTime? minStartTime = null, + DateTime? maxStartTime = null, CancellationToken cancellationToken = default ) { @@ -36,35 +36,36 @@ namespace EventHub.EntityFrameworkCore.Events var organizationQueryable = dbContext.Set().AsQueryable(); var query = (from @event in eventQueryable - join organization in organizationQueryable on @event.OrganizationId equals organization.Id - select new - { - Event = @event, - Organization = organization, - AttendeeCount = (from eventRegistration in eventRegistrationQueryable - where eventRegistration.EventId == @event.Id - select @eventRegistration).Count() - }) - .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(minStartTime.HasValue, x => x.Event.StartTime >= minStartTime) - .WhereIf(maxStartTime.HasValue, x => x.Event.StartTime <= maxStartTime) - .WhereIf(minAttendeeCount.HasValue, x => x.AttendeeCount >= minAttendeeCount) - .WhereIf(maxAttendeeCount.HasValue, x => x.AttendeeCount <= maxAttendeeCount); + join organization in organizationQueryable on @event.OrganizationId equals organization.Id + select new + { + Event = @event, + Organization = organization, + AttendeeCount = (from eventRegistration in eventRegistrationQueryable + where eventRegistration.EventId == @event.Id + select @eventRegistration).Count() + }) + .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(minStartTime.HasValue, x => x.Event.StartTime >= minStartTime) + .WhereIf(maxStartTime.HasValue, x => x.Event.StartTime <= maxStartTime) + .WhereIf(minAttendeeCount.HasValue, x => x.AttendeeCount >= minAttendeeCount) + .WhereIf(maxAttendeeCount.HasValue, x => x.AttendeeCount <= maxAttendeeCount); return await query.CountAsync(GetCancellationToken(cancellationToken)); } public async Task> GetListAsync( string sorting = null, - int skipCount = 0, - int maxResultCount = int.MaxValue, - string title = null, - string organizationDisplayName = null, - int? minAttendeeCount = null, - int? maxAttendeeCount = null, - DateTime? minStartTime = null, - DateTime? maxStartTime = null, + int skipCount = 0, + int maxResultCount = int.MaxValue, + string title = null, + string organizationDisplayName = null, + int? minAttendeeCount = null, + int? maxAttendeeCount = null, + DateTime? minStartTime = null, + DateTime? maxStartTime = null, CancellationToken cancellationToken = default ) { @@ -98,5 +99,13 @@ namespace EventHub.EntityFrameworkCore.Events return await query.ToListAsync(GetCancellationToken(cancellationToken)); } + + public override async Task> WithDetailsAsync() + { + return (await GetQueryableAsync()) + .Include(e => e.Tracks) + .ThenInclude(t => t.Sessions) + .ThenInclude(s => s.Speakers); + } } } diff --git a/src/EventHub.EntityFrameworkCore/EventHub.EntityFrameworkCore.csproj b/src/EventHub.EntityFrameworkCore/EventHub.EntityFrameworkCore.csproj index 1e10f87..3c6f376 100644 --- a/src/EventHub.EntityFrameworkCore/EventHub.EntityFrameworkCore.csproj +++ b/src/EventHub.EntityFrameworkCore/EventHub.EntityFrameworkCore.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.Designer.cs b/src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.Designer.cs new file mode 100644 index 0000000..63d3f93 --- /dev/null +++ b/src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.Designer.cs @@ -0,0 +1,2747 @@ +// +using System; +using EventHub.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +namespace EventHub.Migrations +{ + [DbContext(typeof(EventHubDbContext))] + [Migration("20210725093006_Added_Tracks_And_Sessions")] + partial class Added_Tracks_And_Sessions + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.8") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + modelBuilder.Entity("EventHub.Countries.Country", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("EhCountries"); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("City") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CountryId") + .HasColumnType("uuid"); + + b.Property("CountryName") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDraft") + .HasColumnType("boolean"); + + b.Property("IsEmailSentToMembers") + .HasColumnType("boolean"); + + b.Property("IsOnline") + .HasColumnType("boolean"); + + b.Property("IsRemindingEmailSent") + .HasColumnType("boolean"); + + b.Property("IsTimingChangeEmailSent") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Language") + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("OnlineLink") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("StartTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TimingChangeCount") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(69) + .HasColumnType("character varying(69)"); + + b.Property("UrlCode") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.HasIndex("IsEmailSentToMembers"); + + b.HasIndex("StartTime"); + + b.HasIndex("UrlCode"); + + b.HasIndex("IsRemindingEmailSent", "StartTime"); + + b.HasIndex("OrganizationId", "StartTime"); + + b.ToTable("EhEvents"); + }); + + modelBuilder.Entity("EventHub.Events.Registrations.EventRegistration", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("EventId", "UserId"); + + b.ToTable("EhEventRegistrations"); + }); + + modelBuilder.Entity("EventHub.Events.Session", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("StartTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TrackId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TrackId"); + + b.ToTable("EhEventSessions"); + }); + + modelBuilder.Entity("EventHub.Events.Speaker", b => + { + b.Property("SessionId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("SessionId", "UserId"); + + b.HasIndex("UserId", "SessionId"); + + b.ToTable("EhEventSpeakers"); + }); + + modelBuilder.Entity("EventHub.Events.Track", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("EventId"); + + b.ToTable("EhEventTracks"); + }); + + modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "UserId"); + + b.ToTable("EhOrganizationMemberships"); + }); + + modelBuilder.Entity("EventHub.Organizations.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FacebookUsername") + .HasColumnType("text"); + + b.Property("GitHubUsername") + .HasColumnType("text"); + + b.Property("InstagramUsername") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MediumUsername") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("OwnerUserId") + .HasColumnType("uuid"); + + b.Property("TwitterUsername") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.HasIndex("Name"); + + b.HasIndex("OwnerUserId"); + + b.ToTable("EhOrganizations"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("text"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("integer") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasColumnType("text"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("smallint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uuid"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EntityChangeId") + .HasColumnType("uuid"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("LastTryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("NextTryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContainerId") + .HasColumnType("uuid"); + + b.Property("Content") + .HasMaxLength(2147483647) + .HasColumnType("bytea"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("TenantId", "ContainerId", "Name"); + + b.ToTable("AbpBlobs"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AbpBlobContainers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ValueType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SourceTenantId") + .HasColumnType("uuid"); + + b.Property("SourceUserId") + .HasColumnType("uuid"); + + b.Property("TargetTenantId") + .HasColumnType("uuid"); + + b.Property("TargetUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique(); + + b.ToTable("AbpLinkUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("boolean") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("SecurityStamp"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("character varying(196)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("character varying(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowedAccessTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiResourceClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ApiResourceId", "Key", "Value"); + + b.ToTable("IdentityServerApiResourceProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiResourceId", "Scope"); + + b.ToTable("IdentityServerApiResourceScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Expiration") + .HasColumnType("timestamp without time zone"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiResourceSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.Property("ApiScopeId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiScopeId", "Type"); + + b.ToTable("IdentityServerApiScopeClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.Property("ApiScopeId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ApiScopeId", "Key", "Value"); + + b.ToTable("IdentityServerApiScopeProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AbsoluteRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenType") + .HasColumnType("integer"); + + b.Property("AllowAccessTokensViaBrowser") + .HasColumnType("boolean"); + + b.Property("AllowOfflineAccess") + .HasColumnType("boolean"); + + b.Property("AllowPlainTextPkce") + .HasColumnType("boolean"); + + b.Property("AllowRememberConsent") + .HasColumnType("boolean"); + + b.Property("AllowedIdentityTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AlwaysIncludeUserClaimsInIdToken") + .HasColumnType("boolean"); + + b.Property("AlwaysSendClientClaims") + .HasColumnType("boolean"); + + b.Property("AuthorizationCodeLifetime") + .HasColumnType("integer"); + + b.Property("BackChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime") + .HasColumnType("integer"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DeviceCodeLifetime") + .HasColumnType("integer"); + + b.Property("EnableLocalLogin") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IdentityTokenLifetime") + .HasColumnType("integer"); + + b.Property("IncludeJwtId") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("integer"); + + b.Property("RefreshTokenUsage") + .HasColumnType("integer"); + + b.Property("RequireClientSecret") + .HasColumnType("boolean"); + + b.Property("RequireConsent") + .HasColumnType("boolean"); + + b.Property("RequirePkce") + .HasColumnType("boolean"); + + b.Property("RequireRequestObject") + .HasColumnType("boolean"); + + b.Property("SlidingRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("UpdateAccessTokenClaimsOnRefresh") + .HasColumnType("boolean"); + + b.Property("UserCodeType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UserSsoLifetime") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("IdentityServerClients"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Origin") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("GrantType") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Provider") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "Key", "Value"); + + b.ToTable("IdentityServerClientProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("RedirectUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Expiration") + .HasColumnType("timestamp without time zone"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Devices.DeviceFlowCodes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UserCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.HasIndex("UserCode"); + + b.ToTable("IdentityServerDeviceFlowCodes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsumedTime") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("IdentityServerPersistedGrants"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityResourceClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("IdentityResourceId", "Key", "Value"); + + b.ToTable("IdentityServerIdentityResourceProperties"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.HasOne("EventHub.Countries.Country", null) + .WithMany() + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("EventHub.Organizations.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Events.Registrations.EventRegistration", b => + { + b.HasOne("EventHub.Events.Event", null) + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .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 => + { + b.HasOne("EventHub.Organizations.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Organizations.Organization", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("OwnerUserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null) + .WithMany() + .HasForeignKey("ContainerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Properties") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("UserClaims") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("Properties") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("Properties") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .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 => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Navigation("Properties"); + + b.Navigation("Scopes"); + + b.Navigation("Secrets"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Navigation("AllowedCorsOrigins"); + + b.Navigation("AllowedGrantTypes"); + + b.Navigation("AllowedScopes"); + + b.Navigation("Claims"); + + b.Navigation("ClientSecrets"); + + b.Navigation("IdentityProviderRestrictions"); + + b.Navigation("PostLogoutRedirectUris"); + + b.Navigation("Properties"); + + b.Navigation("RedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.cs b/src/EventHub.EntityFrameworkCore/Migrations/20210725093006_Added_Tracks_And_Sessions.cs new file mode 100644 index 0000000..2935eef --- /dev/null +++ b/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( + name: "IsDraft", + table: "EhEvents", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "EhEventTracks", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + EventId = table.Column(type: "uuid", nullable: false), + Name = table.Column(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(type: "uuid", nullable: false), + TrackId = table.Column(type: "uuid", nullable: false), + Title = table.Column(type: "character varying(128)", maxLength: 128, nullable: false), + StartTime = table.Column(type: "timestamp without time zone", nullable: false), + EndTime = table.Column(type: "timestamp without time zone", nullable: false), + Description = table.Column(type: "character varying(2000)", maxLength: 2000, nullable: false), + Language = table.Column(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(type: "uuid", nullable: false), + UserId = table.Column(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"); + } + } +} diff --git a/src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.Designer.cs b/src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.Designer.cs new file mode 100644 index 0000000..f1cfea2 --- /dev/null +++ b/src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.Designer.cs @@ -0,0 +1,2831 @@ +// +using System; +using EventHub.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace EventHub.Migrations +{ + [DbContext(typeof(EventHubDbContext))] + [Migration("20211224141912_Removed_Unnecessary_Index")] + partial class Removed_Unnecessary_Index + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("EventHub.Countries.Country", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("EhCountries", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("City") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CountryId") + .HasColumnType("uuid"); + + b.Property("CountryName") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDraft") + .HasColumnType("boolean"); + + b.Property("IsEmailSentToMembers") + .HasColumnType("boolean"); + + b.Property("IsOnline") + .HasColumnType("boolean"); + + b.Property("IsRemindingEmailSent") + .HasColumnType("boolean"); + + b.Property("IsTimingChangeEmailSent") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Language") + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("OnlineLink") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("TimingChangeCount") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(69) + .HasColumnType("character varying(69)"); + + b.Property("UrlCode") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.HasIndex("IsEmailSentToMembers"); + + b.HasIndex("StartTime"); + + b.HasIndex("UrlCode"); + + b.HasIndex("IsRemindingEmailSent", "StartTime"); + + b.HasIndex("OrganizationId", "StartTime"); + + b.ToTable("EhEvents", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Registrations.EventRegistration", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("EventId", "UserId"); + + b.ToTable("EhEventRegistrations", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Session", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TrackId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TrackId"); + + b.ToTable("EhEventSessions", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Speaker", b => + { + b.Property("SessionId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("SessionId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("EhEventSpeakers", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Track", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("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 => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "UserId"); + + b.ToTable("EhOrganizationMemberships", (string)null); + }); + + modelBuilder.Entity("EventHub.Organizations.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FacebookUsername") + .HasColumnType("text"); + + b.Property("GitHubUsername") + .HasColumnType("text"); + + b.Property("InstagramUsername") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPremium") + .HasColumnType("boolean"); + + b.Property("IsSendPremiumReminderEmail") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MediumUsername") + .HasColumnType("text"); + + b.Property("MemberCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("OwnerUserId") + .HasColumnType("uuid"); + + b.Property("PremiumEndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TwitterUsername") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.HasIndex("Name"); + + b.HasIndex("OwnerUserId"); + + b.ToTable("EhOrganizations", (string)null); + }); + + modelBuilder.Entity("Payment.PaymentRequests.PaymentRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("text"); + + b.Property("CustomerId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FailReason") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("Price") + .HasColumnType("numeric"); + + b.Property("ProductId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ProductName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("State") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("State"); + + b.ToTable("PayPaymentRequests", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("text"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("integer") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasColumnType("text"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("smallint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uuid"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EntityChangeId") + .HasColumnType("uuid"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("LastTryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("NextTryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContainerId") + .HasColumnType("uuid"); + + b.Property("Content") + .HasMaxLength(2147483647) + .HasColumnType("bytea"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("TenantId", "ContainerId", "Name"); + + b.ToTable("AbpBlobs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AbpBlobContainers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ValueType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SourceTenantId") + .HasColumnType("uuid"); + + b.Property("SourceUserId") + .HasColumnType("uuid"); + + b.Property("TargetTenantId") + .HasColumnType("uuid"); + + b.Property("TargetUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique(); + + b.ToTable("AbpLinkUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("boolean") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("SecurityStamp"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("character varying(196)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("character varying(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowedAccessTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiResourceClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ApiResourceId", "Key", "Value"); + + b.ToTable("IdentityServerApiResourceProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiResourceId", "Scope"); + + b.ToTable("IdentityServerApiResourceScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiResourceSecrets", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.Property("ApiScopeId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiScopeId", "Type"); + + b.ToTable("IdentityServerApiScopeClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.Property("ApiScopeId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ApiScopeId", "Key", "Value"); + + b.ToTable("IdentityServerApiScopeProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AbsoluteRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenType") + .HasColumnType("integer"); + + b.Property("AllowAccessTokensViaBrowser") + .HasColumnType("boolean"); + + b.Property("AllowOfflineAccess") + .HasColumnType("boolean"); + + b.Property("AllowPlainTextPkce") + .HasColumnType("boolean"); + + b.Property("AllowRememberConsent") + .HasColumnType("boolean"); + + b.Property("AllowedIdentityTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AlwaysIncludeUserClaimsInIdToken") + .HasColumnType("boolean"); + + b.Property("AlwaysSendClientClaims") + .HasColumnType("boolean"); + + b.Property("AuthorizationCodeLifetime") + .HasColumnType("integer"); + + b.Property("BackChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime") + .HasColumnType("integer"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DeviceCodeLifetime") + .HasColumnType("integer"); + + b.Property("EnableLocalLogin") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IdentityTokenLifetime") + .HasColumnType("integer"); + + b.Property("IncludeJwtId") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("integer"); + + b.Property("RefreshTokenUsage") + .HasColumnType("integer"); + + b.Property("RequireClientSecret") + .HasColumnType("boolean"); + + b.Property("RequireConsent") + .HasColumnType("boolean"); + + b.Property("RequirePkce") + .HasColumnType("boolean"); + + b.Property("RequireRequestObject") + .HasColumnType("boolean"); + + b.Property("SlidingRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("UpdateAccessTokenClaimsOnRefresh") + .HasColumnType("boolean"); + + b.Property("UserCodeType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UserSsoLifetime") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("IdentityServerClients", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Origin") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("GrantType") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Provider") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "Key", "Value"); + + b.ToTable("IdentityServerClientProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("RedirectUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Devices.DeviceFlowCodes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UserCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.HasIndex("UserCode"); + + b.ToTable("IdentityServerDeviceFlowCodes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsumedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("IdentityServerPersistedGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityResourceClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("IdentityResourceId", "Key", "Value"); + + b.ToTable("IdentityServerIdentityResourceProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.HasOne("EventHub.Countries.Country", null) + .WithMany() + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("EventHub.Organizations.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Events.Registrations.EventRegistration", b => + { + b.HasOne("EventHub.Events.Event", null) + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .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 => + { + b.HasOne("EventHub.Organizations.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Organizations.Organization", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("OwnerUserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null) + .WithMany() + .HasForeignKey("ContainerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Properties") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("UserClaims") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("Properties") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("Properties") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .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 => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Navigation("Properties"); + + b.Navigation("Scopes"); + + b.Navigation("Secrets"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Navigation("AllowedCorsOrigins"); + + b.Navigation("AllowedGrantTypes"); + + b.Navigation("AllowedScopes"); + + b.Navigation("Claims"); + + b.Navigation("ClientSecrets"); + + b.Navigation("IdentityProviderRestrictions"); + + b.Navigation("PostLogoutRedirectUris"); + + b.Navigation("Properties"); + + b.Navigation("RedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.cs b/src/EventHub.EntityFrameworkCore/Migrations/20211224141912_Removed_Unnecessary_Index.cs new file mode 100644 index 0000000..0329c7c --- /dev/null +++ b/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( + name: "StartTime", + table: "EhEventSessions", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + 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( + name: "StartTime", + table: "EhEventSessions", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + 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" }); + } + } +} diff --git a/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs b/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs index d7a2d55..0f35f77 100644 --- a/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs +++ b/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace EventHub.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) - .HasAnnotation("ProductVersion", "6.0.0-rc.2.21480.5") + .HasAnnotation("ProductVersion", "6.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -99,6 +99,9 @@ namespace EventHub.Migrations .HasDefaultValue(false) .HasColumnName("IsDeleted"); + b.Property("IsDraft") + .HasColumnType("boolean"); + b.Property("IsEmailSentToMembers") .HasColumnType("boolean"); @@ -208,6 +211,77 @@ namespace EventHub.Migrations b.ToTable("EhEventRegistrations", (string)null); }); + modelBuilder.Entity("EventHub.Events.Session", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TrackId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TrackId"); + + b.ToTable("EhEventSessions", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Speaker", b => + { + b.Property("SessionId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("SessionId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("EhEventSpeakers", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Track", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("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 => { b.Property("Id") @@ -2253,7 +2327,8 @@ namespace EventHub.Migrations b.HasKey("Id"); - b.HasIndex("Name", "ProviderName", "ProviderKey"); + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique(); b.ToTable("AbpPermissionGrants", (string)null); }); @@ -2284,7 +2359,8 @@ namespace EventHub.Migrations b.HasKey("Id"); - b.HasIndex("Name", "ProviderName", "ProviderKey"); + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); b.ToTable("AbpSettings", (string)null); }); @@ -2318,6 +2394,39 @@ namespace EventHub.Migrations .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 => { b.HasOne("EventHub.Organizations.Organization", null) @@ -2619,6 +2728,21 @@ namespace EventHub.Migrations .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 => { b.Navigation("Actions"); diff --git a/src/EventHub.HttpApi.Client/EventHubHttpApiClientModule.cs b/src/EventHub.HttpApi.Client/EventHubHttpApiClientModule.cs index 5b3604c..ae2e0fd 100644 --- a/src/EventHub.HttpApi.Client/EventHubHttpApiClientModule.cs +++ b/src/EventHub.HttpApi.Client/EventHubHttpApiClientModule.cs @@ -2,6 +2,7 @@ using Volo.Abp.Http.Client; using Volo.Abp.Modularity; using Payment; +using Volo.Abp.VirtualFileSystem; namespace EventHub { @@ -20,6 +21,11 @@ namespace EventHub typeof(EventHubApplicationContractsModule).Assembly, RemoteServiceName ); + + Configure(options => + { + options.FileSets.AddEmbedded(); + }); } } } diff --git a/src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs b/src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs deleted file mode 100644 index 458f67c..0000000 --- a/src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs +++ /dev/null @@ -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 CreateAsync([FromForm] CreateEventDto input) - { - return await _eventAppService.CreateAsync(input); - } - - [HttpGet] - public async Task> GetListAsync(EventListFilterDto input) - { - return await _eventAppService.GetListAsync(input); - } - - [HttpGet] - [Route("by-url-code/{urlCode}")] - public async Task GetByUrlCodeAsync(string urlCode) - { - return await _eventAppService.GetByUrlCodeAsync(urlCode); - } - - [HttpGet] - [Route("location/{id}")] - public async Task GetLocationAsync(Guid id) - { - return await _eventAppService.GetLocationAsync(id); - } - - [HttpGet] - [Route("lookup/countries")] - public async Task> GetCountriesLookupAsync() - { - return await _eventAppService.GetCountriesLookupAsync(); - } - - [HttpGet] - [Route("is-event-owner/{id}")] - public async Task 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 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; - } - } -} \ No newline at end of file diff --git a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs index a9eceb1..d2e5674 100644 --- a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs +++ b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs @@ -30,6 +30,7 @@ using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Swashbuckle; +using Volo.Abp.Timing; using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.VirtualFileSystem; @@ -52,7 +53,6 @@ namespace EventHub public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); - var hostingEnvironment = context.Services.GetHostingEnvironment(); ConfigureAuthentication(context, configuration); ConfigureLocalization(); @@ -64,6 +64,7 @@ namespace EventHub ConfigureSwaggerServices(context, configuration); ConfigureBackgroundJobs(); ConfigureAutoApiControllers(); + ConfigureTiming(); } private void ConfigureAutoApiControllers() @@ -186,6 +187,11 @@ namespace EventHub { context.Services.AddSameSiteCookiePolicy(); } + + private void ConfigureTiming() + { + Configure(options => { options.Kind = DateTimeKind.Utc; }); + } public override void OnApplicationInitialization(ApplicationInitializationContext context) { diff --git a/src/EventHub.HttpApi/Controllers/EventHubController.cs b/src/EventHub.HttpApi/Controllers/EventHubController.cs index cd809a1..118b647 100644 --- a/src/EventHub.HttpApi/Controllers/EventHubController.cs +++ b/src/EventHub.HttpApi/Controllers/EventHubController.cs @@ -3,13 +3,11 @@ using Volo.Abp.AspNetCore.Mvc; namespace EventHub.Controllers { - /* Inherit your controllers from this class. - */ - public abstract class EventHubController : AbpController + public abstract class EventHubController : AbpControllerBase { protected EventHubController() { LocalizationResource = typeof(EventHubResource); } } -} \ No newline at end of file +} diff --git a/src/EventHub.HttpApi/Controllers/Events/EventController.cs b/src/EventHub.HttpApi/Controllers/Events/EventController.cs new file mode 100644 index 0000000..07f1c40 --- /dev/null +++ b/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 CreateAsync([FromForm] CreateEventDto input) + { + return await _eventAppService.CreateAsync(input); + } + + [HttpGet] + public async Task> GetListAsync(EventListFilterDto input) + { + return await _eventAppService.GetListAsync(input); + } + + [HttpGet] + [Route("draft-events-by-user-id")] + public async Task> GetDraftEventsByCurrentUser() + { + return await _eventAppService.GetDraftEventsByCurrentUser(); + } + + [HttpGet] + [Route("by-url-code/{urlCode}")] + public async Task GetByUrlCodeAsync(string urlCode) + { + return await _eventAppService.GetByUrlCodeAsync(urlCode); + } + + [HttpGet] + [Route("location/{id}")] + public async Task GetLocationAsync(Guid id) + { + return await _eventAppService.GetLocationAsync(id); + } + + [HttpGet] + [Route("lookup/countries")] + public async Task> GetCountriesLookupAsync() + { + return await _eventAppService.GetCountriesLookupAsync(); + } + + [HttpGet] + [Route("is-event-owner/{id}")] + public async Task 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 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 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; + } + } +} diff --git a/src/EventHub.HttpApi.Host/Controllers/Events/Registrations/EventRegistrationController.cs b/src/EventHub.HttpApi/Controllers/Events/Registrations/EventRegistrationController.cs similarity index 94% rename from src/EventHub.HttpApi.Host/Controllers/Events/Registrations/EventRegistrationController.cs rename to src/EventHub.HttpApi/Controllers/Events/Registrations/EventRegistrationController.cs index 8eb8341..da392a0 100644 --- a/src/EventHub.HttpApi.Host/Controllers/Events/Registrations/EventRegistrationController.cs +++ b/src/EventHub.HttpApi/Controllers/Events/Registrations/EventRegistrationController.cs @@ -12,7 +12,7 @@ namespace EventHub.Controllers.Events.Registrations [Area("eventhub")] [ControllerName("EventRegistration")] [Route("api/eventhub/event-registration")] - public class EventRegistrationController : AbpController, IEventRegistrationAppService + public class EventRegistrationController : EventHubController, IEventRegistrationAppService { private readonly IEventRegistrationAppService _eventRegistrationAppService; @@ -55,4 +55,4 @@ namespace EventHub.Controllers.Events.Registrations return await _eventRegistrationAppService.IsPastEventAsync(eventId); } } -} \ No newline at end of file +} diff --git a/src/EventHub.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs b/src/EventHub.HttpApi/Controllers/Organizations/Memberships/OrganizationMembershipController.cs similarity index 94% rename from src/EventHub.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs rename to src/EventHub.HttpApi/Controllers/Organizations/Memberships/OrganizationMembershipController.cs index ac1d710..500cdd4 100644 --- a/src/EventHub.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs +++ b/src/EventHub.HttpApi/Controllers/Organizations/Memberships/OrganizationMembershipController.cs @@ -12,7 +12,7 @@ namespace EventHub.Controllers.Organizations.Memberships [Area("eventhub")] [ControllerName("OrganizationMembership")] [Route("api/eventhub/organization-membership")] - public class OrganizationMembershipController : AbpController, IOrganizationMembershipAppService + public class OrganizationMembershipController : EventHubController, IOrganizationMembershipAppService { private readonly IOrganizationMembershipAppService _organizationMembershipAppService; @@ -49,4 +49,4 @@ namespace EventHub.Controllers.Organizations.Memberships return await _organizationMembershipAppService.GetMembersAsync(input); } } -} \ No newline at end of file +} diff --git a/src/EventHub.HttpApi.Host/Controllers/Organizations/OrganizationController.cs b/src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs similarity index 97% rename from src/EventHub.HttpApi.Host/Controllers/Organizations/OrganizationController.cs rename to src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs index 8b4a7b4..ecd383f 100644 --- a/src/EventHub.HttpApi.Host/Controllers/Organizations/OrganizationController.cs +++ b/src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs @@ -14,7 +14,7 @@ namespace EventHub.Controllers.Organizations [Area("eventhub")] [ControllerName("Organization")] [Route("api/eventhub/organization")] - public class OrganizationController : AbpController, IOrganizationAppService + public class OrganizationController : EventHubController, IOrganizationAppService { private readonly IOrganizationAppService _organizationAppService; private readonly IVirtualFileProvider _virtualFileProvider; @@ -86,4 +86,4 @@ namespace EventHub.Controllers.Organizations return remoteStreamContent; } } -} \ No newline at end of file +} diff --git a/src/EventHub.HttpApi.Host/Controllers/Users/UserController.cs b/src/EventHub.HttpApi/Controllers/Users/UserController.cs similarity index 62% rename from src/EventHub.HttpApi.Host/Controllers/Users/UserController.cs rename to src/EventHub.HttpApi/Controllers/Users/UserController.cs index 5364c88..de301b3 100644 --- a/src/EventHub.HttpApi.Host/Controllers/Users/UserController.cs +++ b/src/EventHub.HttpApi/Controllers/Users/UserController.cs @@ -1,17 +1,17 @@ +using System.Collections.Generic; using System.Threading.Tasks; using EventHub.Members; using EventHub.Users; using Microsoft.AspNetCore.Mvc; using Volo.Abp; -using Volo.Abp.AspNetCore.Mvc; namespace EventHub.Controllers.Users { [RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] [Area("eventhub")] [ControllerName("User")] - [Route("/api/eventhub/user")] - public class UserController : AbpController, IUserAppService + [Route("/api/eventhub/users")] + public class UserController : EventHubController, IUserAppService { private readonly IUserAppService _userAppService; @@ -26,5 +26,13 @@ namespace EventHub.Controllers.Users { return await _userAppService.FindByUserNameAsync(username); } + + [HttpGet] + [Route("contains/{username}")] + public async Task> GetListByUserName(string username) + { + username = username.Trim().ToLower(); + return await _userAppService.GetListByUserName(username); + } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web.Theme/Themes/EventHub/Components/MainNavbar/Default.cshtml b/src/EventHub.Web.Theme/Themes/EventHub/Components/MainNavbar/Default.cshtml index 6ba5752..69a219b 100644 --- a/src/EventHub.Web.Theme/Themes/EventHub/Components/MainNavbar/Default.cshtml +++ b/src/EventHub.Web.Theme/Themes/EventHub/Components/MainNavbar/Default.cshtml @@ -48,9 +48,9 @@ + Create @@ -142,4 +142,4 @@ return route.Split("/")[1].EnsureStartsWith('/').EnsureEndsWith('/'); } -} \ No newline at end of file +} diff --git a/src/EventHub.Web.Theme/Themes/EventHub/Layouts/Application.cshtml b/src/EventHub.Web.Theme/Themes/EventHub/Layouts/Application.cshtml index 8e55ae1..42ae264 100644 --- a/src/EventHub.Web.Theme/Themes/EventHub/Layouts/Application.cshtml +++ b/src/EventHub.Web.Theme/Themes/EventHub/Layouts/Application.cshtml @@ -44,7 +44,6 @@ @pageTitle - @* TODO(berkansasmaz): Get SEO content from the marketing team *@ @@ -88,4 +87,4 @@ @await RenderSectionAsync("scripts", false) @await Component.InvokeLayoutHookAsync(LayoutHooks.Body.Last, StandardLayouts.Application) - \ No newline at end of file + diff --git a/src/EventHub.Web.Theme/wwwroot/themes/eventhub/style.css b/src/EventHub.Web.Theme/wwwroot/themes/eventhub/style.css index bc1f35f..1f1efe7 100644 --- a/src/EventHub.Web.Theme/wwwroot/themes/eventhub/style.css +++ b/src/EventHub.Web.Theme/wwwroot/themes/eventhub/style.css @@ -57,6 +57,10 @@ p { word-wrap: break-word; } +textarea { + height: 90px!important; +} + /*NAVBAR*/ .navbar { 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; color: #00044A; font-size: 16px; @@ -853,7 +857,7 @@ p .text-primary { } .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; background: none; 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*/ \ No newline at end of file +/*This area was made to be able to navigate between pages. It should be removed from the project*/ diff --git a/src/EventHub.Web/Controllers/EventController.cs b/src/EventHub.Web/Controllers/EventController.cs index fcc385b..8ee5d4c 100644 --- a/src/EventHub.Web/Controllers/EventController.cs +++ b/src/EventHub.Web/Controllers/EventController.cs @@ -6,7 +6,6 @@ using Volo.Abp.AspNetCore.Mvc; namespace EventHub.Web.Controllers { - [Route("api/event")] public class EventController : AbpController { private readonly IEventAppService _eventAppService; @@ -17,7 +16,6 @@ namespace EventHub.Web.Controllers } [HttpGet] - [Route("get-list")] public async Task GetList(EventListFilterDto input) { ViewData.Model = (await _eventAppService.GetListAsync(input)).Items.ToList(); @@ -29,4 +27,4 @@ namespace EventHub.Web.Controllers }; } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/Controllers/EventRegistrationController.cs b/src/EventHub.Web/Controllers/EventRegistrationController.cs index 8548ffb..80cfd8c 100644 --- a/src/EventHub.Web/Controllers/EventRegistrationController.cs +++ b/src/EventHub.Web/Controllers/EventRegistrationController.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using EventHub.Events.Registrations; -using EventHub.Web.Pages.Events.Components.RegistrationArea; using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc; @@ -29,14 +28,5 @@ namespace EventHub.Web.Controllers await _eventRegistrationAppService.UnregisterAsync(eventId); return NoContent(); } - - [HttpGet] - public IActionResult Widget(Guid eventId) - { - return ViewComponent( - typeof(RegistrationAreaViewComponent), - new {eventId} - ); - } } } diff --git a/src/EventHub.Web/Controllers/OrganizationController.cs b/src/EventHub.Web/Controllers/OrganizationController.cs index 1ac5474..a536a81 100644 --- a/src/EventHub.Web/Controllers/OrganizationController.cs +++ b/src/EventHub.Web/Controllers/OrganizationController.cs @@ -6,7 +6,6 @@ using Volo.Abp.AspNetCore.Mvc; namespace EventHub.Web.Controllers { - [Route("api/organization")] public class OrganizationController : AbpController { private readonly IOrganizationAppService _organizationAppService; @@ -17,7 +16,6 @@ namespace EventHub.Web.Controllers } [HttpGet] - [Route("get-list")] public async Task GetList(OrganizationListFilterDto input) { ViewData.Model = (await _organizationAppService.GetListAsync(input)).Items.ToList(); @@ -29,4 +27,4 @@ namespace EventHub.Web.Controllers }; } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/Controllers/OrganizationMembershipController.cs b/src/EventHub.Web/Controllers/OrganizationMembershipController.cs index 7e05755..9cd12ef 100644 --- a/src/EventHub.Web/Controllers/OrganizationMembershipController.cs +++ b/src/EventHub.Web/Controllers/OrganizationMembershipController.cs @@ -2,7 +2,6 @@ using System; using System.Linq; using System.Threading.Tasks; using EventHub.Organizations.Memberships; -using EventHub.Web.Pages.Organizations.Components.JoinArea; using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc; @@ -18,7 +17,6 @@ namespace EventHub.Web.Controllers } [HttpGet] - [Route("get-list")] public async Task GetList(OrganizationMemberListFilterDto input) { ViewData.Model = (await _organizationMembershipAppService.GetMembersAsync(input)).Items.ToList(); @@ -43,14 +41,5 @@ namespace EventHub.Web.Controllers await _organizationMembershipAppService.LeaveAsync(organizationId); return NoContent(); } - - [HttpGet] - public IActionResult Widget(Guid organizationId) - { - return ViewComponent( - typeof(JoinAreaViewComponent), - new {organizationId} - ); - } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/Controllers/WidgetsController.cs b/src/EventHub.Web/Controllers/WidgetsController.cs index be191a4..8c4d77a 100644 --- a/src/EventHub.Web/Controllers/WidgetsController.cs +++ b/src/EventHub.Web/Controllers/WidgetsController.cs @@ -1,6 +1,9 @@ using System; 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.RegistrationArea; +using EventHub.Web.Pages.Organizations.Components.JoinArea; using EventHub.Web.Pages.Organizations.Components.MembersArea; using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc; @@ -47,5 +50,36 @@ namespace EventHub.Web.Controllers 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 + } + ); + } } } diff --git a/src/EventHub.Web/EventHubWebAutoMapperProfile.cs b/src/EventHub.Web/EventHubWebAutoMapperProfile.cs index 014ea33..813754d 100644 --- a/src/EventHub.Web/EventHubWebAutoMapperProfile.cs +++ b/src/EventHub.Web/EventHubWebAutoMapperProfile.cs @@ -1,6 +1,7 @@ using AutoMapper; using EventHub.Events; using EventHub.Organizations; +using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea; using EventHub.Web.Pages.Organizations; using Volo.Abp.AutoMapper; using EditPageModel = EventHub.Web.Pages.Events.EditPageModel; @@ -12,12 +13,11 @@ namespace EventHub.Web public EventHubWebAutoMapperProfile() { CreateMap(); - CreateMap() + CreateMap() .Ignore(x => x.CoverImageStreamContent); CreateMap(); CreateMap(); - CreateMap(); - CreateMap(); + CreateMap(); } } } diff --git a/src/EventHub.Web/EventHubWebModule.cs b/src/EventHub.Web/EventHubWebModule.cs index 05f9382..2202976 100644 --- a/src/EventHub.Web/EventHubWebModule.cs +++ b/src/EventHub.Web/EventHubWebModule.cs @@ -89,23 +89,7 @@ namespace EventHub.Web ConfigureRazorPageOptions(); ConfigurePremiumPlanInfo(context, configuration); } - - private void ConfigurePremiumPlanInfo(ServiceConfigurationContext context, IConfiguration configuration) - { - context.Services.AddOptions() - .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() { Configure(options => @@ -250,6 +234,22 @@ namespace EventHub.Web .PersistKeysToStackExchangeRedis(redis, "EventHub-Protection-Keys"); } + private void ConfigurePremiumPlanInfo(ServiceConfigurationContext context, IConfiguration configuration) + { + context.Services.AddOptions() + .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) { var app = context.GetApplicationBuilder(); diff --git a/src/EventHub.Web/Pages/Empty.cshtml b/src/EventHub.Web/Pages/Empty.cshtml deleted file mode 100644 index 5db1274..0000000 --- a/src/EventHub.Web/Pages/Empty.cshtml +++ /dev/null @@ -1,853 +0,0 @@ -@page "/test" -@using EventHub.Web.Pages -@model EventHub.Web.Pages.Empty - -
-

Create an Event

-

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.

-
-
-
-
- - - - - - - -

Create a New Event

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam utnulla consequat, tempus sapien in.

- Create -
-
-
-
-
-
- - - - - - -

Drafts

-
-
- -
- Last edit: 01/01/2020 -
-
-
-
-
- -
- Last edit: 01/01/2020 -
-
-
-
-
- -
- Last edit: 01/01/2020 -
-
-
-
-
- -
- Last edit: 01/01/2020 -
-
-
-
-
-
-
- - -
-
-
    -
  • 1New Event
  • -
  • 2Add Track
  • -
  • 3Add Session
  • -
-
-
-
-
-
- - -
-
-
-
-

Create a New Event

-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
-

Cover Image

-
-
-
- -
-
-
-
-

Photo size should be 1050x596 and max 5 MB.

- - - -
-
-
-
-
- -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
- -
-
-
-
-
- - -
-
-
    -
  • 1New Event
  • -
  • 2Add Track
  • -
  • 3Add Session
  • -
-
-
-
-
-
- - -
-
-
-
-

Add Track

-
-
-
- There is a track name here - Edit -
-
-
-
- There is a track name here - Edit -
-
-
-
- There is a track name here - Edit -
-
-
- -
-
-
- - -
-
-
- - -
-
-
    -
  • 1New Event
  • -
  • 2Add Track
  • -
  • 3Add Session
  • -
-
-
-
-
-
- - -
-
-
-
-

Add Session

- - - - - - - - - - - - - - - - - - -
Track 1Track 1Track 1Track 1
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan - Edit -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan - Edit -
- -
- - -
- Session Title - 09:00 - 10:00 | English - Berkan Şaşmaz - Edit -
- -
- -
-
- -
-
-
- - -
-
-
-
-

Event Preview

-
-
- Organization Name: -
-
- Volosoft -
-
-
-
- Title: -
-
- My pretty cool event -
-
-
-
- Start Date: -
-
- 10/08/2025 - 09:00 -
-
-
-
- End Date: -
-
- 10/08/2025 - 10:00 -
-
-
-
- Event’s Explanation: -
-
-

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.

-
-
-
-
- Cover Image: -
-
- -
-
-
-
- Country: -
-
- Turkey -
-
-
-
- City: -
-
- Istanbul -
-
-
-
- Language: -
-
- English -
-
-
-
- Capacity: -
-
- 100+ -
-
-
-
- Event Type: -
-
- Online -
-
-
-
- Tracks: -
-
- 4 -
-
-
-
- Sessions: -
-
- - - - - - - - - - - - - - - - - - - -
Track 1Track 1Track 1Track 1
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
- Session Title - 09:00 - 10:00 | English - Halil İbrahim Kalkan -
-
-
- -
-
-
-
- - - - - - - - - - - - - - - -
-

Eventhub Pricing List

-

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.

-
-
-
-
-

Normal Account

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam utnulla consequat, tempus sapien in.

-
- $ - 0 -
-
    -
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • -
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • -
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • -
-
-
-
-
-
-
-

Premium Account

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam utnulla consequat, tempus sapien in.

-
- $ - 60 -
-
    -
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • -
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • -
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit adipiscing elit.
  • -
  • Lorem ipsum adipiscing elit dolor sit amet, consectetur adipiscing elit.
  • -
  • Lorem adipiscing elit adipiscing elitipsum dolor sit amet, consectetur adipiscing elit.
  • -
  • Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  • -
- Upgrade to Premium -
-
-
- -

Frequently Asked Questions

- -
-
-
-
-
-
-
-

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. -

-
-
-
-
-
-
-
-
-
-

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. -

-
-
-
-
-
-
-
-
-
-

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. -

-
-
-
-
-
-
-
-
-
-

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. -

-
-
-
-
-
-
- - -
-

Title Cart

-
-
-
-
-

Product Detail

-

Premium Account: Volosoft Company

-

Start Date: 02/11/2020

-

End Date: 02/11/2020

-
-
-
-
-
-
-

Summary

-
-

Premium Licence 02/11/2020

-
-

TOTAL 02/11/2020

-
- Checkout -
-
-
-
-
- - - diff --git a/src/EventHub.Web/Pages/Empty.cshtml.cs b/src/EventHub.Web/Pages/Empty.cshtml.cs deleted file mode 100644 index 8c3ad89..0000000 --- a/src/EventHub.Web/Pages/Empty.cshtml.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace EventHub.Web.Pages -{ - public class Empty : EventHubPageModel - { - public void OnGet() - { - - } - } -} \ No newline at end of file diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/CreateOrEditEventAreaViewComponent.cs b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/CreateOrEditEventAreaViewComponent.cs new file mode 100644 index 0000000..af76d49 --- /dev/null +++ b/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 InvokeAsync( + string eventUrlCode, + ProgressStepType stepType = ProgressStepType.Event) + { + NewEventViewModel model = null; + if (!eventUrlCode.IsNullOrWhiteSpace()) + { + var @event = await _eventAppService.GetByUrlCodeAsync(eventUrlCode); + model = ObjectMapper.Map(@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 GetOrganizationNameAsync(Guid organizationId) + { + var organizationList = (await _organizationAppService.GetOrganizationsByUserIdAsync(_currentUser.GetId())).Items; + + return organizationList.Single(x => x.Id == organizationId).Name; + } + + private async Task> 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 GetCountryNameAsync(Guid countryId) + { + var countries = await _eventAppService.GetCountriesLookupAsync(); + + return countries.Single(x => x.Id == countryId).Name; + } + + private async Task> GetCountriesSelectItemAsync() + { + var result = await _eventAppService.GetCountriesLookupAsync(); + + return result.Select( + country => new SelectListItem + { + Value = country.Id.ToString(), + Text = country.Name + } + ).ToList(); + } + + private List 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 Tracks { get; set; } + + public NewEventViewModel() + { + Tracks = new List(); + } + } + + public enum ProgressStepType : byte + { + Event = 0, + Track, + Session, + Preview + } +} diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/Default.cshtml b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/Default.cshtml new file mode 100644 index 0000000..9d9a1e3 --- /dev/null +++ b/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(""); + } +} + +
+ +
+
+ +
+
+ +
+
+ +
+ + + diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_eventSection.cshtml b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_eventSection.cshtml new file mode 100644 index 0000000..3aba351 --- /dev/null +++ b/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 UrlOptions + +@{ + var organizations = new List(); + var countries = new List(); + var languages = new List(); + var eventId = ViewData["EventId"] as Guid?; + + @if (ViewData["StepType"]!.ToString() == CreateOrEditEventAreaViewComponent.ProgressStepType.Event.ToString()) + { + organizations = ViewData["Organizations"] as List; + if (organizations is null || !organizations.Any()) + { + throw new BadHttpRequestException("There is no organization"); + } + + countries = ViewData["Countries"] as List; + if (countries is null || !countries.Any()) + { + throw new BadHttpRequestException("There is no country"); + } + + languages = ViewData["Languages"] as List; + if (languages is null || !languages.Any()) + { + throw new BadHttpRequestException("There is no language"); + } + } +} + + +@if (Model is not null) +{ +
+
+
+
+
+
+

@(eventId.HasValue ? "Edit" : "Create New") Event

+
+
+
+
+ + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ + +
+
+
+

Cover Image

+
+
+ @if (eventId.HasValue) + { +
+ + } + else + { +
+ } + +
+
+
+

For horizontal format 600x400 sized upload images.

+ + + +
+
+
+
+
+ + +
+ +
+
+ + + +
+
+
+ + +
+
+
+ + + +
+
+
+ + +
+
+ +
+
+
+
+
+
+
+
+
+} +else +{ + +} diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_modelNullErrorSection.cshtml b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_modelNullErrorSection.cshtml new file mode 100644 index 0000000..df99d63 --- /dev/null +++ b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_modelNullErrorSection.cshtml @@ -0,0 +1,3 @@ +
+

An unknown error has occurred!

+
diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_previewSection.cshtml b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_previewSection.cshtml new file mode 100644 index 0000000..18d55ab --- /dev/null +++ b/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 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) +{ +
+
+
+
+

Event Preview

+
+
+ Organization Name: +
+
+ @organizationName +
+
+
+
+ Title: +
+
+ @Model.Title +
+
+
+
+ Start Date: +
+
+ @Model.StartTime.ToShortDateString() - @Model.StartTime.ToShortTimeString() +
+
+
+
+ End Date: +
+
+ @Model.EndTime.ToShortDateString() - @Model.EndTime.ToShortTimeString() +
+
+
+
+ Event’s Explanation: +
+
+

@Model.Description

+
+
+
+
+ Cover Image: +
+
+
+
+
+ @if (Model.IsOnline != null && Model.IsOnline!.Value) + { +
+
+ Event Type: +
+
+ Online +
+
+ @if (!Model.OnlineLink.IsNullOrWhiteSpace()) + { +
+
+ Url: +
+
+ @Model.OnlineLink +
+
+ } + } + else if (Model.CountryId.HasValue && !Model.City.IsNullOrEmpty()) + { +
+
+ Country: +
+
+ @countryName +
+
+
+
+ City: +
+
+ @Model.City +
+
+ } + @if (!Model.Language.IsNullOrWhiteSpace()) + { +
+
+ Language: +
+
+ @Model.Language +
+
+ } + @if (Model.Capacity.HasValue) + { +
+
+ Capacity: +
+
+ @Model.Capacity +
+
+ } + @if (Model.Tracks.Any()) + { +
+
+ Tracks: +
+
+ @Model.Tracks.Count +
+
+
+
+ Sessions: +
+
+ + + + @foreach (var track in Model.Tracks) + { + + } + + + + + @foreach (var track in Model.Tracks) + { + + } + + +
@track.Name
+ @if (track.Sessions.Any()) + { + @foreach (var session in track.Sessions) + { +
+ @session.Title.TruncateWithPostfix(55) + @EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(session.StartTime), TimeOnly.FromDateTime(session.EndTime)) | @session.Language + @foreach (var speaker in session.Speakers) + { + @speaker.UserName + } +
+ } + } + else + { +
+ There is no session! + Tracks without Session are not visible after the event is published. +
+ } +
+ } +
+ +
+
+
+} +else +{ + +} + diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_progressSection.cshtml b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_progressSection.cshtml new file mode 100644 index 0000000..77c0b3f --- /dev/null +++ b/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"; +} + +
+
+
    +
  • 1@prefixOfStep Event
  • +
  • 2@prefixOfStep Track
  • +
  • 3@prefixOfStep Session
  • +
+
+
+
+
+
diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_sessionSection.cshtml b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_sessionSection.cshtml new file mode 100644 index 0000000..e86e863 --- /dev/null +++ b/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 + +@{ + var languages = new List(); + @if (ViewData["StepType"]!.ToString() == CreateOrEditEventAreaViewComponent.ProgressStepType.Session.ToString()) + { + languages = ViewData["Languages"] as List; + 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(); + } +} + + + +@if (Model is not null) +{ +
+
+
+
+

Add Session

+ + + + @foreach (var track in Model) + { + + } + + + + + @foreach (var track in Model) + { + + } + + + +
+ @track.Name.TruncateWithPostfix(20) +
+ @foreach (var session in track.Sessions) + { + + + + + + x.UserName)).EnsureEndsWith(',').EnsureEndsWith(' ') : "")"/> + +
+ @session.Title.TruncateWithPostfix(25) + @EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(session.StartTime), TimeOnly.FromDateTime(session.EndTime)) | @session.Language + @foreach (var speaker in session.Speakers) + { + @speaker.UserName + } + + + + Edit + + + + + Delete + + + +
+ } + +
+
+ +
+
+
+ + + + + + +} +else +{ + +} diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_trackSection.cshtml b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/_trackSection.cshtml new file mode 100644 index 0000000..dda3496 --- /dev/null +++ b/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 + + + +@if (Model is not null) +{ +
+
+
+
+

Add Track

+
+ @foreach (var track in Model) + { +
+
+ @track.Name + + + Edit + + + Delete + + +
+
+ } +
+ +
+
+
+ + +
+
+
+ + @* Add Track Modal *@ + + + @* Edit Track Modal *@ + +}else +{ + +} diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.css b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.css new file mode 100644 index 0000000..70ebb24 --- /dev/null +++ b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.css @@ -0,0 +1,3 @@ +.awesomplete { + display: inline!important; +} diff --git a/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.js b/src/EventHub.Web/Pages/Events/Components/CreateOrEditEventArea/create-or-edit-event-area.js new file mode 100644 index 0000000..4626692 --- /dev/null +++ b/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, + }; + } +})(); diff --git a/src/EventHub.Web/Pages/Events/Components/EventsArea/_eventListSection.cshtml b/src/EventHub.Web/Pages/Events/Components/EventsArea/_eventListSection.cshtml index 587f8c1..cfa7c3d 100644 --- a/src/EventHub.Web/Pages/Events/Components/EventsArea/_eventListSection.cshtml +++ b/src/EventHub.Web/Pages/Events/Components/EventsArea/_eventListSection.cshtml @@ -23,7 +23,7 @@
@eventDto.Title
- @eventDto.StartTime.ToString("hh tt", CultureInfo.InvariantCulture) - @eventDto.EndTime.ToString("hh tt", CultureInfo.InvariantCulture) | @EventDateHelper.GetLocationInfo(eventDto) + @EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(eventDto.StartTime), TimeOnly.FromDateTime(eventDto.EndTime)) | @EventDateHelper.GetLocationInfo(eventDto)

@eventDto.Description

Learn More @@ -33,4 +33,4 @@
-} \ No newline at end of file +} diff --git a/src/EventHub.Web/Pages/Events/Components/RegistrationArea/RegistrationAreaViewComponent.cs b/src/EventHub.Web/Pages/Events/Components/RegistrationArea/RegistrationAreaViewComponent.cs index 0582c11..6318f7d 100644 --- a/src/EventHub.Web/Pages/Events/Components/RegistrationArea/RegistrationAreaViewComponent.cs +++ b/src/EventHub.Web/Pages/Events/Components/RegistrationArea/RegistrationAreaViewComponent.cs @@ -10,7 +10,7 @@ namespace EventHub.Web.Pages.Events.Components.RegistrationArea { [Widget( AutoInitialize = true, - RefreshUrl = "/EventRegistration/Widget", + RefreshUrl = "/Widgets/RegistrationArea", ScriptFiles = new[] {"/Pages/Events/Components/RegistrationArea/registration-area.js"} )] public class RegistrationAreaViewComponent : AbpViewComponent diff --git a/src/EventHub.Web/Pages/Events/Detail.cshtml b/src/EventHub.Web/Pages/Events/Detail.cshtml index 11faeaf..c589a9f 100644 --- a/src/EventHub.Web/Pages/Events/Detail.cshtml +++ b/src/EventHub.Web/Pages/Events/Detail.cshtml @@ -2,7 +2,6 @@ @inject IHtmlLocalizer L @using EventHub.Localization @using EventHub.Web -@using EventHub.Web.Helpers @using EventHub.Web.Pages.Events @using EventHub.Web.Pages.Events.Components.AttendeesArea @using EventHub.Web.Pages.Events.Components.EventsArea @@ -37,7 +36,7 @@ ViewBag.OgImage = coverImageUrl; } -
+
@@ -72,17 +71,79 @@
-
-
Event Description
-

- @Model.Event.Description -

- -
Date
-

- @EventDateHelper.GetDateRangeText(Model.Event.StartTime, Model.Event.EndTime) -

- @await Component.InvokeAsync(typeof(AttendeesAreaViewComponent), new {eventId = Model.Event.Id}) +
+
+
+ @if (Model.Event.Tracks.Any()) + { + + +
Event Description
+

+ @Model.Event.Description +

+ +
Date
+

+ @EventDateHelper.GetDateRangeText(Model.Event.StartTime, Model.Event.EndTime) +

+ @await Component.InvokeAsync(typeof(AttendeesAreaViewComponent), new { eventId = Model.Event.Id }) +
+ + + + + @foreach (var track in Model.Event.Tracks) + { + @if (track.Sessions.Any()) + { + + } + } + + + + + @foreach (var track in Model.Event.Tracks) + { + @if (track.Sessions.Any()) + { + + } + } + + +
@track.Name
+ @foreach (var session in track.Sessions) + { +
+ @session.Title.TruncateWithPostfix(55) + @EventDateHelper.GetTimeRangeText(TimeOnly.FromDateTime(session.StartTime), TimeOnly.FromDateTime(session.EndTime)) | @session.Language + @foreach (var speaker in session.Speakers) + { + @speaker.UserName + } +
+ } +
+
+
+ } + else + { +
Event Description
+

+ @Model.Event.Description +

+ +
Date
+

+ @EventDateHelper.GetDateRangeText(Model.Event.StartTime, Model.Event.EndTime) +

+ @await Component.InvokeAsync(typeof(AttendeesAreaViewComponent), new { eventId = Model.Event.Id }) + } +
+
@@ -102,4 +163,4 @@ MaxResultCount = 6, isPagination = false }) -
\ No newline at end of file +
diff --git a/src/EventHub.Web/Pages/Events/Edit.cshtml b/src/EventHub.Web/Pages/Events/Edit.cshtml index 42e6f56..ef7ce64 100644 --- a/src/EventHub.Web/Pages/Events/Edit.cshtml +++ b/src/EventHub.Web/Pages/Events/Edit.cshtml @@ -1,135 +1,19 @@ @page "/event/edit/{url}" -@inject IHtmlLocalizer L -@using EventHub.Localization -@using EventHub.Web -@using Microsoft.AspNetCore.Mvc.Localization -@using Microsoft.Extensions.Options +@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea @model EventHub.Web.Pages.Events.EditPageModel -@inject IOptions UrlOptions @section scripts { - + } -
-
-
-
- -
-
-

Edit Event

-
-
- - -
-
- - -
-
-
-
- - - -
-
-
-
- - - -
-
-
-
- - - -
-
-
-
- - -
-
- -
-

Cover Image

-
-
-
- -
-
-
-
-

For horizontal format 600x300 sized upload images.

- - - -
-
-
-
-
- - -
- -
-
- - - -
-
-
- - -
-
-
- - - -
-
-
- - -
- +@section styles { + +} -
- -
-
-
-
-
-
-
-
-
\ No newline at end of file +
+ @await Component.InvokeAsync(typeof(CreateOrEditEventAreaViewComponent), new + { + eventUrlCode = Model.UrlCode, + stepType = CreateOrEditEventAreaViewComponent.ProgressStepType.Event + }) +
diff --git a/src/EventHub.Web/Pages/Events/Edit.cshtml.cs b/src/EventHub.Web/Pages/Events/Edit.cshtml.cs index 2637acb..a08556b 100644 --- a/src/EventHub.Web/Pages/Events/Edit.cshtml.cs +++ b/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.Organizations; -using EventHub.Web.Helpers; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Rendering; -using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; -using Volo.Abp.Content; -using Volo.Abp.Users; namespace EventHub.Web.Pages.Events { public class EditPageModel : EventHubPageModel { - [BindProperty(SupportsGet = true)] - public new string Url { get; set; } + public string UrlCode { get; set; } - [BindProperty] - public EditEventViewModel Event { get; set; } - - public List Organizations { get; private set; } - public List Countries { get; private set; } - public List 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() + public EditPageModel() { - var urlCode = EventUrlCodeHelper.GetCodeFromUrl(Url); - var eventDetailDto = await _eventAppService.GetByUrlCodeAsync(urlCode); - - Event = ObjectMapper.Map(eventDetailDto); - await FillOrganizationsAsync(); - await FillCountriesAsync(); - FillLanguages(); } - public async Task OnPostAsync() + public void OnGet(string url) { - try - { - ValidateModel(); - - var updateEventDto = ObjectMapper.Map(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; } + UrlCode = EventUrlCodeHelper.GetCodeFromUrl(url); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/Pages/Events/Edit.js b/src/EventHub.Web/Pages/Events/Edit.js deleted file mode 100644 index 75235c3..0000000 --- a/src/EventHub.Web/Pages/Events/Edit.js +++ /dev/null @@ -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); - } - } - } -}); \ No newline at end of file diff --git a/src/EventHub.Web/Pages/Events/EventDateHelper.cs b/src/EventHub.Web/Pages/Events/EventDateHelper.cs index 0aab7c7..2b8605a 100644 --- a/src/EventHub.Web/Pages/Events/EventDateHelper.cs +++ b/src/EventHub.Web/Pages/Events/EventDateHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Text; using EventHub.Events; @@ -24,6 +25,16 @@ namespace EventHub.Web.Pages.Events 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) { if (eventItem.IsOnline) diff --git a/src/EventHub.Web/Pages/Events/New.cshtml b/src/EventHub.Web/Pages/Events/New.cshtml index 28d668e..24048a6 100644 --- a/src/EventHub.Web/Pages/Events/New.cshtml +++ b/src/EventHub.Web/Pages/Events/New.cshtml @@ -1,145 +1,99 @@ -@page "/events/new" +@page "/events/new/{eventUrlCode?}" @inject IHtmlLocalizer L @using EventHub.Localization +@using EventHub.Web.Pages.Events.Components.CreateOrEditEventArea @using Microsoft.AspNetCore.Mvc.Localization @model EventHub.Web.Pages.Events.NewPageModel @section scripts { - + } -@if (!Model.Organizations.Any()) -{ -
-
-
-
-
-

@L["NewEventYouHaveNoOrganizationYetMessage"]

- @L["CreateAnOrganization"] +@section styles { + +} + +
+ @if (!Model.IsHasOrganizations) + { +
+
+
+
+

@L["NewEventYouHaveNoOrganizationYetMessage"]

+ @L["CreateAnOrganization"] +
-
+
-
-
-} -else -{ -
-
-
-
-
-
-

Create New Event

-
-
-
-
- -
-
-
-
- - - -
-
-
-
- - - -
-
-
-
- - - -
-
-
-
- - -
-
-
-

Cover Image

-
-
-
-
-
-
-

For horizontal format 600x400 sized upload images.

- - - -
-
-
-
-
- - -
- -
-
- - - -
-
-
- - -
-
-
- - - -
-
-
- - -
-
- -
-
-
-
-
-
-
-} \ No newline at end of file +
+ } + else + { + @await Component.InvokeAsync(typeof(CreateOrEditEventAreaViewComponent), new + { + eventUrlCode = Model.EventUrlCode, + stepType = CreateOrEditEventAreaViewComponent.ProgressStepType.Event + }) + } +
diff --git a/src/EventHub.Web/Pages/Events/New.cshtml.cs b/src/EventHub.Web/Pages/Events/New.cshtml.cs index d663a90..37896b1 100644 --- a/src/EventHub.Web/Pages/Events/New.cshtml.cs +++ b/src/EventHub.Web/Pages/Events/New.cshtml.cs @@ -1,180 +1,52 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Globalization; -using System.IO; using System.Linq; using System.Threading.Tasks; using EventHub.Events; using EventHub.Organizations; -using EventHub.Web.Helpers; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Rendering; -using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; -using Volo.Abp.Content; using Volo.Abp.Users; namespace EventHub.Web.Pages.Events { public class NewPageModel : EventHubPageModel { - [BindProperty] - public NewEventViewModel Event { get; set; } + [BindProperty(SupportsGet = true)] + public string EventUrlCode { get; set; } - public List Organizations { get; private set; } - public List Countries { get; private set; } - public List Languages { get; private set; } + [BindProperty(SupportsGet = true)] + public bool IsCreateNew { get; set; } + + public bool IsHasOrganizations { get; set; } + + public List DraftEventList { get; set; } private readonly IEventAppService _eventAppService; private readonly IOrganizationAppService _organizationAppService; public NewPageModel( - IEventAppService eventAppService, + IEventAppService eventAppService, IOrganizationAppService organizationAppService) { _eventAppService = eventAppService; _organizationAppService = organizationAppService; + + DraftEventList = new List(); } public async Task OnGetAsync() { - Event = new NewEventViewModel - { - StartTime = DateTime.Now.ClearTime().AddDays(1).AddHours(19), - EndTime = DateTime.Now.ClearTime().AddDays(1).AddHours(21) - }; + IsHasOrganizations = (await _organizationAppService.GetOrganizationsByUserIdAsync(CurrentUser.GetId())).Items.Any(); - await FillOrganizationsAsync(); - await FillCountriesAsync(); - FillLanguages(); - } - - public async Task OnPostAsync() - { - try + if (!EventUrlCode.IsNullOrWhiteSpace()) { - ValidateModel(); - - var createEventDto = ObjectMapper.Map(Event); - - await using var memoryStream = new MemoryStream(); - if (Event.CoverImageFile != null && Event.CoverImageFile.Length > 0) - { - await Event.CoverImageFile.CopyToAsync(memoryStream); - memoryStream.Position = 0; - - createEventDto.CoverImageStreamContent = new RemoteStreamContent(memoryStream, fileName: Event.CoverImageFile.FileName, contentType: Event.CoverImageFile.ContentType); - } - - var eventDto = await _eventAppService.CreateAsync(createEventDto); - await memoryStream.DisposeAsync(); - - return RedirectToPage("/Events/Detail", new {url = eventDto.UrlCode}); + return; } - catch (Exception exception) + + if (IsHasOrganizations && !IsCreateNew) { - ShowAlert(exception); - await FillOrganizationsAsync(); - await FillCountriesAsync(); - FillLanguages(); - return Page(); + DraftEventList = await _eventAppService.GetDraftEventsByCurrentUser(); } } - - 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; } - } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/Pages/Events/New.js b/src/EventHub.Web/Pages/Events/New.js deleted file mode 100644 index cbb895e..0000000 --- a/src/EventHub.Web/Pages/Events/New.js +++ /dev/null @@ -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); - } - } - } -}); \ No newline at end of file diff --git a/src/EventHub.Web/Pages/Organizations/Components/JoinArea/JoinAreaViewComponent.cs b/src/EventHub.Web/Pages/Organizations/Components/JoinArea/JoinAreaViewComponent.cs index 12a1096..fec6dbb 100644 --- a/src/EventHub.Web/Pages/Organizations/Components/JoinArea/JoinAreaViewComponent.cs +++ b/src/EventHub.Web/Pages/Organizations/Components/JoinArea/JoinAreaViewComponent.cs @@ -10,7 +10,7 @@ namespace EventHub.Web.Pages.Organizations.Components.JoinArea { [Widget( AutoInitialize = true, - RefreshUrl = "/OrganizationMembership/Widget", + RefreshUrl = "/Widgets/JoinArea", ScriptFiles = new[] {"/Pages/Organizations/Components/JoinArea/join-area.js"} )] public class JoinAreaViewComponent : AbpViewComponent @@ -49,4 +49,4 @@ namespace EventHub.Web.Pages.Organizations.Components.JoinArea public bool IsJoined { get; set; } } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml b/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml new file mode 100644 index 0000000..e21c089 --- /dev/null +++ b/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 PremiumPlanInfoOptionsSnapshot + + + + @if (Model.IsPremium && PremiumPlanInfoOptionsSnapshot.Value.IsActive && PremiumPlanInfoOptionsSnapshot.Value.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths))) + { + Premium end date: @Model.PremiumEndDate!.Value.ToShortDateString() + } + + Edit + + + @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))) + { + + } + else + { +
+ +
+ } + } + else + { + + } + } + else + { + + + Premium end date: @Model.PremiumEndDate!.Value.ToShortDateString() + + + } +
+
+
diff --git a/src/EventHub.Web/Pages/Organizations/Profile.cshtml b/src/EventHub.Web/Pages/Organizations/Profile.cshtml index f6b13f8..c34938e 100644 --- a/src/EventHub.Web/Pages/Organizations/Profile.cshtml +++ b/src/EventHub.Web/Pages/Organizations/Profile.cshtml @@ -8,13 +8,14 @@ @using EventHub.Web.PaymentRequests @using Microsoft.AspNetCore.Mvc.Localization @using Microsoft.Extensions.Options +@using Volo.Abp.AspNetCore.Mvc.UI.Packages.SweetAlert2 @using Volo.Abp.Timing @model EventHub.Web.Pages.Organizations.ProfilePageModel @inject IClock Clock @inject IOptions UrlOptions -@inject IOptionsSnapshot PremiumPlanInfoOptionsSnapshot @section scripts { + } @@ -66,44 +67,7 @@ @if (Model.IsOrganizationOwner) { - - Edit - - - @if (PremiumPlanInfoOptionsSnapshot.Value.IsActive) - { - - - @if (Model.Organization.IsPremium) - { - Premium end date: @Model.Organization.PremiumEndDate!.Value.ToShortDateString() - @if (PremiumPlanInfoOptionsSnapshot.Value.IsExtendable && Model.Organization.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths))) - { - - } - } - else - { - - } - - - }else if (Model.Organization.IsPremium) - { - - - Premium end date: @Model.Organization.PremiumEndDate!.Value.ToShortDateString() - - - } + } @await Component.InvokeAsync(typeof(JoinAreaViewComponent), new {organizationId = Model.Organization.Id}) @await Component.InvokeAsync(typeof(MembersAreaViewComponent), @@ -212,4 +176,4 @@
- \ No newline at end of file + diff --git a/src/EventHub.Web/Pages/Organizations/Profile.js b/src/EventHub.Web/Pages/Organizations/Profile.js index d289691..3ca78d0 100644 --- a/src/EventHub.Web/Pages/Organizations/Profile.js +++ b/src/EventHub.Web/Pages/Organizations/Profile.js @@ -1,13 +1,36 @@ $(function () { $('.events > .nav-item > a[data-toggle="tab"]').on('shown.bs.tab', function (e) { var target = $(e.target).attr("href") - if (target === "#UpcomingEvent"){ + if (target === "#UpcomingEvent") { $("a[href='#PastEvent']").removeClass('selected'); $("a[href='#UpcomingEvent']").addClass('selected'); - }else{ + } else { $("a[href='#UpcomingEvent']").removeClass('selected'); $("a[href='#PastEvent']").addClass('selected'); } }); -}); \ No newline at end of file + + 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) + }); + } + }); + } + +}); diff --git a/src/EventHub.Web/Pages/Payment/CustomPostCheckout.cshtml.cs b/src/EventHub.Web/Pages/Payment/CustomPostCheckout.cshtml.cs new file mode 100644 index 0000000..dac8f18 --- /dev/null +++ b/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, + IPaymentUrlBuilder paymentUrlBuilder) + : base( + appService, + paymentWebOptions, + paymentUrlBuilder) + { + } + + public override async Task 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(ExtraProperties.ToString()); + if (organizationPaymentExtraParameterConfiguration is null) + { + throw new BadHttpRequestException(""); + } + + OrganizationName = organizationPaymentExtraParameterConfiguration.OrganizationName; + } +} diff --git a/src/EventHub.Web/Pages/Payment/PostCheckout.cshtml b/src/EventHub.Web/Pages/Payment/PostCheckout.cshtml index 64849f0..4d7047b 100644 --- a/src/EventHub.Web/Pages/Payment/PostCheckout.cshtml +++ b/src/EventHub.Web/Pages/Payment/PostCheckout.cshtml @@ -1,32 +1,15 @@ @page -@using EventHub.Organizations @using EventHub.Web -@using Microsoft.AspNetCore.Http @using Microsoft.Extensions.Options -@using Newtonsoft.Json -@using Payment.Web.Pages.Payment @using Payment.PaymentRequests @using Volo.Abp.Users @inherits Payment.Web.Pages.PaymentPageBase -@model Payment.Web.Pages.Payment.PostCheckoutPageModel +@model EventHub.Web.Pages.Payment.CustomPostCheckoutPageModel @inject IOptions UrlOptions @inject ICurrentUser CurrentUser @{ 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(ExtraProperties.ToString()); - if (organizationPaymentExtraParameterConfiguration is null) - { - throw new BadHttpRequestException(""); - } - var organizationName = organizationPaymentExtraParameterConfiguration.OrganizationName; }
@@ -69,8 +52,8 @@ -
\ No newline at end of file + diff --git a/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml b/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml index 4a68285..3fb9203 100644 --- a/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml +++ b/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml @@ -31,79 +31,42 @@ } - -
- -
Shopping Cart
- - -
- - - - - - - - - - - - - - -
Product Name & DetailsTotal
-
-
-

@Model.PaymentRequest.ProductName

- - @if (!organizationPaymentRequestExtraParameterConfiguration.IsExtend) - { - Target Plan: @OrganizationPlanType.Premium - End date: @DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth).ToShortDateString() - } - else - { - Plan: @OrganizationPlanType.Premium - } -   - -
-
-
@Model.PaymentRequest.Price @Model.PaymentRequest.Currency
-
- -
- - - -
- -
@Model.PaymentRequest.Price @Model.PaymentRequest.Currency
-
-
-
-
-
- - - - -
- @if (Model.PaymentRequest.State != PaymentRequestState.Waiting) - { - Current State: @Model.PaymentRequest.State - @L["PaymentRequestIsNotWaiting"] - } - else - { - @L["Checkout"] - Back to pricing - } -
-
-
-
-
-
-
\ No newline at end of file +
+

Shopping Cart

+
+
+
+
+

Product Detail

+

@OrganizationPlanType.Premium Account: @organizationName.ToUpperInvariant()

+ @if (!organizationPaymentRequestExtraParameterConfiguration.IsExtend) + { +

Start Date: @DateTime.Now.ToShortDateString()

+

End Date: @DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth).ToShortDateString()

+ } + else + { +

Your license will be extended for @planInfo.OnePremiumPeriodAsMonth months.

+ } +
+
+
+
+
+
+

Summary

+
+

Premium Licence @Model.PaymentRequest.Price @Model.PaymentRequest.Currency

+
+

TOTAL @Model.PaymentRequest.Price @Model.PaymentRequest.Currency

+
+
+
+ +
+
+
+
+
+
+
diff --git a/src/EventHub.Web/Pages/Pricing.cshtml b/src/EventHub.Web/Pages/Pricing.cshtml index f96572d..bb995a5 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml +++ b/src/EventHub.Web/Pages/Pricing.cshtml @@ -1,136 +1,113 @@ @page "/pricing/{organizationName}" @using EventHub.Localization +@using EventHub.Web.PaymentRequests @using Microsoft.AspNetCore.Mvc.Localization +@using Microsoft.Extensions.Options @model EventHub.Web.Pages.Pricing @inject IHtmlLocalizer L +@inject IOptionsSnapshot PremiumPlanInfoOptionsSnapshot @section styles { } -
-
- -

- Pricing plans -

-
- Choose the plan best for your organization. -
- -
- -
-
-
Free account
-
-
- $ - 0 -
-
-

1 event per month

-

300 attendees per event

-
-
-
-
-
- - @if(Model.PremiumPlanInfo is not null) - { -
-
-
- @Model.PremiumPlanInfo.PlanType account - @if (Model.Organization.IsPremium) - { - Current Plan - } -
-
-
- $ - @Model.PremiumPlanInfo.Price -
-
-

unlimited events

-

unlimited attendees

-
-
-
- - @if (Model.Organization.IsPremium) - { -
- - - Extend Now -
- } - else - { -
- - - Upgrade To @Model.PremiumPlanInfo.PlanType -
- } -
+
+

Pricing

+

Simple plans for everyone

+
+
+
+
+

Free Account

+

For individuals and small teams trying out EventHub for an unlimited period.

+
+ $ + 0 +
+
    +
  • Unlimited event creation.
  • +
  • Adding unlimited tracks and sessions to events.
  • +
  • Unlimited participant capacity for events.
  • +
+
+
- } -
- @* -

Frequently Asked Questions

- -
-
- -
Dapibus ac facilisis in?
-

- Lorem ipsum dolor sit amet, ius virtute suscipit te. Ius prima euismod consequat eu. -

- -
Cras justo odio?
-

- Etiam vivendo eu sea, purto ponderum mediocritatem at pro. -

- -
Porta ac consectetur ac?
-

- Iuvaret deleniti vulputate nec ne, id vix lucilius legendos deseruisse. -

- -
Ne ornatus albucius ius?
-

- Lorem ipsum dolor sit amet, mea in pertinax hendrerit gloriatur. -

- -
-
- -
Ne ornatus albucius ius?
-

- Lorem ipsum dolor sit amet, mea in pertinax hendrerit gloriatur. -

- -
Quo insolens intellegam dissentiet at?
-

- Ex fugit legimus fuisset per. Ex quidam option diceret ius. -

- -
Ad his assum delenit blandit?
-

- Ne ornatus albucius ius, nostrum dignissim repudiandae an usu. -

- -
Dapibus ac facilisis in?
-

- Lorem ipsum dolor sit amet, ius virtute suscipit te. Ius prima euismod consequat eu. -

- -
+ @if (Model.PremiumPlanInfo is not null) + { +
+
+
+

Premium Account

+

For medium teams and large teams who want to highlight their organization for a year.

+
+ $ + @Model.PremiumPlanInfo.Price +
+
    +
  • Unlimited event creation.
  • +
  • Adding unlimited tracks and sessions to events.
  • +
  • Unlimited participant capacity for events.
  • +
  • Be more prominent in the organization list.
  • +
  • More coming soon.
  • +
+ @if (Model.Organization.IsPremium) + { +
+ + + Extend Now +
+ } + else + { +
+ + + Upgrade to @Model.PremiumPlanInfo.PlanType +
+ } +
+
+
+ } +

Frequently Asked Questions

+
+
+
+
+
+
+
+

With Premium organization, you can reach more people by highlighting your organization. However, for now, it has no different from a normal organization in terms of feature set. +

+
+
+
+
+
+
+
+
+
+ @{ + var canExtendBeforeHowManyMonths = PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths; + } +

@(canExtendBeforeHowManyMonths == PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth ? "You " : "In the last " + canExtendBeforeHowManyMonths + " months of your premium period, you") can renew your plan by going to the Organization detail page. +

+
+
+
+
+
+
+
+
+
+

Don't worry about even if your organization has run out of premium, you can upgrade back to the Premium plan at any time. +

+
+
+
+
- *@ -
-
\ No newline at end of file + diff --git a/src/EventHub.Web/Pages/PrivacyPolicy.cshtml.cs b/src/EventHub.Web/Pages/PrivacyPolicy.cshtml.cs index df4766c..f4e78b4 100644 --- a/src/EventHub.Web/Pages/PrivacyPolicy.cshtml.cs +++ b/src/EventHub.Web/Pages/PrivacyPolicy.cshtml.cs @@ -2,11 +2,11 @@ namespace EventHub.Web.Pages { public class PrivacyPolicy : EventHubPageModel { - public static string LastUpdateDate { get; set; } = "February 23, 2020"; // ToDo: Change LastUpdateDate + public static string LastUpdateDate { get; set; } = "February 23, 2020"; public void OnGet() { } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/abp.resourcemapping.js b/src/EventHub.Web/abp.resourcemapping.js index d5c1afb..d611cc2 100644 --- a/src/EventHub.Web/abp.resourcemapping.js +++ b/src/EventHub.Web/abp.resourcemapping.js @@ -9,5 +9,6 @@ mappings: { "@node_modules/daterangepicker/*": "@libs/date-range-picker/", "@node_modules/owl.carousel/dist/**": "@libs/owl.carousel/", + "@node_modules/awesomplete/**": "@libs/awesomplete/", } -}; \ No newline at end of file +}; diff --git a/src/EventHub.Web/package.json b/src/EventHub.Web/package.json index 1e79505..3f7e21f 100644 --- a/src/EventHub.Web/package.json +++ b/src/EventHub.Web/package.json @@ -4,7 +4,8 @@ "private": true, "dependencies": { "@abp/aspnetcore.mvc.ui.theme.basic": "~5.0.0-rc.1", + "awesomplete": "^1.1.5", "daterangepicker": "^3.1.0", "owl.carousel": "^2.3.4" } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/wwwroot/assets/success.svg b/src/EventHub.Web/wwwroot/assets/success.svg new file mode 100644 index 0000000..987540e --- /dev/null +++ b/src/EventHub.Web/wwwroot/assets/success.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/EventHub.Web/wwwroot/global-styles.css b/src/EventHub.Web/wwwroot/global-styles.css index 5eee6b7..439236b 100644 --- a/src/EventHub.Web/wwwroot/global-styles.css +++ b/src/EventHub.Web/wwwroot/global-styles.css @@ -3,8 +3,9 @@ .border-light { border-color: #E1E5E9 !important; } -.text-info { - color: #6F32E2!important; + +.text-info { + color: #6F32E2 !important; } @@ -34,8 +35,8 @@ .progresses ul li.create-steps.blue { color: #fff; - background-color: #00044A; - border-color: #00044A; + background-color: #00044A; + border-color: #00044A; } .progresses ul li.create-steps span { @@ -44,12 +45,12 @@ left: -30px; font-size: 0.8rem; width: 90px; - color:#bfc6cd; -} + color: #bfc6cd; +} -.progresses ul li.create-steps.blue span { - color:#00044A; -} +.progresses ul li.create-steps.blue span { + color: #00044A; +} .progresses .progress { height: 2px; @@ -59,64 +60,73 @@ background-color: #DCE0E5; border-radius: 10px } -.progress-bar { - background-color: #00044A; + +.progress-bar { + background-color: #00044A; } -.nav-pills .nav-link.active, .nav-pills .show > .nav-link { +.nav-pills .nav-link.active, .nav-pills .show > .nav-link { background-color: #00044A !important; } -.big-square-btn .btn { +.big-square-btn .btn { border: 1px solid rgba(107, 126, 146, 0.20); min-height: 177px; background: rgba(107, 126, 146, 0.05); } -.big-square-btn .btn:hover { + +.big-square-btn .btn:hover { color: #E8345D; background: #fff; } -.big-square-btn .fa-plus { - border: 2px dashed; + +.big-square-btn .fa-plus { + border: 2px dashed; width: 34px; height: 34px; border-radius: 34px; line-height: 30px; } -.track-item { +.track-item { border: 1px solid rgba(107, 126, 146, 0.20); padding: 1.5em; border-radius: 10px; min-height: 177px; margin-bottom: 1em; } -.track-item .track-name{ + +.track-item .track-name { font-weight: 500; font-size: 1.125em; color: #03044a; } + .table > :not(:first-child) { border-top: 1px solid currentColor; -} -.table td, .table th { +} + +.table td, .table th { font-weight: normal; padding: 16px 8px; color: #6B7E92; } + .text-primary { color: #E8345D !important; -} +} + .preview-item { color: #00044A; } -.price small{ +.price small { font-size: 20px; color: #00044A; font-weight: 800; } -.price span{ + +.price span { font-size: 60px; color: #00044A; font-weight: 800; @@ -127,11 +137,13 @@ padding: 0; margin: 0 30px 0 40px; } -.list-check-icon li{ + +.list-check-icon li { margin: 1em 2em; } + .list-check-icon .fa { - color:#E8345D; + color: #E8345D; margin: 0 10px 0 -28px; } @@ -140,16 +152,19 @@ border-color: #6F32E2; color: white; } + .btn-info:hover { background: #662ad6; border-color: #662ad6; color: white; } + .btn-check:focus + .btn-info, .btn-info:focus { background: #662ad6; border-color: #662ad6; color: white; } + .faq.shown { background-color: #fff9f7; } @@ -167,17 +182,17 @@ } .faq-container .faq button.btn.btn-faq.collapsed { - + } -.faq-container .faq button.btn.btn-faq .fas{ +.faq-container .faq button.btn.btn-faq .fas { position: absolute; top: 15px; left: 0px; } -.faq-container .faq button.btn.btn-faq.collapsed .fas{ - color:#E8345D; +.faq-container .faq button.btn.btn-faq.collapsed .fas { + color: #E8345D; transform: rotate(180deg); } @@ -196,14 +211,15 @@ padding: 15px 0 10px 0; } -.btn-check:focus + .btn, .btn:focus { +.btn-check:focus + .btn, .btn:focus { box-shadow: none; } .text-dark { font-weight: 700; color: #03044a !important; -} +} + .modal-icon { display: block; font-size: 64px; @@ -211,6 +227,21 @@ margin-bottom: 30px; color: #6F32E2; } + .rounded-3 { border-radius: 1rem !important; -} \ No newline at end of file +} + +.truncate { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + width: 140px; +} + +.nav-tabs .nav-link { + font-size: 20px; + line-height: 24px; + color: #6B7E92; +} diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/.codeclimate.yml b/src/EventHub.Web/wwwroot/libs/awesomplete/.codeclimate.yml new file mode 100644 index 0000000..5f9c00e --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/.codeclimate.yml @@ -0,0 +1,14 @@ +engines: + eslint: + enabled: true + fixme: + enabled: true +ratings: + paths: + - "**.js" +exclude_paths: +- "**.min.js" +- karma*.conf.js +- prism/**/* +- test/**/* +- .* diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/.eslintrc b/src/EventHub.Web/wwwroot/libs/awesomplete/.eslintrc new file mode 100644 index 0000000..cefc88d --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/.eslintrc @@ -0,0 +1,254 @@ +ecmaFeatures: {} +rules: + no-alert: 0 + no-array-constructor: 0 + no-bitwise: 0 + no-caller: 0 + no-catch-shadow: 0 + no-class-assign: 0 + no-cond-assign: 2 + no-console: 2 + no-const-assign: 0 + no-constant-condition: 2 + no-continue: 0 + no-control-regex: 2 + no-debugger: 2 + no-delete-var: 2 + no-div-regex: 0 + no-dupe-keys: 2 + no-dupe-args: 2 + no-duplicate-case: 2 + no-else-return: 0 + no-empty: 2 + no-empty-character-class: 2 + no-empty-label: 0 + no-eq-null: 0 + no-eval: 0 + no-ex-assign: 2 + no-extend-native: 0 + no-extra-bind: 0 + no-extra-boolean-cast: 2 + no-extra-parens: 0 + no-extra-semi: 2 + no-fallthrough: 2 + no-floating-decimal: 0 + no-func-assign: 2 + no-implicit-coercion: 0 + no-implied-eval: 0 + no-inline-comments: 0 + no-inner-declarations: + - 2 + - functions + no-invalid-regexp: 2 + no-invalid-this: 0 + no-irregular-whitespace: 2 + no-iterator: 0 + no-label-var: 0 + no-labels: 0 + no-lone-blocks: 0 + no-lonely-if: 0 + no-loop-func: 0 + no-mixed-requires: + - 0 + - false + no-mixed-spaces-and-tabs: + - 2 + - smart-tabs + linebreak-style: + - 0 + - unix + no-multi-spaces: 0 + no-multi-str: 0 + no-multiple-empty-lines: + - 0 + - max: 2 + no-native-reassign: 0 + no-negated-in-lhs: 2 + no-nested-ternary: 0 + no-new: 0 + no-new-func: 0 + no-new-object: 0 + no-new-require: 0 + no-new-wrappers: 0 + no-obj-calls: 2 + no-octal: 2 + no-octal-escape: 0 + no-param-reassign: 0 + no-path-concat: 0 + no-plusplus: 0 + no-process-env: 0 + no-process-exit: 0 + no-proto: 0 + no-redeclare: 2 + no-regex-spaces: 2 + no-reserved-keys: 0 + no-restricted-modules: 0 + no-return-assign: 0 + no-script-url: 0 + no-self-compare: 0 + no-sequences: 0 + no-shadow: 0 + no-shadow-restricted-names: 0 + no-spaced-func: 0 + no-sparse-arrays: 2 + no-sync: 0 + no-ternary: 0 + no-trailing-spaces: 0 + no-this-before-super: 0 + no-throw-literal: 0 + no-undef: 2 + no-undef-init: 0 + no-undefined: 0 + no-unexpected-multiline: 0 + no-underscore-dangle: 0 + no-unneeded-ternary: 0 + no-unreachable: 2 + no-unused-expressions: 0 + no-unused-vars: + - 2 + - vars: all + args: after-used + no-use-before-define: 0 + no-useless-call: 0 + no-void: 0 + no-var: 0 + no-warning-comments: + - 0 + - terms: + - todo + - fixme + - xxx + location: start + no-with: 0 + array-bracket-spacing: + - 0 + - never + arrow-parens: 0 + arrow-spacing: 0 + accessor-pairs: 0 + block-scoped-var: 0 + brace-style: + - 0 + - 1tbs + callback-return: 0 + camelcase: 0 + comma-dangle: + - 2 + - never + comma-spacing: 0 + comma-style: 0 + complexity: + - 2 + - 11 + computed-property-spacing: + - 0 + - never + consistent-return: 0 + consistent-this: + - 0 + - that + constructor-super: 0 + curly: + - 0 + - all + default-case: 0 + dot-location: 0 + dot-notation: + - 0 + - allowKeywords: true + eol-last: 0 + eqeqeq: 0 + func-names: 0 + func-style: + - 0 + - declaration + generator-star-spacing: 0 + guard-for-in: 0 + handle-callback-err: 0 + indent: 0 + init-declarations: 0 + key-spacing: + - 0 + - beforeColon: false + afterColon: true + lines-around-comment: 0 + max-depth: + - 0 + - 4 + max-len: + - 0 + - 80 + - 4 + max-nested-callbacks: + - 0 + - 2 + max-params: + - 0 + - 3 + max-statements: + - 0 + - 10 + new-cap: 0 + new-parens: 0 + newline-after-var: 0 + object-curly-spacing: + - 0 + - never + object-shorthand: 0 + one-var: 0 + operator-assignment: + - 0 + - always + operator-linebreak: 0 + padded-blocks: 0 + prefer-const: 0 + prefer-spread: 0 + prefer-reflect: 0 + quote-props: 0 + quotes: + - 2 + - double + - avoid-escape + radix: 0 + require-yield: 0 + semi: 0 + semi-spacing: + - 0 + - before: false + after: true + sort-vars: 0 + space-after-keywords: + - 0 + - always + space-before-blocks: + - 0 + - always + space-before-function-paren: + - 0 + - always + space-in-parens: + - 0 + - never + space-infix-ops: 0 + space-return-throw-case: 0 + space-unary-ops: + - 0 + - words: true + nonwords: false + spaced-comment: 0 + strict: 0 + use-isnan: 2 + valid-jsdoc: 0 + valid-typeof: 2 + vars-on-top: 0 + wrap-iife: 0 + wrap-regex: 0 + yoda: + - 0 + - never +env: + browser: true + node: true + jquery: true + amd: true + commonjs: true diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/.travis.yml b/src/EventHub.Web/wwwroot/libs/awesomplete/.travis.yml new file mode 100644 index 0000000..149e75d --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/.travis.yml @@ -0,0 +1,28 @@ +language: node_js + +node_js: + - "0.10" + +branches: + only: + - gh-pages + - /v\d+\.\d+/ + +install: + - npm install -g codeclimate-test-reporter + - npm install + +before_script: + - export CHROME_BIN=chromium-browser + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start + +after_script: + - codeclimate-test-reporter < ./coverage/lcov.info + +deploy: + provider: npm + email: $NPM_EMAIL + api_key: $NPM_TOKEN + on: + tags: true diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/CHANGELOG.md b/src/EventHub.Web/wwwroot/libs/awesomplete/CHANGELOG.md new file mode 100644 index 0000000..f83ec43 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/CHANGELOG.md @@ -0,0 +1,22 @@ +# 1.1.1 (2016-06-25) + +## Fixes + +* Improved docs +* Emit close reason with `awesomplete-close` events ([8c0ff62](https://github.com/LeaVerou/awesomplete/commit/8c0ff6225c96af2f5f3b7312d7ba7b69f71be575)). See [Events](http://leaverou.github.io/awesomplete/#events). +* Fire `awesomplete-close` event only if open ([2cef2c2](https://github.com/LeaVerou/awesomplete/commit/2cef2c28a6f74ee5c0b294d2c3c7d2bad72bd466)) + +# 1.1.0 (2016-03-16) + +## Features + +* Separate label/value for each suggestion on the list +* New `suggestions` property with suggestion items only (to be rendered in completer) +* New `data` method to convert/change suggestion item data and corresponding `Awesomplete.DATA` +* The `item` and `replace` methods have a corresponding `Awesomplete.ITEM` and `Awesomplete.REPLACE` defaults now +* Ajax and Combobox examples +* Nearly 100% test coverage with Travis CI and Code Climate + +# 1.0.0 (2015-04-23) + +## First release diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/CONTRIBUTING.md b/src/EventHub.Web/wwwroot/libs/awesomplete/CONTRIBUTING.md new file mode 100644 index 0000000..af0c206 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/CONTRIBUTING.md @@ -0,0 +1,59 @@ +##Contributing + +**Prerequisites** + +Install [Node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/). On OSX with [Homebrew](http://brew.sh/) installed it is as easy as: +``` +brew install node +``` + +Install dependencies: +``` +npm install +``` + +**Running tests** + +Run tests once and exit: +``` +npm test +``` + +Continuous mode. Whenever any source or test file changes, tests will run automatically: +``` +karma start +``` + +Chrome starts automatically and stops on ```Ctrl+C```. You can also open ```http://localhost:9876/``` in any other browser and it will run the tests as long as the tab is open. + +**Adding a test** + +[Jasmine](http://jasmine.github.io/) is the testing framework used by Awesomplete. + +To write a test (or suite of tests) start by adding a `describe` function which receives a string describing what is being tested and a function containing what you expect the test to do. Inside the function use the `it` block to arrange and assert a functionality. + +A test would look like this: + +```javascript +describe("A fact", function(){ + it("is always true",function(){ + var fact = true; + expect(fact).toBe(true); + }); +}); +``` + +See existing tests in ```test``` directory as an example. More expectations and examples on how to use Jasmine can be found on the official [documentation](http://jasmine.github.io/2.2/introduction.html). + +**Build** + +Run the build with the following command: + +``` +gulp +``` + +The build will: + +1. Minify `awesomplete.js` and generate `awesomplete.min.js`. +2. Merge `awesomplete.base.css` and `awesomplete.theme.css` and generate `awesomplete.css`. \ No newline at end of file diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/LICENSE b/src/EventHub.Web/wwwroot/libs/awesomplete/LICENSE new file mode 100644 index 0000000..ee76987 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Lea Verou + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/README.md b/src/EventHub.Web/wwwroot/libs/awesomplete/README.md new file mode 100644 index 0000000..434f385 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/README.md @@ -0,0 +1,98 @@ +# Awesomplete +[![npm version](https://img.shields.io/npm/v/awesomplete.svg)](https://www.npmjs.com/package/awesomplete) +[![Build Status](https://img.shields.io/travis/LeaVerou/awesomplete/gh-pages.svg)](https://travis-ci.org/LeaVerou/awesomplete) +[![Code Climate](https://img.shields.io/codeclimate/github/LeaVerou/awesomplete.svg)](https://codeclimate.com/github/LeaVerou/awesomplete) +[![Test Coverage](https://img.shields.io/codeclimate/coverage/github/LeaVerou/awesomplete.svg)](https://codeclimate.com/github/LeaVerou/awesomplete/coverage) + +https://leaverou.github.io/awesomplete/ + +Awesomplete is an ultra lightweight, customizable, simple autocomplete widget with zero dependencies, built with modern standards for modern browsers. + +## Installation +There are a few ways to obtain the needed files. +Here are 2 of them: +1. CDN server + +```sh +https://cdnjs.com/libraries/awesomplete +``` + +2. Another way to get up and running is by using `yarn` or `npm`: + +```sh +yarn add awesomplete +``` + +```sh +npm install awesomplete --save +``` + +More information about the npm package can be found [here](https://www.npmjs.com/package/awesomplete). + +## Basic Usage + +Before you try anything, you need to include awesomplete.css and awesomplete.js in your page, via the usual tags: + +```html + + +``` + +Then you can add an Awesomplete widget by adding the following input tag: + +```html + +``` + +Add `class="awesomplete"` for it to be automatically processed (you can still specify many options via HTML attributes) +Otherwise you can instantiate with a few lines of JS code, which allow for more customization. + +There are many ways to link an input to a list of suggestions. +The simple example above could have also been made with the following markup, which provides a nice native fallback in case the script doesn’t load: + +```html + + + + + + + + + + +``` + +Or the following, if you don’t want to use a ``, or if you don’t want to use IDs (since any selector will work in data-list): + +```html + +
    +
  • Ada
  • +
  • Java
  • +
  • JavaScript
  • +
  • Brainfuck
  • +
  • LOLCODE
  • +
  • Node.js
  • +
  • Ruby on Rails
  • +
+``` + +There are multiple customizations and properties able to be instantiated within the JS. Libraries and definitions of the properties are available in the Links below. + +## License + +Awesomplete is released under the MIT License. See [LICENSE][1] file for +details. + +## Links + +The official site for the library is at . + +Documentation for the API and other topics is at +. + +Created by Lea Verou and other fantastic contributors. + +[1]: https://github.com/LeaVerou/awesomplete/blob/gh-pages/LICENSE diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.base.css b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.base.css new file mode 100644 index 0000000..8e5ec12 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.base.css @@ -0,0 +1,33 @@ +.awesomplete [hidden] { + display: none; +} + +.awesomplete .visually-hidden { + position: absolute; + clip: rect(0, 0, 0, 0); +} + +.awesomplete { + display: inline-block; + position: relative; +} + +.awesomplete > input { + display: block; +} + +.awesomplete > ul { + position: absolute; + left: 0; + z-index: 1; + min-width: 100%; + box-sizing: border-box; + list-style: none; + padding: 0; + margin: 0; + background: #fff; +} + +.awesomplete > ul:empty { + display: none; +} diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.css b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.css new file mode 100644 index 0000000..0b2ea34 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.css @@ -0,0 +1,104 @@ +.awesomplete [hidden] { + display: none; +} + +.awesomplete .visually-hidden { + position: absolute; + clip: rect(0, 0, 0, 0); +} + +.awesomplete { + display: inline-block; + position: relative; +} + +.awesomplete > input { + display: block; +} + +.awesomplete > ul { + position: absolute; + left: 0; + z-index: 1; + min-width: 100%; + box-sizing: border-box; + list-style: none; + padding: 0; + margin: 0; + background: #fff; +} + +.awesomplete > ul:empty { + display: none; +} + +.awesomplete > ul { + border-radius: .3em; + margin: .2em 0 0; + background: hsla(0,0%,100%,.9); + background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); + border: 1px solid rgba(0,0,0,.3); + box-shadow: .05em .2em .6em rgba(0,0,0,.2); + text-shadow: none; +} + +@supports (transform: scale(0)) { + .awesomplete > ul { + transition: .3s cubic-bezier(.4,.2,.5,1.4); + transform-origin: 1.43em -.43em; + } + + .awesomplete > ul[hidden], + .awesomplete > ul:empty { + opacity: 0; + transform: scale(0); + display: block; + transition-timing-function: ease; + } +} + + /* Pointer */ + .awesomplete > ul:before { + content: ""; + position: absolute; + top: -.43em; + left: 1em; + width: 0; height: 0; + padding: .4em; + background: white; + border: inherit; + border-right: 0; + border-bottom: 0; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + } + + .awesomplete > ul > li { + position: relative; + padding: .2em .5em; + cursor: pointer; + } + + .awesomplete > ul > li:hover { + background: hsl(200, 40%, 80%); + color: black; + } + + .awesomplete > ul > li[aria-selected="true"] { + background: hsl(205, 40%, 40%); + color: white; + } + + .awesomplete mark { + background: hsl(65, 100%, 50%); + } + + .awesomplete li:hover mark { + background: hsl(68, 100%, 41%); + } + + .awesomplete li[aria-selected="true"] mark { + background: hsl(86, 100%, 21%); + color: inherit; + } +/*# sourceMappingURL=awesomplete.css.map */ diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.css.map b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.css.map new file mode 100644 index 0000000..3d57839 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["awesomplete.base.css","awesomplete.theme.css"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACjCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"awesomplete.css","sourcesContent":[".awesomplete [hidden] {\n display: none;\n}\n\n.awesomplete .visually-hidden {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n}\n\n.awesomplete {\n display: inline-block;\n position: relative;\n}\n\n.awesomplete > input {\n display: block;\n}\n\n.awesomplete > ul {\n position: absolute;\n left: 0;\n z-index: 1;\n min-width: 100%;\n box-sizing: border-box;\n list-style: none;\n padding: 0;\n margin: 0;\n background: #fff;\n}\n\n.awesomplete > ul:empty {\n display: none;\n}\n",".awesomplete > ul {\n\tborder-radius: .3em;\n\tmargin: .2em 0 0;\n\tbackground: hsla(0,0%,100%,.9);\n\tbackground: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8));\n\tborder: 1px solid rgba(0,0,0,.3);\n\tbox-shadow: .05em .2em .6em rgba(0,0,0,.2);\n\ttext-shadow: none;\n}\n\n@supports (transform: scale(0)) {\n\t.awesomplete > ul {\n\t\ttransition: .3s cubic-bezier(.4,.2,.5,1.4);\n\t\ttransform-origin: 1.43em -.43em;\n\t}\n\t\n\t.awesomplete > ul[hidden],\n\t.awesomplete > ul:empty {\n\t\topacity: 0;\n\t\ttransform: scale(0);\n\t\tdisplay: block;\n\t\ttransition-timing-function: ease;\n\t}\n}\n\n\t/* Pointer */\n\t.awesomplete > ul:before {\n\t\tcontent: \"\";\n\t\tposition: absolute;\n\t\ttop: -.43em;\n\t\tleft: 1em;\n\t\twidth: 0; height: 0;\n\t\tpadding: .4em;\n\t\tbackground: white;\n\t\tborder: inherit;\n\t\tborder-right: 0;\n\t\tborder-bottom: 0;\n\t\t-webkit-transform: rotate(45deg);\n\t\ttransform: rotate(45deg);\n\t}\n\n\t.awesomplete > ul > li {\n\t\tposition: relative;\n\t\tpadding: .2em .5em;\n\t\tcursor: pointer;\n\t}\n\t\n\t.awesomplete > ul > li:hover {\n\t\tbackground: hsl(200, 40%, 80%);\n\t\tcolor: black;\n\t}\n\t\n\t.awesomplete > ul > li[aria-selected=\"true\"] {\n\t\tbackground: hsl(205, 40%, 40%);\n\t\tcolor: white;\n\t}\n\t\n\t\t.awesomplete mark {\n\t\t\tbackground: hsl(65, 100%, 50%);\n\t\t}\n\t\t\n\t\t.awesomplete li:hover mark {\n\t\t\tbackground: hsl(68, 100%, 41%);\n\t\t}\n\t\t\n\t\t.awesomplete li[aria-selected=\"true\"] mark {\n\t\t\tbackground: hsl(86, 100%, 21%);\n\t\t\tcolor: inherit;\n\t\t}"]} \ No newline at end of file diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.js b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.js new file mode 100644 index 0000000..396fb71 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.js @@ -0,0 +1,552 @@ +/** + * Simple, lightweight, usable local autocomplete library for modern browsers + * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P + * @author Lea Verou http://leaverou.github.io/awesomplete + * MIT license + */ + +(function () { + +var _ = function (input, o) { + var me = this; + + // Keep track of number of instances for unique IDs + _.count = (_.count || 0) + 1; + this.count = _.count; + + // Setup + + this.isOpened = false; + + this.input = $(input); + this.input.setAttribute("autocomplete", "off"); + this.input.setAttribute("aria-expanded", "false"); + this.input.setAttribute("aria-owns", "awesomplete_list_" + this.count); + this.input.setAttribute("role", "combobox"); + + // store constructor options in case we need to distinguish + // between default and customized behavior later on + this.options = o = o || {}; + + configure(this, { + minChars: 2, + maxItems: 10, + autoFirst: false, + data: _.DATA, + filter: _.FILTER_CONTAINS, + sort: o.sort === false ? false : _.SORT_BYLENGTH, + container: _.CONTAINER, + item: _.ITEM, + replace: _.REPLACE, + tabSelect: false + }, o); + + this.index = -1; + + // Create necessary elements + + this.container = this.container(input); + + this.ul = $.create("ul", { + hidden: "hidden", + role: "listbox", + id: "awesomplete_list_" + this.count, + inside: this.container + }); + + this.status = $.create("span", { + className: "visually-hidden", + role: "status", + "aria-live": "assertive", + "aria-atomic": true, + inside: this.container, + textContent: this.minChars != 0 ? ("Type " + this.minChars + " or more characters for results.") : "Begin typing for results." + }); + + // Bind events + + this._events = { + input: { + "input": this.evaluate.bind(this), + "blur": this.close.bind(this, { reason: "blur" }), + "keydown": function(evt) { + var c = evt.keyCode; + + // If the dropdown `ul` is in view, then act on keydown for the following keys: + // Enter / Esc / Up / Down + if(me.opened) { + if (c === 13 && me.selected) { // Enter + evt.preventDefault(); + me.select(undefined, undefined, evt); + } + else if (c === 9 && me.selected && me.tabSelect) { + me.select(undefined, undefined, evt); + } + else if (c === 27) { // Esc + me.close({ reason: "esc" }); + } + else if (c === 38 || c === 40) { // Down/Up arrow + evt.preventDefault(); + me[c === 38? "previous" : "next"](); + } + } + } + }, + form: { + "submit": this.close.bind(this, { reason: "submit" }) + }, + ul: { + // Prevent the default mousedowm, which ensures the input is not blurred. + // The actual selection will happen on click. This also ensures dragging the + // cursor away from the list item will cancel the selection + "mousedown": function(evt) { + evt.preventDefault(); + }, + // The click event is fired even if the corresponding mousedown event has called preventDefault + "click": function(evt) { + var li = evt.target; + + if (li !== this) { + + while (li && !/li/i.test(li.nodeName)) { + li = li.parentNode; + } + + if (li && evt.button === 0) { // Only select on left click + evt.preventDefault(); + me.select(li, evt.target, evt); + } + } + } + } + }; + + $.bind(this.input, this._events.input); + $.bind(this.input.form, this._events.form); + $.bind(this.ul, this._events.ul); + + if (this.input.hasAttribute("list")) { + this.list = "#" + this.input.getAttribute("list"); + this.input.removeAttribute("list"); + } + else { + this.list = this.input.getAttribute("data-list") || o.list || []; + } + + _.all.push(this); +}; + +_.prototype = { + set list(list) { + if (Array.isArray(list)) { + this._list = list; + } + else if (typeof list === "string" && list.indexOf(",") > -1) { + this._list = list.split(/\s*,\s*/); + } + else { // Element or CSS selector + list = $(list); + + if (list && list.children) { + var items = []; + slice.apply(list.children).forEach(function (el) { + if (!el.disabled) { + var text = el.textContent.trim(); + var value = el.value || text; + var label = el.label || text; + if (value !== "") { + items.push({ label: label, value: value }); + } + } + }); + this._list = items; + } + } + + if (document.activeElement === this.input) { + this.evaluate(); + } + }, + + get selected() { + return this.index > -1; + }, + + get opened() { + return this.isOpened; + }, + + close: function (o) { + if (!this.opened) { + return; + } + + this.input.setAttribute("aria-expanded", "false"); + this.ul.setAttribute("hidden", ""); + this.isOpened = false; + this.index = -1; + + this.status.setAttribute("hidden", ""); + + $.fire(this.input, "awesomplete-close", o || {}); + }, + + open: function () { + this.input.setAttribute("aria-expanded", "true"); + this.ul.removeAttribute("hidden"); + this.isOpened = true; + + this.status.removeAttribute("hidden"); + + if (this.autoFirst && this.index === -1) { + this.goto(0); + } + + $.fire(this.input, "awesomplete-open"); + }, + + destroy: function() { + //remove events from the input and its form + $.unbind(this.input, this._events.input); + $.unbind(this.input.form, this._events.form); + + // cleanup container if it was created by Awesomplete but leave it alone otherwise + if (!this.options.container) { + //move the input out of the awesomplete container and remove the container and its children + var parentNode = this.container.parentNode; + + parentNode.insertBefore(this.input, this.container); + parentNode.removeChild(this.container); + } + + //remove autocomplete and aria-autocomplete attributes + this.input.removeAttribute("autocomplete"); + this.input.removeAttribute("aria-autocomplete"); + + //remove this awesomeplete instance from the global array of instances + var indexOfAwesomplete = _.all.indexOf(this); + + if (indexOfAwesomplete !== -1) { + _.all.splice(indexOfAwesomplete, 1); + } + }, + + next: function () { + var count = this.ul.children.length; + this.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) ); + }, + + previous: function () { + var count = this.ul.children.length; + var pos = this.index - 1; + + this.goto(this.selected && pos !== -1 ? pos : count - 1); + }, + + // Should not be used, highlights specific item without any checks! + goto: function (i) { + var lis = this.ul.children; + + if (this.selected) { + lis[this.index].setAttribute("aria-selected", "false"); + } + + this.index = i; + + if (i > -1 && lis.length > 0) { + lis[i].setAttribute("aria-selected", "true"); + + this.status.textContent = lis[i].textContent + ", list item " + (i + 1) + " of " + lis.length; + + this.input.setAttribute("aria-activedescendant", this.ul.id + "_item_" + this.index); + + // scroll to highlighted element in case parent's height is fixed + this.ul.scrollTop = lis[i].offsetTop - this.ul.clientHeight + lis[i].clientHeight; + + $.fire(this.input, "awesomplete-highlight", { + text: this.suggestions[this.index] + }); + } + }, + + select: function (selected, origin, originalEvent) { + if (selected) { + this.index = $.siblingIndex(selected); + } else { + selected = this.ul.children[this.index]; + } + + if (selected) { + var suggestion = this.suggestions[this.index]; + + var allowed = $.fire(this.input, "awesomplete-select", { + text: suggestion, + origin: origin || selected, + originalEvent: originalEvent + }); + + if (allowed) { + this.replace(suggestion); + this.close({ reason: "select" }); + $.fire(this.input, "awesomplete-selectcomplete", { + text: suggestion, + originalEvent: originalEvent + }); + } + } + }, + + evaluate: function() { + var me = this; + var value = this.input.value; + + if (value.length >= this.minChars && this._list && this._list.length > 0) { + this.index = -1; + // Populate list with options that match + this.ul.innerHTML = ""; + + this.suggestions = this._list + .map(function(item) { + return new Suggestion(me.data(item, value)); + }) + .filter(function(item) { + return me.filter(item, value); + }); + + if (this.sort !== false) { + this.suggestions = this.suggestions.sort(this.sort); + } + + this.suggestions = this.suggestions.slice(0, this.maxItems); + + this.suggestions.forEach(function(text, index) { + me.ul.appendChild(me.item(text, value, index)); + }); + + if (this.ul.children.length === 0) { + + this.status.textContent = "No results found"; + + this.close({ reason: "nomatches" }); + + } else { + this.open(); + + this.status.textContent = this.ul.children.length + " results found"; + } + } + else { + this.close({ reason: "nomatches" }); + + this.status.textContent = "No results found"; + } + } +}; + +// Static methods/properties + +_.all = []; + +_.FILTER_CONTAINS = function (text, input) { + return RegExp($.regExpEscape(input.trim()), "i").test(text); +}; + +_.FILTER_STARTSWITH = function (text, input) { + return RegExp("^" + $.regExpEscape(input.trim()), "i").test(text); +}; + +_.SORT_BYLENGTH = function (a, b) { + if (a.length !== b.length) { + return a.length - b.length; + } + + return a < b? -1 : 1; +}; + +_.CONTAINER = function (input) { + return $.create("div", { + className: "awesomplete", + around: input + }); +} + +_.ITEM = function (text, input, item_id) { + var html = input.trim() === "" ? text : text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "$&"); + return $.create("li", { + innerHTML: html, + "role": "option", + "aria-selected": "false", + "id": "awesomplete_list_" + this.count + "_item_" + item_id + }); +}; + +_.REPLACE = function (text) { + this.input.value = text.value; +}; + +_.DATA = function (item/*, input*/) { return item; }; + +// Private functions + +function Suggestion(data) { + var o = Array.isArray(data) + ? { label: data[0], value: data[1] } + : typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data }; + + this.label = o.label || o.value; + this.value = o.value; +} +Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", { + get: function() { return this.label.length; } +}); +Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () { + return "" + this.label; +}; + +function configure(instance, properties, o) { + for (var i in properties) { + var initial = properties[i], + attrValue = instance.input.getAttribute("data-" + i.toLowerCase()); + + if (typeof initial === "number") { + instance[i] = parseInt(attrValue); + } + else if (initial === false) { // Boolean options must be false by default anyway + instance[i] = attrValue !== null; + } + else if (initial instanceof Function) { + instance[i] = null; + } + else { + instance[i] = attrValue; + } + + if (!instance[i] && instance[i] !== 0) { + instance[i] = (i in o)? o[i] : initial; + } + } +} + +// Helpers + +var slice = Array.prototype.slice; + +function $(expr, con) { + return typeof expr === "string"? (con || document).querySelector(expr) : expr || null; +} + +function $$(expr, con) { + return slice.call((con || document).querySelectorAll(expr)); +} + +$.create = function(tag, o) { + var element = document.createElement(tag); + + for (var i in o) { + var val = o[i]; + + if (i === "inside") { + $(val).appendChild(element); + } + else if (i === "around") { + var ref = $(val); + ref.parentNode.insertBefore(element, ref); + element.appendChild(ref); + + if (ref.getAttribute("autofocus") != null) { + ref.focus(); + } + } + else if (i in element) { + element[i] = val; + } + else { + element.setAttribute(i, val); + } + } + + return element; +}; + +$.bind = function(element, o) { + if (element) { + for (var event in o) { + var callback = o[event]; + + event.split(/\s+/).forEach(function (event) { + element.addEventListener(event, callback); + }); + } + } +}; + +$.unbind = function(element, o) { + if (element) { + for (var event in o) { + var callback = o[event]; + + event.split(/\s+/).forEach(function(event) { + element.removeEventListener(event, callback); + }); + } + } +}; + +$.fire = function(target, type, properties) { + var evt = document.createEvent("HTMLEvents"); + + evt.initEvent(type, true, true ); + + for (var j in properties) { + evt[j] = properties[j]; + } + + return target.dispatchEvent(evt); +}; + +$.regExpEscape = function (s) { + return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); +}; + +$.siblingIndex = function (el) { + /* eslint-disable no-cond-assign */ + for (var i = 0; el = el.previousElementSibling; i++); + return i; +}; + +// Initialization + +function init() { + $$("input.awesomplete").forEach(function (input) { + new _(input); + }); +} + +// Make sure to export Awesomplete on self when in a browser +if (typeof self !== "undefined") { + self.Awesomplete = _; +} + +// Are we in a browser? Check for Document constructor +if (typeof Document !== "undefined") { + // DOM already loaded? + if (document.readyState !== "loading") { + init(); + } + else { + // Wait for it + document.addEventListener("DOMContentLoaded", init); + } +} + +_.$ = $; +_.$$ = $$; + +// Expose Awesomplete as a CJS module +if (typeof module === "object" && module.exports) { + module.exports = _; +} + +return _; + +}()); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.min.js b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.min.js new file mode 100644 index 0000000..a895194 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.min.js @@ -0,0 +1,3 @@ +// Awesomplete - Lea Verou - MIT license +!function(){function t(t){var e=Array.isArray(t)?{label:t[0],value:t[1]}:"object"==typeof t&&"label"in t&&"value"in t?t:{label:t,value:t};this.label=e.label||e.value,this.value=e.value}function e(t,e,i){for(var n in e){var s=e[n],r=t.input.getAttribute("data-"+n.toLowerCase());"number"==typeof s?t[n]=parseInt(r):!1===s?t[n]=null!==r:s instanceof Function?t[n]=null:t[n]=r,t[n]||0===t[n]||(t[n]=n in i?i[n]:s)}}function i(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function n(t,e){return o.call((e||document).querySelectorAll(t))}function s(){n("input.awesomplete").forEach(function(t){new r(t)})}var r=function(t,n){var s=this;r.count=(r.count||0)+1,this.count=r.count,this.isOpened=!1,this.input=i(t),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-expanded","false"),this.input.setAttribute("aria-owns","awesomplete_list_"+this.count),this.input.setAttribute("role","combobox"),this.options=n=n||{},e(this,{minChars:2,maxItems:10,autoFirst:!1,data:r.DATA,filter:r.FILTER_CONTAINS,sort:!1!==n.sort&&r.SORT_BYLENGTH,container:r.CONTAINER,item:r.ITEM,replace:r.REPLACE,tabSelect:!1},n),this.index=-1,this.container=this.container(t),this.ul=i.create("ul",{hidden:"hidden",role:"listbox",id:"awesomplete_list_"+this.count,inside:this.container}),this.status=i.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-atomic":!0,inside:this.container,textContent:0!=this.minChars?"Type "+this.minChars+" or more characters for results.":"Begin typing for results."}),this._events={input:{input:this.evaluate.bind(this),blur:this.close.bind(this,{reason:"blur"}),keydown:function(t){var e=t.keyCode;s.opened&&(13===e&&s.selected?(t.preventDefault(),s.select()):9===e&&s.selected&&s.tabSelect?s.select():27===e?s.close({reason:"esc"}):38!==e&&40!==e||(t.preventDefault(),s[38===e?"previous":"next"]()))}},form:{submit:this.close.bind(this,{reason:"submit"})},ul:{mousedown:function(t){t.preventDefault()},click:function(t){var e=t.target;if(e!==this){for(;e&&!/li/i.test(e.nodeName);)e=e.parentNode;e&&0===t.button&&(t.preventDefault(),s.select(e,t.target))}}}},i.bind(this.input,this._events.input),i.bind(this.input.form,this._events.form),i.bind(this.ul,this._events.ul),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||n.list||[],r.all.push(this)};r.prototype={set list(t){if(Array.isArray(t))this._list=t;else if("string"==typeof t&&t.indexOf(",")>-1)this._list=t.split(/\s*,\s*/);else if((t=i(t))&&t.children){var e=[];o.apply(t.children).forEach(function(t){if(!t.disabled){var i=t.textContent.trim(),n=t.value||i,s=t.label||i;""!==n&&e.push({label:s,value:n})}}),this._list=e}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return this.isOpened},close:function(t){this.opened&&(this.input.setAttribute("aria-expanded","false"),this.ul.setAttribute("hidden",""),this.isOpened=!1,this.index=-1,this.status.setAttribute("hidden",""),i.fire(this.input,"awesomplete-close",t||{}))},open:function(){this.input.setAttribute("aria-expanded","true"),this.ul.removeAttribute("hidden"),this.isOpened=!0,this.status.removeAttribute("hidden"),this.autoFirst&&-1===this.index&&this.goto(0),i.fire(this.input,"awesomplete-open")},destroy:function(){if(i.unbind(this.input,this._events.input),i.unbind(this.input.form,this._events.form),!this.options.container){var t=this.container.parentNode;t.insertBefore(this.input,this.container),t.removeChild(this.container)}this.input.removeAttribute("autocomplete"),this.input.removeAttribute("aria-autocomplete");var e=r.all.indexOf(this);-1!==e&&r.all.splice(e,1)},next:function(){var t=this.ul.children.length;this.goto(this.index-1&&e.length>0&&(e[t].setAttribute("aria-selected","true"),this.status.textContent=e[t].textContent+", list item "+(t+1)+" of "+e.length,this.input.setAttribute("aria-activedescendant",this.ul.id+"_item_"+this.index),this.ul.scrollTop=e[t].offsetTop-this.ul.clientHeight+e[t].clientHeight,i.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(t,e){if(t?this.index=i.siblingIndex(t):t=this.ul.children[this.index],t){var n=this.suggestions[this.index];i.fire(this.input,"awesomplete-select",{text:n,origin:e||t})&&(this.replace(n),this.close({reason:"select"}),i.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var e=this,i=this.input.value;i.length>=this.minChars&&this._list&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(n){return new t(e.data(n,i))}).filter(function(t){return e.filter(t,i)}),!1!==this.sort&&(this.suggestions=this.suggestions.sort(this.sort)),this.suggestions=this.suggestions.slice(0,this.maxItems),this.suggestions.forEach(function(t,n){e.ul.appendChild(e.item(t,i,n))}),0===this.ul.children.length?(this.status.textContent="No results found",this.close({reason:"nomatches"})):(this.open(),this.status.textContent=this.ul.children.length+" results found")):(this.close({reason:"nomatches"}),this.status.textContent="No results found")}},r.all=[],r.FILTER_CONTAINS=function(t,e){return RegExp(i.regExpEscape(e.trim()),"i").test(t)},r.FILTER_STARTSWITH=function(t,e){return RegExp("^"+i.regExpEscape(e.trim()),"i").test(t)},r.SORT_BYLENGTH=function(t,e){return t.length!==e.length?t.length-e.length:t$&"),role:"option","aria-selected":"false",id:"awesomplete_list_"+this.count+"_item_"+n})},r.REPLACE=function(t){this.input.value=t.value},r.DATA=function(t){return t},Object.defineProperty(t.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),t.prototype.toString=t.prototype.valueOf=function(){return""+this.label};var o=Array.prototype.slice;i.create=function(t,e){var n=document.createElement(t);for(var s in e){var r=e[s];if("inside"===s)i(r).appendChild(n);else if("around"===s){var o=i(r);o.parentNode.insertBefore(n,o),n.appendChild(o),null!=o.getAttribute("autofocus")&&o.focus()}else s in n?n[s]=r:n.setAttribute(s,r)}return n},i.bind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,n)})}},i.unbind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,n)})}},i.fire=function(t,e,i){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0);for(var s in i)n[s]=i[s];return t.dispatchEvent(n)},i.regExpEscape=function(t){return t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},i.siblingIndex=function(t){for(var e=0;t=t.previousElementSibling;e++);return e},"undefined"!=typeof self&&(self.Awesomplete=r),"undefined"!=typeof Document&&("loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s)),r.$=i,r.$$=n,"object"==typeof module&&module.exports&&(module.exports=r)}(); +//# sourceMappingURL=awesomplete.min.js.map diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.min.js.map b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.min.js.map new file mode 100644 index 0000000..6ad1411 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["awesomplete.js"],"names":["Suggestion","data","o","Array","isArray","label","value","this","configure","instance","properties","i","initial","attrValue","input","getAttribute","toLowerCase","parseInt","Function","$","expr","con","document","querySelector","$$","slice","call","querySelectorAll","init","forEach","_","me","count","isOpened","setAttribute","options","minChars","maxItems","autoFirst","DATA","filter","FILTER_CONTAINS","sort","SORT_BYLENGTH","container","CONTAINER","item","ITEM","replace","REPLACE","tabSelect","index","ul","create","hidden","role","id","inside","status","className","aria-live","aria-atomic","textContent","_events","evaluate","bind","blur","close","reason","keydown","evt","c","keyCode","opened","selected","preventDefault","select","form","submit","mousedown","click","li","target","test","nodeName","parentNode","button","hasAttribute","list","removeAttribute","all","push","prototype","_list","indexOf","split","children","items","apply","el","disabled","text","trim","activeElement","fire","open","goto","destroy","unbind","insertBefore","removeChild","indexOfAwesomplete","splice","next","length","previous","pos","lis","scrollTop","offsetTop","clientHeight","suggestions","origin","siblingIndex","suggestion","innerHTML","map","appendChild","RegExp","regExpEscape","FILTER_STARTSWITH","a","b","around","item_id","aria-selected","Object","defineProperty","String","get","toString","valueOf","tag","element","createElement","val","ref","focus","event","callback","addEventListener","removeEventListener","type","createEvent","initEvent","j","dispatchEvent","s","previousElementSibling","self","Awesomplete","Document","readyState","module","exports"],"mappings":";CAOC,WA6XD,QAASA,GAAWC,GACnB,GAAIC,GAAIC,MAAMC,QAAQH,IAChBI,MAAOJ,EAAK,GAAIK,MAAOL,EAAK,IACd,gBAATA,IAAqB,SAAWA,IAAQ,SAAWA,GAAOA,GAASI,MAAOJ,EAAMK,MAAOL,EAElGM,MAAKF,MAAQH,EAAEG,OAASH,EAAEI,MAC1BC,KAAKD,MAAQJ,EAAEI,MAShB,QAASE,GAAUC,EAAUC,EAAYR,GACxC,IAAK,GAAIS,KAAKD,GAAY,CACzB,GAAIE,GAAUF,EAAWC,GACrBE,EAAYJ,EAASK,MAAMC,aAAa,QAAUJ,EAAEK,cAEjC,iBAAZJ,GACVH,EAASE,GAAKM,SAASJ,IAEH,IAAZD,EACRH,EAASE,GAAmB,OAAdE,EAEND,YAAmBM,UAC3BT,EAASE,GAAK,KAGdF,EAASE,GAAKE,EAGVJ,EAASE,IAAsB,IAAhBF,EAASE,KAC5BF,EAASE,GAAMA,IAAKT,GAAIA,EAAES,GAAKC,IASlC,QAASO,GAAEC,EAAMC,GAChB,MAAuB,gBAATD,IAAoBC,GAAOC,UAAUC,cAAcH,GAAQA,GAAQ,KAGlF,QAASI,GAAGJ,EAAMC,GACjB,MAAOI,GAAMC,MAAML,GAAOC,UAAUK,iBAAiBP,IAgFtD,QAASQ,KACRJ,EAAG,qBAAqBK,QAAQ,SAAUf,GACzC,GAAIgB,GAAEhB,KA7fR,GAAIgB,GAAI,SAAUhB,EAAOZ,GACxB,GAAI6B,GAAKxB,IAGNuB,GAAEE,OAASF,EAAEE,OAAS,GAAK,EAC3BzB,KAAKyB,MAAQF,EAAEE,MAIlBzB,KAAK0B,UAAW,EAEhB1B,KAAKO,MAAQK,EAAEL,GACfP,KAAKO,MAAMoB,aAAa,eAAgB,OACxC3B,KAAKO,MAAMoB,aAAa,gBAAiB,SACzC3B,KAAKO,MAAMoB,aAAa,YAAa,oBAAsB3B,KAAKyB,OAChEzB,KAAKO,MAAMoB,aAAa,OAAQ,YAIhC3B,KAAK4B,QAAUjC,EAAIA,MAEnBM,EAAUD,MACT6B,SAAU,EACVC,SAAU,GACVC,WAAW,EACXrC,KAAM6B,EAAES,KACRC,OAAQV,EAAEW,gBACVC,MAAiB,IAAXxC,EAAEwC,MAAyBZ,EAAEa,cACnCC,UAAWd,EAAEe,UACbC,KAAMhB,EAAEiB,KACRC,QAASlB,EAAEmB,QACXC,WAAW,GACThD,GAEHK,KAAK4C,OAAS,EAId5C,KAAKqC,UAAYrC,KAAKqC,UAAU9B,GAEhCP,KAAK6C,GAAKjC,EAAEkC,OAAO,MAClBC,OAAQ,SACFC,KAAM,UACNC,GAAI,oBAAsBjD,KAAKyB,MACrCyB,OAAQlD,KAAKqC,YAGdrC,KAAKmD,OAASvC,EAAEkC,OAAO,QACtBM,UAAW,kBACXJ,KAAM,SACNK,YAAa,YACPC,eAAe,EACfJ,OAAQlD,KAAKqC,UACbkB,YAA8B,GAAjBvD,KAAK6B,SAAiB,QAAU7B,KAAK6B,SAAW,mCAAsC,8BAK1G7B,KAAKwD,SACJjD,OACCA,MAASP,KAAKyD,SAASC,KAAK1D,MAC5B2D,KAAQ3D,KAAK4D,MAAMF,KAAK1D,MAAQ6D,OAAQ,SACxCC,QAAW,SAASC,GACnB,GAAIC,GAAID,EAAIE,OAITzC,GAAG0C,SACK,KAANF,GAAYxC,EAAG2C,UAClBJ,EAAIK,iBACJ5C,EAAG6C,UAEW,IAANL,GAAWxC,EAAG2C,UAAY3C,EAAGmB,UACrCnB,EAAG6C,SAEW,KAANL,EACRxC,EAAGoC,OAAQC,OAAQ,QAEL,KAANG,GAAkB,KAANA,IACpBD,EAAIK,iBACJ5C,EAAS,KAANwC,EAAU,WAAa,cAK9BM,MACCC,OAAUvE,KAAK4D,MAAMF,KAAK1D,MAAQ6D,OAAQ,YAE3ChB,IAIC2B,UAAa,SAAST,GACrBA,EAAIK,kBAGLK,MAAS,SAASV,GACjB,GAAIW,GAAKX,EAAIY,MAEb,IAAID,IAAO1E,KAAM,CAEhB,KAAO0E,IAAO,MAAME,KAAKF,EAAGG,WAC3BH,EAAKA,EAAGI,UAGLJ,IAAqB,IAAfX,EAAIgB,SACbhB,EAAIK,iBACJ5C,EAAG6C,OAAOK,EAAIX,EAAIY,aAOvB/D,EAAE8C,KAAK1D,KAAKO,MAAOP,KAAKwD,QAAQjD,OAChCK,EAAE8C,KAAK1D,KAAKO,MAAM+D,KAAMtE,KAAKwD,QAAQc,MACrC1D,EAAE8C,KAAK1D,KAAK6C,GAAI7C,KAAKwD,QAAQX,IAEzB7C,KAAKO,MAAMyE,aAAa,SAC3BhF,KAAKiF,KAAO,IAAMjF,KAAKO,MAAMC,aAAa,QAC1CR,KAAKO,MAAM2E,gBAAgB,SAG3BlF,KAAKiF,KAAOjF,KAAKO,MAAMC,aAAa,cAAgBb,EAAEsF,SAGvD1D,EAAE4D,IAAIC,KAAKpF,MAGZuB,GAAE8D,WACDJ,SAASA,GACR,GAAIrF,MAAMC,QAAQoF,GACjBjF,KAAKsF,MAAQL,MAET,IAAoB,gBAATA,IAAqBA,EAAKM,QAAQ,MAAQ,EACxDvF,KAAKsF,MAAQL,EAAKO,MAAM,eAKzB,KAFAP,EAAOrE,EAAEqE,KAEGA,EAAKQ,SAAU,CAC1B,GAAIC,KACJxE,GAAMyE,MAAMV,EAAKQ,UAAUnE,QAAQ,SAAUsE,GAC5C,IAAKA,EAAGC,SAAU,CACjB,GAAIC,GAAOF,EAAGrC,YAAYwC,OACtBhG,EAAQ6F,EAAG7F,OAAS+F,EACpBhG,EAAQ8F,EAAG9F,OAASgG,CACV,MAAV/F,GACH2F,EAAMN,MAAOtF,MAAOA,EAAOC,MAAOA,OAIrCC,KAAKsF,MAAQI,EAIX3E,SAASiF,gBAAkBhG,KAAKO,OACnCP,KAAKyD,YAIPU,eACC,MAAOnE,MAAK4C,OAAS,GAGtBsB,aACC,MAAOlE,MAAK0B,UAGbkC,MAAO,SAAUjE,GACXK,KAAKkE,SAIVlE,KAAKO,MAAMoB,aAAa,gBAAiB,SACzC3B,KAAK6C,GAAGlB,aAAa,SAAU,IAC/B3B,KAAK0B,UAAW,EAChB1B,KAAK4C,OAAS,EAEd5C,KAAKmD,OAAOxB,aAAa,SAAU,IAEnCf,EAAEqF,KAAKjG,KAAKO,MAAO,oBAAqBZ,SAGzCuG,KAAM,WACLlG,KAAKO,MAAMoB,aAAa,gBAAiB,QACzC3B,KAAK6C,GAAGqC,gBAAgB,UACxBlF,KAAK0B,UAAW,EAEhB1B,KAAKmD,OAAO+B,gBAAgB,UAExBlF,KAAK+B,YAA6B,IAAhB/B,KAAK4C,OAC1B5C,KAAKmG,KAAK,GAGXvF,EAAEqF,KAAKjG,KAAKO,MAAO,qBAGpB6F,QAAS,WAMR,GAJAxF,EAAEyF,OAAOrG,KAAKO,MAAOP,KAAKwD,QAAQjD,OAClCK,EAAEyF,OAAOrG,KAAKO,MAAM+D,KAAMtE,KAAKwD,QAAQc,OAGlCtE,KAAK4B,QAAQS,UAAW,CAE5B,GAAIyC,GAAa9E,KAAKqC,UAAUyC,UAEhCA,GAAWwB,aAAatG,KAAKO,MAAOP,KAAKqC,WACzCyC,EAAWyB,YAAYvG,KAAKqC,WAI7BrC,KAAKO,MAAM2E,gBAAgB,gBAC3BlF,KAAKO,MAAM2E,gBAAgB,oBAG3B,IAAIsB,GAAqBjF,EAAE4D,IAAII,QAAQvF,OAEX,IAAxBwG,GACHjF,EAAE4D,IAAIsB,OAAOD,EAAoB,IAInCE,KAAM,WACL,GAAIjF,GAAQzB,KAAK6C,GAAG4C,SAASkB,MAC7B3G,MAAKmG,KAAKnG,KAAK4C,MAAQnB,EAAQ,EAAIzB,KAAK4C,MAAQ,EAAKnB,EAAQ,GAAK,IAGnEmF,SAAU,WACT,GAAInF,GAAQzB,KAAK6C,GAAG4C,SAASkB,OACzBE,EAAM7G,KAAK4C,MAAQ,CAEvB5C,MAAKmG,KAAKnG,KAAKmE,WAAqB,IAAT0C,EAAaA,EAAMpF,EAAQ,IAIvD0E,KAAM,SAAU/F,GACf,GAAI0G,GAAM9G,KAAK6C,GAAG4C,QAEdzF,MAAKmE,UACR2C,EAAI9G,KAAK4C,OAAOjB,aAAa,gBAAiB,SAG/C3B,KAAK4C,MAAQxC,EAETA,GAAK,GAAK0G,EAAIH,OAAS,IAC1BG,EAAI1G,GAAGuB,aAAa,gBAAiB,QAErC3B,KAAKmD,OAAOI,YAAcuD,EAAI1G,GAAGmD,YAAc,gBAAkBnD,EAAI,GAAK,OAAS0G,EAAIH,OAE9E3G,KAAKO,MAAMoB,aAAa,wBAAyB3B,KAAK6C,GAAGI,GAAK,SAAWjD,KAAK4C,OAGvF5C,KAAK6C,GAAGkE,UAAYD,EAAI1G,GAAG4G,UAAYhH,KAAK6C,GAAGoE,aAAeH,EAAI1G,GAAG6G,aAErErG,EAAEqF,KAAKjG,KAAKO,MAAO,yBAClBuF,KAAM9F,KAAKkH,YAAYlH,KAAK4C,WAK/ByB,OAAQ,SAAUF,EAAUgD,GAO3B,GANIhD,EACHnE,KAAK4C,MAAQhC,EAAEwG,aAAajD,GAE5BA,EAAWnE,KAAK6C,GAAG4C,SAASzF,KAAK4C,OAG9BuB,EAAU,CACb,GAAIkD,GAAarH,KAAKkH,YAAYlH,KAAK4C,MAEzBhC,GAAEqF,KAAKjG,KAAKO,MAAO,sBAChCuF,KAAMuB,EACNF,OAAQA,GAAUhD,MAIlBnE,KAAKyC,QAAQ4E,GACbrH,KAAK4D,OAAQC,OAAQ,WACrBjD,EAAEqF,KAAKjG,KAAKO,MAAO,8BAClBuF,KAAMuB,OAMV5D,SAAU,WACT,GAAIjC,GAAKxB,KACLD,EAAQC,KAAKO,MAAMR,KAEnBA,GAAM4G,QAAU3G,KAAK6B,UAAY7B,KAAKsF,OAAStF,KAAKsF,MAAMqB,OAAS,GACtE3G,KAAK4C,OAAS,EAEd5C,KAAK6C,GAAGyE,UAAY,GAEpBtH,KAAKkH,YAAclH,KAAKsF,MACtBiC,IAAI,SAAShF,GACb,MAAO,IAAI9C,GAAW+B,EAAG9B,KAAK6C,EAAMxC,MAEpCkC,OAAO,SAASM,GAChB,MAAOf,GAAGS,OAAOM,EAAMxC,MAGP,IAAdC,KAAKmC,OACRnC,KAAKkH,YAAclH,KAAKkH,YAAY/E,KAAKnC,KAAKmC,OAG/CnC,KAAKkH,YAAclH,KAAKkH,YAAYhG,MAAM,EAAGlB,KAAK8B,UAElD9B,KAAKkH,YAAY5F,QAAQ,SAASwE,EAAMlD,GACtCpB,EAAGqB,GAAG2E,YAAYhG,EAAGe,KAAKuD,EAAM/F,EAAO6C,MAGT,IAA5B5C,KAAK6C,GAAG4C,SAASkB,QAER3G,KAAKmD,OAAOI,YAAc,mBAEtCvD,KAAK4D,OAAQC,OAAQ,gBAGrB7D,KAAKkG,OAEOlG,KAAKmD,OAAOI,YAAcvD,KAAK6C,GAAG4C,SAASkB,OAAS,oBAIjE3G,KAAK4D,OAAQC,OAAQ,cAER7D,KAAKmD,OAAOI,YAAc,sBAO1ChC,EAAE4D,OAEF5D,EAAEW,gBAAkB,SAAU4D,EAAMvF,GACnC,MAAOkH,QAAO7G,EAAE8G,aAAanH,EAAMwF,QAAS,KAAKnB,KAAKkB,IAGvDvE,EAAEoG,kBAAoB,SAAU7B,EAAMvF,GACrC,MAAOkH,QAAO,IAAM7G,EAAE8G,aAAanH,EAAMwF,QAAS,KAAKnB,KAAKkB,IAG7DvE,EAAEa,cAAgB,SAAUwF,EAAGC,GAC9B,MAAID,GAAEjB,SAAWkB,EAAElB,OACXiB,EAAEjB,OAASkB,EAAElB,OAGdiB,EAAIC,GAAI,EAAI,GAGpBtG,EAAEe,UAAY,SAAU/B,GACvB,MAAOK,GAAEkC,OAAO,OACfM,UAAW,cACX0E,OAAQvH,KAIVgB,EAAEiB,KAAO,SAAUsD,EAAMvF,EAAOwH,GAE/B,MAAOnH,GAAEkC,OAAO,MACfwE,UAF2B,KAAjB/G,EAAMwF,OAAgBD,EAAOA,EAAKrD,QAAQgF,OAAO7G,EAAE8G,aAAanH,EAAMwF,QAAS,MAAO,mBAGhG/C,KAAQ,SACRgF,gBAAiB,QACjB/E,GAAM,oBAAsBjD,KAAKyB,MAAQ,SAAWsG,KAItDxG,EAAEmB,QAAU,SAAUoD,GACrB9F,KAAKO,MAAMR,MAAQ+F,EAAK/F,OAGzBwB,EAAES,KAAO,SAAUO,GAAmB,MAAOA,IAY7C0F,OAAOC,eAAezI,EAAW4F,UAAY4C,OAAOnF,OAAOqF,OAAO9C,WAAY,UAC7E+C,IAAK,WAAa,MAAOpI,MAAKF,MAAM6G,UAErClH,EAAW4F,UAAUgD,SAAW5I,EAAW4F,UAAUiD,QAAU,WAC9D,MAAO,GAAKtI,KAAKF,MA6BlB,IAAIoB,GAAQtB,MAAMyF,UAAUnE,KAU5BN,GAAEkC,OAAS,SAASyF,EAAK5I,GACxB,GAAI6I,GAAUzH,SAAS0H,cAAcF,EAErC,KAAK,GAAInI,KAAKT,GAAG,CAChB,GAAI+I,GAAM/I,EAAES,EAEZ,IAAU,WAANA,EACHQ,EAAE8H,GAAKlB,YAAYgB,OAEf,IAAU,WAANpI,EAAgB,CACxB,GAAIuI,GAAM/H,EAAE8H,EACZC,GAAI7D,WAAWwB,aAAakC,EAASG,GACrCH,EAAQhB,YAAYmB,GAEiB,MAAjCA,EAAInI,aAAa,cACpBmI,EAAIC,YAGGxI,KAAKoI,GACbA,EAAQpI,GAAKsI,EAGbF,EAAQ7G,aAAavB,EAAGsI,GAI1B,MAAOF,IAGR5H,EAAE8C,KAAO,SAAS8E,EAAS7I,GAC1B,GAAI6I,EACH,IAAK,GAAIK,KAASlJ,GAAG,CACpB,GAAImJ,GAAWnJ,EAAEkJ,EAEjBA,GAAMrD,MAAM,OAAOlE,QAAQ,SAAUuH,GACpCL,EAAQO,iBAAiBF,EAAOC,OAMpClI,EAAEyF,OAAS,SAASmC,EAAS7I,GAC5B,GAAI6I,EACH,IAAK,GAAIK,KAASlJ,GAAG,CACpB,GAAImJ,GAAWnJ,EAAEkJ,EAEjBA,GAAMrD,MAAM,OAAOlE,QAAQ,SAASuH,GACnCL,EAAQQ,oBAAoBH,EAAOC,OAMvClI,EAAEqF,KAAO,SAAStB,EAAQsE,EAAM9I,GAC/B,GAAI4D,GAAMhD,SAASmI,YAAY,aAE/BnF,GAAIoF,UAAUF,GAAM,GAAM,EAE1B,KAAK,GAAIG,KAAKjJ,GACb4D,EAAIqF,GAAKjJ,EAAWiJ,EAGrB,OAAOzE,GAAO0E,cAActF,IAG7BnD,EAAE8G,aAAe,SAAU4B,GAC1B,MAAOA,GAAE7G,QAAQ,uBAAwB,SAG1C7B,EAAEwG,aAAe,SAAUxB,GAE1B,IAAK,GAAIxF,GAAI,EAAGwF,EAAKA,EAAG2D,uBAAwBnJ,KAChD,MAAOA,IAYY,mBAAToJ,QACVA,KAAKC,YAAclI,GAII,mBAAbmI,YAEkB,YAAxB3I,SAAS4I,WACZtI,IAIAN,SAASgI,iBAAiB,mBAAoB1H,IAIhDE,EAAEX,EAAIA,EACNW,EAAEN,GAAKA,EAGe,gBAAX2I,SAAuBA,OAAOC,UACxCD,OAAOC,QAAUtI","file":"awesomplete.min.js","sourcesContent":["/**\n * Simple, lightweight, usable local autocomplete library for modern browsers\n * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P\n * @author Lea Verou http://leaverou.github.io/awesomplete\n * MIT license\n */\n\n(function () {\n\nvar _ = function (input, o) {\n\tvar me = this;\n\n // Keep track of number of instances for unique IDs\n _.count = (_.count || 0) + 1;\n this.count = _.count;\n\n\t// Setup\n\n\tthis.isOpened = false;\n\n\tthis.input = $(input);\n\tthis.input.setAttribute(\"autocomplete\", \"off\");\n\tthis.input.setAttribute(\"aria-expanded\", \"false\");\n\tthis.input.setAttribute(\"aria-owns\", \"awesomplete_list_\" + this.count);\n\tthis.input.setAttribute(\"role\", \"combobox\");\n\n\t// store constructor options in case we need to distinguish\n\t// between default and customized behavior later on\n\tthis.options = o = o || {};\n\n\tconfigure(this, {\n\t\tminChars: 2,\n\t\tmaxItems: 10,\n\t\tautoFirst: false,\n\t\tdata: _.DATA,\n\t\tfilter: _.FILTER_CONTAINS,\n\t\tsort: o.sort === false ? false : _.SORT_BYLENGTH,\n\t\tcontainer: _.CONTAINER,\n\t\titem: _.ITEM,\n\t\treplace: _.REPLACE,\n\t\ttabSelect: false\n\t}, o);\n\n\tthis.index = -1;\n\n\t// Create necessary elements\n\n\tthis.container = this.container(input);\n\n\tthis.ul = $.create(\"ul\", {\n\t\thidden: \"hidden\",\n role: \"listbox\",\n id: \"awesomplete_list_\" + this.count,\n\t\tinside: this.container\n\t});\n\n\tthis.status = $.create(\"span\", {\n\t\tclassName: \"visually-hidden\",\n\t\trole: \"status\",\n\t\t\"aria-live\": \"assertive\",\n \"aria-atomic\": true,\n inside: this.container,\n textContent: this.minChars != 0 ? (\"Type \" + this.minChars + \" or more characters for results.\") : \"Begin typing for results.\"\n\t});\n\n\t// Bind events\n\n\tthis._events = {\n\t\tinput: {\n\t\t\t\"input\": this.evaluate.bind(this),\n\t\t\t\"blur\": this.close.bind(this, { reason: \"blur\" }),\n\t\t\t\"keydown\": function(evt) {\n\t\t\t\tvar c = evt.keyCode;\n\n\t\t\t\t// If the dropdown `ul` is in view, then act on keydown for the following keys:\n\t\t\t\t// Enter / Esc / Up / Down\n\t\t\t\tif(me.opened) {\n\t\t\t\t\tif (c === 13 && me.selected) { // Enter\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme.select();\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 9 && me.selected && me.tabSelect) {\n\t\t\t\t\t\tme.select();\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 27) { // Esc\n\t\t\t\t\t\tme.close({ reason: \"esc\" });\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 38 || c === 40) { // Down/Up arrow\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme[c === 38? \"previous\" : \"next\"]();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tform: {\n\t\t\t\"submit\": this.close.bind(this, { reason: \"submit\" })\n\t\t},\n\t\tul: {\n\t\t\t// Prevent the default mousedowm, which ensures the input is not blurred.\n\t\t\t// The actual selection will happen on click. This also ensures dragging the\n\t\t\t// cursor away from the list item will cancel the selection\n\t\t\t\"mousedown\": function(evt) {\n\t\t\t\tevt.preventDefault();\n\t\t\t},\n\t\t\t// The click event is fired even if the corresponding mousedown event has called preventDefault\n\t\t\t\"click\": function(evt) {\n\t\t\t\tvar li = evt.target;\n\n\t\t\t\tif (li !== this) {\n\n\t\t\t\t\twhile (li && !/li/i.test(li.nodeName)) {\n\t\t\t\t\t\tli = li.parentNode;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (li && evt.button === 0) { // Only select on left click\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme.select(li, evt.target);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\t$.bind(this.input, this._events.input);\n\t$.bind(this.input.form, this._events.form);\n\t$.bind(this.ul, this._events.ul);\n\n\tif (this.input.hasAttribute(\"list\")) {\n\t\tthis.list = \"#\" + this.input.getAttribute(\"list\");\n\t\tthis.input.removeAttribute(\"list\");\n\t}\n\telse {\n\t\tthis.list = this.input.getAttribute(\"data-list\") || o.list || [];\n\t}\n\n\t_.all.push(this);\n};\n\n_.prototype = {\n\tset list(list) {\n\t\tif (Array.isArray(list)) {\n\t\t\tthis._list = list;\n\t\t}\n\t\telse if (typeof list === \"string\" && list.indexOf(\",\") > -1) {\n\t\t\t\tthis._list = list.split(/\\s*,\\s*/);\n\t\t}\n\t\telse { // Element or CSS selector\n\t\t\tlist = $(list);\n\n\t\t\tif (list && list.children) {\n\t\t\t\tvar items = [];\n\t\t\t\tslice.apply(list.children).forEach(function (el) {\n\t\t\t\t\tif (!el.disabled) {\n\t\t\t\t\t\tvar text = el.textContent.trim();\n\t\t\t\t\t\tvar value = el.value || text;\n\t\t\t\t\t\tvar label = el.label || text;\n\t\t\t\t\t\tif (value !== \"\") {\n\t\t\t\t\t\t\titems.push({ label: label, value: value });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis._list = items;\n\t\t\t}\n\t\t}\n\n\t\tif (document.activeElement === this.input) {\n\t\t\tthis.evaluate();\n\t\t}\n\t},\n\n\tget selected() {\n\t\treturn this.index > -1;\n\t},\n\n\tget opened() {\n\t\treturn this.isOpened;\n\t},\n\n\tclose: function (o) {\n\t\tif (!this.opened) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.input.setAttribute(\"aria-expanded\", \"false\");\n\t\tthis.ul.setAttribute(\"hidden\", \"\");\n\t\tthis.isOpened = false;\n\t\tthis.index = -1;\n\n\t\tthis.status.setAttribute(\"hidden\", \"\");\n\n\t\t$.fire(this.input, \"awesomplete-close\", o || {});\n\t},\n\n\topen: function () {\n\t\tthis.input.setAttribute(\"aria-expanded\", \"true\");\n\t\tthis.ul.removeAttribute(\"hidden\");\n\t\tthis.isOpened = true;\n\n\t\tthis.status.removeAttribute(\"hidden\");\n\n\t\tif (this.autoFirst && this.index === -1) {\n\t\t\tthis.goto(0);\n\t\t}\n\n\t\t$.fire(this.input, \"awesomplete-open\");\n\t},\n\n\tdestroy: function() {\n\t\t//remove events from the input and its form\n\t\t$.unbind(this.input, this._events.input);\n\t\t$.unbind(this.input.form, this._events.form);\n\n\t\t// cleanup container if it was created by Awesomplete but leave it alone otherwise\n\t\tif (!this.options.container) {\n\t\t\t//move the input out of the awesomplete container and remove the container and its children\n\t\t\tvar parentNode = this.container.parentNode;\n\n\t\t\tparentNode.insertBefore(this.input, this.container);\n\t\t\tparentNode.removeChild(this.container);\n\t\t}\n\n\t\t//remove autocomplete and aria-autocomplete attributes\n\t\tthis.input.removeAttribute(\"autocomplete\");\n\t\tthis.input.removeAttribute(\"aria-autocomplete\");\n\n\t\t//remove this awesomeplete instance from the global array of instances\n\t\tvar indexOfAwesomplete = _.all.indexOf(this);\n\n\t\tif (indexOfAwesomplete !== -1) {\n\t\t\t_.all.splice(indexOfAwesomplete, 1);\n\t\t}\n\t},\n\n\tnext: function () {\n\t\tvar count = this.ul.children.length;\n\t\tthis.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) );\n\t},\n\n\tprevious: function () {\n\t\tvar count = this.ul.children.length;\n\t\tvar pos = this.index - 1;\n\n\t\tthis.goto(this.selected && pos !== -1 ? pos : count - 1);\n\t},\n\n\t// Should not be used, highlights specific item without any checks!\n\tgoto: function (i) {\n\t\tvar lis = this.ul.children;\n\n\t\tif (this.selected) {\n\t\t\tlis[this.index].setAttribute(\"aria-selected\", \"false\");\n\t\t}\n\n\t\tthis.index = i;\n\n\t\tif (i > -1 && lis.length > 0) {\n\t\t\tlis[i].setAttribute(\"aria-selected\", \"true\");\n\n\t\t\tthis.status.textContent = lis[i].textContent + \", list item \" + (i + 1) + \" of \" + lis.length;\n\n this.input.setAttribute(\"aria-activedescendant\", this.ul.id + \"_item_\" + this.index);\n\n\t\t\t// scroll to highlighted element in case parent's height is fixed\n\t\t\tthis.ul.scrollTop = lis[i].offsetTop - this.ul.clientHeight + lis[i].clientHeight;\n\n\t\t\t$.fire(this.input, \"awesomplete-highlight\", {\n\t\t\t\ttext: this.suggestions[this.index]\n\t\t\t});\n\t\t}\n\t},\n\n\tselect: function (selected, origin) {\n\t\tif (selected) {\n\t\t\tthis.index = $.siblingIndex(selected);\n\t\t} else {\n\t\t\tselected = this.ul.children[this.index];\n\t\t}\n\n\t\tif (selected) {\n\t\t\tvar suggestion = this.suggestions[this.index];\n\n\t\t\tvar allowed = $.fire(this.input, \"awesomplete-select\", {\n\t\t\t\ttext: suggestion,\n\t\t\t\torigin: origin || selected\n\t\t\t});\n\n\t\t\tif (allowed) {\n\t\t\t\tthis.replace(suggestion);\n\t\t\t\tthis.close({ reason: \"select\" });\n\t\t\t\t$.fire(this.input, \"awesomplete-selectcomplete\", {\n\t\t\t\t\ttext: suggestion\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n\n\tevaluate: function() {\n\t\tvar me = this;\n\t\tvar value = this.input.value;\n\n\t\tif (value.length >= this.minChars && this._list && this._list.length > 0) {\n\t\t\tthis.index = -1;\n\t\t\t// Populate list with options that match\n\t\t\tthis.ul.innerHTML = \"\";\n\n\t\t\tthis.suggestions = this._list\n\t\t\t\t.map(function(item) {\n\t\t\t\t\treturn new Suggestion(me.data(item, value));\n\t\t\t\t})\n\t\t\t\t.filter(function(item) {\n\t\t\t\t\treturn me.filter(item, value);\n\t\t\t\t});\n\n\t\t\tif (this.sort !== false) {\n\t\t\t\tthis.suggestions = this.suggestions.sort(this.sort);\n\t\t\t}\n\n\t\t\tthis.suggestions = this.suggestions.slice(0, this.maxItems);\n\n\t\t\tthis.suggestions.forEach(function(text, index) {\n\t\t\t\t\tme.ul.appendChild(me.item(text, value, index));\n\t\t\t\t});\n\n\t\t\tif (this.ul.children.length === 0) {\n\n this.status.textContent = \"No results found\";\n\n\t\t\t\tthis.close({ reason: \"nomatches\" });\n\n\t\t\t} else {\n\t\t\t\tthis.open();\n\n this.status.textContent = this.ul.children.length + \" results found\";\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthis.close({ reason: \"nomatches\" });\n\n this.status.textContent = \"No results found\";\n\t\t}\n\t}\n};\n\n// Static methods/properties\n\n_.all = [];\n\n_.FILTER_CONTAINS = function (text, input) {\n\treturn RegExp($.regExpEscape(input.trim()), \"i\").test(text);\n};\n\n_.FILTER_STARTSWITH = function (text, input) {\n\treturn RegExp(\"^\" + $.regExpEscape(input.trim()), \"i\").test(text);\n};\n\n_.SORT_BYLENGTH = function (a, b) {\n\tif (a.length !== b.length) {\n\t\treturn a.length - b.length;\n\t}\n\n\treturn a < b? -1 : 1;\n};\n\n_.CONTAINER = function (input) {\n\treturn $.create(\"div\", {\n\t\tclassName: \"awesomplete\",\n\t\taround: input\n\t});\n}\n\n_.ITEM = function (text, input, item_id) {\n\tvar html = input.trim() === \"\" ? text : text.replace(RegExp($.regExpEscape(input.trim()), \"gi\"), \"$&\");\n\treturn $.create(\"li\", {\n\t\tinnerHTML: html,\n\t\t\"role\": \"option\",\n\t\t\"aria-selected\": \"false\",\n\t\t\"id\": \"awesomplete_list_\" + this.count + \"_item_\" + item_id\n\t});\n};\n\n_.REPLACE = function (text) {\n\tthis.input.value = text.value;\n};\n\n_.DATA = function (item/*, input*/) { return item; };\n\n// Private functions\n\nfunction Suggestion(data) {\n\tvar o = Array.isArray(data)\n\t ? { label: data[0], value: data[1] }\n\t : typeof data === \"object\" && \"label\" in data && \"value\" in data ? data : { label: data, value: data };\n\n\tthis.label = o.label || o.value;\n\tthis.value = o.value;\n}\nObject.defineProperty(Suggestion.prototype = Object.create(String.prototype), \"length\", {\n\tget: function() { return this.label.length; }\n});\nSuggestion.prototype.toString = Suggestion.prototype.valueOf = function () {\n\treturn \"\" + this.label;\n};\n\nfunction configure(instance, properties, o) {\n\tfor (var i in properties) {\n\t\tvar initial = properties[i],\n\t\t attrValue = instance.input.getAttribute(\"data-\" + i.toLowerCase());\n\n\t\tif (typeof initial === \"number\") {\n\t\t\tinstance[i] = parseInt(attrValue);\n\t\t}\n\t\telse if (initial === false) { // Boolean options must be false by default anyway\n\t\t\tinstance[i] = attrValue !== null;\n\t\t}\n\t\telse if (initial instanceof Function) {\n\t\t\tinstance[i] = null;\n\t\t}\n\t\telse {\n\t\t\tinstance[i] = attrValue;\n\t\t}\n\n\t\tif (!instance[i] && instance[i] !== 0) {\n\t\t\tinstance[i] = (i in o)? o[i] : initial;\n\t\t}\n\t}\n}\n\n// Helpers\n\nvar slice = Array.prototype.slice;\n\nfunction $(expr, con) {\n\treturn typeof expr === \"string\"? (con || document).querySelector(expr) : expr || null;\n}\n\nfunction $$(expr, con) {\n\treturn slice.call((con || document).querySelectorAll(expr));\n}\n\n$.create = function(tag, o) {\n\tvar element = document.createElement(tag);\n\n\tfor (var i in o) {\n\t\tvar val = o[i];\n\n\t\tif (i === \"inside\") {\n\t\t\t$(val).appendChild(element);\n\t\t}\n\t\telse if (i === \"around\") {\n\t\t\tvar ref = $(val);\n\t\t\tref.parentNode.insertBefore(element, ref);\n\t\t\telement.appendChild(ref);\n\n\t\t\tif (ref.getAttribute(\"autofocus\") != null) {\n\t\t\t\tref.focus();\n\t\t\t}\n\t\t}\n\t\telse if (i in element) {\n\t\t\telement[i] = val;\n\t\t}\n\t\telse {\n\t\t\telement.setAttribute(i, val);\n\t\t}\n\t}\n\n\treturn element;\n};\n\n$.bind = function(element, o) {\n\tif (element) {\n\t\tfor (var event in o) {\n\t\t\tvar callback = o[event];\n\n\t\t\tevent.split(/\\s+/).forEach(function (event) {\n\t\t\t\telement.addEventListener(event, callback);\n\t\t\t});\n\t\t}\n\t}\n};\n\n$.unbind = function(element, o) {\n\tif (element) {\n\t\tfor (var event in o) {\n\t\t\tvar callback = o[event];\n\n\t\t\tevent.split(/\\s+/).forEach(function(event) {\n\t\t\t\telement.removeEventListener(event, callback);\n\t\t\t});\n\t\t}\n\t}\n};\n\n$.fire = function(target, type, properties) {\n\tvar evt = document.createEvent(\"HTMLEvents\");\n\n\tevt.initEvent(type, true, true );\n\n\tfor (var j in properties) {\n\t\tevt[j] = properties[j];\n\t}\n\n\treturn target.dispatchEvent(evt);\n};\n\n$.regExpEscape = function (s) {\n\treturn s.replace(/[-\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n};\n\n$.siblingIndex = function (el) {\n\t/* eslint-disable no-cond-assign */\n\tfor (var i = 0; el = el.previousElementSibling; i++);\n\treturn i;\n};\n\n// Initialization\n\nfunction init() {\n\t$$(\"input.awesomplete\").forEach(function (input) {\n\t\tnew _(input);\n\t});\n}\n\n// Make sure to export Awesomplete on self when in a browser\nif (typeof self !== \"undefined\") {\n\tself.Awesomplete = _;\n}\n\n// Are we in a browser? Check for Document constructor\nif (typeof Document !== \"undefined\") {\n\t// DOM already loaded?\n\tif (document.readyState !== \"loading\") {\n\t\tinit();\n\t}\n\telse {\n\t\t// Wait for it\n\t\tdocument.addEventListener(\"DOMContentLoaded\", init);\n\t}\n}\n\n_.$ = $;\n_.$$ = $$;\n\n// Expose Awesomplete as a CJS module\nif (typeof module === \"object\" && module.exports) {\n\tmodule.exports = _;\n}\n\nreturn _;\n\n}());\n"]} \ No newline at end of file diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.theme.css b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.theme.css new file mode 100644 index 0000000..8a964f1 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/awesomplete.theme.css @@ -0,0 +1,69 @@ +.awesomplete > ul { + border-radius: .3em; + margin: .2em 0 0; + background: hsla(0,0%,100%,.9); + background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); + border: 1px solid rgba(0,0,0,.3); + box-shadow: .05em .2em .6em rgba(0,0,0,.2); + text-shadow: none; +} + +@supports (transform: scale(0)) { + .awesomplete > ul { + transition: .3s cubic-bezier(.4,.2,.5,1.4); + transform-origin: 1.43em -.43em; + } + + .awesomplete > ul[hidden], + .awesomplete > ul:empty { + opacity: 0; + transform: scale(0); + display: block; + transition-timing-function: ease; + } +} + + /* Pointer */ + .awesomplete > ul:before { + content: ""; + position: absolute; + top: -.43em; + left: 1em; + width: 0; height: 0; + padding: .4em; + background: white; + border: inherit; + border-right: 0; + border-bottom: 0; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + } + + .awesomplete > ul > li { + position: relative; + padding: .2em .5em; + cursor: pointer; + } + + .awesomplete > ul > li:hover { + background: hsl(200, 40%, 80%); + color: black; + } + + .awesomplete > ul > li[aria-selected="true"] { + background: hsl(205, 40%, 40%); + color: white; + } + + .awesomplete mark { + background: hsl(65, 100%, 50%); + } + + .awesomplete li:hover mark { + background: hsl(68, 100%, 41%); + } + + .awesomplete li[aria-selected="true"] mark { + background: hsl(86, 100%, 21%); + color: inherit; + } \ No newline at end of file diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/bower.json b/src/EventHub.Web/wwwroot/libs/awesomplete/bower.json new file mode 100644 index 0000000..c372cf8 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/bower.json @@ -0,0 +1,17 @@ +{ + "name": "awesomplete", + "main": [ + "./awesomplete.css", + "./awesomplete.js" + ], + "homepage": "https://github.com/LeaVerou/awesomplete", + "authors": [ + "Lea Verou <>" + ], + "description": "Ultra lightweight, usable autocomplete with zero dependencies.", + "keywords": [ + "autocomplete", + "lightweight" + ], + "license": "MIT" +} diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/gulpfile.js b/src/EventHub.Web/wwwroot/libs/awesomplete/gulpfile.js new file mode 100644 index 0000000..89db87b --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/gulpfile.js @@ -0,0 +1,31 @@ +var gulp = require('gulp'); +var uglify = require('gulp-uglify'); +var rename = require('gulp-rename'); +var header = require('gulp-header'); +var concat = require('gulp-concat'); +var sourcemaps = require('gulp-sourcemaps'); + +var banner = "// Awesomplete - Lea Verou - MIT license\n"; + +gulp.task('minify', function() { + return gulp.src(['awesomplete.js']) + .pipe(sourcemaps.init()) + .pipe(uglify()) + .pipe(rename({ suffix: '.min' })) + .pipe(header(banner)) + .pipe(sourcemaps.write('.')) + .pipe(gulp.dest('.')); +}); + +gulp.task('concat', function() { + + return gulp.src(['awesomplete.base.css', 'awesomplete.theme.css']) + .pipe(sourcemaps.init()) + .pipe(concat('awesomplete.css')) + .pipe(sourcemaps.write('.')) + .pipe(gulp.dest('.')); + + +}); + +gulp.task('default', ['minify', 'concat']); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/index.html b/src/EventHub.Web/wwwroot/libs/awesomplete/index.html new file mode 100644 index 0000000..be08ec9 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/index.html @@ -0,0 +1,501 @@ + + + + + +Awesomplete: Ultra lightweight, highly customizable, simple autocomplete, by Lea Verou + + + + + + + + + +
+ +

Awesomplete

+ +2KB minified & gzipped! + +

Ultra lightweight, customizable, simple autocomplete widget with zero dependencies, built with modern standards for modern browsers. Because <datalist> still doesn’t cut it.

+ + +
+ +
+ +

Demo (no JS, minimal options)

+ + +

Note that by default you need to type at least 2 characters for the popup to show up, though that’s super easy to customize. With Awesomplete, making something like this can be as simple as:

+ +
<input class="awesomplete"
+       data-list="Ada, Java, JavaScript, Brainfuck, LOLCODE, Node.js, Ruby on Rails" />
+ +
// No extra JS needed for basic usage!
+ +
+ +
+ +

Basic usage

+

Before you try anything, you need to include awesomplete.css and awesomplete.js in your page, via the usual <link rel="stylesheet" href="awesomplete.css" /> and <script src="awesomplete.js" async></script> tags.

+ +

For the autocomplete, you just need an <input> text field (might work on <textarea> and elements with contentEditable, but that hasn’t been tested). Add class="awesomplete" for it to be automatically processed (you can still specify many options via HTML attributes), otherwise you can instantiate with a few lines of JS code, which allow for more customization.

+

There are many ways to link an input to a list of suggestions. The simple example above could have also been made with the following markup, which provides a nice native fallback in case the script doesn’t load:

+ +
<input class="awesomplete" list="mylist" />
+<datalist id="mylist">
+	<option>Ada</option>
+	<option>Java</option>
+	<option>JavaScript</option>
+	<option>Brainfuck</option>
+	<option>LOLCODE</option>
+	<option>Node.js</option>
+	<option>Ruby on Rails</option>
+</datalist>
+ +
// None!
+ +

Or the following, if you don’t want to use a <datalist>, or if you don’t want to use IDs (since any selector will work in data-list):

+ +
<input class="awesomplete" data-list="#mylist" />
+<ul id="mylist">
+	<li>Ada</li>
+	<li>Java</li>
+	<li>JavaScript</li>
+	<li>Brainfuck</li>
+	<li>LOLCODE</li>
+	<li>Node.js</li>
+	<li>Ruby on Rails</li>
+</ul>
+ +
// None!
+ +

Or the following, if we want to instantiate in JS:

+ +
<input id="myinput" />
+<ul id="mylist">
+	<li>Ada</li>
+	<li>Java</li>
+	<li>JavaScript</li>
+	<li>Brainfuck</li>
+	<li>LOLCODE</li>
+	<li>Node.js</li>
+	<li>Ruby on Rails</li>
+</ul>
+
var input = document.getElementById("myinput");
+new Awesomplete(input, {list: "#mylist"});
+ +

We can use an element reference for the list instead of a selector:

+ +
<input id="myinput" />
+<ul id="mylist">
+	<li>Ada</li>
+	<li>Java</li>
+	<li>JavaScript</li>
+	<li>Brainfuck</li>
+	<li>LOLCODE</li>
+	<li>Node.js</li>
+	<li>Ruby on Rails</li>
+</ul>
+
var input = document.getElementById("myinput");
+new Awesomplete(input, {list: document.querySelector("#mylist")});
+ +

We can also directly use an array of strings:

+ +
<input id="myinput" />
+
var input = document.getElementById("myinput");
+new Awesomplete(input, {
+	list: ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.js", "Ruby on Rails"]
+});
+ +

We can even set it (or override it) later and it will just work:

+ +
<input id="myinput" />
+
var input = document.getElementById("myinput");
+var awesomplete = new Awesomplete(input);
+
+/* ...more code... */
+
+awesomplete.list = ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.js", "Ruby on Rails"];
+ +

Suggestions with different label and value are supported too. The label will be shown in autocompleter and the value will be inserted into the input. If you want to insert the label into the input you can provide your own replace function

+ +
<input id="myinput" />
+
var input = document.getElementById("myinput");
+
+// Show label but insert value into the input:
+new Awesomplete(input, {
+	list: [
+		{ label: "Belarus", value: "BY" },
+		{ label: "China", value: "CN" },
+		{ label: "United States", value: "US" }
+	]
+});
+
+// Same with arrays:
+new Awesomplete(input, {
+	list: [
+		[ "Belarus", "BY" ],
+		[ "China", "CN" ],
+		[ "United States", "US" ]
+	]
+});
+
+// Show label and insert label into the input:
+new Awesomplete(input, {
+	list: [
+		{ label: "Belarus", value: "BY" },
+		{ label: "China", value: "CN" },
+		{ label: "United States", value: "US" }
+	],
+	// insert label instead of value into the input.
+	replace: function(suggestion) {
+		this.input.value = suggestion.label;
+	}
+});
+
+ +
+ +
+

Customize

+ +

All settings discussed in this section are settable via either a data- attribute on the <input> element or a JS property on the second argument of the Awesomplete constructor, like so:

+ +
new Awesomplete(inputReference, {
+	minChars: 3,
+	maxItems: 15,
+	...
+});
+

You can of course combine both HTML attributes and JS properties. In case of conflict (e.g. you’ve specified both a data-minchars on the text field and a minChars JS property, the HTML attribute wins. You can also use the JS properties to change a parameter after the object has been created, in which case the change will apply even if there is a conflicting HTML attribute.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
JS propertyHTML attributeDescriptionValueDefault
listdata-listWhere to find the list of suggestions. Described in more detail in the “Basic usage” section above.
    +
  • Array of strings
  • +
  • HTML element
  • +
  • CSS selector (no groups, i.e. no commas)
  • +
  • String containing a comma-separated list of items
  • +
N/A
minCharsdata-mincharsMinimum characters the user has to type before the autocomplete popup shows up.Number2
maxItemsdata-maxitemsMaximum number of suggestions to display.Number10
autoFirstdata-autofirstShould the first element be automatically selected? Demo: Booleanfalse
+ +
+ +
+

Extend

+

The following JS properties do not have equivalent HTML attributes, because their values are functions. They allow you to completely change the way Awesomplete works:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyDescriptionValueDefault
filterControls how entries get matched. By default, the input can match anywhere in the string and it’s a case insensitive match.Function that takes two parameters, the first one being the current suggestion text that’s being tested and the second a string with the user’s input it’s matched against. Returns true if the match is successful and false if it is not. For example, to only match strings that start with the user’s input, case sensitive, we can do this: +
filter: function (text, input) {
+	return text.indexOf(input) === 0;
+}
For case-insensitive matching from the start of the word, there is a predefined filter that you can use, Awesomplete.FILTER_STARTSWITH +
Awesomplete.FILTER_CONTAINS: Text can match anywhere, case insensitive.
sortControls how list items are ordered.Sort function (will be passed directly to Array.prototype.sort()) to sort the items after they have been filtered and before they are truncated and converted to HTML elements. If value is false, sorting will be disabled.Sorted by length first, order second.
containerControls how list container element is generated.Function that takes one parameter, the user’s input and returns an element.Generates <div> with class awesomplete
itemControls how list items are generated.Function that takes two parameters, the first one being the suggestion text and the second one the user’s input and returns a list item.Generates list items with the user’s input highlighted via <mark>.
replaceControls how the user’s selection replaces the user’s input. For example, this is useful if you want the selection to only partially replace the user’s input.Function that takes one parameter, the text of the selected option, and is responsible for replacing the current input value with it. +
function (text) {
+	this.input.value = text;
+}
dataControls suggestions' label and value. This is useful if you have list items in custom format, or want to change list items based on user's input.Function that takes two parameters, the first one being the original list item and the second a string with the user’s input and returns a list item in one of supported by default formats: +
    +
  • "JavaScript"
  • +
  • { label: "JavaScript", value: "JS" }
  • +
  • [ "JavaScript", "JS" ]
  • +
+To use objects without label or value properties, e.g. name and id instead, you can do this: +
data: function (item, input) {
+	return { label: item.name, value: item.id };
+}
+You can use any object for label and value and it will be converted to String where necessary: +
list: [ new Date("2015-01-01"), ... ] 
+Original list items as Date objects will be accessible in filter, sort, item and replace functions, but by default we'll just see Date objects converted to strings in autocompleter and the same value will be inserted to the input. +
+We can also generate list items based on user's input. See E-mail autocomplete example in Advanced Examples section. +
Awesomplete.DATA: Identity function which just returns the original list item.
+
+ +
+

Events

+ +

Custom events are thrown in several places and are often cancellable. To avoid conflicts, all custom events are prefixed with awesomplete-.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionevent.preventDefault()?
awesomplete-selectThe user has made a selection (either via pressing enter or clicking on an item), but it has not been applied yet. Callback will be passed an object with text (selected suggestion), origin (DOM element) properties and originalEvent the original triggering DOM event.Yes. The selection will not be applied and the popup will not close.
awesomplete-selectcompleteThe user has made a selection (either via pressing enter or clicking on an item), and it has been applied. Callback will be passed an object with a text property containing the selected suggestion and originalEvent the original triggering DOM event.No
awesomplete-openThe popup just appeared.No
awesomplete-closeThe popup just closed. Callback will be passed an object with a reason property that indicates why the event was fired. Reasons include "blur", "esc", "submit", "select", and "nomatches".No
awesomplete-highlightThe highlighted item just changed (in response to pressing an arrow key or via an API call). Callback will be passed an object with a text property containing the highlighted suggestion.No
+
+ +
+

API

+ +

There are several methods on every Awesomplete instance that you can call to customize behavior:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodDescription
open()Opens the popup.
close()Closes the popup.
next()Highlights the next item in the popup.
previous()Highlights the previous item in the popup.
goto(i)Highlights the item with index i in the popup (-1 to deselect all). Avoid using this directly and try to use next() or previous() instead when possible.
select()Selects the currently highlighted item, replaces the text field’s value with it and closes the popup.
evaluate()Evaluates the current state of the widget and regenerates the list of suggestions or closes the popup if none are available. You need to call it if you dynamically set list while the popup is open.
destroy()Clean up and remove the instance from the input.
+
+ +
+

Advanced Examples

+

These examples show how powerful Awesomplete’s minimal API can be.

+ +
+

E-mail autocomplete

+ +
<input type="email" />
+
+
+ +
+

Multiple values

+ +
<input data-list="CSS, JavaScript, HTML, SVG, ARIA, MathML" data-multiple />
+
+
+ +
+

Ajax example (restcountries.eu api)

+ +
+
+ +
+

Combobox dropdown

+ +
<input data-list="CSS, JavaScript, HTML, SVG, ARIA, MathML" class="dropdown-input" />
+
+
+ +
+

Download!

+ + + +

Pull requests are very welcome, as long as you maintain the code style and ask before adding tons of LOCs to the codebase!

+
+ + + + + + + + + + + + + + diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/index.js b/src/EventHub.Web/wwwroot/libs/awesomplete/index.js new file mode 100644 index 0000000..f081872 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/index.js @@ -0,0 +1,16 @@ +/* global Awesomplete, $:true, $$:true */ +$ = Awesomplete.$; +$$ = Awesomplete.$$; + +document.addEventListener("DOMContentLoaded", function() { + var nav = $("nav") + $$("section > h1").forEach(function (h1) { + if (h1.parentNode.id) { + $.create("a", { + href: "#" + h1.parentNode.id, + textContent: h1.textContent.replace(/\(.+?\)/g, ""), + inside: nav + }); + } + }); +}); \ No newline at end of file diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/karma.conf.js b/src/EventHub.Web/wwwroot/libs/awesomplete/karma.conf.js new file mode 100644 index 0000000..f4146f4 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/karma.conf.js @@ -0,0 +1,70 @@ +module.exports = function(config) { + config.set({ + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine', 'jasmine-def', 'fixture'], + + // list of files / patterns to load in the browser + files: [ + 'awesomplete.js', + 'test/specHelper.js', + { + pattern: 'test/fixtures/**/*.html', + watched: true, included: true, served: true + }, + 'test/**/*Spec.js' + ], + + // list of files to exclude + exclude: [ + '**/*.swp' + ], + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'awesomplete.js': ['coverage'], + '**/*.html' : ['html2js'] + }, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['dots', 'coverage'], + coverageReporter: { + type: 'lcov', + subdir: '.' + }, + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: process.env.TRAVIS ? ['ChromeTravisCI'] : ['Chrome'], + + // need this to run Chrome on Travis CI + customLaunchers: { + ChromeTravisCI: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }, + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }); +}; diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/package.json b/src/EventHub.Web/wwwroot/libs/awesomplete/package.json new file mode 100644 index 0000000..6122946 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/package.json @@ -0,0 +1,32 @@ +{ + "name": "awesomplete", + "version": "1.1.5", + "description": "http://leaverou.github.io/awesomplete/", + "main": "awesomplete.js", + "scripts": { + "test": "karma start --single-run" + }, + "repository": "LeaVerou/awesomplete", + "author": "Lea Verou", + "license": "MIT", + "bugs": { + "url": "https://github.com/LeaVerou/awesomplete/issues" + }, + "homepage": "https://leaverou.github.io/awesomplete/", + "devDependencies": { + "gulp": "^3.9.0", + "gulp-concat": "^2.6.0", + "gulp-header": "^1.7.1", + "gulp-rename": "^1.2.2", + "gulp-sourcemaps": "^1.6.0", + "gulp-uglify": "^2.0.0", + "jasmine-core": "^2.4.1", + "karma": "^0.13.19", + "karma-chrome-launcher": "^0.2.2", + "karma-coverage": "^0.5.3", + "karma-fixture": "^0.2.5", + "karma-html2js-preprocessor": "^0.1.0", + "karma-jasmine": "^0.3.6", + "karma-jasmine-def": "^0.1.0" + } +} diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/prism/prism.css b/src/EventHub.Web/wwwroot/libs/awesomplete/prism/prism.css new file mode 100644 index 0000000..4606f50 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/prism/prism.css @@ -0,0 +1,139 @@ +/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+json */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #a67f59; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/prism/prism.js b/src/EventHub.Web/wwwroot/libs/awesomplete/prism/prism.js new file mode 100644 index 0000000..7ad05a3 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/prism/prism.js @@ -0,0 +1,7 @@ +/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+json */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=_self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),P=[p,1];b&&P.push(b);var A=new a(i,g?t.tokenize(m,g):m,h);P.push(A),w&&P.push(w),Array.prototype.splice.apply(r,P)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var l={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}t.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code,l=n.immediateClose;_self.postMessage(t.highlight(r,t.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; +Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; +Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; +Prism.languages.json={property:/".*?"(?=\s*:)/gi,string:/"(?!:)(\\?[^"])*?"(?!:)/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,punctuation:/[{}[\]);,]/g,operator:/:/g,"boolean":/\b(true|false)\b/gi,"null":/\bnull\b/gi},Prism.languages.jsonp=Prism.languages.json; diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/style.css b/src/EventHub.Web/wwwroot/libs/awesomplete/style.css new file mode 100644 index 0000000..68763ff --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/style.css @@ -0,0 +1,292 @@ +body { + max-width: 50rem; + padding: 1rem; + margin: auto; + background: hsl(35, 80%, 94%); + color: hsl(35, 50%, 20%); + text-shadow: 0 1px 1px white; + font: 120%/1.6 Baskerville, Palatino Linotype, Palatino, serif; +} + +a { + color: #58a; +} + +h1 { + text-align: center; + font-weight: 600; +} + + section > h1 { + margin-top: 2em; + } + +h2 { + color: hsl(35, 50%, 40%); + font-weight: 600; + font-size: 120%; +} + +ul { + margin: 0; + padding-left: .8em; +} + +strong { + font-weight: 600; +} + +abbr { + border-bottom: .1em dotted hsl(40, 80%, 40%); + cursor: help; +} + +input, button { + font: inherit; + text-shadow: inherit; +} + +button { + padding: .1em .5em; + border-radius: .3em; + background: hsl(80, 80%, 80%); + background: linear-gradient(hsl(40, 70%, 80%), hsl(40, 70%, 70%)); + border: 1px solid rgba(0,0,0,.3); + box-shadow: 0 1px white inset, 0 .3em .3em -.3em rgba(0,0,0,.3); +} + +input { + width: 12em; + padding: .1em .3em; + border: 0; + border: 1px solid hsl(35, 80%, 60%); + background: hsla(0,0%,100%,.2); + border-radius: .3em; + box-shadow: .05em .1em .3em rgba(0,0,0,.3) inset; +} + +@keyframes pulsate { + to { + box-shadow: .05em .1em .3em rgba(0,0,0,.3) inset, 0 0 .3em .1em #58a; + } +} + +input:focus { + outline: none; + border: 1px solid #58a; + animation: pulsate 2s infinite alternate linear; + background: hsla(0,0%,100%,.5); +} + +header { + max-width: 37rem; + margin: 0 auto 2em; + text-align: center; + font-size: 150%; + font-style: italic; +} + + header h1 { + margin: .1em 0; + text-align: center; + color: hsl(35, 50%, 40%); + font-size: 400%; + line-height: 1; + font-style: italic; + font-weight: normal; + } + + header p { + margin: 0; + } + + header p a { + text-decoration: none; + color: inherit; + } + + .size { + position: absolute; + top: 1em; + right: 1em; + width: 4em; + padding: .8em .6em 1em; + background: hsl(40, 80%, 40%); + color: hsl(35, 80%, 94%); + text-shadow: none; + text-align: center; + line-height: 1.1; + text-indent: -.1em; + outline: .1em dotted; + outline-offset: -.3em; + text-decoration: none; + border-radius: .15em; + } + + .size:hover { + background: #58a; + } + + .size strong { + display: block; + margin-bottom: .1em; + font-size: 150%; + line-height: 1; + font-weight: 900; + font-style: normal; + } + + .size .amp { + position: absolute; + left: 0; right: 0; + bottom: 0; + color: hsla(35, 80%, 94%,.3); + font-size: 350%; + } + + nav { + margin-top: .5em; + } + + nav a { + position: relative; + display: inline-block; + padding: 0 .4em; + margin: 0 .1em; + + color: hsl(35, 80%, 94%); + text-shadow: none; + text-decoration: none; + font-size: 80%; + } + + nav a::before { + content: ""; + position: absolute; + top: 0; left: 0; right: 0; bottom: 0; + z-index: -1; + border-radius: .2em; + background: hsl(40, 80%, 40%); + -webkit-transform: skew(-16deg); + transform: skew(-16deg); + } + + nav a[href="#download"] { + font-weight: 600; + } + + nav a[href="#download"]::before { + background: hsl(65, 85%, 35%); + } + + nav a:hover::before { + background: #58a; + } + + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: hsl(35, 70%, 88%); + font-size: 80%; + border-radius: .3em; +} + +footer { + padding: .6em; + border-top: 1px solid rgba(0,0,0,.3); + margin-top: 1em; + text-align: center; +} + +pre { + position: relative; +} + +pre::before { + position: absolute; + top: 0; + right: .5em; + padding: .3em .4em; + border-radius: 0 0 .3em .3em; + background: hsl(35, 50%, 60%); + color: hsl(35, 80%, 90%); + text-shadow: none; + font: bold 100%/1 Baskerville, Palatino Linotype, Palatino, serif; +} + +pre.language-markup::before { + content: "HTML"; +} + +pre.language-javascript::before { + content: "JS"; +} + +table { + table-layout: fixed; + border-spacing: 0; + border-collapse: collapse; +} + +th { + font-size: 75%; +} + +td { + vertical-align: top; + padding: .5em; + border: 1px solid rgba(0,0,0,.1); +} + +:not(pre) > code[class*="language-"] { + background: none; +} + +.github-star { + position: absolute; + top: 1em; + left: 1em; +} + +#carbonads { + position: absolute; + top: 100%; + top: calc(100vh - 15em); + right: 1em; + max-width: 10em; + max-width: -webkit-min-content; + max-width: min-content; + font-size: 75%; +} + + #carbonads a { + text-decoration: none; + color: inherit; + } + + #carbonads img { + display: block; + } + +#combobox .dropdown-input { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +#combobox .dropdown-btn { + vertical-align: top; + height: 36.5px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +#combobox .caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/closeSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/closeSpec.js new file mode 100644 index 0000000..ae1bea0 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/closeSpec.js @@ -0,0 +1,39 @@ +describe("awesomplete.close", function () { + + $.fixture("plain"); + + subject(function () { return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }) }); + + beforeEach(function () { + $.type(this.subject.input, "ite"); + this.subject.open(); + this.subject.next(); + }); + + it("closes completer", function () { + this.subject.close(); + expect(this.subject.ul.hasAttribute("hidden")).toBe(true); + }); + + it("makes no item selected", function () { + this.subject.close(); + expect(this.subject.index).toBe(-1); + }); + + it("fires awesomplete-close event", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-close"); + this.subject.close(); + + expect(handler).toHaveBeenCalledWith( + jasmine.any(document.createEvent("HTMLEvents").constructor) + ); + }); + + it("returns early if already closed", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-close"); + this.subject.close(); + this.subject.close(); + + expect(handler.calls.count()).toBe(1); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/evaluateSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/evaluateSpec.js new file mode 100644 index 0000000..b8107bb --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/evaluateSpec.js @@ -0,0 +1,96 @@ +describe("awesomplete.evaluate", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + describe("with too short input value", function () { + beforeEach(function () { + $.type(this.subject.input, "i"); + }); + + it("closes completer", function () { + spyOn(this.subject, "close"); + this.subject.evaluate(); + + expect(this.subject.close).toHaveBeenCalledWith({ + reason: "nomatches" + }); + }); + }); + + describe("with no items found", function () { + beforeEach(function () { + $.type(this.subject.input, "nosuchitem"); + }); + + it("closes completer", function () { + spyOn(this.subject, "close"); + this.subject.evaluate(); + + expect(this.subject.close).toHaveBeenCalledWith({ + reason: "nomatches" + }); + }); + }); + + describe("with some items found", function () { + beforeEach(function () { + $.type(this.subject.input, "ite"); + }); + + it("opens completer", function () { + spyOn(this.subject, "open"); + this.subject.evaluate(); + + expect(this.subject.open).toHaveBeenCalled(); + }); + + it("fills completer with found items", function () { + this.subject.evaluate(); + expect(this.subject.ul.children.length).toBe(3); + }); + + it("shows no more than maxItems", function () { + this.subject.maxItems = 2; + this.subject.evaluate(); + expect(this.subject.ul.children.length).toBe(2); + }); + + it("makes no item selected", function () { + this.subject.evaluate(); + expect(this.subject.index).toBe(-1); + }); + }); + + describe("with minChars: 0", function () { + beforeEach(function () { + this.subject.minChars = 0; + }); + + it("opens completer", function () { + spyOn(this.subject, "open"); + this.subject.evaluate(); + + expect(this.subject.open).toHaveBeenCalled(); + }); + + it("fills completer with all items", function () { + this.subject.evaluate(); + expect(this.subject.ul.children.length).toBe(3); + }); + + it("shows no more than maxItems", function () { + this.subject.maxItems = 2; + this.subject.evaluate(); + expect(this.subject.ul.children.length).toBe(2); + }); + + it("makes no item selected", function () { + this.subject.evaluate(); + expect(this.subject.index).toBe(-1); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/gotoSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/gotoSpec.js new file mode 100644 index 0000000..8a31dde --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/gotoSpec.js @@ -0,0 +1,67 @@ +describe("awesomplete.goto", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + def("lastIndex", function () { return this.subject.ul.children.length - 1 }); + + beforeEach(function () { + $.type(this.subject.input, "ite"); + }); + + it("clears previous aria-selected", function () { + this.subject.goto(0); + this.subject.goto(this.lastIndex); + + expect(this.subject.ul.children[0].getAttribute("aria-selected")).toBe("false"); + }); + + it("goes to first item", function () { + this.subject.goto(0); + expect(this.subject.index).toBe(0); + }); + + it("goes to last item", function () { + this.subject.goto(this.lastIndex); + expect(this.subject.index).toBe(this.lastIndex); + }); + + it("fires awesomplete-highlight event", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-highlight"); + this.subject.goto(1); + + expect(handler).toHaveBeenCalledWith( + jasmine.objectContaining({ + text: jasmine.objectContaining({ label: "item2", value: "item2" }) + }) + ); + }); + + describe("with item index > -1", function () { + beforeEach(function () { + this.subject.goto(0); + }); + + it("sets aria-selected", function () { + expect(this.subject.ul.children[0].getAttribute("aria-selected")).toBe("true"); + }); + + it("updates status", function () { + expect(this.subject.status.textContent).toBe("item1, list item 1 of 3"); + }); + }); + + describe("with item index = -1", function () { + beforeEach(function () { + this.subject.goto(0); + this.subject.goto(-1); + }); + + it("does not update status", function () { + expect(this.subject.status.textContent).toBe("item1, list item 1 of 3"); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/nextSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/nextSpec.js new file mode 100644 index 0000000..461e57c --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/nextSpec.js @@ -0,0 +1,56 @@ +describe("awesomplete.next", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + def("lastIndex", function () { return this.subject.ul.children.length - 1 }); + + describe("without any items found", function () { + beforeEach(function () { + $.type(this.subject.input, "nosuchitem"); + this.subject.open(); + }); + + it("does not select any item", function () { + this.subject.next(); + expect(this.subject.index).toBe(-1); + }); + }); + + describe("with some items found", function () { + beforeEach(function () { + $.type(this.subject.input, "ite"); + this.subject.open(); + }); + + describe("and no item was already selected", function () { + it("selects the first item ", function () { + this.subject.next(); + expect(this.subject.index).toBe(0); + }); + }); + + describe("and some item was already selected", function () { + it("selects the second item", function () { + this.subject.goto(0); + this.subject.next(); + expect(this.subject.index).toBe(1); + }); + + it("selects the last item", function () { + this.subject.goto(this.lastIndex - 1); + this.subject.next(); + expect(this.subject.index).toBe(this.lastIndex); + }); + + it("selects the first item after reaching the end", function () { + this.subject.goto(this.lastIndex); + this.subject.next(); + expect(this.subject.index).toBe(0); + }); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/openSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/openSpec.js new file mode 100644 index 0000000..1bfc467 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/openSpec.js @@ -0,0 +1,59 @@ +describe("awesomplete.open", function () { + + $.fixture("plain"); + + subject(function () { return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }) }); + + // Exposes this bug https://github.com/LeaVerou/awesomplete/pull/16740 + // FIXME better fix is probably required as discussed in PR above + xit("fills in the list on creation", function () { + $("#plain").value = "ite"; + this.subject.open(); + + expect(this.subject.ul.children.length).toBe(3); + }); + + it("opens completer", function () { + this.subject.open(); + expect(this.subject.ul.hasAttribute("hidden")).toBe(false); + }); + + describe("with autoFirst: true", function () { + beforeEach(function () { + this.subject.autoFirst = true; + spyOn(this.subject, "goto"); + }); + + it("selects first item if wasn't seleted before", function () { + this.subject.open(); + + expect(this.subject.goto).toHaveBeenCalledWith(0); + }); + + it("does not select any item if was seleted before", function () { + this.subject.index = 0; + this.subject.open(); + + expect(this.subject.goto).not.toHaveBeenCalled(); + }); + }); + + describe("with autoFirst: false", function () { + it("does not select any item", function () { + this.subject.autoFirst = false; + spyOn(this.subject, "goto"); + + this.subject.open(); + + expect(this.subject.goto).not.toHaveBeenCalled(); + }); + }); + + it("fires awesomplete-open event", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-open"); + + this.subject.open(); + + expect(handler).toHaveBeenCalled(); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/openedSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/openedSpec.js new file mode 100644 index 0000000..370ffdc --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/openedSpec.js @@ -0,0 +1,26 @@ +describe("awesomplete.opened", function () { + + $.fixture("plain"); + + subject(function () { return new Awesomplete("#plain") }); + + describe("with newly created completer", function () { + it("is false", function () { + expect(this.subject.opened).toBe(false); + }); + }); + + describe("with opened completer", function () { + it("is true", function () { + this.subject.open(); + expect(this.subject.opened).toBe(true); + }); + }); + + describe("with closed completer", function () { + it("is false", function () { + this.subject.close(); + expect(this.subject.opened).toBe(false); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/previousSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/previousSpec.js new file mode 100644 index 0000000..ac1d93b --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/previousSpec.js @@ -0,0 +1,56 @@ +describe("awesomplete.previous", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + def("lastIndex", function () { return this.subject.ul.children.length - 1 }); + + describe("without any items found", function () { + beforeEach(function () { + $.type(this.subject.input, "nosuchitem"); + this.subject.open(); + }); + + it("does not select any item", function () { + this.subject.previous(); + expect(this.subject.index).toBe(-1); + }); + }); + + describe("with some items found", function () { + beforeEach(function () { + $.type(this.subject.input, "ite"); + this.subject.open(); + }); + + describe("and no item was already selected", function () { + it("selects the last item ", function () { + this.subject.previous(); + expect(this.subject.index).toBe(this.lastIndex); + }); + }); + + describe("and some item was already selected", function () { + it("selects the second item from the end", function () { + this.subject.goto(this.lastIndex); + this.subject.previous(); + expect(this.subject.index).toBe(this.lastIndex - 1); + }); + + it("selects the first item", function () { + this.subject.goto(1); + this.subject.previous(); + expect(this.subject.index).toBe(0); + }); + + it("selects the last item after reaching the start", function () { + this.subject.goto(0); + this.subject.previous(); + expect(this.subject.index).toBe(this.lastIndex); + }); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/removeSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/removeSpec.js new file mode 100644 index 0000000..5885e54 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/removeSpec.js @@ -0,0 +1,55 @@ +describe("awesomplete.destroy", function () { + + $.fixture("plain"); + + subject(function () { return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }) }); + + beforeEach(function () { + $.type(this.subject.input, "ite"); + this.subject.open(); + this.subject.next(); + }); + + it("removes its elements from the DOM", function () { + var instance = this.subject; + var inputParentBefore = this.subject.input.parentNode; + + instance.destroy(); + + expect(instance.input.parentNode).not.toBe(inputParentBefore); + expect(document.body.contains(instance.container)).toBeFalsy(); + expect(document.body.contains(instance.ul)).toBeFalsy() + }); + + it("removes the autocomplete and aria-autocomplete attributes", function() { + var instance = this.subject; + + instance.destroy(); + + expect(instance.input.getAttribute("autocomplete")).toBeNull(); + expect(instance.input.getAttribute("aria-autocomplete")).toBeNull(); + }); + + it("removes event listeners from form and input", function () { + var instance = this.subject; + var events = instance._events; + + spyOn(events.input, "blur"); + + instance.destroy(); + + Awesomplete.$.fire(instance.input, "blur"); + + expect(events.input.blur).not.toHaveBeenCalledTimes(1); + }); + + it("removes itself from the global list of instances", function() { + var countBefore = Awesomplete.all.length; + + this.subject.destroy(); + + var countAfter = Awesomplete.all.length; + + expect(countAfter).toBeLessThan(countBefore); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/selectSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/selectSpec.js new file mode 100644 index 0000000..f9fd2c9 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/selectSpec.js @@ -0,0 +1,132 @@ +describe("awesomplete.select", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + def("lastIndex", function () { return this.subject.ul.children.length - 1 }); + def("lastLi", function () { return this.subject.ul.children[this.lastIndex] }); + + beforeEach(function () { + $.type(this.subject.input, "ite"); + }); + + describe("with closed completer", itDoesNotSelectAnyItem); + + describe("with opened completer", function () { + beforeEach(function () { + this.subject.open(); + }); + + describe("and no current item", itDoesNotSelectAnyItem); + + describe("and current item", function () { + beforeEach(function () { + this.subject.goto(0); + }); + + itSelects("item1"); + }); + + describe("and item specified as argument", function () { + def("selectArgument", function () { return this.lastLi }); + + itSelects("item3"); + }); + }); + + // Shared behaviors + + function itSelects(expectedTxt) { + it("fires awesomplete-select event", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-select"); + this.subject.select(this.selectArgument); + + expect(handler).toHaveBeenCalledWith( + jasmine.objectContaining({ + text: jasmine.objectContaining({ label: expectedTxt, value: expectedTxt }), + origin: this.selectArgument || this.subject.ul.children[0] + }) + ); + }); + + describe("and awesomplete-select event was not prevented", function () { + beforeEach(function () { + $.on(this.subject.input, "awesomplete-select", $.noop); + }); + + it("changes the input value", function () { + this.subject.select(this.selectArgument); + expect(this.subject.input.value).toBe(expectedTxt); + }); + + it("closes completer", function () { + spyOn(this.subject, "close"); + this.subject.select(this.selectArgument); + + expect(this.subject.close).toHaveBeenCalledWith({ + reason: "select" + }); + }); + + it("fires awesomplete-selectcomplete event", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-selectcomplete"); + this.subject.select(this.selectArgument); + + expect(handler).toHaveBeenCalledWith( + jasmine.objectContaining({ + text: jasmine.objectContaining({ label: expectedTxt, value: expectedTxt }) + }) + ); + }); + }); + + describe("and awesomplete-select event was prevented", function () { + beforeEach(function () { + $.on(this.subject.input, "awesomplete-select", function (evt) { evt.preventDefault() }); + }); + + it("does not change the input value", function () { + this.subject.select(this.selectArgument); + expect(this.subject.input.value).toBe("ite"); + }); + + it("does not close completer", function () { + spyOn(this.subject, "close"); + this.subject.select(this.selectArgument); + + expect(this.subject.close).not.toHaveBeenCalled(); + }); + + it("does not fire awesomplete-selectcomplete event", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-selectcomplete"); + this.subject.select(this.selectArgument); + + expect(handler).not.toHaveBeenCalled(); + }); + }); + } + + function itDoesNotSelectAnyItem() { + it("does not change the input value", function () { + this.subject.select(); + expect(this.subject.input.value).toBe("ite"); + }); + + it("does not fire awesomplete-select event", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-select"); + this.subject.select(); + + expect(handler).not.toHaveBeenCalled(); + }); + + it("does not fire awesomplete-selectcomplete event", function () { + var handler = $.spyOnEvent(this.subject.input, "awesomplete-selectcomplete"); + this.subject.select(); + + expect(handler).not.toHaveBeenCalled(); + }); + } +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/selectedSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/selectedSpec.js new file mode 100644 index 0000000..6e645be --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/api/selectedSpec.js @@ -0,0 +1,41 @@ +describe("awesomplete.selected", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + describe("with newly created completer", function () { + it("is false", function () { + expect(this.subject.selected).toBe(false); + }); + }); + + describe("with opened completer", function () { + beforeEach(function () { + this.subject.open(); + $.type(this.subject.input, "ite"); + }); + + describe("and no item selected", function () { + it("is false", function () { + expect(this.subject.selected).toBe(false); + }); + }); + + describe("and some item selected", function () { + it("is true", function () { + this.subject.next(); + expect(this.subject.selected).toBe(true); + }); + }); + }); + + describe("with closed completer", function () { + it("is false", function () { + this.subject.close(); + expect(this.subject.selected).toBe(false); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/blurSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/blurSpec.js new file mode 100644 index 0000000..77d6f38 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/blurSpec.js @@ -0,0 +1,15 @@ +describe("blur event", function () { + + $.fixture("plain"); + + subject(function () { return new Awesomplete("#plain") }); + + it("closes completer", function () { + spyOn(Awesomplete.prototype, "close"); + $.fire(this.subject.input, "blur"); + expect(Awesomplete.prototype.close).toHaveBeenCalledWith( + { reason: "blur" }, + jasmine.any(document.createEvent("HTMLEvents").constructor) + ); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/clickSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/clickSpec.js new file mode 100644 index 0000000..e1b08b3 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/clickSpec.js @@ -0,0 +1,61 @@ +describe("click event", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + def("li", function () { return this.subject.ul.children[1] }); + + beforeEach(function () { + $.type(this.subject.input, "ite"); + spyOn(this.subject, "select"); + }); + + describe("with ul target", function () { + def("target", function () { return this.subject.ul }); + + it("does not select item", function () { + $.fire(this.target, "click", { button: 0 }); + expect(this.subject.select).not.toHaveBeenCalled(); + }); + }); + + describe("with li target", function () { + def("target", function () { return this.li }); + + describe("on left click", function () { + it("selects item", function () { + var event = $.fire(this.target, "click", { button: 0 }); + expect(this.subject.select).toHaveBeenCalledWith(this.li, this.target, event); + }); + }); + + describe("on right click", function () { + it("does not select item", function () { + $.fire(this.target, "click", { button: 2 }); + expect(this.subject.select).not.toHaveBeenCalled(); + }); + }); + }); + + describe("with child of li target", function () { + def("target", function () { return $("mark", this.li) }); + + describe("on left click", function () { + it("selects item", function () { + var event = $.fire(this.target, "click", { button: 0 }); + expect(this.subject.select).toHaveBeenCalledWith(this.li, this.target, event); + expect(event.defaultPrevented).toBe(true); + }); + }); + + describe("on right click", function () { + it("does not select item", function () { + $.fire(this.target, "click", { button: 2 }); + expect(this.subject.select).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/inputSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/inputSpec.js new file mode 100644 index 0000000..b32091e --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/inputSpec.js @@ -0,0 +1,12 @@ +describe("input event", function () { + + $.fixture("plain"); + + subject(function () { return new Awesomplete("#plain") }); + + it("rebuilds the list", function () { + spyOn(Awesomplete.prototype, "evaluate"); + $.type(this.subject.input, "ite"); + expect(Awesomplete.prototype.evaluate).toHaveBeenCalled(); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/keydownSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/keydownSpec.js new file mode 100644 index 0000000..9e13068 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/keydownSpec.js @@ -0,0 +1,96 @@ +describe("keydown event", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + beforeEach(function () { + $.type(this.subject.input, "ite"); + }); + + it("supports enter", function () { + this.subject.next(); + + spyOn(this.subject, "select"); + $.keydown(this.subject.input, $.k.ENTER); + + expect(this.subject.select).toHaveBeenCalled(); + }); + + it("supports escape", function () { + spyOn(this.subject, "close"); + $.keydown(this.subject.input, $.k.ESC); + + expect(this.subject.close).toHaveBeenCalledWith({ + reason: "esc" + }); + }); + + it("supports down arrow", function () { + spyOn(this.subject, "next"); + $.keydown(this.subject.input, $.k.DOWN); + + expect(this.subject.next).toHaveBeenCalled(); + }); + + it("supports up arrow", function () { + spyOn(this.subject, "previous"); + $.keydown(this.subject.input, $.k.UP); + + expect(this.subject.previous).toHaveBeenCalled(); + }); + + it("ignores other keys", function() { + spyOn(this.subject, "select"); + spyOn(this.subject, "close"); + spyOn(this.subject, "next"); + spyOn(this.subject, "previous"); + + $.keydown(this.subject.input, 111); + + expect(this.subject.select).not.toHaveBeenCalled(); + expect(this.subject.close).not.toHaveBeenCalled(); + expect(this.subject.next).not.toHaveBeenCalled(); + expect(this.subject.previous).not.toHaveBeenCalled(); + }); + + it("does nothing if not opened", function () { + this.subject.close(); + + spyOn(this.subject, "next"); + $.keydown(this.subject.input, $.k.DOWN); + + expect(this.subject.next).not.toHaveBeenCalled(); + }); + it("does not select on tab", function () { + this.subject.next(); + + spyOn(this.subject, "select"); + $.keydown(this.subject.input, $.k.TAB); + + expect(this.subject.select).not.toHaveBeenCalled(); + }); +}); + +describe("tabSelect option true ", function () { + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"], tabSelect: true }); + }); + + beforeEach(function () { + $.type(this.subject.input, "ite"); + }); + + it("selects on tab", function () { + this.subject.next(); + + spyOn(this.subject, "select"); + $.keydown(this.subject.input, $.k.TAB); + + expect(this.subject.select).toHaveBeenCalled(); + }); +}) diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/mousedownSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/mousedownSpec.js new file mode 100644 index 0000000..88f9783 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/mousedownSpec.js @@ -0,0 +1,69 @@ +describe("mousedown event", function () { + + $.fixture("plain"); + + subject(function () { + return new Awesomplete("#plain", { list: ["item1", "item2", "item3"] }); + }); + + def("li", function () { return this.subject.ul.children[1] }); + + beforeEach(function () { + $.type(this.subject.input, "ite"); + spyOn(this.subject, "select"); + }); + + describe("with ul target", function () { + def("target", function () { return this.subject.ul }); + + it("does not select item and keeps the input focussed", function () { + $.fire(this.target, "mousedown", { button: 0 }); + expect(this.subject.select).not.toHaveBeenCalled(); + expect(this.subject.input).toBe(document.activeElement); + }); + }); + + describe("with li target", function () { + def("target", function () { return this.li }); + + describe("on left click", function () { + it("does not select item and keeps the input focussed", function () { + var event = $.fire(this.target, "mousedown", { button: 0 }); + expect(this.subject.select).not.toHaveBeenCalled(); + expect(event.defaultPrevented).toBe(true); + expect(this.subject.input).toBe(document.activeElement); + }); + }); + + describe("on right click", function () { + it("does not select item and keeps the input focussed", function () { + var event = $.fire(this.target, "mousedown", { button: 2 }); + expect(this.subject.select).not.toHaveBeenCalled(); + expect(event.defaultPrevented).toBe(true); + expect(this.subject.input).toBe(document.activeElement); + }); + }); + }); + + describe("with child of li target", function () { + def("target", function () { return $("mark", this.li) }); + + describe("on left click", function () { + it("does not select item and keeps the input focussed", function () { + var event = $.fire(this.target, "mousedown", { button: 0 }); + expect(this.subject.select).not.toHaveBeenCalled(); + expect(event.defaultPrevented).toBe(true); + expect(this.subject.input).toBe(document.activeElement); + }); + }); + + describe("on right click", function () { + it("does not select item and keeps the input focussed", function () { + var event = $.fire(this.target, "mousedown", { button: 2 }); + expect(this.subject.select).not.toHaveBeenCalled(); + expect(event.defaultPrevented).toBe(true); + expect(this.subject.input).toBe(document.activeElement); + }); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/submitSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/submitSpec.js new file mode 100644 index 0000000..2e55744 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/events/submitSpec.js @@ -0,0 +1,17 @@ +describe("form submit event", function () { + + $.fixture("options"); + + subject(function () { return new Awesomplete("#inside-form") }); + + it("closes completer", function () { + spyOn(Awesomplete.prototype, "close"); + // prevent full page reload in Firefox, which causes tests to stop running + $.on(this.subject.input.form, "submit", function (evt) { evt.preventDefault() }); + $.fire(this.subject.input.form, "submit"); + expect(Awesomplete.prototype.close).toHaveBeenCalledWith( + { reason: "submit" }, + jasmine.any(document.createEvent("HTMLEvents").constructor) + ); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/fixtures/options.html b/src/EventHub.Web/wwwroot/libs/awesomplete/test/fixtures/options.html new file mode 100644 index 0000000..996992b --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/fixtures/options.html @@ -0,0 +1,22 @@ + + + + + + + +
    +
  • With
  • +
  • Data
  • +
  • List
  • +
+ + + + + + + +
+ +
diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/fixtures/plain.html b/src/EventHub.Web/wwwroot/libs/awesomplete/test/fixtures/plain.html new file mode 100644 index 0000000..9869bcc --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/fixtures/plain.html @@ -0,0 +1 @@ + diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/bindSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/bindSpec.js new file mode 100644 index 0000000..c1e7ac1 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/bindSpec.js @@ -0,0 +1,59 @@ +describe("Awesomplete.$.bind", function () { + + $.fixture("plain"); + + subject(function () { + return function () { Awesomplete.$.bind(this.element, this.events) }; + }); + + describe("with invalid element", function () { + it("does nothing if element is undefined", function () { + this.element = undefined; + expect(this.subject).not.toThrow(); + }); + + it("does nothing if element is null", function () { + this.element = null; + expect(this.subject).not.toThrow(); + }); + + it("does nothing if element is false", function () { + this.element = false; + expect(this.subject).not.toThrow(); + }); + + it("does nothing if element is 0", function () { + this.element = 0; + expect(this.subject).not.toThrow(); + }); + + it("does nothing if element is empty string", function () { + this.element = ""; + expect(this.subject).not.toThrow(); + }); + }); + + describe("with valid element", function () { + def("element", function () { return $("#plain") }); + + beforeEach(function () { + spyOn(this.element, "addEventListener"); + }); + + it("adds event listeners for all events", function () { + this.events = { click: $.noop, input: $.noop }; + this.subject(); + + expect(this.element.addEventListener).toHaveBeenCalledWith("click", this.events.click); + expect(this.element.addEventListener).toHaveBeenCalledWith("input", this.events.input); + }); + + it("adds single event listener for multiple events", function () { + this.events = { "click input": $.noop }; + this.subject(); + + expect(this.element.addEventListener).toHaveBeenCalledWith("click", this.events["click input"]); + expect(this.element.addEventListener).toHaveBeenCalledWith("input", this.events["click input"]); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/createSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/createSpec.js new file mode 100644 index 0000000..a9ef31e --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/createSpec.js @@ -0,0 +1,102 @@ +describe("Awesomplete.$.create", function () { + + $.fixture("options"); + + subject(function () { return Awesomplete.$.create(this.tag, this.options || {}) }); + + def("tag", "div"); + + it("creates DOM element", function () { + expect(this.subject instanceof HTMLElement).toBe(true); + }); + + describe("with various tag names", function () { + it("creates
    element", function () { + this.tag = "ul"; + expect(this.subject.tagName).toEqual("UL"); + }); + + it("creates
  • element", function () { + this.tag = "li"; + expect(this.subject.tagName).toEqual("LI"); + }); + }); + + describe("without options", function () { + it("creates element without any attributes", function () { + expect(this.subject.attributes.length).toEqual(0); + }); + }); + + describe("with simple options", function () { + it("assigns properties", function () { + this.options = { id: "id1", className: "class-name" }; + + expect(this.subject.id).toEqual("id1"); + expect(this.subject.className).toEqual("class-name"); + }); + + it("assigns attributes", function () { + this.options = { attr1: "val1", attr2: "val2" }; + + expect(this.subject.getAttribute("attr1")).toEqual("val1"); + expect(this.subject.getAttribute("attr2")).toEqual("val2"); + }); + }); + + describe("with option for boolean attribute/property", function () { + it("assigns from true value", function () { + this.options = { hidden: true }; + expect(this.subject.hasAttribute("hidden")).toBe(true); + }); + + it("assigns from truthy value", function () { + this.options = { hidden: "hidden" }; + expect(this.subject.hasAttribute("hidden")).toBe(true); + }); + + it("assigns from false value", function () { + this.options = { hidden: false }; + expect(this.subject.hasAttribute("hidden")).toBe(false); + }); + + it("assigns from falsy value", function () { + this.options = { hidden: "" }; + expect(this.subject.hasAttribute("hidden")).toBe(false); + }); + }); + + describe("with inside: option", function () { + it("appends to container by element", function () { + this.options = { inside: $("#data-list") }; + + expect(this.subject).toEqual(this.options.inside.lastChild); + }); + + it("appends to container by selector", function () { + this.options = { inside: "#data-list" }; + + expect(this.subject).toEqual($(this.options.inside).lastChild); + }); + }); + + describe("with around: option", function () { + it("wraps specified element", function () { + this.options = { around: $("#no-options") }; + + var originalParent = this.options.around.parentNode; + expect(this.subject.parentNode).toEqual(originalParent); + + expect(this.subject.firstChild).toEqual(this.options.around); + }); + + it("wraps element specified by selector", function () { + this.options = { around: "#no-options" }; + + var originalParent = $(this.options.around).parentNode; + expect(this.subject.parentNode).toEqual(originalParent); + + expect(this.subject.firstChild).toEqual($(this.options.around)); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/dollarSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/dollarSpec.js new file mode 100644 index 0000000..3ba864e --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/dollarSpec.js @@ -0,0 +1,62 @@ +describe("Awesomplete.$", function () { + + $.fixture("options"); + + subject(function () { return Awesomplete.$(this.expression, this.context) }); + + describe("with default context", itFindsElement); + + describe("with custom context", function () { + def("context", function () { return fixture.el }); + + itFindsElement(); + }); + + describe("with truthy non string expression", function () { + it("returns the expression back", function () { + this.expression = $("#no-options"); + expect(this.subject).toBe(this.expression); + }); + }); + + describe("with falsy non string expression", function () { + it("returns null if expression is undefined", function () { + this.expression = undefined; + expect(this.subject).toBeNull(); + }); + + it("returns null if expression is null", function () { + this.expression = null; + expect(this.subject).toBeNull(); + }); + + it("returns null if expression is false", function () { + this.expression = false; + expect(this.subject).toBeNull(); + }); + }); + + // Shared behaviors + + function itFindsElement() { + it("returns DOM element", function () { + this.expression = "#no-options"; + expect(this.subject instanceof HTMLElement).toBe(true); + }); + + it("finds by id", function () { + this.expression = "#no-options"; + expect(this.subject.id).toEqual("no-options"); + }); + + it("finds by class name", function () { + this.expression = ".simple-input"; + expect(this.subject.id).toEqual("no-options"); + }); + + it("finds by tag name", function () { + this.expression = "datalist"; + expect(this.subject.id).toEqual("list"); + }); + } +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/doubleDollarSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/doubleDollarSpec.js new file mode 100644 index 0000000..c76a10d --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/doubleDollarSpec.js @@ -0,0 +1,49 @@ +describe("Awesomplete.$$", function () { + + $.fixture("options"); + + subject(function () { return Awesomplete.$$(this.expression, this.context) }); + + describe("with default context", itFindsAllElements); + + describe("with custom context", function () { + def("context", function () { return fixture.el }); + + itFindsAllElements(); + }); + + // Shared behaviors + + function itFindsAllElements() { + it("returns an array of DOM elements", function () { + this.expression = "#no-options"; + expect(this.subject).toEqual(jasmine.any(Array)); + expect(this.subject[0] instanceof HTMLElement).toBe(true); + }); + + it("finds all elements", function () { + this.expression = "input"; + expect(this.subject.length).toEqual($$("input").length); + }); + + it("finds DOM element", function () { + this.expression = "#no-options"; + expect(this.subject[0] instanceof HTMLElement).toBe(true); + }); + + it("finds by id", function () { + this.expression = "#no-options"; + expect(this.subject[0].id).toEqual("no-options"); + }); + + it("finds by class name", function () { + this.expression = ".simple-input"; + expect(this.subject[0].id).toEqual("no-options"); + }); + + it("finds by tag name", function () { + this.expression = "datalist"; + expect(this.subject[0].id).toEqual("list"); + }); + } +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/fireSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/fireSpec.js new file mode 100644 index 0000000..5d5dcbb --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/fireSpec.js @@ -0,0 +1,60 @@ +describe("Awesomplete.$.fire", function () { + + $.fixture("plain"); + + subject(function () { + return Awesomplete.$.fire.bind(Awesomplete.$, this.element); + }); + + def("element", function () { return $("#plain") }); + + beforeEach(function () { + spyOn(this.element, "dispatchEvent"); + }); + + it("fires event once", function () { + this.subject("click"); + expect(this.element.dispatchEvent.calls.count()).toEqual(1); + }); + + describe("fires different event types", function () { + it("fires click event", function () { + this.subject("click"); + expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ type: "click" })); + }); + + it("fires input event", function () { + this.subject("input"); + expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ type: "input" })); + }); + }); + + describe("sets event properties", function () { + it("makes cancelable event", function () { + this.subject("click"); + expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ cancelable: true })); + }); + + it("can't make non cancelable event", function () { + this.subject("click", { cancelable: false }); + expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ cancelable: true })); + }); + + it("makes event that bubbles", function () { + this.subject("click"); + expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ bubbles: true })); + }); + + it("can't make event that does not bubble", function () { + this.subject("click", { bubbles: false }); + expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ bubbles: true })); + }); + + it("sets properties on the event", function () { + var properties = { text: "hello", preventDefault: $.noop }; + + this.subject("click", properties); + expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining(properties)); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/regExpEscapeSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/regExpEscapeSpec.js new file mode 100644 index 0000000..c50167b --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/regExpEscapeSpec.js @@ -0,0 +1,38 @@ +describe("Awesomplete.$.regExpEscape", function () { + + subject(function () { return Awesomplete.$.regExpEscape(this.str) }); + + describe("with regular expression special characters", function () { + it("escapes backslashes", function () { + this.str = "\\"; + expect(this.subject).toBe("\\\\"); + }); + + it("escapes brackets, braces and parentheses", function () { + this.str = "[]{}()"; + expect(this.subject).toBe("\\[\\]\\{\\}\\(\\)"); + }); + + it("escapes other special characters", function () { + this.str = "-^$*+?.|"; + expect(this.subject).toBe("\\-\\^\\$\\*\\+\\?\\.\\|"); + }); + + it("escapes the whole string", function () { + this.str = "**"; + expect(this.subject).toBe("\\*\\*"); + }); + }); + + describe("with plain characters", function () { + it("does not escape letters", function () { + this.str = "abcdefjhijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ"; + expect(this.subject).toBe(this.str); + }); + + it("does not escape numbers", function () { + this.str = "0123456789"; + expect(this.subject).toBe(this.str); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/unbindSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/unbindSpec.js new file mode 100644 index 0000000..0ea5a50 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/helpers/unbindSpec.js @@ -0,0 +1,59 @@ +describe("Awesomplete.$.unbind", function () { + + $.fixture("plain"); + + subject(function () { + return function () { Awesomplete.$.unbind(this.element, this.events) }; + }); + + describe("with invalid element", function () { + it("does nothing if element is undefined", function () { + this.element = undefined; + expect(this.subject).not.toThrow(); + }); + + it("does nothing if element is null", function () { + this.element = null; + expect(this.subject).not.toThrow(); + }); + + it("does nothing if element is false", function () { + this.element = false; + expect(this.subject).not.toThrow(); + }); + + it("does nothing if element is 0", function () { + this.element = 0; + expect(this.subject).not.toThrow(); + }); + + it("does nothing if element is empty string", function () { + this.element = ""; + expect(this.subject).not.toThrow(); + }); + }); + + describe("with valid element", function () { + def("element", function () { return $("#plain") }); + + beforeEach(function () { + spyOn(this.element, "removeEventListener"); + }); + + it("removes event listeners for all events", function () { + this.events = { click: $.noop, input: $.noop }; + this.subject(); + + expect(this.element.removeEventListener).toHaveBeenCalledWith("click", this.events.click); + expect(this.element.removeEventListener).toHaveBeenCalledWith("input", this.events.input); + }); + + it("removes single event listener for multiple events", function () { + this.events = { "click input": $.noop }; + this.subject(); + + expect(this.element.removeEventListener).toHaveBeenCalledWith("click", this.events["click input"]); + expect(this.element.removeEventListener).toHaveBeenCalledWith("input", this.events["click input"]); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/htmlSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/htmlSpec.js new file mode 100644 index 0000000..994bf21 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/htmlSpec.js @@ -0,0 +1,60 @@ +describe("Html modifications", function () { + + $.fixture("plain"); + + subject(function () { return new Awesomplete("#plain") }); + + it("binds to correct input", function () { + expect(this.subject.input instanceof HTMLElement).toBe(true); + expect(this.subject.input.id).toBe("plain"); + }); + + it("turns native autocompleter off", function () { + expect(this.subject.input.getAttribute("autocomplete")).toBe("off"); + }); + + describe("HTML tweaks", function () { + it("creates container", function () { + expect(this.subject.container instanceof HTMLElement).toBe(true); + expect(this.subject.container.className).toBe("awesomplete"); + }); + + it("places input inside container", function () { + expect(this.subject.input.parentNode).toBe(this.subject.container); + }); + + it("creates list", function () { + expect(this.subject.ul instanceof HTMLElement).toBe(true); + expect(this.subject.ul.tagName).toBe("UL"); + }); + + it("puts list inside container", function () { + expect(this.subject.ul.parentNode).toBe(this.subject.container); + }); + + it("hides list", function () { + expect(this.subject.ul.hasAttribute("hidden")).toBe(true); + }); + }); + + describe("ARIA support", function () { + it("makes input accessible", function () { + expect(this.subject.input.getAttribute("role")).toBe("combobox"); + expect(this.subject.input.getAttribute("aria-owns")).toMatch(/awesomplete_list_[0-9]+/); + }); + + it("creates status", function () { + expect(this.subject.status instanceof HTMLElement).toBe(true); + expect(this.subject.status.getAttribute("role")).toBe("status"); + expect(this.subject.status.getAttribute("aria-live")).toBe("assertive"); + }); + + it("puts status inside container", function () { + expect(this.subject.status.parentNode).toBe(this.subject.container); + }); + + it("hides status", function () { + expect(this.subject.status.className).toBe("visually-hidden"); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/listSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/listSpec.js new file mode 100644 index 0000000..eff78d6 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/listSpec.js @@ -0,0 +1,121 @@ +describe("Awesomplete list", function () { + + $.fixture("options"); + + subject(function () { return new Awesomplete(this.element, this.options) }); + + def("element", "#no-options"); + + it("is empty if not provided", function () { + expect(this.subject._list).toEqual([]); + }); + + describe("setter", function () { + it("assigns from array", function () { + this.subject.list = [ "From", "Array" ]; + expect(this.subject._list).toEqual([ "From", "Array" ]); + }); + + it("assigns from comma separated list", function () { + this.subject.list = "From, Inline, List"; + expect(this.subject._list).toEqual([ "From", "Inline", "List" ]); + }); + + it("assigns from element specified by selector", function () { + this.subject.list = "#data-list"; + expect(this.subject._list).toEqual([ + { label: "With", value: "With" }, + { label: "Data", value: "Data" }, + { label: "List", value: "List" } + ]); + }); + + it("assigns from element", function () { + this.subject.list = $("#data-list"); + expect(this.subject._list).toEqual([ + { label: "With", value: "With" }, + { label: "Data", value: "Data" }, + { label: "List", value: "List" } + ]); + }); + + it("does not assigns from not found list", function () { + this.subject.list = "#nosuchlist"; + expect(this.subject._list).toEqual([]); + }); + + it("does not assigns from empty list", function () { + this.subject.list = "#empty-list"; + expect(this.subject._list).toEqual([]); + }); + + describe("with active input", function() { + beforeEach(function() { + this.subject.input.focus(); + }); + + it("evaluates completer", function() { + spyOn(this.subject, "evaluate"); + this.subject.list = "#data-list"; + + expect(this.subject.evaluate).toHaveBeenCalled(); + }); + }); + }); + + describe("constructor option", function () { + it("assigns from array", function () { + this.options = { list: [ "From", "Array" ] }; + expect(this.subject._list).toEqual([ "From", "Array" ]); + }); + + it("assigns from comma separated list", function () { + this.options = { list: "From, Inline, List" }; + expect(this.subject._list).toEqual([ "From", "Inline", "List" ]); + }); + + it("assigns from element specified by selector", function () { + this.options = { list: "#data-list" }; + expect(this.subject._list).toEqual([ + { label: "With", value: "With" }, + { label: "Data", value: "Data" }, + { label: "List", value: "List" } + ]); + }); + + it("assigns from list specified by element", function () { + this.options = { list: $("#data-list") }; + expect(this.subject._list).toEqual([ + { label: "With", value: "With" }, + { label: "Data", value: "Data" }, + { label: "List", value: "List" } + ]); + }); + }); + + describe("data-list html attribute", function () { + it("assigns from comma separated list", function () { + this.element = "#with-data-list-inline"; + expect(this.subject._list).toEqual(["With", "Data", "List", "Inline"]); + }); + + it("assigns from element referenced by selector", function () { + this.element = "#with-data-list"; + expect(this.subject._list).toEqual([ + { label: "With", value: "With" }, + { label: "Data", value: "Data" }, + { label: "List", value: "List" } + ]); + }); + }); + + describe("list html attribute", function () { + it("assigns from element referenced by id", function () { + this.element = "#with-list"; + expect(this.subject._list).toEqual([ + { label: "With", value: "With" }, + { label: "List", value: "List" } + ]); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/optionsSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/optionsSpec.js new file mode 100644 index 0000000..fc60997 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/init/optionsSpec.js @@ -0,0 +1,80 @@ +describe("Constructor options", function () { + + $.fixture("options"); + + subject(function () { return new Awesomplete(this.element, this.options) }); + + describe("with default options", function () { + def("element", "#with-data-list"); + + it("requires minimum 2 chars to open completer", function () { + expect(this.subject.minChars).toBe(2); + }); + + it("shows 10 items in completer", function () { + expect(this.subject.maxItems).toBe(10); + }); + + it("does not select the first ocurrence automatically" , function () { + expect(this.subject.autoFirst).toBe(false); + }); + + it("modifies list item with DATA", function () { + expect(this.subject.data).toBe(Awesomplete.DATA); + }); + + it("filters with FILTER_CONTAINS", function () { + expect(this.subject.filter).toBe(Awesomplete.FILTER_CONTAINS); + }); + + it("orders with SORT_BYLENGTH", function () { + expect(this.subject.sort).toBe(Awesomplete.SORT_BYLENGTH); + }); + + it("creates item with ITEM", function () { + expect(this.subject.item).toEqual(Awesomplete.ITEM); + }); + + it("replaces input value with REPLACE", function () { + expect(this.subject.replace).toEqual(Awesomplete.REPLACE); + }); + }); + + describe("with custom options in constructor", function () { + def("element", "#with-data-list"); + def("options", function () { + return { + minChars: 3, + maxItems: 9, + autoFirst: true, + filter: $.noop, + sort: $.noop, + item: $.noop, + replace: $.noop + }; + }); + + it("overrides simple default options", function () { + expect(this.subject.minChars).toBe(3); + expect(this.subject.maxItems).toBe(9); + expect(this.subject.autoFirst).toBe(true); + }); + + it("overrides default functions", function () { + expect(this.subject.filter).toBe(this.options.filter); + expect(this.subject.sort).toBe(this.options.sort); + expect(this.subject.item).toBe(this.options.item); + expect(this.subject.replace).toBe(this.options.replace); + }); + }); + + describe("with custom options in data-* attributes", function () { + def("element", "#with-custom-options"); + + it("overrides simple default options", function () { + expect(this.subject.minChars).toBe(4); + expect(this.subject.maxItems).toBe(8); + expect(this.subject.autoFirst).toBe(true); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/specHelper.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/specHelper.js new file mode 100644 index 0000000..c614e87 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/specHelper.js @@ -0,0 +1,70 @@ +fixture.setBase("test/fixtures"); + +// finds DOM elements in tests +function $ (str, context) { + return (context || fixture.el).querySelector(str); +} + +function $$ (str, context) { + return (context || fixture.el).querySelectorAll(str); +} + +// bundled fixture load/cleanup +$.fixture = function (fixtureName) { + beforeEach(function () { + // Awesomplete probably needs to cleanup this by itself + try { Awesomplete.all = []; } catch(e) {}; + fixture.load(fixtureName + ".html"); + }); + + afterEach(function () { + fixture.cleanup(); + }); +}; + +// spy to check if event was fired or not +$.spyOnEvent = function (target, type) { + var handler = jasmine.createSpy(type); + $.on(target, type, handler); + return handler; +}; + +$.on = function (target, type, callback) { + target.addEventListener(type, callback); +}; + +$.fire = function (target, type, properties) { + var evt = document.createEvent("HTMLEvents"); + evt.initEvent(type, true, true ); + for (var j in properties) { + evt[j] = properties[j]; + } + target.dispatchEvent(evt); + return evt; +}; + +// simulates text input (very simple, only "input" event is fired) +$.type = function (input, text) { + input.focus(); + input.value = text; + return $.fire(input, "input"); +}; + +// simulates keydown events +$.keydown = function (target, keyCode) { + return $.fire(target, "keydown", { keyCode: keyCode }); +}; +$.k = { + TAB: 9, + ENTER: 13, + ESC: 27, + DOWN: 40, + UP: 38 +}; + +// $.noop returns a new empty function each time it's being called +Object.defineProperty($, "noop", { + get: function () { + return function noop () {} + } +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/allSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/allSpec.js new file mode 100644 index 0000000..59e23eb --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/allSpec.js @@ -0,0 +1,21 @@ +describe("Awesomplete.all", function () { + + $.fixture("options"); + + subject(function () { return Awesomplete.all }); + + it("is empty initially", function () { + expect(this.subject.length).toBe(0); + }); + + it("keeps a list of created instances", function () { + var first = new Awesomplete("#with-data-list-inline"); + expect(this.subject.length).toBe(1); + expect(this.subject).toContain(first); + + var second = new Awesomplete("#with-data-list"); + expect(this.subject.length).toBe(2); + expect(this.subject).toContain(first); + expect(this.subject).toContain(second); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/dataSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/dataSpec.js new file mode 100644 index 0000000..311c6eb --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/dataSpec.js @@ -0,0 +1,19 @@ +describe("Awesomplete.DATA", function () { + + subject(function () { return Awesomplete.DATA(this.item) }); + + it("returns original String", function () { + this.item = "JavaScript"; + expect(this.subject).toEqual("JavaScript"); + }); + + it("returns original Object", function () { + this.item = { label: "JavaScript", value: "JS" }; + expect(this.subject).toEqual({ label: "JavaScript", value: "JS" }); + }); + + it("returns original Array", function () { + this.item = [ "JavaScript", "JS" ]; + expect(this.subject).toEqual([ "JavaScript", "JS" ]); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/filterContainsSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/filterContainsSpec.js new file mode 100644 index 0000000..1cfbbfd --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/filterContainsSpec.js @@ -0,0 +1,56 @@ +describe("Awesomplete.FILTER_CONTAINS", function () { + + subject(function () { return Awesomplete.FILTER_CONTAINS }); + + describe("search in a plain string", function () { + it("matches at the start", function () { + expect(this.subject("Hello world", "Hello")).toBe(true); + }); + + it("matches in the middle", function () { + expect(this.subject("Ticket to the moon", "to the")).toBe(true); + }); + + it("matches at the end", function () { + expect(this.subject("This is the end", "end")).toBe(true); + }); + + it("performs case insensitive match", function () { + expect(this.subject("Hey You", "HEY YOU")).toBe(true); + }); + + it("ignores whitespaces around the search value", function () { + expect(this.subject("Watch this", " Watch ")).toBe(true); + }); + + it("does not match if substring is not found", function () { + expect(this.subject("No", "way")).toBe(false); + }); + }); + + describe("search in string with special RegExp chars", function () { + it("matches at the start", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "[^j(a)v?a-")).toBe(true); + }); + + it("matches in the middle", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "sc|ri\\p+t*")).toBe(true); + }); + + it("matches at the end", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "{.$}")).toBe(true); + }); + + it("performs case insensitive match", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "[^J(A)V?A-SC|RI\\P+T*]{.$}")).toBe(true); + }); + + it("ignores whitespaces around the search value", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", " [^j(a)v?a- ")).toBe(true); + }); + + it("does not match if substring is not found", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "no way")).toBe(false); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/filterStartsWithSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/filterStartsWithSpec.js new file mode 100644 index 0000000..ce30aab --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/filterStartsWithSpec.js @@ -0,0 +1,56 @@ +describe("Awesomplete.FILTER_STARTSWITH", function () { + + subject(function () { return Awesomplete.FILTER_STARTSWITH }); + + describe("search in plain string", function () { + it("matches at the start", function () { + expect(this.subject("Hello world", "Hello")).toBe(true); + }); + + it("does not match in the middle", function () { + expect(this.subject("Ticket to the moon", "to the")).toBe(false); + }); + + it("does not match at the end", function () { + expect(this.subject("This is the end", "end")).toBe(false); + }); + + it("performs case insensitive match", function () { + expect(this.subject("Hey You", "HEY YOU")).toBe(true); + }); + + it("ignores whitespaces around the search value", function () { + expect(this.subject("Watch this", " Watch ")).toBe(true); + }); + + it("does not match if substring is not found", function () { + expect(this.subject("No", "way")).toBe(false); + }); + }); + + describe("search in string with special RegExp chars", function () { + it("matches at the start", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "[^j(a)v?a-")).toBe(true); + }); + + it("does not match in the middle", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "sc|ri\\p+t*")).toBe(false); + }); + + it("does not match at the end", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "{.$}")).toBe(false); + }); + + it("performs case insensitive match", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "[^J(A)V?A-SC|RI\\P+T*]{.$}")).toBe(true); + }); + + it("ignores whitespaces around the search value", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", " [^j(a)v?a- ")).toBe(true); + }); + + it("does not match if substring is not found", function () { + expect(this.subject("[^j(a)v?a-sc|ri\\p+t*]{.$}", "no way")).toBe(false); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/itemSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/itemSpec.js new file mode 100644 index 0000000..ee89193 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/itemSpec.js @@ -0,0 +1,58 @@ +describe("Awesomplete.ITEM", function () { + + subject(function () { return Awesomplete.ITEM }); + + def("element", function () { return this.subject("JavaScript", this.input || "") }); + + it("creates DOM element", function () { + expect(this.element instanceof HTMLElement).toBe(true); + }); + + it("creates LI element", function () { + expect(this.element.tagName).toBe("LI"); + }); + + it("sets aria-selected to false", function () { + expect(this.element.getAttribute("aria-selected")).toBe("false"); + }); + + describe("with matched input", function () { + def("input", "jav"); + + it("marks the match", function () { + expect(this.element.innerHTML).toBe("JavaScript"); + }); + }); + + describe("with multiple matches", function () { + def("input", "a"); + + it("marks all matches", function () { + expect(this.element.innerHTML).toBe("JavaScript"); + }); + }); + + describe("with no match", function () { + def("input", "foo"); + + it("does not mark anything", function () { + expect(this.element.innerHTML).toBe("JavaScript"); + }); + }); + + describe("with empty input", function () { + def("input", ""); + + it("does not mark anything", function () { + expect(this.element.innerHTML).toBe("JavaScript"); + }); + }); + + describe("with space input", function () { + def("input", " "); + + it("does not mark anything", function () { + expect(this.element.innerHTML).toBe("JavaScript"); + }); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/replaceSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/replaceSpec.js new file mode 100644 index 0000000..e2eddba --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/replaceSpec.js @@ -0,0 +1,11 @@ +describe("Awesomplete.REPLACE", function () { + + subject(function () { return Awesomplete.REPLACE }); + + def("instance", function() { return { input: { value: "initial" } } }); + + it("replaces input value", function () { + this.subject.call(this.instance, { value: "JavaScript" }); + expect(this.instance.input.value).toBe("JavaScript"); + }); +}); diff --git a/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/sortByLengthSpec.js b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/sortByLengthSpec.js new file mode 100644 index 0000000..982ae40 --- /dev/null +++ b/src/EventHub.Web/wwwroot/libs/awesomplete/test/static/sortByLengthSpec.js @@ -0,0 +1,47 @@ +describe("Awesomplete.SORT_BYLENGTH", function () { + + subject(function () { return Awesomplete.SORT_BYLENGTH }); + + describe("with strings of different length", function () { + it("returns negative number if the first string is shorter", function () { + expect(this.subject("a", "aa")).toBe(-1); + expect(this.subject("a", "bb")).toBe(-1); + expect(this.subject("b", "aa")).toBe(-1); + + expect(this.subject("a", "aaa")).toBe(-2); + expect(this.subject("a", "bbb")).toBe(-2); + expect(this.subject("b", "aaa")).toBe(-2); + }); + + it("returns positive number if the first string is longer", function () { + expect(this.subject("aa", "a")).toBe(1); + expect(this.subject("bb", "a")).toBe(1); + expect(this.subject("aa", "b")).toBe(1); + + expect(this.subject("aaa", "a")).toBe(2); + expect(this.subject("bbb", "a")).toBe(2); + expect(this.subject("aaa", "b")).toBe(2); + }); + }); + + describe("with strings of the same length", function () { + it("returns -1 if the first string < second string", function () { + expect(this.subject("a", "b")).toBe(-1); + expect(this.subject("aa", "bb")).toBe(-1); + expect(this.subject("aaa", "bbb")).toBe(-1); + }); + + it("returns 1 if the first string > second string", function () { + expect(this.subject("b", "a")).toBe(1); + expect(this.subject("bb", "aa")).toBe(1); + expect(this.subject("bbb", "aaa")).toBe(1); + }); + + // FIXME SORT_BYLENGTH should probably return 0 like classic string comparison + it("returns 1 if the first string == second string", function () { + expect(this.subject("a", "a")).toBe(1); + expect(this.subject("aa", "aa")).toBe(1); + expect(this.subject("aaa", "aaa")).toBe(1); + }); + }); +}); diff --git a/src/EventHub.Web/yarn.lock b/src/EventHub.Web/yarn.lock index a0d82db..44f340a 100644 --- a/src/EventHub.Web/yarn.lock +++ b/src/EventHub.Web/yarn.lock @@ -360,6 +360,11 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +awesomplete@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/awesomplete/-/awesomplete-1.1.5.tgz#1b2b5dd106d3955595619c03da472a1dc0faf0af" + integrity sha512-UFw1mPW8NaSECDSTC36HbAOTpF9JK2wBUJcNn4MSvlNtK7SZ9N72gB+ajHtA6D1abYXRcszZnBA4nHBwvFwzHw== + bach@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" diff --git a/test/EventHub.Application.Tests/Events/EventAppServiceTests.cs b/test/EventHub.Application.Tests/Events/EventAppServiceTests.cs index 0d9485e..f1f3265 100644 --- a/test/EventHub.Application.Tests/Events/EventAppServiceTests.cs +++ b/test/EventHub.Application.Tests/Events/EventAppServiceTests.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using EventHub.Events.Registrations; using Microsoft.Extensions.DependencyInjection; using NSubstitute; using Shouldly; +using Volo.Abp; using Volo.Abp.Authorization; using Volo.Abp.Domain.Repositories; using Volo.Abp.Timing; @@ -227,6 +230,107 @@ namespace EventHub.Events updatedEvent.Description.ShouldBe("Updated_Microservices_Event_Description-Updated_Microservices_Event_Description-Updated_Blazor_Microservices_Description"); updatedEvent.IsOnline.ShouldBeTrue(); } + + [Fact] + public async Task Should_Add_The_Track() + { + Login(_testData.UserAdminId); + + var eventDetailDto = await _eventAppService.GetByUrlCodeAsync(_testData.AbpMicroservicesFutureEventUrlCode); + + await _eventAppService.AddTrackAsync(eventDetailDto.Id, new AddTrackDto + { + Name = "Track-1" + }); + + var updatedEvent = await _eventAppService.GetByUrlCodeAsync(_testData.AbpMicroservicesFutureEventUrlCode); + + updatedEvent.ShouldNotBeNull(); + updatedEvent.Tracks.ShouldContain(x => x.Name == "Track-1"); + } + + [Fact] + public async Task Should_Add_The_Session() + { + Login(_testData.UserAdminId); + + var eventDetailDto = await _eventAppService.GetByUrlCodeAsync(_testData.AbpMicroservicesFutureEventUrlCode); + + await _eventAppService.AddTrackAsync(eventDetailDto.Id, new AddTrackDto + { + Name = "Track-1" + }); + + var updatedEvent = await _eventAppService.GetByUrlCodeAsync(_testData.AbpMicroservicesFutureEventUrlCode); + + var speakerUserNames = new List(); + speakerUserNames.Add(_testData.UserAdminUserName); + speakerUserNames.Add(_testData.UserJohnUserName); + await _eventAppService.AddSessionAsync(eventDetailDto.Id, updatedEvent.Tracks.First().Id, new AddSessionDto + { + Title = "Session-1 Title", + Description = "Session-1 Description".PadLeft(50, 't'), + StartTime = updatedEvent.StartTime.AddSeconds(1), + EndTime = updatedEvent.StartTime.AddSeconds(50), + Language = "tr", + SpeakerUserNames = speakerUserNames + }); + + updatedEvent = await _eventAppService.GetByUrlCodeAsync(_testData.AbpMicroservicesFutureEventUrlCode); + + updatedEvent.ShouldNotBeNull(); + updatedEvent.Tracks.ShouldContain(x => x.Name == "Track-1"); + var track = updatedEvent.Tracks.Single(x => x.Name == "Track-1"); + + + track.Sessions.ShouldContain(x => x.Title == "Session-1 Title"); + track.Sessions.ShouldContain(x => x.StartTime == updatedEvent.StartTime.AddSeconds(1)); + track.Sessions.ShouldContain(x => x.EndTime == updatedEvent.StartTime.AddSeconds(50)); + track.Sessions.ShouldContain(x => x.Language == "tr"); + + var session = track.Sessions.Single(x => x.Title == "Session-1 Title"); + session.Speakers.ShouldContain(x => x.UserId == _testData.UserAdminId); + session.Speakers.ShouldContain(x => x.UserId == _testData.UserJohnId); + } + + [Fact] + public async Task Should_Publish_Event() + { + Login(_testData.UserAdminId); + + var eventDto = await _eventAppService.CreateAsync( + new CreateEventDto + { + OrganizationId = _testData.OrganizationVolosoftId, + Title = "Introduction to the ABP Framework", + Description = "In this event, we will introduce the ABP Framework and explore the fundamental features.", + StartTime = DateTime.Now.AddDays(1), + EndTime = DateTime.Now.AddDays(1).AddHours(3), + IsOnline = true, + Capacity = 2, + Language = "en" + } + ); + + Login(_testData.UserJohnId); + + var exception = await Assert.ThrowsAsync(async () => + { + await _eventAppService.GetByUrlCodeAsync(eventDto.UrlCode); + }); + + exception.Code.ShouldBe(EventHubErrorCodes.NotAuthorizedToUpdateEvent); + + Login(_testData.UserAdminId); + + await _eventAppService.PublishAsync(eventDto.Id); + + Login(_testData.UserJohnId); + + var @event = await _eventAppService.GetByUrlCodeAsync(eventDto.UrlCode); + @event.ShouldNotBeNull(); + @event.Title.ShouldBe("Introduction to the ABP Framework"); + } private void Login(Guid userId) { diff --git a/test/EventHub.Domain.Tests/Events/EventTests.cs b/test/EventHub.Domain.Tests/Events/EventTests.cs new file mode 100644 index 0000000..d66a061 --- /dev/null +++ b/test/EventHub.Domain.Tests/Events/EventTests.cs @@ -0,0 +1,396 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Shouldly; +using Volo.Abp; +using Xunit; + +namespace EventHub.Events +{ + public class EventTests : EventHubDomainTestBase + { + private readonly EventHubTestData _testData; + + public EventTests() + { + _testData = GetRequiredService(); + } + + + [Fact] + public void Should_Not_Allow_End_Time_To_Be_Earlier_Than_Start_Time() + { + var exception = Assert.Throws(() => + { + new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(-2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + }); + + exception.Code.ShouldBe(EventHubErrorCodes.EndTimeCantBeEarlierThanStartTime); + } + + [Fact] + public void Should_Add_Track_A_Valid_Track() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + @event.AddTrack(Guid.NewGuid(), "Track-1"); + + @event.Tracks.ShouldContain(x => x.Name == "Track-1"); + } + + [Fact] + public void Should_Not_Add_Track_For_Exist_Same_Track_Name() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + @event.AddTrack(Guid.NewGuid(), "Track-1"); + + var exception = Assert.Throws(() => { @event.AddTrack(Guid.NewGuid(), "Track-1"); }); + + exception.Code.ShouldBe(EventHubErrorCodes.TrackNameAlreadyExist); + } + + [Fact] + public void Should_Update_Track_A_Valid_Track() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + @event.AddTrack(Guid.NewGuid(), "Track-1"); + @event.AddTrack(Guid.NewGuid(), "Track-2"); + + @event.Tracks.ShouldContain(x => x.Name == "Track-2"); + } + + [Fact] + public void Should_Not_Update_Track_For_Exist_Same_Track_Name() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + @event.AddTrack(Guid.NewGuid(), "Track-2"); + + var exception = Assert.Throws(() => { @event.UpdateTrack(track1Id, "Track-2"); }); + + exception.Code.ShouldBe(EventHubErrorCodes.TrackNameAlreadyExist); + } + + [Fact] + public void Should_Not_Update_Track_For_Not_Exist_Track_Id() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var exception = Assert.Throws(() => { @event.UpdateTrack(Guid.NewGuid(), "Track-1"); }); + + exception.Code.ShouldBe(EventHubErrorCodes.TrackNotFound); + } + + [Fact] + public void Should_Remove_Track_A_Valid_Track() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + @event.AddTrack(Guid.NewGuid(), "Track-2"); + + @event.RemoveTrack(track1Id); + + @event.Tracks.ShouldNotContain(x => x.Name == "Track-1"); + } + + [Fact] + public void Should_Not_Remove_Track_For_Not_Exist_Track_Id() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var exception = Assert.Throws(() => { @event.RemoveTrack(Guid.NewGuid()); }); + + exception.Code.ShouldBe(EventHubErrorCodes.TrackNotFound); + } + + [Fact] + public void Should_Add_Session_A_Valid_Session() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + var session1Id = Guid.NewGuid(); + @event.AddSession(track1Id, session1Id, "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), DateTime.Now, DateTime.Now, "en", new List()); + + @event.Tracks.ShouldContain(x => x.Name == "Track-1"); + var track = @event.Tracks.Single(x => x.Name == "Track-1"); + track.Sessions.ShouldContain(x => x.Id == session1Id); + } + + [Fact] + public void Should_Not_Add_Session_For_Exist_Same_Session_Name() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + @event.AddSession(track1Id, Guid.NewGuid(), "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), DateTime.Now, DateTime.Now.AddHours(1), "en", new List()); + + var exception = Assert.Throws(() => + { + @event.AddSession(track1Id, Guid.NewGuid(), "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), DateTime.Now.AddHours(2), DateTime.Now.AddHours(3), "en", new List()); + }); + + exception.Code.ShouldBe(EventHubErrorCodes.SessionTitleAlreadyExist); + } + + [Fact] + public void Should_Not_Add_Session_For_End_Time_To_Be_Earlier_Than_Start_Time() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + var exception = Assert.Throws(() => + { + @event.AddSession(track1Id, Guid.NewGuid(), "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), DateTime.Now.AddHours(2), DateTime.Now.AddHours(1), "en", new List()); + }); + + exception.Code.ShouldBe(EventHubErrorCodes.SessionEndTimeCantBeEarlierThanStartTime); + } + + [Fact] + public void Should_Not_Add_Session_For_Session_Time_Is_Not_In_Event_Time() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + var exception = Assert.Throws(() => + { + @event.AddSession(track1Id, Guid.NewGuid(), "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), DateTime.Now.AddHours(2), DateTime.Now.AddDays(3), "en", new List()); + }); + + exception.Code.ShouldBe(EventHubErrorCodes.SessionTimeShouldBeInTheEventTime); + } + + [Fact] + public void Should_Not_Add_Session_For_Session_Time_Conflicts_With_An_Existing_Session() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + + @event.AddSession(track1Id, Guid.NewGuid(), "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), DateTime.Now, DateTime.Now.AddDays(1), "en", new List()); + + var exception = Assert.Throws(() => + { + @event.AddSession(track1Id, Guid.NewGuid(), "Session-2 Title", "Session-2 desc".PadLeft(50, 't'), DateTime.Now, DateTime.Now.AddDays(1), "en", new List()); + }); + + exception.Code.ShouldBe(EventHubErrorCodes.SessionTimeConflictsWithAnExistingSession); + } + + [Fact] + public void Should_Add_Session_With_Speakers() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + + var speakerUserIds = new List(); + speakerUserIds.Add(_testData.UserAdminId); + speakerUserIds.Add(_testData.UserJohnId); + + var sessionId = Guid.NewGuid(); + @event.AddSession(track1Id, sessionId, "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), DateTime.Now, DateTime.Now.AddDays(1), "en", speakerUserIds); + + var track = @event.Tracks.Single(x => x.Id == track1Id); + var session = track.Sessions.Single(x => x.Id == sessionId); + + session.Speakers.ShouldContain(x => x.UserId == _testData.UserAdminId); + session.Speakers.ShouldContain(x => x.UserId == _testData.UserJohnId); + } + + [Fact] + public void Should_Update_Session_With_Valid_Session() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + + var speakerUserIds = new List(); + speakerUserIds.Add(_testData.UserAdminId); + speakerUserIds.Add(_testData.UserJohnId); + + var sessionId = Guid.NewGuid(); + @event.AddSession(track1Id, sessionId, "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), @event.StartTime.AddMinutes(30), @event.EndTime.AddHours(-1), "en", speakerUserIds); + + var track = @event.Tracks.Single(x => x.Id == track1Id); + var session = track.Sessions.Single(x => x.Id == sessionId); + session.Title.ShouldContain("Session-1 Title"); + + + speakerUserIds = new List(); + speakerUserIds.Add(_testData.UserAdminId); + @event.UpdateSession(track1Id, sessionId, "Session-1 Title Updated", "Session-1 desc".PadLeft(50, 't'), @event.StartTime.AddMinutes(30), @event.EndTime.AddMinutes(-70), "en", speakerUserIds); + + track = @event.Tracks.Single(x => x.Id == track1Id); + session = track.Sessions.Single(x => x.Id == sessionId); + session.Title.ShouldContain("Session-1 Title Updated"); + session.Speakers.ShouldContain(x => x.UserId == _testData.UserAdminId); + session.Speakers.ShouldNotContain(x => x.UserId == _testData.UserJohnId); + } + + [Fact] + public void Should_Remove_Session_A_Valid_Session() + { + var @event = new Event( + Guid.NewGuid(), + Guid.NewGuid(), + "1a8j3v0d", + "Introduction to the ABP Framework", + DateTime.Now, + DateTime.Now.AddDays(2), + "In this event, we will introduce the ABP Framework and explore the fundamental features." + ); + + var track1Id = Guid.NewGuid(); + @event.AddTrack(track1Id, "Track-1"); + + var speakerUserIds = new List(); + speakerUserIds.Add(_testData.UserAdminId); + speakerUserIds.Add(_testData.UserJohnId); + + var sessionId = Guid.NewGuid(); + @event.AddSession(track1Id, sessionId, "Session-1 Title", "Session-1 desc".PadLeft(50, 't'), DateTime.Now, DateTime.Now.AddDays(1), "en", speakerUserIds); + + speakerUserIds = new List(); + speakerUserIds.Add(_testData.UserAdminId); + @event.RemoveSession(track1Id, sessionId); + + var track = @event.Tracks.Single(x => x.Id == track1Id); + track.Sessions.ShouldNotContain(x => x.Id == sessionId); + } + } +} diff --git a/test/EventHub.Domain.Tests/Events/Event_Tests.cs b/test/EventHub.Domain.Tests/Events/Event_Tests.cs index 6a49efd..e295449 100644 --- a/test/EventHub.Domain.Tests/Events/Event_Tests.cs +++ b/test/EventHub.Domain.Tests/Events/Event_Tests.cs @@ -44,8 +44,6 @@ namespace EventHub.Events //ASSERT evnt.StartTime.ShouldBe(newStartTime); evnt.EndTime.ShouldBe(newEndTime); - evnt.GetLocalEvents() - .ShouldContain(x => x.EventData is EventTimeChangingEventData); } [Fact] @@ -65,7 +63,7 @@ namespace EventHub.Events }); exception.Code.ShouldBe( - EventHubErrorCodes.EventEndTimeCantBeEarlierThanStartTime); + EventHubErrorCodes.EndTimeCantBeEarlierThanStartTime); } } } diff --git a/test/EventHub.TestBase/EventHubTestDataSeedContributor.cs b/test/EventHub.TestBase/EventHubTestDataSeedContributor.cs index ac87493..53e12fb 100644 --- a/test/EventHub.TestBase/EventHubTestDataSeedContributor.cs +++ b/test/EventHub.TestBase/EventHubTestDataSeedContributor.cs @@ -78,31 +78,33 @@ namespace EventHub private async Task CreateEventsAsync() { + var pastEvent = new Event( + _eventHubTestData.AbpBlazorPastEventId, + _eventHubTestData.OrganizationVolosoftId, + _eventHubTestData.AbpBlazorPastEventUrlCode, + _eventHubTestData.AbpBlazorPastEventTitle, + _clock.Now.ClearTime().AddDays(-2).AddHours(15), + _clock.Now.ClearTime().AddDays(-2).AddHours(17), + "This is a past event about Blazor and the ABP Framework." + ); + pastEvent.Publish(); await _eventRepository.InsertAsync( - new Event( - _eventHubTestData.AbpBlazorPastEventId, - _eventHubTestData.OrganizationVolosoftId, - _eventHubTestData.AbpBlazorPastEventUrlCode, - _eventHubTestData.AbpBlazorPastEventTitle, - _clock.Now.ClearTime().AddDays(-2).AddHours(15), - _clock.Now.ClearTime().AddDays(-2).AddHours(17), - "This is a past event about Blazor and the ABP Framework." - ) + pastEvent ); + var futureEvent = new Event( + _eventHubTestData.AbpMicroservicesFutureEventId, + _eventHubTestData.OrganizationVolosoftId, + _eventHubTestData.AbpMicroservicesFutureEventUrlCode, + _eventHubTestData.AbpMicroservicesFutureEventTitle, + _clock.Now.ClearTime().AddDays(1).AddHours(15), + _clock.Now.ClearTime().AddDays(1).AddHours(17), + "This is a future event about the ABP Framework and Microservices that is set for tomorrow." + ); + futureEvent.Capacity = 1; + futureEvent.Publish(); await _eventRepository.InsertAsync( - new Event( - _eventHubTestData.AbpMicroservicesFutureEventId, - _eventHubTestData.OrganizationVolosoftId, - _eventHubTestData.AbpMicroservicesFutureEventUrlCode, - _eventHubTestData.AbpMicroservicesFutureEventTitle, - _clock.Now.ClearTime().AddDays(1).AddHours(15), - _clock.Now.ClearTime().AddDays(1).AddHours(17), - "This is a future event about the ABP Framework and Microservices that is set for tomorrow." - ) - { - Capacity = 1 - } + futureEvent ); } }