Browse Source

Add track feature

pull/79/head
berkansasmaz 5 years ago
parent
commit
0c513122ac
No known key found for this signature in database GPG Key ID: 884D815C3F32BE00
  1. 23
      src/EventHub.Application.Contracts/Events/AddSessionDto.cs
  2. 10
      src/EventHub.Application.Contracts/Events/AddTractDto.cs
  3. 9
      src/EventHub.Application.Contracts/Events/EventDetailDto.cs
  4. 7
      src/EventHub.Application.Contracts/Events/EventInListDto.cs
  5. 24
      src/EventHub.Application.Contracts/Events/IEventAppService.cs
  6. 9
      src/EventHub.Application.Contracts/Events/TrackDto.cs
  7. 4
      src/EventHub.Application.Contracts/Events/UpdateEventDto.cs
  8. 3
      src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs
  9. 109
      src/EventHub.Application/Events/EventAppService.cs
  10. 1
      src/EventHub.Domain.Shared/EventHubErrorCodes.cs
  11. 3
      src/EventHub.Domain.Shared/Localization/EventHub/en.json
  12. 22
      src/EventHub.Domain/Events/Event.cs
  13. 4
      src/EventHub.Domain/Events/Track.cs
  14. 5
      src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Events/EventRepository.cs
  15. 6
      src/EventHub.HttpApi.Client/EventHubHttpApiClientModule.cs
  16. 104
      src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs
  17. 1
      src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs
  18. 6
      src/EventHub.HttpApi/Controllers/EventHubController.cs
  19. 124
      src/EventHub.HttpApi/Controllers/Events/EventController.cs
  20. 4
      src/EventHub.HttpApi/Controllers/Events/Registrations/EventRegistrationController.cs
  21. 4
      src/EventHub.HttpApi/Controllers/Organizations/Memberships/OrganizationMembershipController.cs
  22. 6
      src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs
  23. 6
      src/EventHub.HttpApi/Controllers/Users/UserController.cs
  24. 17
      src/EventHub.Web/Controllers/EventController.cs
  25. 10
      src/EventHub.Web/Controllers/EventRegistrationController.cs
  26. 12
      src/EventHub.Web/Controllers/OrganizationMembershipController.cs
  27. 30
      src/EventHub.Web/Controllers/WidgetsController.cs
  28. 4
      src/EventHub.Web/EventHubWebAutoMapperProfile.cs
  29. 175
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/CreateEventAreaViewComponent.cs
  30. 21
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/Default.cshtml
  31. 71
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/_addTrackSection.cshtml
  32. 143
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/_newEventSection.cshtml
  33. 15
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/_progressSection.cshtml
  34. 69
      src/EventHub.Web/Pages/Events/Components/CreateEventArea/create-event-area.js
  35. 2
      src/EventHub.Web/Pages/Events/Components/RegistrationArea/RegistrationAreaViewComponent.cs
  36. 207
      src/EventHub.Web/Pages/Events/New.cshtml
  37. 164
      src/EventHub.Web/Pages/Events/New.cshtml.cs
  38. 4
      src/EventHub.Web/Pages/Organizations/Components/JoinArea/JoinAreaViewComponent.cs
  39. 2
      src/EventHub.Web/Pages/Pricing.cshtml

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

@ -0,0 +1,23 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace EventHub.Events;
public class AddSessionDto
{
public Guid TrackId { get; set; }
[Required]
[StringLength(SessionConsts.MaxTitleLength, MinimumLength = SessionConsts.MinTitleLength)]
public string Title { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
[Required]
[StringLength(SessionConsts.MaxDescriptionLength, MinimumLength = SessionConsts.MinDescriptionLength)]
public string Description { get; set; }
public string Language { get; set; }
}

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

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

9
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<TrackDto> Tracks { get; set; }
public EventDetailDto()
{
Tracks = new List<TrackDto>();
}
}
}

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

@ -1,9 +1,10 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Auditing;
namespace EventHub.Events
{
public class EventInListDto : EntityDto<Guid>
public class EventInListDto : EntityDto<Guid>, IHasModificationTime, IHasCreationTime
{
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; }
public DateTime? LastModificationTime { get; set; }
}
}

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

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Content;
using Volo.Abp.SettingManagement;
namespace EventHub.Events
{
@ -15,6 +13,8 @@ namespace EventHub.Events
Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input);
Task<List<EventInListDto>> GetDraftEventsByUserId(Guid userId);
Task<EventDetailDto> GetByUrlCodeAsync(string urlCode);
Task<EventLocationDto> GetLocationAsync(Guid id);
@ -25,24 +25,12 @@ namespace EventHub.Events
Task UpdateAsync(Guid id, UpdateEventDto input);
Task AddTrackAsync(Guid id, AddTractDto input);
Task<List<TrackDto>> GetTracksAsync(Guid id);
Task AddSessionAsync(Guid id, AddSessionDto input);
Task<IRemoteStreamContent> GetCoverImageAsync(Guid id);
}
public class AddSessionDto
{
public Guid TrackId { get; set; }
[Required]
[StringLength(SessionConsts.MaxTitleLength,
MinimumLength = SessionConsts.MinTitleLength)]
public string Title { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
[Required]
[StringLength(SessionConsts.MaxDescriptionLength,
MinimumLength = SessionConsts.MinDescriptionLength)]
public string Description { get; set; }
public string Language { get; set; }
}
}

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

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Application.Dtos;
namespace EventHub.Events;
public class TrackDto : EntityDto<Guid>
{
public string Name { get; set; }
}

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

@ -7,6 +7,8 @@ namespace EventHub.Events
{
public class UpdateEventDto
{
//TODO: Add OrganizationId
[Required]
[StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)]
public string Title { get; set; }
@ -45,4 +47,4 @@ namespace EventHub.Events
[Range(1, int.MaxValue)]
public int? Capacity { get; set; }
}
}
}

3
src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs

@ -27,10 +27,13 @@ namespace EventHub
.Ignore(x => x.OrganizationDisplayName)
.Ignore(x => x.IsLiveNow);
CreateMap<Event, EventDetailDto>()
.ForMember(x => x.Tracks, memberOptions => memberOptions.MapFrom(m => m.Tracks))
.Ignore(x => x.OrganizationId)
.Ignore(x => x.OrganizationName)
.Ignore(x => x.OrganizationDisplayName);
CreateMap<Track, TrackDto>();
CreateMap<IdentityUser, EventAttendeeDto>();
CreateMap<Event, EventLocationDto>()

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

@ -12,7 +12,6 @@ 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
@ -79,6 +78,8 @@ namespace EventHub.Events
await SaveCoverImageAsync(@event.Id, input.CoverImageStreamContent);
}
@event.Publish(false);
await _eventRepository.InsertAsync(@event);
return ObjectMapper.Map<Event, EventDto>(@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,32 @@ 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<Event, EventInListDto>(i.@event);
dto.OrganizationName = i.organization.Name;
dto.OrganizationDisplayName = i.organization.DisplayName;
dto.IsLiveNow = now.IsBetween(i.@event.StartTime, i.@event.EndTime);
dto.Country = i.@event.CountryName;
return dto;
}
).ToList();
var events = GetEventInListDtoFromEventAndOrganizationTupleList(items);
return new PagedResultDto<EventInListDto>(totalCount, events);
}
[Authorize]
public async Task<List<EventInListDto>> GetDraftEventsByUserId(Guid userId)
{
var eventQueryable = await _eventRepository.GetQueryableAsync();
var organizationQueryable = await _organizationRepository.GetQueryableAsync();
var query = from @event in eventQueryable
join organization in organizationQueryable on @event.OrganizationId equals organization.Id
where organization.OwnerUserId == userId && @event.IsDraft
select new { @event, organization };
var items = (await AsyncExecuter.ToListAsync(query)).Select(a => (a.@event, a.organization)).ToList();
return GetEventInListDtoFromEventAndOrganizationTupleList(items);
}
public async Task<EventDetailDto> GetByUrlCodeAsync(string urlCode)
{
var @event = await _eventRepository.GetAsync(x => x.UrlCode == urlCode);
var @event = await _eventRepository.GetAsync(x => x.UrlCode == urlCode, true);
var organization = await _organizationRepository.GetAsync(@event.OrganizationId);
var dto = ObjectMapper.Map<Event, EventDetailDto>(@event);
@ -175,7 +182,7 @@ namespace EventHub.Events
dto.OrganizationId = organization.Id;
dto.OrganizationName = organization.Name;
dto.OrganizationDisplayName = organization.DisplayName;
var user = await _userRepository.GetAsync(organization.OwnerUserId);
dto.OwnerUserName = user.UserName;
dto.OwnerEmail = user.Email;
@ -232,15 +239,7 @@ 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 CheckOwnerControlAsync(@event);
await _eventManager.SetLocationAsync(@event, input.IsOnline, input.OnlineLink, input.CountryId, input.City);
@event.SetTitle(input.Title);
@ -257,6 +256,29 @@ namespace EventHub.Events
await _eventRepository.UpdateAsync(@event);
}
[Authorize]
public async Task AddTrackAsync(Guid id, AddTractDto input)
{
var @event = await _eventRepository.GetAsync(id, true);
await CheckOwnerControlAsync(@event);
@event.AddTract(
GuidGenerator.Create(),
input.Name
);
await _eventRepository.UpdateAsync(@event);
}
[Authorize]
public async Task<List<TrackDto>> GetTracksAsync(Guid id)
{
var @event = await _eventRepository.GetAsync(id, true);
return ObjectMapper.Map<List<Track>, List<TrackDto>>(@event.Tracks.ToList());
}
[Authorize]
public async Task AddSessionAsync(Guid id, AddSessionDto input)
{
var @event = await _eventRepository.GetAsync(id);
@ -277,7 +299,7 @@ namespace EventHub.Events
var blobName = id.ToString();
var coverImageStream = await _eventBlobContainer.GetOrNullAsync(blobName);
if (coverImageStream is null)
{
return null;
@ -292,5 +314,36 @@ namespace EventHub.Events
await _eventBlobContainer.SaveAsync(blobName, streamContent.GetStream(), overrideExisting: true);
}
private async Task CheckOwnerControlAsync(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 List<EventInListDto> GetEventInListDtoFromEventAndOrganizationTupleList(List<(Event @event, Organization organization)> items)
{
var now = Clock.Now;
var events = items.Select(
i =>
{
var dto = ObjectMapper.Map<Event, EventInListDto>(i.@event);
dto.OrganizationName = i.organization.Name;
dto.OrganizationDisplayName = i.organization.DisplayName;
dto.IsLiveNow = now.IsBetween(i.@event.StartTime, i.@event.EndTime);
dto.Country = i.@event.CountryName;
return dto;
}
).ToList();
return events;
}
}
}
}

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

@ -16,5 +16,6 @@
public const string SessionTimeConflictsWithAnExistingSession = "EventHub:SessionTimeConflictsWithAnExistingSession";
public const string UserNotFound = "EventHub:UserNotFound";
public const string OrganizationNotFound = "EventHub:OrganizationNotFound";
public const string TrackNameAlreadyExist = "EventHub:TrackNameAlreadyExist";
}
}

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

@ -152,6 +152,7 @@
"MaxStartTime": "Max Start Time",
"Online": "Online",
"InPerson": "In Person",
"UpgradeToPremium": "Upgrade To Premium"
"UpgradeToPremium": "Upgrade To Premium",
"EventHub:TrackNameAlreadyExist": "The track {Name} already exists"
}
}

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

@ -46,7 +46,7 @@ namespace EventHub.Events
public bool IsTimingChangeEmailSent { get; set; }
public bool IsDraft { get; set; }
public bool IsDraft { get; private set; }
public ICollection<Track> Tracks { get; private set; }
@ -127,6 +127,19 @@ namespace EventHub.Events
return this;
}
public Event AddTract(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 AddSession(
Guid trackId,
Guid sessionId,
@ -150,6 +163,13 @@ namespace EventHub.Events
track.AddSession(sessionId, title, startTime, endTime, description, language);
return this;
}
public Event Publish(bool isPublish)
{
IsDraft = isPublish;
return this;
}
private Track GetTrack(Guid trackId)
{

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

@ -26,7 +26,7 @@ namespace EventHub.Events
: base(id)
{
EventId = eventId;
Name = name;
SetName(name);
Sessions = new Collection<Session>();
}
@ -63,4 +63,4 @@ namespace EventHub.Events
return this;
}
}
}
}

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

@ -98,5 +98,10 @@ namespace EventHub.EntityFrameworkCore.Events
return await query.ToListAsync(GetCancellationToken(cancellationToken));
}
public override async Task<IQueryable<Event>> WithDetailsAsync()
{
return (await GetQueryableAsync()).Include(x => x.Tracks).ThenInclude(x => x.Sessions);
}
}
}

6
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<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EventHubHttpApiClientModule>();
});
}
}
}

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

@ -1,104 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EventHub.Events;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Content;
using Volo.Abp.VirtualFileSystem;
namespace EventHub.Controllers.Events
{
[RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)]
[Area("eventhub")]
[ControllerName("Event")]
[Route("api/eventhub/event")]
public class EventController : AbpController, IEventAppService
{
private readonly IEventAppService _eventAppService;
private readonly IVirtualFileProvider _virtualFileProvider;
public EventController(
IEventAppService eventAppService,
IVirtualFileProvider virtualFileProvider)
{
_eventAppService = eventAppService;
_virtualFileProvider = virtualFileProvider;
}
[HttpPost]
public async Task<EventDto> CreateAsync([FromForm] CreateEventDto input)
{
return await _eventAppService.CreateAsync(input);
}
[HttpGet]
public async Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input)
{
return await _eventAppService.GetListAsync(input);
}
[HttpGet]
[Route("by-url-code/{urlCode}")]
public async Task<EventDetailDto> GetByUrlCodeAsync(string urlCode)
{
return await _eventAppService.GetByUrlCodeAsync(urlCode);
}
[HttpGet]
[Route("location/{id}")]
public async Task<EventLocationDto> GetLocationAsync(Guid id)
{
return await _eventAppService.GetLocationAsync(id);
}
[HttpGet]
[Route("lookup/countries")]
public async Task<List<CountryLookupDto>> GetCountriesLookupAsync()
{
return await _eventAppService.GetCountriesLookupAsync();
}
[HttpGet]
[Route("is-event-owner/{id}")]
public async Task<bool> IsEventOwnerAsync(Guid id)
{
return await _eventAppService.IsEventOwnerAsync(id);
}
[HttpPut]
[Route("{id}")]
public async Task UpdateAsync(Guid id, [FromForm] UpdateEventDto input)
{
await _eventAppService.UpdateAsync(id, input);
}
[HttpPost]
[Route("{id}/sessions")]
public async Task AddSessionAsync(Guid id, AddSessionDto input)
{
await _eventAppService.AddSessionAsync(id, input);
}
[HttpGet]
[Route("cover-image/{id}")]
public async Task<IRemoteStreamContent> GetCoverImageAsync(Guid id)
{
var remoteStreamContent = await _eventAppService.GetCoverImageAsync(id);
if (remoteStreamContent is null)
{
var stream = _virtualFileProvider.GetFileInfo("/Images/eh-event.png").CreateReadStream();
remoteStreamContent = new RemoteStreamContent(stream);
await stream.FlushAsync();
}
Response.Headers.Add("Accept-Ranges", "bytes");
Response.ContentType = remoteStreamContent.ContentType;
return remoteStreamContent;
}
}
}

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

@ -52,7 +52,6 @@ namespace EventHub
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
ConfigureAuthentication(context, configuration);
ConfigureLocalization();

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

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

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EventHub.Events;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Content;
using Volo.Abp.VirtualFileSystem;
namespace EventHub.Controllers.Events
{
[RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)]
[Area("eventhubm")]
[ControllerName("Event")]
[Route("api/eventhub/event")]
public class EventController : EventHubController, IEventAppService
{
private readonly IEventAppService _eventAppService;
private readonly IVirtualFileProvider _virtualFileProvider;
public EventController(
IEventAppService eventAppService,
IVirtualFileProvider virtualFileProvider)
{
_eventAppService = eventAppService;
_virtualFileProvider = virtualFileProvider;
}
[HttpPost]
public async Task<EventDto> CreateAsync([FromForm] CreateEventDto input)
{
return await _eventAppService.CreateAsync(input);
}
[HttpGet]
public async Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input)
{
return await _eventAppService.GetListAsync(input);
}
[HttpGet]
[Route("draft-events-by-user-id/{userId}")]
public async Task<List<EventInListDto>> GetDraftEventsByUserId(Guid userId)
{
return await _eventAppService.GetDraftEventsByUserId(userId);
}
[HttpGet]
[Route("by-url-code/{urlCode}")]
public async Task<EventDetailDto> GetByUrlCodeAsync(string urlCode)
{
return await _eventAppService.GetByUrlCodeAsync(urlCode);
}
[HttpGet]
[Route("location/{id}")]
public async Task<EventLocationDto> GetLocationAsync(Guid id)
{
return await _eventAppService.GetLocationAsync(id);
}
[HttpGet]
[Route("lookup/countries")]
public async Task<List<CountryLookupDto>> GetCountriesLookupAsync()
{
return await _eventAppService.GetCountriesLookupAsync();
}
[HttpGet]
[Route("is-event-owner/{id}")]
public async Task<bool> IsEventOwnerAsync(Guid id)
{
return await _eventAppService.IsEventOwnerAsync(id);
}
[HttpPut]
[Route("{id}")]
public async Task UpdateAsync(Guid id, [FromForm] UpdateEventDto input)
{
await _eventAppService.UpdateAsync(id, input);
}
[HttpPost]
[Route("{id}/tracks")]
public async Task AddTrackAsync(Guid id, AddTractDto input)
{
await _eventAppService.AddTrackAsync(id, input);
}
[HttpGet]
[Route("{id}/tracks")]
public async Task<List<TrackDto>> GetTracksAsync(Guid id)
{
return await _eventAppService.GetTracksAsync(id);
}
[HttpPost]
[Route("{id}/sessions")]
public async Task AddSessionAsync(Guid id, AddSessionDto input)
{
await _eventAppService.AddSessionAsync(id, input);
}
[HttpGet]
[Route("cover-image/{id}")]
public async Task<IRemoteStreamContent> GetCoverImageAsync(Guid id)
{
var remoteStreamContent = await _eventAppService.GetCoverImageAsync(id);
if (remoteStreamContent is null)
{
var stream = _virtualFileProvider.GetFileInfo("/Images/eh-event.png").CreateReadStream();
remoteStreamContent = new RemoteStreamContent(stream);
await stream.FlushAsync();
}
Response.Headers.Add("Accept-Ranges", "bytes");
Response.ContentType = remoteStreamContent.ContentType;
return remoteStreamContent;
}
}
}

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

@ -12,7 +12,7 @@ namespace EventHub.Controllers.Events.Registrations
[Area("eventhub")]
[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);
}
}
}
}

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

@ -12,7 +12,7 @@ namespace EventHub.Controllers.Organizations.Memberships
[Area("eventhub")]
[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);
}
}
}
}

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

@ -11,10 +11,10 @@ using Volo.Abp.VirtualFileSystem;
namespace EventHub.Controllers.Organizations
{
[RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)]
[Area("eventhub")]
[Area("eventhubm")]
[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;
}
}
}
}

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

@ -8,10 +8,10 @@ using Volo.Abp.AspNetCore.Mvc;
namespace EventHub.Controllers.Users
{
[RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)]
[Area("eventhub")]
[Area("eventhubm")]
[ControllerName("User")]
[Route("/api/eventhub/user")]
public class UserController : AbpController, IUserAppService
public class UserController : EventHubController, IUserAppService
{
private readonly IUserAppService _userAppService;
@ -27,4 +27,4 @@ namespace EventHub.Controllers.Users
return await _userAppService.FindByUserNameAsync(username);
}
}
}
}

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

@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EventHub.Events;
@ -28,5 +30,18 @@ namespace EventHub.Web.Controllers
ViewData = ViewData
};
}
[HttpGet]
[Route("{id}/tracks")]
public async Task<IActionResult> GetTracksSection(Guid id)
{
ViewData.Model = await _eventAppService.GetTracksAsync(id);
return await Task.FromResult(new PartialViewResult
{
ViewName = "~/Pages/Events/Components/CreateEventArea/_addTrackSection.cshtml",
ViewData = ViewData
});
}
}
}
}

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

12
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;
@ -43,14 +42,5 @@ namespace EventHub.Web.Controllers
await _organizationMembershipAppService.LeaveAsync(organizationId);
return NoContent();
}
[HttpGet]
public IActionResult Widget(Guid organizationId)
{
return ViewComponent(
typeof(JoinAreaViewComponent),
new {organizationId}
);
}
}
}
}

30
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.CreateEventArea;
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,32 @@ 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 CreateEventArea(string eventUrlCode)
{
return ViewComponent(
typeof(CreateEventAreaViewComponent),
new {eventUrlCode}
);
}
}
}

4
src/EventHub.Web/EventHubWebAutoMapperProfile.cs

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

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

@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using EventHub.Events;
using EventHub.Organizations;
using EventHub.Web.Helpers;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using Volo.Abp.Users;
namespace EventHub.Web.Pages.Events.Components.CreateEventArea;
[Widget(
AutoInitialize = true,
RefreshUrl = "/Widgets/CreateEventArea",
ScriptFiles = new[] { "/Pages/Events/Components/CreateEventArea/create-event-area.js" }
)]
public class CreateEventAreaViewComponent : AbpViewComponent
{
private readonly IEventAppService _eventAppService;
private readonly IOrganizationAppService _organizationAppService;
private readonly ICurrentUser _currentUser;
public CreateEventAreaViewComponent(
IEventAppService eventAppService,
IOrganizationAppService organizationAppService,
ICurrentUser currentUser)
{
_eventAppService = eventAppService;
_organizationAppService = organizationAppService;
_currentUser = currentUser;
}
public async Task<IViewComponentResult> InvokeAsync(string eventUrlCode)
{
NewEventViewModel model = null;
if (!eventUrlCode.IsNullOrWhiteSpace())
{
var @event = await _eventAppService.GetByUrlCodeAsync(eventUrlCode);
model = ObjectMapper.Map<EventDetailDto, NewEventViewModel>(@event);
ViewData["EventId"] = @event.Id;
}
model ??= new NewEventViewModel
{
StartTime = DateTime.Now.ClearTime().AddDays(1).AddHours(19),
EndTime = DateTime.Now.ClearTime().AddDays(1).AddHours(21)
};
ViewData["Organizations"] = await GetOrganizationsSelectItemAsync();
ViewData["Countries"] = await GetCountriesSelectItemAsync();
ViewData["Languages"] = GetLanguagesSelectItem();
return View("~/Pages/Events/Components/CreateEventArea/Default.cshtml", model);
}
private async Task<List<SelectListItem>> GetOrganizationsSelectItemAsync()
{
var result = await _organizationAppService.GetOrganizationsByUserIdAsync(_currentUser.GetId());
return result.Items.Select(
organization => new SelectListItem
{
Value = organization.Id.ToString(),
Text = organization.DisplayName
}
).ToList();
}
private async Task<List<SelectListItem>> GetCountriesSelectItemAsync()
{
var result = await _eventAppService.GetCountriesLookupAsync();
return result.Select(
country => new SelectListItem
{
Value = country.Id.ToString(),
Text = country.Name
}
).ToList();
}
private List<SelectListItem> GetLanguagesSelectItem()
{
var result = CultureInfo.GetCultures(CultureTypes.NeutralCultures)
.DistinctBy(x => x.EnglishName)
.OrderBy(x => x.EnglishName)
.ToList();
result.Remove(result.Single(x => x.TwoLetterISOLanguageName == "iv")); // Invariant Language
return result.Select(
cultureInfo => new SelectListItem
{
Value = cultureInfo.TwoLetterISOLanguageName,
Text = cultureInfo.EnglishName
}
).ToList();
}
public class NewEventViewModel
{
[SelectItems(nameof(Organizations))]
[DisplayName("Organization")]
public Guid OrganizationId { get; set; }
[Required]
[StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)]
public string Title { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime StartTime { get; set; } = DateTime.Now;
[Required]
[DataType(DataType.DateTime)]
public DateTime EndTime { get; set; } = DateTime.Now;
[Required]
[StringLength(EventConsts.MaxDescriptionLength, MinimumLength = EventConsts.MinDescriptionLength)]
[TextArea]
public string Description { get; set; }
[CanBeNull]
[Display(Name = "Cover Image")]
[DataType(DataType.Upload)]
[MaxFileSize(EventConsts.MaxCoverImageFileSize)]
[AllowedExtensions(new string[] { ".jpg", ".png", ".jpeg" })]
public IFormFile CoverImageFile { get; set; }
[Required] public bool? IsOnline { get; set; }
[CanBeNull]
[StringLength(EventConsts.MaxOnlineLinkLength, MinimumLength = EventConsts.MinOnlineLinkLength)]
public string OnlineLink { get; set; }
[SelectItems(nameof(Countries))]
[DisplayName("Country")]
public Guid? CountryId { get; set; }
[CanBeNull]
[StringLength(EventConsts.MaxCityLength, MinimumLength = EventConsts.MinCityLength)]
public string City { get; set; }
[DisplayName("Language")]
public string Language { get; set; }
[Range(1, int.MaxValue)]
public int? Capacity { get; set; }
public List<TrackDto> Tracks { get; set; }
public NewEventViewModel()
{
Tracks = new List<TrackDto>();
}
}
public enum ProgressStepType : byte
{
NewEvent = 0,
NewTrack,
NewSession
}
}

21
src/EventHub.Web/Pages/Events/Components/CreateEventArea/Default.cshtml

@ -0,0 +1,21 @@
@using Microsoft.AspNetCore.Http
@model EventHub.Web.Pages.Events.Components.CreateEventArea.CreateEventAreaViewComponent.NewEventViewModel
@{
if (Model is null)
{
throw new BadHttpRequestException("");
}
var eventId = ViewData["EventId"] as Guid?;
}
<div id="CreateEventContainer">
<partial name="_newEventSection.cshtml" model="@Model"/>
</div>
<div id="CreateTrackContainer" style="display: none">
<partial name="_addTrackSection.cshtml" model="@Model.Tracks"/>
</div>
<input type="hidden" id="EventId" value="@eventId"/>

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

@ -0,0 +1,71 @@
@using EventHub.Web.Pages.Events.Components.CreateEventArea
@model List<EventHub.Events.TrackDto>
<partial name="_progressSection.cshtml" model="@CreateEventAreaViewComponent.ProgressStepType.NewTrack"/>
@if (Model is not null)
{
<div class="row">
<div class="col-md-10 mx-auto">
<div class="card">
<div class="card-body profile-content p-5" style="min-height: 480px;">
<h2 class="mb-4">Add Track</h2>
<div class="row">
@foreach (var track in Model)
{
<div class="col-md-3">
<div class="track-item mb-4">
<span class="track-name">@track.Name</span>
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#editTrackModal" class="mt-3 d-block text-info"><small><i class="fa fa-edit me-1"></i>Edit</small></a>
</div>
</div>
}
<div class="col-md-3">
<div class="big-square-btn mb-4 text-center">
<a href="javascript:;" data-bs-toggle="modal" data-bs-target="#AddTrackModal" class="btn btn-outline-secondary d-block px-2 py-5"><i class="fa fa-plus mb-3"></i><br>Add New Track</a>
</div>
</div>
</div>
</div>
<div class="card-footer bg-white border-0 p-5 pt-1">
<div class="row">
<div class="col-6">
<a href="#" class="btn btn-secondary"><i class="fa fa-arrow-left me-2"></i> Previous Step </a>
</div>
<div class="col-6 text-end">
<a href="#" class="btn btn-link">Skip This Step</a>
<a href="#" class="btn btn-primary">Next Step <i class="fa fa-arrow-right ms-2"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
@* Add Track modal *@
<div class="modal fade" id="AddTrackModal" tabindex="-1" aria-labelledby="AddTrackModal" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header border-0 p-5 pb-2">
<h5 class="modal-title">Add New Track</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body profile-content px-5">
<div class="form-floating">
<input type="text" class="form-control" id="TrackName" placeholder=" ">
<label for="TrackName">Track Name</label>
</div>
</div>
<div class="modal-footer border-0 p-5 pt-2">
<button type="button" class="btn btn-secondary modal-close-button" data-bs-dismiss="modal">Close</button>
<button id="AddNewTrackButton" type="button" class="btn btn-primary"
data-url="@Url.Action("GetTracksSection", "Event", new { id = "eventIdPlaceholder" })">
<i class="fa fa-plus me-2"></i> Add
</button>
</div>
</div>
</div>
</div>
}

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

@ -0,0 +1,143 @@
@using EventHub.Web.Pages.Events.Components.CreateEventArea
@using Microsoft.AspNetCore.Http
@model EventHub.Web.Pages.Events.Components.CreateEventArea.CreateEventAreaViewComponent.NewEventViewModel
@{
var organizations = ViewData["Organizations"] as IList<SelectListItem>;
if (organizations is null || !organizations.Any())
{
throw new BadHttpRequestException("There is no organization");
}
var countries = ViewData["Countries"] as IList<SelectListItem>;
if (countries is null || !countries.Any())
{
throw new BadHttpRequestException("There is no country");
}
var languages = ViewData["Languages"] as IList<SelectListItem>;
if (languages is null || !languages.Any())
{
throw new BadHttpRequestException("There is no language");
}
}
<partial name="_progressSection.cshtml" model="@CreateEventAreaViewComponent.ProgressStepType.NewEvent"/>
<form id="CreateEventForm" method="post" enctype="multipart/form-data">
<div class="container pt-4">
<div class="row py-4">
<div class="col-md-9 mx-auto profile-content">
<div class="card">
<div class="card-body" style="min-height: 496px">
<h3 class="mb-4">Create New Event</h3>
<div class="form">
<div class="row">
<div class="col-md-6">
<div class="form-label-group">
<select asp-for="@Model.OrganizationId" asp-items="organizations" class="form-select">
@if (organizations.Count > 1)
{
<option selected value="">Pick an organization</option>
}
</select>
<span asp-validation-for="@Model.OrganizationId" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input type="text" id="inputTitle" asp-for="@Model.Title" class="form-control" placeholder="Title">
<label for="inputTitle">Title</label>
<span asp-validation-for="@Model.Title" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input id="inputStartdate" asp-for="@Model.StartTime" class="form-control" placeholder="Start Date">
<label for="inputStartdate">Start Date</label>
<span asp-validation-for="@Model.StartTime" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input id="inputEnddate" asp-for="@Model.EndTime" class="form-control" placeholder="End Date">
<label for="inputEnddate">End Date</label>
<span asp-validation-for="@Model.EndTime" class="text-danger"></span>
</div>
</div>
<div class="col-md-12">
<div class="form-label-group">
<textarea type="text" id="inputDescription" asp-for="@Model.Description" class="form-control" placeholder="Description"></textarea>
<span asp-validation-for="@Model.Description" class="text-danger"></span>
</div>
</div>
<div class="col-md-12">
<h4 class="mb-4">Cover Image</h4>
<div class="row">
<div class="col-6">
<div class="image-area"><img id="imageResult" src="#" alt="" class="img-fluid rounded shadow-sm mx-auto d-block"></div>
</div>
<div class="col-6">
<div>
<p class="mb-3">For horizontal format 600x400 sized upload images.</p>
<input type="file" id="Event_CoverImageFile" asp-for="@Model.CoverImageFile" class="form-control border-0" hidden>
<label id="upload-label" for="Event_CoverImageFile" class="btn btn-primary btn-lg text-white">Choose file</label>
<span asp-validation-for="@Model.CoverImageFile" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<select class="form-select" asp-for="@Model.IsOnline">
<option selected="selected" value="">Event Type</option>
<option value="@Boolean.TrueString">Online</option>
<option value="@Boolean.FalseString">In Person</option>
</select>
<span asp-validation-for="@Model.IsOnline" class="text-danger"></span>
</div>
<div class="col-md-6 event-link-group" style="display: none">
<div class="form-label-group">
<input type="text" id="inputOnlineLink" asp-for="@Model.OnlineLink" class="form-control" placeholder="Online Link">
<label for="inputOnlineLink">Online Link</label>
<span asp-validation-for="@Model.OnlineLink" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input type="number" id="inputCapacity" asp-for="@Model.Capacity" class="form-control" placeholder="Capacity">
<label for="inputCapacity">Capacity</label>
<span asp-validation-for="@Model.Capacity" class="text-danger"></span>
</div>
</div>
<div class="col-md-6 event-location-group">
<select asp-for="@Model.CountryId" asp-items="countries" class="form-select">
<option selected="" value="">Country</option>
</select>
<span asp-validation-for="@Model.CountryId" class="text-danger"></span>
</div>
<div class="col-md-6 event-location-group">
<div class="form-label-group">
<input type="text" id="inputCity" asp-for="@Model.City" class="form-control" placeholder="City">
<label for="inputCity">City</label>
<span asp-validation-for="@Model.City" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<select asp-for="@Model.Language" asp-items="languages" aria-label="en" class="form-select">
<option selected value="">Language</option>
</select>
<span asp-validation-for="@Model.Language" class="text-danger"></span>
</div>
<div class="col-md-12 text-end">
<button id="CreateEventButton" type="submit" class="btn btn-primary btn-lg">
Next Step
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>

15
src/EventHub.Web/Pages/Events/Components/CreateEventArea/_progressSection.cshtml

@ -0,0 +1,15 @@
@using EventHub.Web.Pages.Events.Components.CreateEventArea
@model EventHub.Web.Pages.Events.Components.CreateEventArea.CreateEventAreaViewComponent.ProgressStepType
<div class="px-4 py-1">
<div class="progresses py-4">
<ul class="d-flex align-items-center justify-content-between">
<li id="step-1" class="create-steps @(Model == CreateEventAreaViewComponent.ProgressStepType.NewEvent ? "blue" : "")">1<span>New Event</span></li>
<li id="step-2" class="create-steps @(Model == CreateEventAreaViewComponent.ProgressStepType.NewTrack ? "blue" : "")">2<span>Add Track</span></li>
<li id="step-3" class="create-steps @(Model == CreateEventAreaViewComponent.ProgressStepType.NewSession ? "blue" : "")">3<span>Add Session</span></li>
</ul>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>

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

@ -0,0 +1,69 @@
(function () {
abp.widgets.CreateEventArea = function ($wrapper) {
var eventApiService = eventHub.controllers.events.event;
var eventIdInput = $('#EventId');
if (eventIdInput.val().length === 36) {
// TODO: Add organization in UppdateEventDto
$('#OrganizationId').prop("disabled", true).removeAttr('name');
}
$("#CreateEventForm").submit(function (e) {
e.preventDefault();
if (!$(this).valid()) {
return false;
}
var input = $(this).serializeFormToObject();
if (eventIdInput.val().length === 36) {
eventApiService.update(eventIdInput.val(), input).then(function (eventUpdatedResponse) {
abp.notify.success('Updated event');
SwitchToTrackCreation()
ScrollToWrapperBegin();
});
} else {
eventApiService.create(input).then(function (eventCreatedResponse) {
abp.notify.success('Created event as a draft');
eventIdInput.val(eventCreatedResponse.id);
SwitchToTrackCreation()
ScrollToWrapperBegin();
});
}
});
function ScrollToWrapperBegin() {
$([document.documentElement, document.body]).animate({
scrollTop: $wrapper.offset().top
}, 100);
}
function SwitchToTrackCreation() {
$('#CreateEventContainer').css('display', 'none');
$('#CreateTrackContainer').css('display', '');
AddNewTrackButtonClickEventHandler();
}
function AddNewTrackButtonClickEventHandler() {
var addNewTrackButton = $('#AddNewTrackButton');
addNewTrackButton.click(function (e) {
e.preventDefault();
var trackName = $('#TrackName').val().trim();
eventApiService.addTrack(eventIdInput.val(), {name: trackName}).then(function () {
var url = addNewTrackButton.attr('data-url').replace("eventIdPlaceholder", eventIdInput.val());
abp.ajax({
type: 'GET', dataType: 'html', contentType: 'text/html; charset=utf-8', url: url
}).then(function (getTracksResponse) {
$('#AddTrackModal').modal('hide');
var createTrackContainer = $wrapper.find('#CreateTrackContainer');
createTrackContainer.text("");
createTrackContainer.append(getTracksResponse);
AddNewTrackButtonClickEventHandler();
});
abp.notify.success('Added the track');
});
});
}
}
})();

2
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

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

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

164
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<SelectListItem> Organizations { get; private set; }
public List<SelectListItem> Countries { get; private set; }
public List<SelectListItem> Languages { get; private set; }
[BindProperty(SupportsGet = true)]
public bool IsCreateNew { get; set; }
public bool IsHasOrganizations { get; set; }
public List<EventInListDto> DraftEventList { get; set; }
private readonly IEventAppService _eventAppService;
private readonly IOrganizationAppService _organizationAppService;
public NewPageModel(
IEventAppService eventAppService,
IEventAppService eventAppService,
IOrganizationAppService organizationAppService)
{
_eventAppService = eventAppService;
_organizationAppService = organizationAppService;
DraftEventList = new List<EventInListDto>();
}
public async Task OnGetAsync()
{
Event = new NewEventViewModel
{
StartTime = DateTime.Now.ClearTime().AddDays(1).AddHours(19),
EndTime = DateTime.Now.ClearTime().AddDays(1).AddHours(21)
};
IsHasOrganizations = (await _organizationAppService.GetOrganizationsByUserIdAsync(CurrentUser.GetId())).Items.Any();
await FillOrganizationsAsync();
await FillCountriesAsync();
FillLanguages();
}
public async Task<IActionResult> OnPostAsync()
{
try
if (!EventUrlCode.IsNullOrWhiteSpace())
{
ValidateModel();
var createEventDto = ObjectMapper.Map<NewEventViewModel, CreateEventDto>(Event);
await using var memoryStream = new MemoryStream();
if (Event.CoverImageFile != null && Event.CoverImageFile.Length > 0)
{
await Event.CoverImageFile.CopyToAsync(memoryStream);
memoryStream.Position = 0;
createEventDto.CoverImageStreamContent = new RemoteStreamContent(memoryStream, fileName: Event.CoverImageFile.FileName, contentType: Event.CoverImageFile.ContentType);
}
var eventDto = await _eventAppService.CreateAsync(createEventDto);
await memoryStream.DisposeAsync();
return RedirectToPage("/Events/Detail", new {url = eventDto.UrlCode});
return;
}
catch (Exception exception)
if (IsHasOrganizations && !IsCreateNew)
{
ShowAlert(exception);
await FillOrganizationsAsync();
await FillCountriesAsync();
FillLanguages();
return Page();
DraftEventList = await _eventAppService.GetDraftEventsByUserId(CurrentUser.GetId());
}
}
private async Task FillOrganizationsAsync()
{
var result = await _organizationAppService.GetOrganizationsByUserIdAsync(CurrentUser.GetId());
Organizations = result.Items.Select(
organization => new SelectListItem
{
Value = organization.Id.ToString(),
Text = organization.DisplayName
}
).ToList();
}
private async Task FillCountriesAsync()
{
var result = await _eventAppService.GetCountriesLookupAsync();
Countries = result.Select(
country => new SelectListItem
{
Value = country.Id.ToString(),
Text = country.Name
}
).ToList();
}
private void FillLanguages()
{
var result = CultureInfo.GetCultures(CultureTypes.NeutralCultures)
.DistinctBy(x => x.EnglishName)
.OrderBy(x => x.EnglishName)
.ToList();
result.Remove(result.Single(x => x.TwoLetterISOLanguageName == "iv")); // Invariant Language
Languages = result.Select(
cultureInfo => new SelectListItem
{
Value = cultureInfo.TwoLetterISOLanguageName,
Text = cultureInfo.EnglishName
}
).ToList();
}
public class NewEventViewModel
{
[SelectItems(nameof(Organizations))]
[DisplayName("Organization")]
public Guid OrganizationId { get; set; }
[Required]
[StringLength(EventConsts.MaxTitleLength, MinimumLength = EventConsts.MinTitleLength)]
public string Title { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime StartTime { get; set; } = DateTime.Now;
[Required]
[DataType(DataType.DateTime)]
public DateTime EndTime { get; set; } = DateTime.Now;
[Required]
[StringLength(EventConsts.MaxDescriptionLength, MinimumLength = EventConsts.MinDescriptionLength)]
[TextArea]
public string Description { get; set; }
[CanBeNull]
[Display(Name = "Cover Image")]
[DataType(DataType.Upload)]
[MaxFileSize(EventConsts.MaxCoverImageFileSize)]
[AllowedExtensions(new string[] {".jpg", ".png", ".jpeg"})]
public IFormFile CoverImageFile { get; set; }
[Required] public bool? IsOnline { get; set; }
[CanBeNull]
[StringLength(EventConsts.MaxOnlineLinkLength, MinimumLength = EventConsts.MinOnlineLinkLength)]
public string OnlineLink { get; set; }
[SelectItems(nameof(Countries))]
[DisplayName("Country")]
public Guid? CountryId { get; set; }
[CanBeNull]
[StringLength(EventConsts.MaxCityLength, MinimumLength = EventConsts.MinCityLength)]
public string City { get; set; }
[SelectItems(nameof(Languages))]
[DisplayName("Language")]
public string Language { get; set; }
[Range(1, int.MaxValue)] public int? Capacity { get; set; }
}
}
}
}

4
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; }
}
}
}
}

2
src/EventHub.Web/Pages/Pricing.cshtml

@ -9,7 +9,7 @@
}
<main class="static-page container mt-5">
<h1 class="text-center mb-3">Eventhub Pricing List</h1>
<h1 class="text-center mb-3">Pricing</h1>
<p class="text-center mb-5">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam ut nulla consequat, tempus sapien in,
pellentesque nunc. Sed at nunc pellentesque, fermentum mi et, fermentum massa. </p>
<div class="row">

Loading…
Cancel
Save