diff --git a/api/src/Sozsoft.Platform.Application.Contracts/Intranet/EventCommentDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/Intranet/EventCommentDto.cs new file mode 100644 index 0000000..db90c67 --- /dev/null +++ b/api/src/Sozsoft.Platform.Application.Contracts/Intranet/EventCommentDto.cs @@ -0,0 +1,15 @@ +using System; +using Sozsoft.Platform.Identity.Dto; + +namespace Sozsoft.Platform.Intranet; + +public class EventCommentDto +{ + public Guid Id { get; set; } + public Guid EventId { get; set; } + public Guid UserId { get; set; } + public UserInfoViewModel User { get; set; } + public string Content { get; set; } + public int Likes { get; set; } + public DateTime CreationTime { get; set; } +} diff --git a/api/src/Sozsoft.Platform.Application.Contracts/Intranet/EventDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/Intranet/EventDto.cs index d39ba95..e00487d 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/Intranet/EventDto.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/Intranet/EventDto.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Sozsoft.Platform.Identity.Dto; namespace Sozsoft.Platform.Intranet; @@ -18,5 +19,7 @@ public class EventDto public int ParticipantsCount { get; set; } public int Likes { get; set; } public bool IsPublished { get; set; } + public string Photos { get; set; } + public List Comments { get; set; } = []; } diff --git a/api/src/Sozsoft.Platform.Application.Contracts/Intranet/IIntranetAppService.cs b/api/src/Sozsoft.Platform.Application.Contracts/Intranet/IIntranetAppService.cs index bebf119..0b5fce5 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/Intranet/IIntranetAppService.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/Intranet/IIntranetAppService.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Volo.Abp.Application.Services; namespace Sozsoft.Platform.Intranet; @@ -13,4 +15,6 @@ public interface IIntranetAppService : IApplicationService Task CommentSocialPostAsync(System.Guid id, string content); Task VoteSocialPollAsync(System.Guid postId, System.Guid optionId); Task IncrementAnnouncementViewCountAsync(System.Guid id); + Task> GetEventCommentsAsync(Guid eventId); + Task CreateEventCommentAsync(Guid eventId, string content); } diff --git a/api/src/Sozsoft.Platform.Application/Intranet/IntranetAppService.cs b/api/src/Sozsoft.Platform.Application/Intranet/IntranetAppService.cs index 7e4b36a..40b93ae 100644 --- a/api/src/Sozsoft.Platform.Application/Intranet/IntranetAppService.cs +++ b/api/src/Sozsoft.Platform.Application/Intranet/IntranetAppService.cs @@ -42,6 +42,7 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService private readonly IRepository _socialLikeRepository; private readonly IRepository _socialMediaRepository; private readonly IRepository _socialPollOptionRepository; + private readonly IRepository _eventCommentRepository; public IntranetAppService( ICurrentTenant currentTenant, @@ -61,7 +62,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService IRepository socialCommentRepository, IRepository socialLikeRepository, IRepository socialMediaRepository, - IRepository socialPollOptionRepository + IRepository socialPollOptionRepository, + IRepository eventCommentRepository ) { _currentTenant = currentTenant; @@ -81,6 +83,7 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService _socialLikeRepository = socialLikeRepository; _socialMediaRepository = socialMediaRepository; _socialPollOptionRepository = socialPollOptionRepository; + _eventCommentRepository = eventCommentRepository; } [UnitOfWork] @@ -130,14 +133,24 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService if (events.Count == 0) return []; - // Tüm unique user ID'lerini topla (event'ler ve comment'ler için) + var eventIds = events.Select(e => e.Id).ToList(); + + // Load all comments for these events + var commentsQueryable = await _eventCommentRepository.GetQueryableAsync(); + var allComments = await AsyncExecuter.ToListAsync( + commentsQueryable.Where(c => eventIds.Contains(c.EventId)).OrderBy(c => c.CreationTime) + ); + + // Collect all unique user IDs var userIds = new HashSet(); foreach (var evt in events) { if (evt.UserId.HasValue) - { userIds.Add(evt.UserId.Value); - } + } + foreach (var comment in allComments) + { + userIds.Add(comment.UserId); } var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); @@ -147,12 +160,33 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService .Where(u => userIds.Contains(u.Id)) .ToDictionary(u => u.Id, u => MapUserInfoViewModel(u, departmentDict, jobPositionDict)); + var commentsByEvent = allComments.GroupBy(c => c.EventId) + .ToDictionary(g => g.Key, g => g.ToList()); + var result = new List(); foreach (var evt in events) { if (!evt.UserId.HasValue || !userDict.TryGetValue(evt.UserId.Value, out var user)) continue; + var commentDtos = new List(); + if (commentsByEvent.TryGetValue(evt.Id, out var eventComments)) + { + foreach (var c in eventComments) + { + commentDtos.Add(new EventCommentDto + { + Id = c.Id, + EventId = c.EventId, + UserId = c.UserId, + User = userDict.TryGetValue(c.UserId, out var commentUser) ? commentUser : null, + Content = c.Content, + Likes = c.Likes, + CreationTime = c.CreationTime + }); + } + } + var calendarEvent = new EventDto { Id = evt.Id, @@ -165,7 +199,9 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService User = user, ParticipantsCount = evt.ParticipantsCount, Likes = evt.Likes, - IsPublished = evt.isPublished + IsPublished = evt.isPublished, + Photos = evt.Photos, + Comments = commentDtos }; result.Add(calendarEvent); @@ -174,6 +210,63 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService return result; } + public async Task> GetEventCommentsAsync(Guid eventId) + { + var commentsQueryable = await _eventCommentRepository.GetQueryableAsync(); + var comments = await AsyncExecuter.ToListAsync( + commentsQueryable.Where(c => c.EventId == eventId).OrderBy(c => c.CreationTime) + ); + + if (comments.Count == 0) + return []; + + var userIds = comments.Select(c => c.UserId).Distinct().ToList(); + var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); + var users = await _identityUserRepository.GetListAsync(); + var userDict = users + .Where(u => userIds.Contains(u.Id)) + .ToDictionary(u => u.Id, u => MapUserInfoViewModel(u, departmentDict, jobPositionDict)); + + return comments.Select(c => new EventCommentDto + { + Id = c.Id, + EventId = c.EventId, + UserId = c.UserId, + User = userDict.TryGetValue(c.UserId, out var u) ? u : null, + Content = c.Content, + Likes = c.Likes, + CreationTime = c.CreationTime + }).ToList(); + } + + public async Task CreateEventCommentAsync(Guid eventId, string content) + { + var comment = new EventComment + { + EventId = eventId, + UserId = CurrentUser.Id ?? Guid.Empty, + Content = content, + Likes = 0 + }; + + comment = await _eventCommentRepository.InsertAsync(comment, autoSave: true); + + var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); + var user = await _identityUserRepository.FindAsync(comment.UserId); + var userViewModel = user != null ? MapUserInfoViewModel(user, departmentDict, jobPositionDict) : null; + + return new EventCommentDto + { + Id = comment.Id, + EventId = comment.EventId, + UserId = comment.UserId, + User = userViewModel, + Content = comment.Content, + Likes = comment.Likes, + CreationTime = comment.CreationTime + }; + } + private async Task> GetBirthdaysAsync() { var today = DateTime.Now; diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json index 766ab99..ed81159 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json @@ -12842,6 +12842,12 @@ } ], "LanguageFieldTitles": [ + { + "resourceName": "Platform", + "key": "App.Listform.ListformField.Photos", + "en": "Photos", + "tr": "Fotoğraflar" + }, { "resourceName": "Platform", "key": "App.Listform.ListformField.WorkHour", diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Administration.cs b/api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Administration.cs index 627f581..27ef96e 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Administration.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Administration.cs @@ -4176,6 +4176,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep new EditingFormItemDto { Order = 6, DataField = "UserId", ColSpan = 1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton }, new EditingFormItemDto { Order = 7, DataField = "Description", ColSpan = 2, EditorType2 = EditorTypes.dxTextArea }, new EditingFormItemDto { Order = 8, DataField = "Status", ColSpan = 1, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton }, + new EditingFormItemDto { Order = 9, DataField = "Photos", ColSpan = 1, EditorType2 = EditorTypes.dxImageUpload, EditorOptions = EditorOptionValues.ImageUploadOptions }, ]} }), InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(), @@ -4393,6 +4394,22 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep ColumnCustomizationJson = DefaultColumnCustomizationJson, PermissionJson = DefaultFieldPermissionJson(listForm.Name), PivotSettingsJson = DefaultPivotSettingsJson + }, + // Photos + new() + { + ListFormCode = listForm.ListFormCode, + CultureName = LanguageCodes.En, + SourceDbType = DbType.String, + FieldName = "Photos", + CaptionName = "App.Listform.ListformField.Photos", + Width = 250, + ListOrderNo = 12, + Visible = false, + IsActive = true, + ColumnCustomizationJson = DefaultColumnCustomizationJson, + PermissionJson = DefaultFieldPermissionJson(listForm.Name), + PivotSettingsJson = DefaultPivotSettingsJson } }); #endregion diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Intranet/Event.cs b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Intranet/Event.cs index 3a3550f..66d5a2e 100644 --- a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Intranet/Event.cs +++ b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Intranet/Event.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; @@ -26,6 +27,10 @@ public class Event : FullAuditedEntity, IMultiTenant public int Likes { get; set; } public bool isPublished { get; set; } = false; + public string Photos { get; set; } + + public ICollection Comments { get; set; } = []; + Guid? IMultiTenant.TenantId => TenantId; } diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Intranet/EventComment.cs b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Intranet/EventComment.cs new file mode 100644 index 0000000..36775e7 --- /dev/null +++ b/api/src/Sozsoft.Platform.Domain/Entities/Tenant/Intranet/EventComment.cs @@ -0,0 +1,19 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Sozsoft.Platform.Entities; + +public class EventComment : FullAuditedEntity, IMultiTenant +{ + public Guid? TenantId { get; set; } + + public Guid EventId { get; set; } + public Event Event { get; set; } + + public Guid UserId { get; set; } + public string Content { get; set; } + public int Likes { get; set; } + + Guid? IMultiTenant.TenantId => TenantId; +} diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index 6268274..f804a0a 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -113,6 +113,7 @@ public class PlatformDbContext : public DbSet Events { get; set; } public DbSet EventCategories { get; set; } public DbSet EventTypes { get; set; } + public DbSet EventComments { get; set; } public DbSet Announcements { get; set; } @@ -1239,6 +1240,7 @@ public class PlatformDbContext : b.Property(x => x.Description).HasMaxLength(1024); b.Property(x => x.Status).HasMaxLength(20); b.Property(x => x.isPublished).HasDefaultValue(false); + b.Property(x => x.Photos).HasColumnType("text"); b.HasOne(x => x.Category) .WithMany(x => x.Events) @@ -1250,5 +1252,20 @@ public class PlatformDbContext : .HasForeignKey(x => x.TypeId) .OnDelete(DeleteBehavior.Restrict); }); + + builder.Entity(b => + { + b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.EventComment)), Prefix.DbSchema); + b.ConfigureByConvention(); + + b.Property(x => x.Content).HasMaxLength(512); + b.Property(x => x.Likes).HasDefaultValue(0); + + // Event -> EventComment (1 - N) + b.HasOne(x => x.Event) + .WithMany(x => x.Comments) + .HasForeignKey(x => x.EventId) + .OnDelete(DeleteBehavior.Cascade); + }); } } diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260506141749_Initial.Designer.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260507140651_Initial.Designer.cs similarity index 99% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260506141749_Initial.Designer.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260507140651_Initial.Designer.cs index 1fcb81e..b4928dc 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260506141749_Initial.Designer.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260507140651_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Sozsoft.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20260506141749_Initial")] + [Migration("20260507140651_Initial")] partial class Initial { /// @@ -603,7 +603,7 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("datetime2"); b.Property("ImageUrl") - .HasColumnType("nvarchar(max)"); + .HasColumnType("text"); b.Property("IsDeleted") .ValueGeneratedOnAdd() @@ -2001,6 +2001,9 @@ namespace Sozsoft.Platform.Migrations b.Property("ParticipantsCount") .HasColumnType("int"); + b.Property("Photos") + .HasColumnType("text"); + b.Property("Place") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -2133,7 +2136,7 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("uniqueidentifier") .HasColumnName("TenantId"); - b.Property("UserId") + b.Property("UserId") .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -2143,59 +2146,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Adm_T_EventComment", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.EventPhoto", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("EventId") - .HasColumnType("uniqueidentifier"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("TenantId") - .HasColumnType("uniqueidentifier") - .HasColumnName("TenantId"); - - b.Property("Url") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.HasKey("Id"); - - b.HasIndex("EventId"); - - b.ToTable("Adm_T_EventPhoto", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.EventType", b => { b.Property("Id") @@ -7437,17 +7387,6 @@ namespace Sozsoft.Platform.Migrations b.Navigation("Event"); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.EventPhoto", b => - { - b.HasOne("Sozsoft.Platform.Entities.Event", "Event") - .WithMany("Photos") - .HasForeignKey("EventId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Event"); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.JobPosition", b => { b.HasOne("Sozsoft.Platform.Entities.Department", "Department") @@ -7879,8 +7818,6 @@ namespace Sozsoft.Platform.Migrations modelBuilder.Entity("Sozsoft.Platform.Entities.Event", b => { b.Navigation("Comments"); - - b.Navigation("Photos"); }); modelBuilder.Entity("Sozsoft.Platform.Entities.EventCategory", b => diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260506141749_Initial.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260507140651_Initial.cs similarity index 99% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260506141749_Initial.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260507140651_Initial.cs index e631e80..afd2eec 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260506141749_Initial.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260507140651_Initial.cs @@ -471,7 +471,7 @@ namespace Sozsoft.Platform.Migrations Title = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), Excerpt = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: false), Content = table.Column(type: "nvarchar(max)", maxLength: 4096, nullable: false), - ImageUrl = table.Column(type: "nvarchar(max)", nullable: true), + ImageUrl = table.Column(type: "text", nullable: true), Category = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: false), UserId = table.Column(type: "uniqueidentifier", nullable: true), PublishDate = table.Column(type: "datetime2", nullable: false), @@ -1973,6 +1973,7 @@ namespace Sozsoft.Platform.Migrations ParticipantsCount = table.Column(type: "int", nullable: false), Likes = table.Column(type: "int", nullable: false), isPublished = table.Column(type: "bit", nullable: false, defaultValue: false), + Photos = table.Column(type: "text", nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), CreatorId = table.Column(type: "uniqueidentifier", nullable: true), LastModificationTime = table.Column(type: "datetime2", nullable: true), @@ -2709,7 +2710,7 @@ namespace Sozsoft.Platform.Migrations Id = table.Column(type: "uniqueidentifier", nullable: false), TenantId = table.Column(type: "uniqueidentifier", nullable: true), EventId = table.Column(type: "uniqueidentifier", nullable: false), - UserId = table.Column(type: "uniqueidentifier", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false), Content = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), Likes = table.Column(type: "int", nullable: false, defaultValue: 0), CreationTime = table.Column(type: "datetime2", nullable: false), @@ -2731,33 +2732,6 @@ namespace Sozsoft.Platform.Migrations onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "Adm_T_EventPhoto", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - TenantId = table.Column(type: "uniqueidentifier", nullable: true), - EventId = table.Column(type: "uniqueidentifier", nullable: false), - Url = table.Column(type: "nvarchar(512)", maxLength: 512, nullable: true), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Adm_T_EventPhoto", x => x.Id); - table.ForeignKey( - name: "FK_Adm_T_EventPhoto_Adm_T_Event_EventId", - column: x => x.EventId, - principalTable: "Adm_T_Event", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - migrationBuilder.CreateTable( name: "Adm_T_SocialPollOption", columns: table => new @@ -3270,11 +3244,6 @@ namespace Sozsoft.Platform.Migrations table: "Adm_T_EventComment", column: "EventId"); - migrationBuilder.CreateIndex( - name: "IX_Adm_T_EventPhoto_EventId", - table: "Adm_T_EventPhoto", - column: "EventId"); - migrationBuilder.CreateIndex( name: "IX_Adm_T_JobPosition_DepartmentId", table: "Adm_T_JobPosition", @@ -3627,9 +3596,6 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "Adm_T_EventComment"); - migrationBuilder.DropTable( - name: "Adm_T_EventPhoto"); - migrationBuilder.DropTable( name: "Adm_T_IpRestriction"); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 6433ee9..ec465ef 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -600,7 +600,7 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("datetime2"); b.Property("ImageUrl") - .HasColumnType("nvarchar(max)"); + .HasColumnType("text"); b.Property("IsDeleted") .ValueGeneratedOnAdd() @@ -1998,6 +1998,9 @@ namespace Sozsoft.Platform.Migrations b.Property("ParticipantsCount") .HasColumnType("int"); + b.Property("Photos") + .HasColumnType("text"); + b.Property("Place") .HasMaxLength(256) .HasColumnType("nvarchar(256)"); @@ -2130,7 +2133,7 @@ namespace Sozsoft.Platform.Migrations .HasColumnType("uniqueidentifier") .HasColumnName("TenantId"); - b.Property("UserId") + b.Property("UserId") .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -2140,59 +2143,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Adm_T_EventComment", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.EventPhoto", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("EventId") - .HasColumnType("uniqueidentifier"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("TenantId") - .HasColumnType("uniqueidentifier") - .HasColumnName("TenantId"); - - b.Property("Url") - .HasMaxLength(512) - .HasColumnType("nvarchar(512)"); - - b.HasKey("Id"); - - b.HasIndex("EventId"); - - b.ToTable("Adm_T_EventPhoto", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.EventType", b => { b.Property("Id") @@ -7434,17 +7384,6 @@ namespace Sozsoft.Platform.Migrations b.Navigation("Event"); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.EventPhoto", b => - { - b.HasOne("Sozsoft.Platform.Entities.Event", "Event") - .WithMany("Photos") - .HasForeignKey("EventId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Event"); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.JobPosition", b => { b.HasOne("Sozsoft.Platform.Entities.Department", "Department") @@ -7876,8 +7815,6 @@ namespace Sozsoft.Platform.Migrations modelBuilder.Entity("Sozsoft.Platform.Entities.Event", b => { b.Navigation("Comments"); - - b.Navigation("Photos"); }); modelBuilder.Entity("Sozsoft.Platform.Entities.EventCategory", b => diff --git a/ui/src/proxy/intranet/models.ts b/ui/src/proxy/intranet/models.ts index 2ac9d1e..e9161d4 100644 --- a/ui/src/proxy/intranet/models.ts +++ b/ui/src/proxy/intranet/models.ts @@ -1,4 +1,4 @@ -import { UserInfoViewModel } from "../admin/models" +import { UserInfoViewModel } from '../admin/models' export interface IntranetDashboardDto { events: EventDto[] @@ -23,7 +23,7 @@ export interface EventDto { participantsCount: number likes: number isPublished: boolean - photos: string[] + photos: string comments: EventCommentDto[] } diff --git a/ui/src/services/intranet.service.ts b/ui/src/services/intranet.service.ts index 007bdcb..4c0caf8 100644 --- a/ui/src/services/intranet.service.ts +++ b/ui/src/services/intranet.service.ts @@ -1,4 +1,4 @@ -import { IntranetDashboardDto, SocialCommentDto, SocialPostDto } from '@/proxy/intranet/models' +import { EventCommentDto, IntranetDashboardDto, SocialCommentDto, SocialPostDto } from '@/proxy/intranet/models' import apiService, { Config } from './api.service' export interface CreateSocialPostInput { @@ -95,6 +95,25 @@ export class IntranetService { }, { apiName: this.apiName, ...config }, ) + + getEventComments = (eventId: string, config?: Partial) => + apiService.fetchData( + { + method: 'GET', + url: `/api/app/intranet/event-comments/${eventId}`, + }, + { apiName: this.apiName, ...config }, + ) + + createEventComment = (eventId: string, content: string, config?: Partial) => + apiService.fetchData( + { + method: 'POST', + url: `/api/app/intranet/event-comment/${eventId}`, + params: { content }, + }, + { apiName: this.apiName, ...config }, + ) } export const intranetService = new IntranetService() diff --git a/ui/src/views/intranet/Dashboard.tsx b/ui/src/views/intranet/Dashboard.tsx index 464cf63..eeb1c71 100644 --- a/ui/src/views/intranet/Dashboard.tsx +++ b/ui/src/views/intranet/Dashboard.tsx @@ -13,6 +13,7 @@ import Surveys from './widgets/Surveys' // Modals import SurveyModal from './widgets/SurveyModal' import AnnouncementModal from './widgets/AnnouncementModal' +import EventModal from './widgets/EventModal' // Social Wall import SocialWall from './SocialWall' @@ -20,6 +21,7 @@ import { Container } from '@/components/shared' import { usePermission } from '@/utils/hooks/usePermission' import { AnnouncementDto, + EventDto, IntranetDashboardDto, SurveyAnswerDto, SurveyDto, @@ -41,6 +43,7 @@ const WIDGET_ORDER_KEY = 'dashboard-widget-order' const IntranetDashboard: React.FC = () => { const { checkPermission } = usePermission() const [selectedAnnouncement, setSelectedAnnouncement] = useState(null) + const [selectedEvent, setSelectedEvent] = useState(null) const [selectedSurvey, setSelectedSurvey] = useState(null) const [showSurveyModal, setShowSurveyModal] = useState(false) const [isDesignMode, setIsDesignMode] = useState(false) @@ -242,7 +245,7 @@ const IntranetDashboard: React.FC = () => { const renderWidgetComponent = (widgetId: string) => { switch (widgetId) { case 'upcoming-events': - return + return case 'today-birthdays': return case 'documents': @@ -625,6 +628,15 @@ const IntranetDashboard: React.FC = () => { )} + + {selectedEvent && ( + setSelectedEvent(null)} + /> + )} + + {selectedAnnouncement && ( void +} + +const imgSrc = (img: string) => { + if ( + img.startsWith('data:') || + img.startsWith('http://') || + img.startsWith('https://') || + img.startsWith('/') + ) + return img + return `data:image/jpeg;base64,${img}` +} + +const EventModal: React.FC = ({ event, onClose }) => { + const currentLocale = useLocale() + const photos = (event.photos || '').split('|').filter(Boolean) + + // Photo slider state + const [activePhoto, setActivePhoto] = useState(0) + const [lightboxOpen, setLightboxOpen] = useState(false) + const [lightboxIndex, setLightboxIndex] = useState(0) + + // Comments state + const [comments, setComments] = useState(event.comments || []) + const [commentText, setCommentText] = useState('') + const [submitting, setSubmitting] = useState(false) + const commentInputRef = useRef(null) + const commentsEndRef = useRef(null) + + useEffect(() => { + // Refresh comments from API on open + intranetService.getEventComments(event.id).then((res) => { + if (res.data) setComments(res.data) + }) + }, [event.id]) + + const scrollToBottom = () => { + commentsEndRef.current?.scrollIntoView({ behavior: 'smooth' }) + } + + const handleSubmitComment = async () => { + if (!commentText.trim() || submitting) return + setSubmitting(true) + try { + const res = await intranetService.createEventComment(event.id, commentText.trim()) + if (res.data) { + setComments((prev) => [...prev, res.data!]) + setCommentText('') + setTimeout(scrollToBottom, 100) + } + } finally { + setSubmitting(false) + } + } + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault() + handleSubmitComment() + } + } + + const openLightbox = (idx: number) => { + setLightboxIndex(idx) + setLightboxOpen(true) + } + + const closeLightbox = () => setLightboxOpen(false) + + const prevLightbox = (e: React.MouseEvent) => { + e.stopPropagation() + setLightboxIndex((i) => (i - 1 + photos.length) % photos.length) + } + + const nextLightbox = (e: React.MouseEvent) => { + e.stopPropagation() + setLightboxIndex((i) => (i + 1) % photos.length) + } + + return ( + <> + {/* Backdrop */} + + + {/* Modal */} +
+ e.stopPropagation()} + > + {/* Header */} +
+
+
+

+ {event.name} +

+
+ + + {currentLocalDate(event.date, currentLocale || 'tr')} + + {event.place && ( + + + {event.place} + + )} + {event.participantsCount > 0 && ( + + + {event.participantsCount} + + )} +
+ {event.user && ( +
+ + + {event.user.fullName} + +
+ )} +
+ +
+
+ + {/* Scrollable body */} +
+ {/* Photo Gallery */} + {photos.length > 0 && ( +
+ {/* Main photo */} +
+ {`${event.name} openLightbox(activePhoto)} + /> + + {photos.length > 1 && ( + <> + + + + )} +
+ {/* Thumbnail strip */} + {photos.length > 1 && ( +
+ {photos.map((photo, idx) => ( + + ))} +
+ )} +
+ )} + + {/* Description */} + {event.description && ( +
+

+ {event.description} +

+
+ )} + + {/* Comments Section */} +
+

+ + Yorumlar ({comments.length}) +

+ + {/* Comment List */} +
+ {comments.length === 0 ? ( +

+ Henüz yorum yok. İlk yorumu sen yap! +

+ ) : ( + comments.map((comment) => ( +
+ {comment.user && ( + + )} +
+
+

+ {comment.user?.fullName ?? 'Kullanıcı'} +

+

+ {comment.content} +

+
+

+ {dayjs(comment.creationTime).fromNow()} +

+
+
+ )) + )} +
+
+ + {/* Comment Input */} +
+