Intranet App Service ve Intranet Dashboard
This commit is contained in:
parent
0e102b3dfc
commit
c350acbf2a
101 changed files with 8321 additions and 1362 deletions
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Sozsoft.Platform.Hr;
|
||||
|
||||
public class DepartmentDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public Guid? ParentId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Sozsoft.Platform.Hr;
|
||||
|
||||
public class JobPositionDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public Guid DepartmentId { get; set; }
|
||||
|
||||
public Guid? ParentId { get; set; }
|
||||
}
|
||||
|
|
@ -4,13 +4,15 @@ using Volo.Abp.ObjectExtending;
|
|||
|
||||
namespace Sozsoft.Platform.Identity.Dto;
|
||||
|
||||
public class UserInfoViewModel: ExtensibleObject
|
||||
public class UserInfoViewModel : ExtensibleObject
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid? TenantId { get; set; }
|
||||
public string ConcurrencyStamp { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Surname { get; set; }
|
||||
public string FullName => $"{Name} {Surname}".TrimEnd();
|
||||
public string Email { get; set; }
|
||||
public string PhoneNumber { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using Sozsoft.Platform.Identity.Dto;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
public class AnnouncementDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
public string Excerpt { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
public string Category { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public UserInfoViewModel User { get; set; }
|
||||
public DateTime PublishDate { get; set; }
|
||||
public DateTime? ExpiryDate { get; set; }
|
||||
public bool IsPinned { get; set; }
|
||||
public int ViewCount { get; set; }
|
||||
public string Attachments { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
using Volo.Abp.Application.Services;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
public interface IIntranetAppService : IApplicationService
|
||||
{
|
||||
Task<IntranetDashboardDto> GetIntranetDashboardAsync();
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using Sozsoft.Platform.FileManagement;
|
||||
using Sozsoft.Platform.Identity.Dto;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
public class IntranetDashboardDto
|
||||
{
|
||||
public List<UserInfoViewModel> Birthdays { get; set; } = [];
|
||||
public List<FileItemDto> Documents { get; set; } = [];
|
||||
public List<AnnouncementDto> Announcements { get; set; } = [];
|
||||
public List<SurveyDto> Surveys { get; set; } = [];
|
||||
public List<SocialPostDto> SocialPosts { get; set; } = [];
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Sozsoft.Platform.Identity.Dto;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
public class SocialPostDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid? UserId { get; set; }
|
||||
public UserInfoViewModel? User { get; set; }
|
||||
public string Content { get; set; }
|
||||
|
||||
public int LikeCount { get; set; }
|
||||
public bool IsLiked { get; set; }
|
||||
public bool IsOwnPost { get; set; }
|
||||
|
||||
public SocialLocationDto? Location { get; set; }
|
||||
public SocialMediaDto? Media { get; set; }
|
||||
public List<SocialCommentDto> Comments { get; set; }
|
||||
public List<SocialLikeDto> Likes { get; set; }
|
||||
}
|
||||
|
||||
public class SocialLocationDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid SocialPostId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public double? Lat { get; set; }
|
||||
public double? Lng { get; set; }
|
||||
public string? PlaceId { get; set; }
|
||||
}
|
||||
|
||||
public class SocialMediaDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid SocialPostId { get; set; }
|
||||
public string Type { get; set; } // image | video | poll
|
||||
public string[] Urls { get; set; }
|
||||
|
||||
// Poll Fields
|
||||
public string? PollQuestion { get; set; }
|
||||
public int? PollTotalVotes { get; set; }
|
||||
public DateTime? PollEndsAt { get; set; }
|
||||
public string? PollUserVoteId { get; set; }
|
||||
|
||||
public List<SocialPollOptionDto> PollOptions { get; set; }
|
||||
}
|
||||
|
||||
public class SocialPollOptionDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid SocialMediaId { get; set; }
|
||||
public string Text { get; set; }
|
||||
public int Votes { get; set; }
|
||||
}
|
||||
|
||||
public class SocialCommentDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid SocialPostId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public UserInfoViewModel? User { get; set; }
|
||||
public string Content { get; set; }
|
||||
}
|
||||
|
||||
public class SocialLikeDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid SocialPostId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public UserInfoViewModel? User { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
public class SurveyDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public DateTime Deadline { get; set; }
|
||||
public int Responses { get; set; }
|
||||
public string Status { get; set; } // draft | active | closed
|
||||
public bool IsAnonymous { get; set; }
|
||||
|
||||
public List<SurveyQuestionDto> Questions { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyQuestionDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid SurveyId { get; set; }
|
||||
public string QuestionText { get; set; }
|
||||
public string Type { get; set; } // rating | multiple-choice | text | textarea | yes-no
|
||||
public int Order { get; set; }
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
public List<SurveyQuestionOptionDto> Options { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyQuestionOptionDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid QuestionId { get; set; }
|
||||
public string Text { get; set; }
|
||||
public int Order { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyResponseDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid SurveyId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public DateTime SubmissionTime { get; set; }
|
||||
|
||||
public List<SurveyAnswerDto> Answers { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyAnswerDto : FullAuditedEntityDto<Guid>
|
||||
{
|
||||
public Guid ResponseId { get; set; }
|
||||
public Guid QuestionId { get; set; }
|
||||
public string QuestionType { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ public class SearchBlogPostsInput : PagedAndSortedResultRequestDto
|
|||
public string Query { get; set; }
|
||||
public Guid? CategoryId { get; set; }
|
||||
public string Tag { get; set; }
|
||||
public Guid? EmployeeId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public bool? IsPublished { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
using AutoMapper;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Hr;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
public class HrAutoMapperProfile : Profile
|
||||
{
|
||||
public HrAutoMapperProfile()
|
||||
{
|
||||
CreateMap<Department, DepartmentDto>();
|
||||
CreateMap<JobPosition, JobPositionDto>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -11,7 +11,14 @@ public class IdentityAutoMapperProfile : Profile
|
|||
{
|
||||
public IdentityAutoMapperProfile()
|
||||
{
|
||||
CreateMap<IdentityUser, UserInfoViewModel>();
|
||||
CreateMap<IdentityUser, UserInfoViewModel>()
|
||||
.ForMember(dest => dest.Roles, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Branches, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Claims, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.WorkHours, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Departments, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.JobPositions, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.userRoleNames, opt => opt.Ignore());
|
||||
CreateMap<OrganizationUnit, OrganizationUnitDto>();
|
||||
CreateMap<CreateUpdateOrganizationUnitDto, OrganizationUnit>();
|
||||
CreateMap<Tenant, CustomTenantDto>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,350 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sozsoft.Platform.BlobStoring;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.FileManagement;
|
||||
using Sozsoft.Platform.Identity.Dto;
|
||||
using Sozsoft.Platform.Extensions;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.Identity;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.Uow;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
[Authorize]
|
||||
public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||
{
|
||||
private readonly ICurrentTenant _currentTenant;
|
||||
private readonly BlobManager _blobContainer;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private readonly IIdentityUserAppService _identityUserAppService;
|
||||
private readonly IIdentityUserRepository _identityUserRepository;
|
||||
private readonly IRepository<Department, Guid> _departmentRepository;
|
||||
private readonly IRepository<JobPosition, Guid> _jobPositionRepository;
|
||||
private readonly IRepository<Announcement, Guid> _announcementRepository;
|
||||
private readonly IRepository<Survey, Guid> _surveyRepository;
|
||||
private readonly IRepository<SocialPost, Guid> _socialPostRepository;
|
||||
|
||||
public IntranetAppService(
|
||||
ICurrentTenant currentTenant,
|
||||
BlobManager blobContainer,
|
||||
IConfiguration configuration,
|
||||
|
||||
IIdentityUserAppService identityUserAppService,
|
||||
IIdentityUserRepository identityUserRepository,
|
||||
IRepository<Department, Guid> departmentRepository,
|
||||
IRepository<JobPosition, Guid> jobPositionRepository,
|
||||
IRepository<Announcement, Guid> announcementRepository,
|
||||
IRepository<Survey, Guid> surveyRepository,
|
||||
IRepository<SocialPost, Guid> socialPostRepository
|
||||
)
|
||||
{
|
||||
_currentTenant = currentTenant;
|
||||
_blobContainer = blobContainer;
|
||||
_configuration = configuration;
|
||||
_identityUserAppService = identityUserAppService;
|
||||
_identityUserRepository = identityUserRepository;
|
||||
_departmentRepository = departmentRepository;
|
||||
_jobPositionRepository = jobPositionRepository;
|
||||
_announcementRepository = announcementRepository;
|
||||
_surveyRepository = surveyRepository;
|
||||
_socialPostRepository = socialPostRepository;
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public async Task<IntranetDashboardDto> GetIntranetDashboardAsync()
|
||||
{
|
||||
return new IntranetDashboardDto
|
||||
{
|
||||
Birthdays = await GetBirthdaysAsync(), //1
|
||||
Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet), //2
|
||||
Announcements = await GetAnnouncementsAsync(), //3
|
||||
Surveys = await GetSurveysAsync(), //4
|
||||
SocialPosts = await GetSocialPostsAsync(), //5
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<List<UserInfoViewModel>> GetBirthdaysAsync()
|
||||
{
|
||||
var today = DateTime.Now;
|
||||
|
||||
var users = await _identityUserRepository.GetListAsync();
|
||||
|
||||
var userList = users
|
||||
.Where(u =>
|
||||
{
|
||||
var birthDate = u.GetBirthDate();
|
||||
return birthDate.HasValue
|
||||
&& birthDate.Value.Day == today.Day
|
||||
&& birthDate.Value.Month == today.Month;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var allDepartments = await _departmentRepository.GetListAsync();
|
||||
var departmentDict = allDepartments.ToDictionary(d => d.Id, d => d.Name);
|
||||
|
||||
var allJobPositions = await _jobPositionRepository.GetListAsync();
|
||||
var jobPositionDict = allJobPositions.ToDictionary(j => j.Id, j => j);
|
||||
|
||||
var result = ObjectMapper.Map<List<IdentityUser>, List<UserInfoViewModel>>(userList);
|
||||
|
||||
for (var i = 0; i < userList.Count; i++)
|
||||
{
|
||||
var user = userList[i];
|
||||
|
||||
result[i].BirthDate = user.GetBirthDate();
|
||||
result[i].WorkHour = user.GetWorkHour();
|
||||
result[i].Nationality = user.GetNationality();
|
||||
|
||||
var deptId = user.GetDepartmentId();
|
||||
if (deptId != Guid.Empty && departmentDict.TryGetValue(deptId, out var deptName))
|
||||
{
|
||||
result[i].DepartmentId = deptId;
|
||||
result[i].Departments =
|
||||
[
|
||||
new AssignedDepartmentViewModel { Id = deptId, Name = deptName, IsAssigned = true }
|
||||
];
|
||||
}
|
||||
|
||||
var jobPosId = user.GetJobPositionId();
|
||||
if (jobPosId != Guid.Empty && jobPositionDict.TryGetValue(jobPosId, out var jobPosition))
|
||||
{
|
||||
result[i].JobPositionId = jobPosId;
|
||||
result[i].JobPositions =
|
||||
[
|
||||
new AssignedJobPoisitionViewModel { Id = jobPosId, Name = jobPosition.Name, DepartmentId = jobPosition.DepartmentId, IsAssigned = true }
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<List<AnnouncementDto>> GetAnnouncementsAsync()
|
||||
{
|
||||
var announcements = await _announcementRepository.GetListAsync();
|
||||
var announcementDtos = new List<AnnouncementDto>();
|
||||
|
||||
var allDepartments = await _departmentRepository.GetListAsync();
|
||||
var departmentDict = allDepartments.ToDictionary(d => d.Id, d => d.Name);
|
||||
|
||||
var allJobPositions = await _jobPositionRepository.GetListAsync();
|
||||
var jobPositionDict = allJobPositions.ToDictionary(j => j.Id, j => j);
|
||||
|
||||
foreach (var announcement in announcements)
|
||||
{
|
||||
var dto = ObjectMapper.Map<Announcement, AnnouncementDto>(announcement);
|
||||
|
||||
var user = await _identityUserRepository.FindAsync(announcement.UserId ?? Guid.Empty);
|
||||
if (user != null)
|
||||
{
|
||||
var userVm = ObjectMapper.Map<IdentityUser, UserInfoViewModel>(user);
|
||||
|
||||
userVm.BirthDate = user.GetBirthDate();
|
||||
userVm.WorkHour = user.GetWorkHour();
|
||||
userVm.Nationality = user.GetNationality();
|
||||
|
||||
var deptId = user.GetDepartmentId();
|
||||
if (deptId != Guid.Empty && departmentDict.TryGetValue(deptId, out var deptName))
|
||||
{
|
||||
userVm.DepartmentId = deptId;
|
||||
userVm.Departments =
|
||||
[
|
||||
new AssignedDepartmentViewModel { Id = deptId, Name = deptName, IsAssigned = true }
|
||||
];
|
||||
}
|
||||
|
||||
var jobPosId = user.GetJobPositionId();
|
||||
if (jobPosId != Guid.Empty && jobPositionDict.TryGetValue(jobPosId, out var jobPosition))
|
||||
{
|
||||
userVm.JobPositionId = jobPosId;
|
||||
userVm.JobPositions =
|
||||
[
|
||||
new AssignedJobPoisitionViewModel { Id = jobPosId, Name = jobPosition.Name, DepartmentId = jobPosition.DepartmentId, IsAssigned = true }
|
||||
];
|
||||
}
|
||||
|
||||
dto.User = userVm;
|
||||
}
|
||||
|
||||
announcementDtos.Add(dto);
|
||||
}
|
||||
|
||||
return announcementDtos;
|
||||
}
|
||||
|
||||
private async Task<List<SurveyDto>> GetSurveysAsync()
|
||||
{
|
||||
var queryable = await _surveyRepository.GetQueryableAsync();
|
||||
|
||||
var surveys = await AsyncExecuter.ToListAsync(
|
||||
queryable
|
||||
.Where(s => s.Status == "active")
|
||||
.Include(s => s.Questions)
|
||||
.ThenInclude(q => q.Options)
|
||||
);
|
||||
|
||||
return ObjectMapper.Map<List<Survey>, List<SurveyDto>>(surveys);
|
||||
}
|
||||
|
||||
private async Task<List<SocialPostDto>> GetSocialPostsAsync()
|
||||
{
|
||||
var queryable = await _socialPostRepository
|
||||
.WithDetailsAsync(e => e.Location, e => e.Media, e => e.Comments, e => e.Likes);
|
||||
|
||||
var socialPosts = await AsyncExecuter.ToListAsync(queryable);
|
||||
|
||||
var dtos = ObjectMapper.Map<List<SocialPost>, List<SocialPostDto>>(socialPosts);
|
||||
|
||||
// Collect all unique user IDs to resolve in a single query
|
||||
var userIds = dtos
|
||||
.Select(p => p.UserId)
|
||||
.Union(dtos.SelectMany(p => p.Comments.Select(c => c.UserId)))
|
||||
.Union(dtos.SelectMany(p => p.Likes.Select(l => l.UserId)))
|
||||
.Where(id => id.HasValue)
|
||||
.Select(id => id!.Value)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (userIds.Count > 0)
|
||||
{
|
||||
var allDepartments = await _departmentRepository.GetListAsync();
|
||||
var departmentDict = allDepartments.ToDictionary(d => d.Id, d => d.Name);
|
||||
|
||||
var allJobPositions = await _jobPositionRepository.GetListAsync();
|
||||
var jobPositionDict = allJobPositions.ToDictionary(j => j.Id, j => j);
|
||||
|
||||
var users = await _identityUserRepository.GetListAsync();
|
||||
var userMap = users
|
||||
.Where(u => userIds.Contains(u.Id))
|
||||
.ToDictionary(u => u.Id, u =>
|
||||
{
|
||||
var vm = ObjectMapper.Map<IdentityUser, UserInfoViewModel>(u);
|
||||
vm.BirthDate = u.GetBirthDate();
|
||||
vm.WorkHour = u.GetWorkHour();
|
||||
vm.Nationality = u.GetNationality();
|
||||
|
||||
var deptId = u.GetDepartmentId();
|
||||
if (deptId != Guid.Empty && departmentDict.TryGetValue(deptId, out var deptName))
|
||||
{
|
||||
vm.DepartmentId = deptId;
|
||||
vm.Departments =
|
||||
[
|
||||
new AssignedDepartmentViewModel { Id = deptId, Name = deptName, IsAssigned = true }
|
||||
];
|
||||
}
|
||||
|
||||
var jobPosId = u.GetJobPositionId();
|
||||
if (jobPosId != Guid.Empty && jobPositionDict.TryGetValue(jobPosId, out var jobPosition))
|
||||
{
|
||||
vm.JobPositionId = jobPosId;
|
||||
vm.JobPositions =
|
||||
[
|
||||
new AssignedJobPoisitionViewModel { Id = jobPosId, Name = jobPosition.Name, DepartmentId = jobPosition.DepartmentId, IsAssigned = true }
|
||||
];
|
||||
}
|
||||
|
||||
return vm;
|
||||
});
|
||||
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
if (dto.UserId.HasValue && userMap.TryGetValue(dto.UserId.Value, out var postUser))
|
||||
dto.User = postUser;
|
||||
|
||||
foreach (var comment in dto.Comments)
|
||||
if (comment.UserId.HasValue && userMap.TryGetValue(comment.UserId.Value, out var commentUser))
|
||||
comment.User = commentUser;
|
||||
|
||||
foreach (var like in dto.Likes)
|
||||
if (like.UserId.HasValue && userMap.TryGetValue(like.UserId.Value, out var likeUser))
|
||||
like.User = likeUser;
|
||||
}
|
||||
}
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public async Task<List<FileItemDto>> GetIntranetDocumentsAsync(string folderPath)
|
||||
{
|
||||
var items = new List<FileItemDto>();
|
||||
var cdnBasePath = _configuration["App:CdnPath"];
|
||||
|
||||
if (string.IsNullOrEmpty(cdnBasePath))
|
||||
{
|
||||
Logger.LogWarning("CDN path is not configured");
|
||||
return items;
|
||||
}
|
||||
|
||||
var tenantId = _currentTenant.Id?.ToString() ?? "host";
|
||||
var fullPath = Path.Combine(cdnBasePath, tenantId);
|
||||
|
||||
if (!string.IsNullOrEmpty(folderPath))
|
||||
{
|
||||
fullPath = Path.Combine(fullPath, folderPath);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(fullPath))
|
||||
{
|
||||
Logger.LogWarning($"Directory not found: {fullPath}");
|
||||
return items;
|
||||
}
|
||||
|
||||
var files = Directory.GetFiles(fullPath);
|
||||
foreach (var file in files)
|
||||
{
|
||||
var fileInfo = new FileInfo(file);
|
||||
var relativePath = string.IsNullOrEmpty(folderPath) ? fileInfo.Name : $"{folderPath}/{fileInfo.Name}";
|
||||
|
||||
items.Add(new FileItemDto
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Name = fileInfo.Name,
|
||||
Type = "file",
|
||||
Size = fileInfo.Length,
|
||||
Extension = fileInfo.Extension,
|
||||
MimeType = GetMimeType(fileInfo.Extension),
|
||||
CreatedAt = fileInfo.CreationTime,
|
||||
ModifiedAt = fileInfo.LastWriteTime,
|
||||
Path = relativePath,
|
||||
ParentId = string.Empty,
|
||||
IsReadOnly = false,
|
||||
ChildCount = 0,
|
||||
TenantId = _currentTenant.Id?.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
return items.OrderBy(x => x.Name).ToList();
|
||||
}
|
||||
|
||||
private string GetMimeType(string extension)
|
||||
{
|
||||
return extension.ToLowerInvariant() switch
|
||||
{
|
||||
".pdf" => "application/pdf",
|
||||
".doc" => "application/msword",
|
||||
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
".xls" => "application/vnd.ms-excel",
|
||||
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
".ppt" => "application/vnd.ms-powerpoint",
|
||||
".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
".jpg" or ".jpeg" => "image/jpeg",
|
||||
".png" => "image/png",
|
||||
".gif" => "image/gif",
|
||||
".txt" => "text/plain",
|
||||
".zip" => "application/zip",
|
||||
".rar" => "application/x-rar-compressed",
|
||||
_ => "application/octet-stream"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using AutoMapper;
|
||||
using Sozsoft.Platform.Entities;
|
||||
|
||||
namespace Sozsoft.Platform.Intranet;
|
||||
|
||||
public class IntranetAutoMapperProfile : Profile
|
||||
{
|
||||
public IntranetAutoMapperProfile()
|
||||
{
|
||||
CreateMap<Announcement, AnnouncementDto>();
|
||||
CreateMap<Survey, SurveyDto>();
|
||||
CreateMap<SurveyQuestion, SurveyQuestionDto>();
|
||||
CreateMap<SurveyQuestionOption, SurveyQuestionOptionDto>();
|
||||
CreateMap<SurveyResponse, SurveyResponseDto>();
|
||||
CreateMap<SurveyAnswer, SurveyAnswerDto>();
|
||||
|
||||
CreateMap<SocialPost, SocialPostDto>();
|
||||
CreateMap<SocialLocation, SocialLocationDto>();
|
||||
CreateMap<SocialMedia, SocialMediaDto>();
|
||||
CreateMap<SocialPollOption, SocialPollOptionDto>();
|
||||
CreateMap<SocialComment, SocialCommentDto>();
|
||||
CreateMap<SocialLike, SocialLikeDto>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,12 +138,6 @@
|
|||
"en": "Administration",
|
||||
"tr": "Yönetim"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Intranet",
|
||||
"en": "Intranet",
|
||||
"tr": "Intranet"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.SupplyChain",
|
||||
|
|
@ -3644,6 +3638,12 @@
|
|||
"en": "Audit Logs",
|
||||
"tr": "Audit Günlükleri"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Intranet",
|
||||
"en": "Intranet",
|
||||
"tr": "Intranet"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.AuditLogs.FetchFailed",
|
||||
|
|
@ -6092,6 +6092,12 @@
|
|||
"tr": "Eğitim Durumu",
|
||||
"en": "Education Status"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Intranet.SocialComment",
|
||||
"tr": "Sosyal Yorumlar",
|
||||
"en": "Social Comments"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Intranet.Events",
|
||||
|
|
@ -12284,6 +12290,12 @@
|
|||
"tr": "Acil",
|
||||
"en": "Urgent"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Platform.Intranet.Widgets.ActiveSurveys.Questions",
|
||||
"tr": "Sorular",
|
||||
"en": "Questions"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "App.Platform.Intranet.Widgets.ActiveSurveys.Title",
|
||||
|
|
|
|||
|
|
@ -2989,6 +2989,15 @@
|
|||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Erp|Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "AbpIdentity.Users.Widget",
|
||||
"ParentName": "AbpIdentity.Users",
|
||||
"DisplayName": "Widget",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Erp|Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "AbpIdentity.Users.Update.ManageRoles",
|
||||
|
|
@ -3304,6 +3313,15 @@
|
|||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Erp|Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Files.Widget",
|
||||
"ParentName": "App.Files",
|
||||
"DisplayName": "Widget",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Erp|Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.ForumManagement.Publish",
|
||||
|
|
@ -3312,6 +3330,429 @@
|
|||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Erp|Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet",
|
||||
"ParentName": null,
|
||||
"DisplayName": "App.Intranet",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Erp|Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventType",
|
||||
"ParentName": "App.Intranet",
|
||||
"DisplayName": "App.Intranet.Events.EventType",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventType.Create",
|
||||
"ParentName": "App.Intranet.Events.EventType",
|
||||
"DisplayName": "Create",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventType.Update",
|
||||
"ParentName": "App.Intranet.Events.EventType",
|
||||
"DisplayName": "Update",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventType.Delete",
|
||||
"ParentName": "App.Intranet.Events.EventType",
|
||||
"DisplayName": "Delete",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventType.Export",
|
||||
"ParentName": "App.Intranet.Events.EventType",
|
||||
"DisplayName": "Export",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventType.Import",
|
||||
"ParentName": "App.Intranet.Events.EventType",
|
||||
"DisplayName": "Import",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventType.Note",
|
||||
"ParentName": "App.Intranet.Events.EventType",
|
||||
"DisplayName": "Note",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventCategory",
|
||||
"ParentName": "App.Intranet",
|
||||
"DisplayName": "App.Intranet.Events.EventCategory",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventCategory.Create",
|
||||
"ParentName": "App.Intranet.Events.EventCategory",
|
||||
"DisplayName": "Create",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventCategory.Update",
|
||||
"ParentName": "App.Intranet.Events.EventCategory",
|
||||
"DisplayName": "Update",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventCategory.Delete",
|
||||
"ParentName": "App.Intranet.Events.EventCategory",
|
||||
"DisplayName": "Delete",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventCategory.Export",
|
||||
"ParentName": "App.Intranet.Events.EventCategory",
|
||||
"DisplayName": "Export",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventCategory.Import",
|
||||
"ParentName": "App.Intranet.Events.EventCategory",
|
||||
"DisplayName": "Import",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.EventCategory.Note",
|
||||
"ParentName": "App.Intranet.Events.EventCategory",
|
||||
"DisplayName": "Note",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.Event",
|
||||
"ParentName": "App.Intranet",
|
||||
"DisplayName": "App.Intranet.Events.Event",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.Event.Create",
|
||||
"ParentName": "App.Intranet.Events.Event",
|
||||
"DisplayName": "Create",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.Event.Update",
|
||||
"ParentName": "App.Intranet.Events.Event",
|
||||
"DisplayName": "Update",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.Event.Delete",
|
||||
"ParentName": "App.Intranet.Events.Event",
|
||||
"DisplayName": "Delete",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.Event.Export",
|
||||
"ParentName": "App.Intranet.Events.Event",
|
||||
"DisplayName": "Export",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.Event.Import",
|
||||
"ParentName": "App.Intranet.Events.Event",
|
||||
"DisplayName": "Import",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.Event.Note",
|
||||
"ParentName": "App.Intranet.Events.Event",
|
||||
"DisplayName": "Note",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Events.Event.Widget",
|
||||
"ParentName": "App.Intranet.Events.Event",
|
||||
"DisplayName": "Widget",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Announcement",
|
||||
"ParentName": "App.Intranet",
|
||||
"DisplayName": "App.Intranet.Announcement",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Announcement.Create",
|
||||
"ParentName": "App.Intranet.Announcement",
|
||||
"DisplayName": "Create",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Announcement.Update",
|
||||
"ParentName": "App.Intranet.Announcement",
|
||||
"DisplayName": "Update",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Announcement.Delete",
|
||||
"ParentName": "App.Intranet.Announcement",
|
||||
"DisplayName": "Delete",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Announcement.Export",
|
||||
"ParentName": "App.Intranet.Announcement",
|
||||
"DisplayName": "Export",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Announcement.Import",
|
||||
"ParentName": "App.Intranet.Announcement",
|
||||
"DisplayName": "Import",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Announcement.Note",
|
||||
"ParentName": "App.Intranet.Announcement",
|
||||
"DisplayName": "Note",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Announcement.Widget",
|
||||
"ParentName": "App.Intranet.Announcement",
|
||||
"DisplayName": "Widget",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialPost",
|
||||
"ParentName": "App.Intranet",
|
||||
"DisplayName": "App.Intranet.SocialPost",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialPost.Create",
|
||||
"ParentName": "App.Intranet.SocialPost",
|
||||
"DisplayName": "Create",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialPost.Update",
|
||||
"ParentName": "App.Intranet.SocialPost",
|
||||
"DisplayName": "Update",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialPost.Delete",
|
||||
"ParentName": "App.Intranet.SocialPost",
|
||||
"DisplayName": "Delete",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialPost.Export",
|
||||
"ParentName": "App.Intranet.SocialPost",
|
||||
"DisplayName": "Export",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialPost.Import",
|
||||
"ParentName": "App.Intranet.SocialPost",
|
||||
"DisplayName": "Import",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialPost.Note",
|
||||
"ParentName": "App.Intranet.SocialPost",
|
||||
"DisplayName": "Note",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialPost.Widget",
|
||||
"ParentName": "App.Intranet.SocialPost",
|
||||
"DisplayName": "Widget",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialComment",
|
||||
"ParentName": "App.Intranet",
|
||||
"DisplayName": "App.Intranet.SocialComment",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialComment.Create",
|
||||
"ParentName": "App.Intranet.SocialComment",
|
||||
"DisplayName": "Create",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialComment.Update",
|
||||
"ParentName": "App.Intranet.SocialComment",
|
||||
"DisplayName": "Update",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialComment.Delete",
|
||||
"ParentName": "App.Intranet.SocialComment",
|
||||
"DisplayName": "Delete",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialComment.Export",
|
||||
"ParentName": "App.Intranet.SocialComment",
|
||||
"DisplayName": "Export",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialComment.Import",
|
||||
"ParentName": "App.Intranet.SocialComment",
|
||||
"DisplayName": "Import",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.SocialComment.Note",
|
||||
"ParentName": "App.Intranet.SocialComment",
|
||||
"DisplayName": "Note",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Kurs"
|
||||
},
|
||||
{
|
||||
"GroupName": "App.Administration",
|
||||
"Name": "App.Intranet.Survey.Widget",
|
||||
"ParentName": "App.Intranet",
|
||||
"DisplayName": "App.Intranet.Survey.Widget",
|
||||
"IsEnabled": true,
|
||||
"MultiTenancySide": 3,
|
||||
"MenuGroup": "Erp|Kurs"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -70,14 +70,14 @@ public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
var wizardDataPath = Path.Combine(Directory.GetCurrentDirectory(), "Seeds", "WizardData");
|
||||
if (!Directory.Exists(wizardDataPath))
|
||||
{
|
||||
_logger.LogInformation("Seeds/WizardData directory not found, skipping.");
|
||||
_logger.LogInformation("WizardData directory not found, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json").OrderBy(f => Path.GetFileName(f)).ToArray();
|
||||
if (jsonFiles.Length == 0)
|
||||
{
|
||||
_logger.LogInformation("No JSON files found in Seeds/WizardData directory, skipping.");
|
||||
_logger.LogInformation("No JSON files found in WizardData directory, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,4 +64,16 @@ public enum TableNameEnum
|
|||
BlogPost,
|
||||
Demo,
|
||||
Contact,
|
||||
Announcement,
|
||||
Survey,
|
||||
SurveyQuestion,
|
||||
SurveyQuestionOption,
|
||||
SurveyResponse,
|
||||
SurveyAnswer,
|
||||
SocialPost,
|
||||
SocialLocation,
|
||||
SocialMedia,
|
||||
SocialPollOption,
|
||||
SocialComment,
|
||||
SocialLike
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,21 @@ public static class TableNameResolver
|
|||
{ nameof(TableNameEnum.Note), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.ReportCategory), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.ReportTemplate), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
|
||||
// 🔹 INTRANET TABLOLARI
|
||||
{ nameof(TableNameEnum.Announcement), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.Survey), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SurveyQuestion), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SurveyQuestionOption), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SurveyResponse), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SurveyAnswer), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SocialPost), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SocialLocation), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SocialMedia), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SocialPollOption), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SocialComment), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
{ nameof(TableNameEnum.SocialLike), (TablePrefix.TenantByName, MenuPrefix.Administration) },
|
||||
|
||||
};
|
||||
|
||||
public static string GetFullTableName(string tableName)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using Volo.Abp.Domain.Entities.Auditing;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
|
||||
namespace Sozsoft.Platform.Entities;
|
||||
|
||||
public class Announcement : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
public string Excerpt { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
public string Category { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public DateTime PublishDate { get; set; }
|
||||
public DateTime? ExpiryDate { get; set; }
|
||||
public bool IsPinned { get; set; }
|
||||
public int ViewCount { get; set; }
|
||||
public string Attachments { get; set; }
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Volo.Abp.Domain.Entities.Auditing;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
|
||||
namespace Sozsoft.Platform.Entities;
|
||||
|
||||
public class SocialPost : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid? UserId { get; set; }
|
||||
|
||||
public string Content { get; set; }
|
||||
|
||||
public int LikeCount { get; set; }
|
||||
public bool IsLiked { get; set; }
|
||||
public bool IsOwnPost { get; set; }
|
||||
|
||||
// Relations
|
||||
public SocialLocation Location { get; set; }
|
||||
public SocialMedia Media { get; set; }
|
||||
public ICollection<SocialComment> Comments { get; set; }
|
||||
public ICollection<SocialLike> Likes { get; set; }
|
||||
}
|
||||
|
||||
public class SocialLocation : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid SocialPostId { get; set; }
|
||||
public SocialPost SocialPost { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public double? Lat { get; set; }
|
||||
public double? Lng { get; set; }
|
||||
public string? PlaceId { get; set; }
|
||||
}
|
||||
|
||||
public class SocialMedia : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid SocialPostId { get; set; }
|
||||
public SocialPost SocialPost { get; set; }
|
||||
|
||||
public string Type { get; set; } // image | video | poll
|
||||
public string[] Urls { get; set; }
|
||||
|
||||
// Poll fields
|
||||
public string? PollQuestion { get; set; }
|
||||
public int? PollTotalVotes { get; set; }
|
||||
public DateTime? PollEndsAt { get; set; }
|
||||
public string? PollUserVoteId { get; set; }
|
||||
|
||||
public ICollection<SocialPollOption> PollOptions { get; set; }
|
||||
}
|
||||
|
||||
public class SocialPollOption : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid SocialMediaId { get; set; }
|
||||
public SocialMedia SocialMedia { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
public int Votes { get; set; }
|
||||
}
|
||||
|
||||
public class SocialComment : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid SocialPostId { get; set; }
|
||||
public SocialPost SocialPost { get; set; }
|
||||
|
||||
public Guid? UserId { get; set; }
|
||||
|
||||
public string Content { get; set; }
|
||||
}
|
||||
|
||||
public class SocialLike : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid SocialPostId { get; set; }
|
||||
public SocialPost SocialPost { get; set; }
|
||||
|
||||
public Guid? UserId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Volo.Abp.Domain.Entities.Auditing;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
|
||||
namespace Sozsoft.Platform.Entities;
|
||||
|
||||
public class Survey : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public DateTime Deadline { get; set; }
|
||||
public int Responses { get; set; }
|
||||
public string Status { get; set; } // draft | active | closed
|
||||
public bool IsAnonymous { get; set; }
|
||||
|
||||
public ICollection<SurveyQuestion> Questions { get; set; }
|
||||
public ICollection<SurveyResponse> SurveyResponses { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyQuestion : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid SurveyId { get; set; }
|
||||
public Survey Survey { get; set; }
|
||||
|
||||
public string QuestionText { get; set; }
|
||||
public string Type { get; set; } // rating | multiple-choice | text | textarea | yes-no
|
||||
public int Order { get; set; }
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
public ICollection<SurveyQuestionOption> Options { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyQuestionOption : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid QuestionId { get; set; }
|
||||
public SurveyQuestion Question { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
public int Order { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyResponse : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid SurveyId { get; set; }
|
||||
public Survey Survey { get; set; }
|
||||
|
||||
public Guid? UserId { get; set; }
|
||||
|
||||
public DateTime SubmissionTime { get; set; }
|
||||
|
||||
public ICollection<SurveyAnswer> Answers { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyAnswer : FullAuditedEntity<Guid>, IMultiTenant
|
||||
{
|
||||
public Guid? TenantId { get; set; }
|
||||
|
||||
public Guid ResponseId { get; set; }
|
||||
public SurveyResponse Response { get; set; }
|
||||
|
||||
public Guid QuestionId { get; set; }
|
||||
public SurveyQuestion Question { get; set; }
|
||||
|
||||
public string QuestionType { get; set; } // rating | multiple-choice | text | textarea | yes-no
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
|
@ -109,6 +109,23 @@ public class PlatformDbContext :
|
|||
public DbSet<JobPosition> JobPositions { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Intranet
|
||||
public DbSet<Announcement> Announcements { get; set; }
|
||||
|
||||
public DbSet<Survey> Surveys { get; set; }
|
||||
public DbSet<SurveyQuestion> SurveyQuestions { get; set; }
|
||||
public DbSet<SurveyQuestionOption> SurveyQuestionOptions { get; set; }
|
||||
public DbSet<SurveyResponse> SurveyResponses { get; set; }
|
||||
public DbSet<SurveyAnswer> SurveyAnswers { get; set; }
|
||||
|
||||
public DbSet<SocialPost> SocialPosts { get; set; }
|
||||
public DbSet<SocialLocation> SocialLocations { get; set; }
|
||||
public DbSet<SocialMedia> SocialMedias { get; set; }
|
||||
public DbSet<SocialPollOption> SocialPollOptions { get; set; }
|
||||
public DbSet<SocialComment> SocialComments { get; set; }
|
||||
public DbSet<SocialLike> SocialLikes { get; set; }
|
||||
#endregion
|
||||
|
||||
public PlatformDbContext(DbContextOptions<PlatformDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
|
|
@ -1026,5 +1043,171 @@ public class PlatformDbContext :
|
|||
b.Property(x => x.PrimaryEntityType).HasMaxLength(256);
|
||||
b.Property(x => x.ControllerName).HasMaxLength(256);
|
||||
});
|
||||
|
||||
builder.Entity<Announcement>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Announcement)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.Title).IsRequired().HasMaxLength(256);
|
||||
b.Property(x => x.Excerpt).IsRequired().HasMaxLength(512);
|
||||
b.Property(x => x.Content).IsRequired().HasMaxLength(4096);
|
||||
b.Property(x => x.ImageUrl).HasMaxLength(512);
|
||||
b.Property(x => x.Category).IsRequired().HasMaxLength(64);
|
||||
b.Property(x => x.PublishDate).IsRequired();
|
||||
b.Property(x => x.Attachments).HasMaxLength(2048);
|
||||
b.Property(x => x.ViewCount).HasDefaultValue(0);
|
||||
});
|
||||
|
||||
builder.Entity<Survey>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.Survey)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.Title).IsRequired().HasMaxLength(256);
|
||||
b.Property(x => x.Description).HasMaxLength(2048);
|
||||
b.Property(x => x.Deadline).IsRequired();
|
||||
b.Property(x => x.Responses).HasDefaultValue(0);
|
||||
b.Property(x => x.Status).IsRequired().HasMaxLength(10);
|
||||
});
|
||||
|
||||
builder.Entity<SurveyQuestion>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SurveyQuestion)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.QuestionText).IsRequired().HasMaxLength(1024);
|
||||
b.Property(x => x.Type).IsRequired().HasMaxLength(64);
|
||||
|
||||
b.HasOne(x => x.Survey)
|
||||
.WithMany(x => x.Questions)
|
||||
.HasForeignKey(x => x.SurveyId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
builder.Entity<SurveyQuestionOption>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SurveyQuestionOption)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.Text).IsRequired().HasMaxLength(512);
|
||||
b.Property(x => x.Order).IsRequired();
|
||||
|
||||
b.HasOne(x => x.Question)
|
||||
.WithMany(x => x.Options)
|
||||
.HasForeignKey(x => x.QuestionId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
builder.Entity<SurveyResponse>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SurveyResponse)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.SubmissionTime).IsRequired();
|
||||
|
||||
b.HasOne(x => x.Survey)
|
||||
.WithMany(x => x.SurveyResponses)
|
||||
.HasForeignKey(x => x.SurveyId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
builder.Entity<SurveyAnswer>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SurveyAnswer)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.QuestionType).IsRequired().HasMaxLength(64);
|
||||
b.Property(x => x.Value).IsRequired().HasMaxLength(1024);
|
||||
|
||||
b.HasOne(x => x.Response)
|
||||
.WithMany(x => x.Answers)
|
||||
.HasForeignKey(x => x.ResponseId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne(x => x.Question)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.QuestionId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
builder.Entity<SocialPost>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialPost)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.Content).IsRequired().HasMaxLength(4096);
|
||||
b.Property(x => x.LikeCount).HasDefaultValue(0);
|
||||
b.Property(x => x.IsOwnPost).HasDefaultValue(false);
|
||||
b.Property(x => x.IsLiked).HasDefaultValue(false);
|
||||
});
|
||||
|
||||
builder.Entity<SocialLocation>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialLocation)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
|
||||
b.Property(x => x.Address).HasMaxLength(512);
|
||||
b.Property(x => x.PlaceId).HasMaxLength(128);
|
||||
|
||||
b.HasOne(x => x.SocialPost)
|
||||
.WithOne(p => p.Location)
|
||||
.HasForeignKey<SocialLocation>(x => x.SocialPostId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
builder.Entity<SocialMedia>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialMedia)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.Type).IsRequired().HasMaxLength(64);
|
||||
b.Property(x => x.Urls).HasMaxLength(2048);
|
||||
b.Property(x => x.PollQuestion).HasMaxLength(512);
|
||||
b.Property(x => x.PollUserVoteId).HasMaxLength(128);
|
||||
|
||||
b.HasOne(x => x.SocialPost)
|
||||
.WithOne(p => p.Media)
|
||||
.HasForeignKey<SocialMedia>(x => x.SocialPostId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
builder.Entity<SocialPollOption>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialPollOption)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.Text).IsRequired().HasMaxLength(512);
|
||||
|
||||
b.HasOne(x => x.SocialMedia)
|
||||
.WithMany(x => x.PollOptions)
|
||||
.HasForeignKey(x => x.SocialMediaId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
builder.Entity<SocialComment>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialComment)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.Property(x => x.Content).IsRequired().HasMaxLength(8192);
|
||||
|
||||
b.HasOne(x => x.SocialPost)
|
||||
.WithMany(x => x.Comments)
|
||||
.HasForeignKey(x => x.SocialPostId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
builder.Entity<SocialLike>(b =>
|
||||
{
|
||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.SocialLike)), Prefix.DbSchema);
|
||||
b.ConfigureByConvention();
|
||||
|
||||
b.HasOne(x => x.SocialPost)
|
||||
.WithMany(x => x.Likes)
|
||||
.HasForeignKey(x => x.SocialPostId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
|||
namespace Sozsoft.Platform.Migrations
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
[Migration("20260505071050_Initial")]
|
||||
[Migration("20260505120031_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -559,6 +559,95 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.ToTable("Sas_H_AiBot", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Announcement", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Attachments")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("nvarchar(2048)");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<string>("Excerpt")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<DateTime?>("ExpiryDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<bool>("IsPinned")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<DateTime>("PublishDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<int>("ViewCount")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Adm_T_Announcement", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.BackgroundWorker", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -3757,6 +3846,691 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.ToTable("Sas_H_SkillType", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialComment", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasMaxLength(8192)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<Guid>("SocialPostId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialPostId");
|
||||
|
||||
b.ToTable("Adm_T_SocialComment", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialLike", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<Guid>("SocialPostId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialPostId");
|
||||
|
||||
b.ToTable("Adm_T_SocialLike", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialLocation", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Address")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<double?>("Lat")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<double?>("Lng")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PlaceId")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<Guid>("SocialPostId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialPostId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Adm_T_SocialLocation", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialMedia", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<DateTime?>("PollEndsAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("PollQuestion")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<int?>("PollTotalVotes")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PollUserVoteId")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<Guid>("SocialPostId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.PrimitiveCollection<string>("Urls")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("nvarchar(2048)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialPostId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Adm_T_SocialMedia", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialPollOption", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<Guid>("SocialMediaId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<int>("Votes")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialMediaId");
|
||||
|
||||
b.ToTable("Adm_T_SocialPollOption", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialPost", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<bool>("IsLiked")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsOwnPost")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<int>("LikeCount")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Adm_T_SocialPost", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Survey", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<DateTime>("Deadline")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("nvarchar(2048)");
|
||||
|
||||
b.Property<bool>("IsAnonymous")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<int>("Responses")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("nvarchar(10)");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Adm_T_Survey", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyAnswer", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<Guid>("QuestionId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("QuestionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<Guid>("ResponseId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("nvarchar(1024)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("QuestionId");
|
||||
|
||||
b.HasIndex("ResponseId");
|
||||
|
||||
b.ToTable("Adm_T_SurveyAnswer", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestion", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<bool>("IsRequired")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<int>("Order")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("QuestionText")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("nvarchar(1024)");
|
||||
|
||||
b.Property<Guid>("SurveyId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SurveyId");
|
||||
|
||||
b.ToTable("Adm_T_SurveyQuestion", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestionOption", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<int>("Order")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("QuestionId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("QuestionId");
|
||||
|
||||
b.ToTable("Adm_T_SurveyQuestionOption", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyResponse", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<DateTime>("SubmissionTime")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid>("SurveyId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SurveyId");
|
||||
|
||||
b.ToTable("Adm_T_SurveyResponse", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
|
|
@ -6429,6 +7203,113 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Navigation("SkillType");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialComment", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialPost", "SocialPost")
|
||||
.WithMany("Comments")
|
||||
.HasForeignKey("SocialPostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialLike", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialPost", "SocialPost")
|
||||
.WithMany("Likes")
|
||||
.HasForeignKey("SocialPostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialLocation", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialPost", "SocialPost")
|
||||
.WithOne("Location")
|
||||
.HasForeignKey("Sozsoft.Platform.Entities.SocialLocation", "SocialPostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialMedia", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialPost", "SocialPost")
|
||||
.WithOne("Media")
|
||||
.HasForeignKey("Sozsoft.Platform.Entities.SocialMedia", "SocialPostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialPollOption", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialMedia", "SocialMedia")
|
||||
.WithMany("PollOptions")
|
||||
.HasForeignKey("SocialMediaId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialMedia");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyAnswer", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SurveyQuestion", "Question")
|
||||
.WithMany()
|
||||
.HasForeignKey("QuestionId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Sozsoft.Platform.Entities.SurveyResponse", "Response")
|
||||
.WithMany("Answers")
|
||||
.HasForeignKey("ResponseId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Question");
|
||||
|
||||
b.Navigation("Response");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestion", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.Survey", "Survey")
|
||||
.WithMany("Questions")
|
||||
.HasForeignKey("SurveyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Survey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestionOption", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SurveyQuestion", "Question")
|
||||
.WithMany("Options")
|
||||
.HasForeignKey("QuestionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Question");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyResponse", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.Survey", "Survey")
|
||||
.WithMany("SurveyResponses")
|
||||
.HasForeignKey("SurveyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Survey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory")
|
||||
|
|
@ -6673,6 +7554,39 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Navigation("Skills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialMedia", b =>
|
||||
{
|
||||
b.Navigation("PollOptions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialPost", b =>
|
||||
{
|
||||
b.Navigation("Comments");
|
||||
|
||||
b.Navigation("Likes");
|
||||
|
||||
b.Navigation("Location");
|
||||
|
||||
b.Navigation("Media");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Survey", b =>
|
||||
{
|
||||
b.Navigation("Questions");
|
||||
|
||||
b.Navigation("SurveyResponses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestion", b =>
|
||||
{
|
||||
b.Navigation("Options");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyResponse", b =>
|
||||
{
|
||||
b.Navigation("Answers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b =>
|
||||
{
|
||||
b.Navigation("Uoms");
|
||||
|
|
@ -462,6 +462,36 @@ namespace Sozsoft.Platform.Migrations
|
|||
table.PrimaryKey("PK_AbpUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_Announcement",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||
Excerpt = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: false),
|
||||
ImageUrl = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||
Category = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
PublishDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
ExpiryDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
IsPinned = table.Column<bool>(type: "bit", nullable: false),
|
||||
ViewCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
||||
Attachments = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_Announcement", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_Department",
|
||||
columns: table => new
|
||||
|
|
@ -572,6 +602,55 @@ namespace Sozsoft.Platform.Migrations
|
|||
table.PrimaryKey("PK_Adm_T_Sector", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SocialPost",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: false),
|
||||
LikeCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
||||
IsLiked = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
IsOwnPost = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SocialPost", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_Survey",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||
Description = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
||||
Deadline = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
Responses = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
||||
Status = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false),
|
||||
IsAnonymous = table.Column<bool>(type: "bit", nullable: false),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_Survey", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_WorkHour",
|
||||
columns: table => new
|
||||
|
|
@ -1867,6 +1946,182 @@ namespace Sozsoft.Platform.Migrations
|
|||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SocialComment",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
SocialPostId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 8192, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SocialComment", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SocialComment_Adm_T_SocialPost_SocialPostId",
|
||||
column: x => x.SocialPostId,
|
||||
principalTable: "Adm_T_SocialPost",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SocialLike",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
SocialPostId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SocialLike", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SocialLike_Adm_T_SocialPost_SocialPostId",
|
||||
column: x => x.SocialPostId,
|
||||
principalTable: "Adm_T_SocialPost",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SocialLocation",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
SocialPostId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||
Address = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||
Lat = table.Column<double>(type: "float", nullable: true),
|
||||
Lng = table.Column<double>(type: "float", nullable: true),
|
||||
PlaceId = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SocialLocation", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SocialLocation_Adm_T_SocialPost_SocialPostId",
|
||||
column: x => x.SocialPostId,
|
||||
principalTable: "Adm_T_SocialPost",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SocialMedia",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
SocialPostId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Type = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Urls = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
||||
PollQuestion = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||
PollTotalVotes = table.Column<int>(type: "int", nullable: true),
|
||||
PollEndsAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
PollUserVoteId = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SocialMedia", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SocialMedia_Adm_T_SocialPost_SocialPostId",
|
||||
column: x => x.SocialPostId,
|
||||
principalTable: "Adm_T_SocialPost",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SurveyQuestion",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
SurveyId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
QuestionText = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: false),
|
||||
Type = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Order = table.Column<int>(type: "int", nullable: false),
|
||||
IsRequired = table.Column<bool>(type: "bit", nullable: false),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SurveyQuestion", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SurveyQuestion_Adm_T_Survey_SurveyId",
|
||||
column: x => x.SurveyId,
|
||||
principalTable: "Adm_T_Survey",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SurveyResponse",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
SurveyId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
SubmissionTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SurveyResponse", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SurveyResponse_Adm_T_Survey_SurveyId",
|
||||
column: x => x.SurveyId,
|
||||
principalTable: "Adm_T_Survey",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OpenIddictAuthorizations",
|
||||
columns: table => new
|
||||
|
|
@ -2365,6 +2620,97 @@ namespace Sozsoft.Platform.Migrations
|
|||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SocialPollOption",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
SocialMediaId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Text = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||
Votes = table.Column<int>(type: "int", nullable: false),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SocialPollOption", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SocialPollOption_Adm_T_SocialMedia_SocialMediaId",
|
||||
column: x => x.SocialMediaId,
|
||||
principalTable: "Adm_T_SocialMedia",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SurveyQuestionOption",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
QuestionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
Text = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||
Order = table.Column<int>(type: "int", nullable: false),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SurveyQuestionOption", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SurveyQuestionOption_Adm_T_SurveyQuestion_QuestionId",
|
||||
column: x => x.QuestionId,
|
||||
principalTable: "Adm_T_SurveyQuestion",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Adm_T_SurveyAnswer",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
ResponseId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
QuestionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
QuestionType = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
Value = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: false),
|
||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Adm_T_SurveyAnswer", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SurveyAnswer_Adm_T_SurveyQuestion_QuestionId",
|
||||
column: x => x.QuestionId,
|
||||
principalTable: "Adm_T_SurveyQuestion",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_Adm_T_SurveyAnswer_Adm_T_SurveyResponse_ResponseId",
|
||||
column: x => x.ResponseId,
|
||||
principalTable: "Adm_T_SurveyResponse",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OpenIddictTokens",
|
||||
columns: table => new
|
||||
|
|
@ -2781,6 +3127,58 @@ namespace Sozsoft.Platform.Migrations
|
|||
table: "Adm_T_ReportTemplate",
|
||||
column: "CategoryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SocialComment_SocialPostId",
|
||||
table: "Adm_T_SocialComment",
|
||||
column: "SocialPostId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SocialLike_SocialPostId",
|
||||
table: "Adm_T_SocialLike",
|
||||
column: "SocialPostId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SocialLocation_SocialPostId",
|
||||
table: "Adm_T_SocialLocation",
|
||||
column: "SocialPostId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SocialMedia_SocialPostId",
|
||||
table: "Adm_T_SocialMedia",
|
||||
column: "SocialPostId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SocialPollOption_SocialMediaId",
|
||||
table: "Adm_T_SocialPollOption",
|
||||
column: "SocialMediaId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SurveyAnswer_QuestionId",
|
||||
table: "Adm_T_SurveyAnswer",
|
||||
column: "QuestionId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SurveyAnswer_ResponseId",
|
||||
table: "Adm_T_SurveyAnswer",
|
||||
column: "ResponseId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SurveyQuestion_SurveyId",
|
||||
table: "Adm_T_SurveyQuestion",
|
||||
column: "SurveyId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SurveyQuestionOption_QuestionId",
|
||||
table: "Adm_T_SurveyQuestionOption",
|
||||
column: "QuestionId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Adm_T_SurveyResponse_SurveyId",
|
||||
table: "Adm_T_SurveyResponse",
|
||||
column: "SurveyId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OpenIddictApplications_ClientId",
|
||||
table: "OpenIddictApplications",
|
||||
|
|
@ -3065,6 +3463,9 @@ namespace Sozsoft.Platform.Migrations
|
|||
migrationBuilder.DropTable(
|
||||
name: "AbpUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_Announcement");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_IpRestriction");
|
||||
|
||||
|
|
@ -3080,6 +3481,24 @@ namespace Sozsoft.Platform.Migrations
|
|||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_Sector");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SocialComment");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SocialLike");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SocialLocation");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SocialPollOption");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SurveyAnswer");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SurveyQuestionOption");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_WorkHour");
|
||||
|
||||
|
|
@ -3221,6 +3640,15 @@ namespace Sozsoft.Platform.Migrations
|
|||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_ReportCategory");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SocialMedia");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SurveyResponse");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SurveyQuestion");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OpenIddictAuthorizations");
|
||||
|
||||
|
|
@ -3263,6 +3691,12 @@ namespace Sozsoft.Platform.Migrations
|
|||
migrationBuilder.DropTable(
|
||||
name: "AbpAuditLogs");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_SocialPost");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Adm_T_Survey");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OpenIddictApplications");
|
||||
|
||||
|
|
@ -556,6 +556,95 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.ToTable("Sas_H_AiBot", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Announcement", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Attachments")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("nvarchar(2048)");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<string>("Excerpt")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<DateTime?>("ExpiryDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<bool>("IsPinned")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<DateTime>("PublishDate")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<int>("ViewCount")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Adm_T_Announcement", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.BackgroundWorker", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
|
|
@ -3754,6 +3843,691 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.ToTable("Sas_H_SkillType", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialComment", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasMaxLength(8192)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<Guid>("SocialPostId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialPostId");
|
||||
|
||||
b.ToTable("Adm_T_SocialComment", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialLike", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<Guid>("SocialPostId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialPostId");
|
||||
|
||||
b.ToTable("Adm_T_SocialLike", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialLocation", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Address")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<double?>("Lat")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<double?>("Lng")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.Property<string>("PlaceId")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<Guid>("SocialPostId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialPostId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Adm_T_SocialLocation", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialMedia", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<DateTime?>("PollEndsAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("PollQuestion")
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<int?>("PollTotalVotes")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("PollUserVoteId")
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("nvarchar(128)");
|
||||
|
||||
b.Property<Guid>("SocialPostId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.PrimitiveCollection<string>("Urls")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("nvarchar(2048)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialPostId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Adm_T_SocialMedia", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialPollOption", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<Guid>("SocialMediaId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.Property<int>("Votes")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SocialMediaId");
|
||||
|
||||
b.ToTable("Adm_T_SocialPollOption", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialPost", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<bool>("IsLiked")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsOwnPost")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<int>("LikeCount")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Adm_T_SocialPost", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Survey", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<DateTime>("Deadline")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("nvarchar(2048)");
|
||||
|
||||
b.Property<bool>("IsAnonymous")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<int>("Responses")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("nvarchar(10)");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("nvarchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Adm_T_Survey", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyAnswer", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<Guid>("QuestionId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("QuestionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.Property<Guid>("ResponseId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("nvarchar(1024)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("QuestionId");
|
||||
|
||||
b.HasIndex("ResponseId");
|
||||
|
||||
b.ToTable("Adm_T_SurveyAnswer", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestion", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<bool>("IsRequired")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<int>("Order")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("QuestionText")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("nvarchar(1024)");
|
||||
|
||||
b.Property<Guid>("SurveyId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("nvarchar(64)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SurveyId");
|
||||
|
||||
b.ToTable("Adm_T_SurveyQuestion", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestionOption", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<int>("Order")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("QuestionId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("nvarchar(512)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("QuestionId");
|
||||
|
||||
b.ToTable("Adm_T_SurveyQuestionOption", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyResponse", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("CreationTime");
|
||||
|
||||
b.Property<Guid?>("CreatorId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("CreatorId");
|
||||
|
||||
b.Property<Guid?>("DeleterId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("DeleterId");
|
||||
|
||||
b.Property<DateTime?>("DeletionTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("DeletionTime");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bit")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("IsDeleted");
|
||||
|
||||
b.Property<DateTime?>("LastModificationTime")
|
||||
.HasColumnType("datetime2")
|
||||
.HasColumnName("LastModificationTime");
|
||||
|
||||
b.Property<Guid?>("LastModifierId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("LastModifierId");
|
||||
|
||||
b.Property<DateTime>("SubmissionTime")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<Guid>("SurveyId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("TenantId")
|
||||
.HasColumnType("uniqueidentifier")
|
||||
.HasColumnName("TenantId");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SurveyId");
|
||||
|
||||
b.ToTable("Adm_T_SurveyResponse", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
|
|
@ -6426,6 +7200,113 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Navigation("SkillType");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialComment", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialPost", "SocialPost")
|
||||
.WithMany("Comments")
|
||||
.HasForeignKey("SocialPostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialLike", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialPost", "SocialPost")
|
||||
.WithMany("Likes")
|
||||
.HasForeignKey("SocialPostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialLocation", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialPost", "SocialPost")
|
||||
.WithOne("Location")
|
||||
.HasForeignKey("Sozsoft.Platform.Entities.SocialLocation", "SocialPostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialMedia", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialPost", "SocialPost")
|
||||
.WithOne("Media")
|
||||
.HasForeignKey("Sozsoft.Platform.Entities.SocialMedia", "SocialPostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialPollOption", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SocialMedia", "SocialMedia")
|
||||
.WithMany("PollOptions")
|
||||
.HasForeignKey("SocialMediaId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("SocialMedia");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyAnswer", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SurveyQuestion", "Question")
|
||||
.WithMany()
|
||||
.HasForeignKey("QuestionId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Sozsoft.Platform.Entities.SurveyResponse", "Response")
|
||||
.WithMany("Answers")
|
||||
.HasForeignKey("ResponseId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Question");
|
||||
|
||||
b.Navigation("Response");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestion", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.Survey", "Survey")
|
||||
.WithMany("Questions")
|
||||
.HasForeignKey("SurveyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Survey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestionOption", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.SurveyQuestion", "Question")
|
||||
.WithMany("Options")
|
||||
.HasForeignKey("QuestionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Question");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyResponse", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.Survey", "Survey")
|
||||
.WithMany("SurveyResponses")
|
||||
.HasForeignKey("SurveyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Survey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Uom", b =>
|
||||
{
|
||||
b.HasOne("Sozsoft.Platform.Entities.UomCategory", "UomCategory")
|
||||
|
|
@ -6670,6 +7551,39 @@ namespace Sozsoft.Platform.Migrations
|
|||
b.Navigation("Skills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialMedia", b =>
|
||||
{
|
||||
b.Navigation("PollOptions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SocialPost", b =>
|
||||
{
|
||||
b.Navigation("Comments");
|
||||
|
||||
b.Navigation("Likes");
|
||||
|
||||
b.Navigation("Location");
|
||||
|
||||
b.Navigation("Media");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.Survey", b =>
|
||||
{
|
||||
b.Navigation("Questions");
|
||||
|
||||
b.Navigation("SurveyResponses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyQuestion", b =>
|
||||
{
|
||||
b.Navigation("Options");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.SurveyResponse", b =>
|
||||
{
|
||||
b.Navigation("Answers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Sozsoft.Platform.Entities.UomCategory", b =>
|
||||
{
|
||||
b.Navigation("Uoms");
|
||||
|
|
|
|||
|
|
@ -1289,5 +1289,395 @@
|
|||
"Name": "Muhasebe Şefi",
|
||||
"ParentName": "Muhasebe Müdürü"
|
||||
}
|
||||
],
|
||||
"Announcements": [
|
||||
{
|
||||
"title": "🎉 Yeni Ofis Açılışı",
|
||||
"content": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor! Tüm çalışanlarımızı açılış törenimize davet ediyoruz.",
|
||||
"excerpt": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor!",
|
||||
"category": "general",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "12-10-2024",
|
||||
"isPinned": true,
|
||||
"viewCount": 156,
|
||||
"imageUrl": "https://images.unsplash.com/photo-1497366216548-37526070297c?w=800&q=80"
|
||||
},
|
||||
{
|
||||
"title": "📅 Performans Değerlendirme Dönemi",
|
||||
"content": "Yıl sonu performans değerlendirmelerimiz 20 Ekim - 5 Kasım tarihleri arasında gerçekleştirilecektir. Lütfen formları zamanında doldurunuz.",
|
||||
"excerpt": "Yıl sonu performans değerlendirmeleri başlıyor.",
|
||||
"category": "hr",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "08-10-2024",
|
||||
"expiryDate": "05-11-2024",
|
||||
"isPinned": true,
|
||||
"viewCount": 89
|
||||
},
|
||||
{
|
||||
"title": "💻 Sistem Bakımı Duyurusu",
|
||||
"content": "Bu Cumartesi saat 02: 00 - 06: 00 arası sistemlerimizde bakım çalışması yapılacaktır. Bu süre içinde sistemlere erişim sağlanamayacaktır.",
|
||||
"excerpt": "Cumartesi gecesi planlı bakım çalışması",
|
||||
"category": "it",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "08-10-2024",
|
||||
"isPinned": false,
|
||||
"viewCount": 234
|
||||
},
|
||||
{
|
||||
"title": "🎓 React İleri Seviye Eğitimi",
|
||||
"content": "Yazılım Geliştirme ekibimiz için React İleri Seviye eğitimi 25-26 Ekim tarihlerinde düzenlenecektir. Katılım için IK birimine başvurunuz.",
|
||||
"excerpt": "React İleri Seviye eğitimi kayıtları başladı",
|
||||
"category": "event",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "09-10-2024",
|
||||
"isPinned": false,
|
||||
"viewCount": 67
|
||||
},
|
||||
{
|
||||
"title": "⚠️ Güvenlik Politikası Güncellemesi",
|
||||
"content": "Bilgi güvenliği politikamız güncellenmiştir. Tüm çalışanlarımızın yeni politikayı okuması ve onaylaması gerekmektedir.",
|
||||
"excerpt": "Güvenlik politikası güncellendi - Onay gerekli",
|
||||
"category": "urgent",
|
||||
"userName": "system@sozsoft.com",
|
||||
"publishDate": "04-10-2024",
|
||||
"isPinned": true,
|
||||
"viewCount": 312
|
||||
}
|
||||
],
|
||||
"Surveys": [
|
||||
{
|
||||
"Title": "Çalışan Memnuniyet Anketi 2024",
|
||||
"Description": "Yıllık çalışan memnuniyeti ve bağlılık araştırması",
|
||||
"Deadline": "2024-10-31T00:00:00",
|
||||
"Responses": 45,
|
||||
"Status": "active",
|
||||
"IsAnonymous": true
|
||||
},
|
||||
{
|
||||
"Title": "Eğitim İhtiyaç Analizi",
|
||||
"Description": "2025 yılı eğitim planlaması için ihtiyaç tespiti",
|
||||
"Deadline": "2024-11-15T00:00:00",
|
||||
"Responses": 28,
|
||||
"Status": "active",
|
||||
"IsAnonymous": false
|
||||
},
|
||||
{
|
||||
"Title": "Kafeterya Memnuniyet Anketi",
|
||||
"Description": "Yemek kalitesi ve servis değerlendirmesi",
|
||||
"Deadline": "2024-09-30T00:00:00",
|
||||
"Responses": 62,
|
||||
"Status": "passive",
|
||||
"IsAnonymous": true
|
||||
}
|
||||
],
|
||||
"SurveyQuestions": [
|
||||
{
|
||||
"SurveyTitle": "Çalışan Memnuniyet Anketi 2024",
|
||||
"QuestionText": "Genel memnuniyet düzeyiniz nedir?",
|
||||
"Type": "rating",
|
||||
"Order": 1,
|
||||
"IsRequired": true
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Çalışan Memnuniyet Anketi 2024",
|
||||
"QuestionText": "Hangi departmanda çalışıyorsunuz?",
|
||||
"Type": "multiple-choice",
|
||||
"Order": 2,
|
||||
"IsRequired": true
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Çalışan Memnuniyet Anketi 2024",
|
||||
"QuestionText": "Görüş ve önerileriniz",
|
||||
"Type": "textarea",
|
||||
"Order": 3,
|
||||
"IsRequired": false
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Çalışan Memnuniyet Anketi 2024",
|
||||
"QuestionText": "Çalışma ortamından memnun musunuz?",
|
||||
"Type": "yes-no",
|
||||
"Order": 4,
|
||||
"IsRequired": true
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Eğitim İhtiyaç Analizi",
|
||||
"QuestionText": "Hangi teknoloji konularında eğitim almak istiyorsunuz?",
|
||||
"Type": "multiple-choice",
|
||||
"Order": 1,
|
||||
"IsRequired": true
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Eğitim İhtiyaç Analizi",
|
||||
"QuestionText": "Eğitim formatı tercihiniz nedir?",
|
||||
"Type": "multiple-choice",
|
||||
"Order": 2,
|
||||
"IsRequired": true
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Eğitim İhtiyaç Analizi",
|
||||
"QuestionText": "Eğitim için haftalık ne kadar zaman ayırabilirsiniz?",
|
||||
"Type": "rating",
|
||||
"Order": 3,
|
||||
"IsRequired": true
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Kafeterya Memnuniyet Anketi",
|
||||
"QuestionText": "Yemek kalitesini nasıl değerlendiriyorsunuz?",
|
||||
"Type": "rating",
|
||||
"Order": 1,
|
||||
"IsRequired": true
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Kafeterya Memnuniyet Anketi",
|
||||
"QuestionText": "Hangi yemekleri daha sık görmek istiyorsunuz?",
|
||||
"Type": "textarea",
|
||||
"Order": 2,
|
||||
"IsRequired": false
|
||||
},
|
||||
{
|
||||
"SurveyTitle": "Kafeterya Memnuniyet Anketi",
|
||||
"QuestionText": "Servis hızından memnun musunuz?",
|
||||
"Type": "yes-no",
|
||||
"Order": 3,
|
||||
"IsRequired": true
|
||||
}
|
||||
],
|
||||
"SurveyQuestionOptions": [
|
||||
{
|
||||
"QuestionText": "Hangi departmanda çalışıyorsunuz?",
|
||||
"Text": "Bilgi Teknolojileri",
|
||||
"Order": 1
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi departmanda çalışıyorsunuz?",
|
||||
"Text": "İnsan Kaynakları",
|
||||
"Order": 2
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi departmanda çalışıyorsunuz?",
|
||||
"Text": "Finans",
|
||||
"Order": 3
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi departmanda çalışıyorsunuz?",
|
||||
"Text": "Satış",
|
||||
"Order": 4
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi departmanda çalışıyorsunuz?",
|
||||
"Text": "Pazarlama",
|
||||
"Order": 5
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi teknoloji konularında eğitim almak istiyorsunuz?",
|
||||
"Text": "React / Frontend",
|
||||
"Order": 1
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi teknoloji konularında eğitim almak istiyorsunuz?",
|
||||
"Text": "Node.js / Backend",
|
||||
"Order": 2
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi teknoloji konularında eğitim almak istiyorsunuz?",
|
||||
"Text": "Database / SQL",
|
||||
"Order": 3
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi teknoloji konularında eğitim almak istiyorsunuz?",
|
||||
"Text": "DevOps / Cloud",
|
||||
"Order": 4
|
||||
},
|
||||
{
|
||||
"QuestionText": "Hangi teknoloji konularında eğitim almak istiyorsunuz?",
|
||||
"Text": "Mobile Development",
|
||||
"Order": 5
|
||||
},
|
||||
{
|
||||
"QuestionText": "Eğitim formatı tercihiniz nedir?",
|
||||
"Text": "Online Eğitim",
|
||||
"Order": 1
|
||||
},
|
||||
{
|
||||
"QuestionText": "Eğitim formatı tercihiniz nedir?",
|
||||
"Text": "Yüz Yüze Eğitim",
|
||||
"Order": 2
|
||||
},
|
||||
{
|
||||
"QuestionText": "Eğitim formatı tercihiniz nedir?",
|
||||
"Text": "Hibrit (Karma)",
|
||||
"Order": 3
|
||||
}
|
||||
],
|
||||
"SocialPosts": [
|
||||
{
|
||||
"content": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 24,
|
||||
"isLiked": true,
|
||||
"isOwnPost": false
|
||||
},
|
||||
{
|
||||
"content": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 18,
|
||||
"isLiked": false,
|
||||
"isOwnPost": true
|
||||
},
|
||||
{
|
||||
"content": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 42,
|
||||
"isLiked": true,
|
||||
"isOwnPost": false
|
||||
},
|
||||
{
|
||||
"content": "CI/CD pipeline güncellememiz tamamlandı! Deployment süremiz %40 azaldı. Otomasyonun gücü 💪",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 31,
|
||||
"isLiked": false,
|
||||
"isOwnPost": false
|
||||
},
|
||||
{
|
||||
"content": "Ekip üyelerimize yeni eğitim programımızı duyurmak istiyorum! 🎓 React, TypeScript ve Modern Web Geliştirme konularında kapsamlı bir program hazırladık.",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 56,
|
||||
"isLiked": true,
|
||||
"isOwnPost": false
|
||||
},
|
||||
{
|
||||
"content": "Bugün müşteri ile harika bir toplantı yaptık! Yeni projenin detaylarını konuştuk. 🎯",
|
||||
"userName": "system@sozsoft.com",
|
||||
"likeCount": 18,
|
||||
"isLiked": false,
|
||||
"isOwnPost": false
|
||||
}
|
||||
],
|
||||
"SocialLocations": [
|
||||
{
|
||||
"postContent": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"name": "Taksim Meydanı",
|
||||
"address": "Taksim, Gümüşsuyu Mahallesi, 34437 Beyoğlu/İstanbul",
|
||||
"lat": 41.0369,
|
||||
"lng": 28.985,
|
||||
"placeId": "ChIJBQRGmL25yhQRXwqRTHAwAAQ"
|
||||
},
|
||||
{
|
||||
"postContent": "Bugün müşteri ile harika bir toplantı yaptık! Yeni projenin detaylarını konuştuk. 🎯",
|
||||
"name": "Sultanahmet Meydanı",
|
||||
"address": "Sultanahmet Mahallesi, 34122 Fatih/İstanbul",
|
||||
"lat": 41.0058,
|
||||
"lng": 28.9768,
|
||||
"placeId": "ChIJ7fVVZiy5yhQRzsXXXXXXXXk"
|
||||
}
|
||||
],
|
||||
"SocialMedias": [
|
||||
{
|
||||
"postContent": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"type": "image",
|
||||
"urls": [
|
||||
"https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=800&q=80"
|
||||
]
|
||||
},
|
||||
{
|
||||
"postContent": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"type": "poll",
|
||||
"pollQuestion": "Hangi özelliği öncelikli olarak geliştirmeliyiz?",
|
||||
"pollTotalVotes": 40,
|
||||
"pollEndsAt": "2024-10-20T23:59:59",
|
||||
"pollUserVoteId": "p3"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"type": "image",
|
||||
"urls": [
|
||||
"https://images.unsplash.com/photo-1561070791-2526d30994b5?w=800&q=80",
|
||||
"https://images.unsplash.com/photo-1586717799252-bd134ad00e26?w=800&q=80",
|
||||
"https://images.unsplash.com/photo-1609921212029-bb5a28e60960?w=800&q=80"
|
||||
]
|
||||
},
|
||||
{
|
||||
"postContent": "CI/CD pipeline güncellememiz tamamlandı! Deployment süremiz %40 azaldı. Otomasyonun gücü 💪",
|
||||
"type": "video",
|
||||
"urls": ["https://www.w3schools.com/html/mov_bbb.mp4"]
|
||||
}
|
||||
],
|
||||
"SocialPollOptions": [
|
||||
{
|
||||
"postContent": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"pollQuestion": "Hangi özelliği öncelikli olarak geliştirmeliyiz?",
|
||||
"Text": "Kullanıcı profilleri",
|
||||
"Votes": 12
|
||||
},
|
||||
{
|
||||
"postContent": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"pollQuestion": "Hangi özelliği öncelikli olarak geliştirmeliyiz?",
|
||||
"Text": "Bildirim sistemi",
|
||||
"Votes": 8
|
||||
},
|
||||
{
|
||||
"postContent": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"pollQuestion": "Hangi özelliği öncelikli olarak geliştirmeliyiz?",
|
||||
"Text": "Mesajlaşma",
|
||||
"Votes": 15
|
||||
},
|
||||
{
|
||||
"postContent": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"pollQuestion": "Hangi özelliği öncelikli olarak geliştirmeliyiz?",
|
||||
"Text": "Raporlama",
|
||||
"Votes": 5
|
||||
}
|
||||
],
|
||||
"SocialComments": [
|
||||
{
|
||||
"postContent": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Harika görünüyor! Başarılar 👏"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "TypeScript gerçekten fark yaratıyor!"
|
||||
},
|
||||
{
|
||||
"postContent": "Bu hafta sprint planlamasını yaptık. Ekibimizle birlikte yeni özellikleri değerlendirdik. Heyecan verici bir hafta olacak!",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Mesajlaşma özelliğine kesinlikle ihtiyacımız var!"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Tasarımlar çok şık! Renk paleti özellikle güzel 😍"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Dark mode opsiyonu da olacak mı?"
|
||||
},
|
||||
{
|
||||
"postContent": "CI/CD pipeline güncellememiz tamamlandı! Deployment süremiz %40 azaldı. Otomasyonun gücü 💪",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Harika iş! Detayları paylaşabilir misin?"
|
||||
},
|
||||
{
|
||||
"postContent": "Ekip üyelerimize yeni eğitim programımızı duyurmak istiyorum! 🎓 React, TypeScript ve Modern Web Geliştirme konularında kapsamlı bir program hazırladık.",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Ne zaman başlıyor?"
|
||||
},
|
||||
{
|
||||
"postContent": "Ekip üyelerimize yeni eğitim programımızı duyurmak istiyorum! 🎓 React, TypeScript ve Modern Web Geliştirme konularında kapsamlı bir program hazırladık.",
|
||||
"userName": "system@sozsoft.com",
|
||||
"content": "Gelecek hafta başlıyoruz! Kayıt linki mail ile paylaşılacak."
|
||||
}
|
||||
],
|
||||
"SocialLikes": [
|
||||
{
|
||||
"postContent": "Yeni proje üzerinde çalışıyoruz! React ve TypeScript ile harika bir deneyim oluşturuyoruz. Ekip çalışması harika gidiyor! 🚀",
|
||||
"userName": "system@sozsoft.com"
|
||||
},
|
||||
{
|
||||
"postContent": "Yeni tasarım sistemimizin ilk prototipini hazırladık! Kullanıcı deneyimini iyileştirmek için çok çalıştık. Geri bildirimlerinizi bekliyorum! 🎨",
|
||||
"userName": "system@sozsoft.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ using Volo.Abp.Timing;
|
|||
using System.Collections.Generic;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Sozsoft.Platform.Extensions;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sozsoft.Platform.Data.Seeds;
|
||||
|
||||
|
|
@ -52,6 +53,113 @@ public class TenantSeederDto
|
|||
|
||||
//Forum
|
||||
public List<ForumCategorySeedDto> ForumCategories { get; set; }
|
||||
|
||||
//Intranet
|
||||
public List<AnnouncementSeedDto> Announcements { get; set; }
|
||||
public List<SurveySeedDto> Surveys { get; set; }
|
||||
public List<SurveyQuestionSeedDto> SurveyQuestions { get; set; }
|
||||
public List<SurveyQuestionOptionSeedDto> SurveyQuestionOptions { get; set; }
|
||||
public List<SocialPostSeedDto> SocialPosts { get; set; }
|
||||
public List<SocialLocationSeedDto> SocialLocations { get; set; }
|
||||
public List<SocialMediaSeedDto> SocialMedias { get; set; }
|
||||
public List<SocialPollOptionSeedDto> SocialPollOptions { get; set; }
|
||||
public List<SocialCommentSeedDto> SocialComments { get; set; }
|
||||
public List<SocialLikeSeedDto> SocialLikes { get; set; }
|
||||
}
|
||||
|
||||
public class SocialPostSeedDto
|
||||
{
|
||||
public string Content { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public int LikeCount { get; set; }
|
||||
public bool IsLiked { get; set; }
|
||||
public bool IsOwnPost { get; set; }
|
||||
}
|
||||
|
||||
public class SocialLocationSeedDto
|
||||
{
|
||||
public string PostContent { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Address { get; set; }
|
||||
public double? Lat { get; set; }
|
||||
public double? Lng { get; set; }
|
||||
public string PlaceId { get; set; }
|
||||
}
|
||||
|
||||
public class SocialMediaSeedDto
|
||||
{
|
||||
public string PostContent { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string[] Urls { get; set; }
|
||||
|
||||
public string PollQuestion { get; set; }
|
||||
public int? PollTotalVotes { get; set; }
|
||||
public DateTime? PollEndsAt { get; set; }
|
||||
public string PollUserVoteId { get; set; }
|
||||
|
||||
public List<SocialPollOptionSeedDto> PollOptions { get; set; }
|
||||
}
|
||||
|
||||
public class SocialPollOptionSeedDto
|
||||
{
|
||||
public string PostContent { get; set; }
|
||||
public string PollQuestion { get; set; }
|
||||
public string Text { get; set; }
|
||||
public int Votes { get; set; }
|
||||
}
|
||||
|
||||
public class SocialCommentSeedDto
|
||||
{
|
||||
public string PostContent { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string Content { get; set; }
|
||||
}
|
||||
|
||||
public class SocialLikeSeedDto
|
||||
{
|
||||
public string PostContent { get; set; }
|
||||
public string UserName { get; set; }
|
||||
}
|
||||
|
||||
public class SurveySeedDto
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public DateTime Deadline { get; set; }
|
||||
public int Responses { get; set; }
|
||||
public string Status { get; set; }
|
||||
public bool IsAnonymous { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyQuestionSeedDto
|
||||
{
|
||||
public string QuestionText { get; set; }
|
||||
public string SurveyTitle { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int Order { get; set; }
|
||||
public bool IsRequired { get; set; }
|
||||
}
|
||||
|
||||
public class SurveyQuestionOptionSeedDto
|
||||
{
|
||||
public string QuestionText { get; set; }
|
||||
public string Text { get; set; }
|
||||
public int Order { get; set; }
|
||||
}
|
||||
|
||||
public class AnnouncementSeedDto
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Excerpt { get; set; }
|
||||
public string Content { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public DateTime PublishDate { get; set; }
|
||||
public DateTime? ExpiryDate { get; set; }
|
||||
public bool IsPinned { get; set; }
|
||||
public int ViewCount { get; set; }
|
||||
public string DepartmentNames { get; set; }
|
||||
}
|
||||
|
||||
public class JobPositionSeedDto
|
||||
|
|
@ -291,6 +399,16 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
private readonly IRepository<Product, Guid> _productRepository;
|
||||
private readonly IRepository<Department, Guid> _departmentRepository;
|
||||
private readonly IRepository<JobPosition, Guid> _jobPositionRepository;
|
||||
private readonly IRepository<Announcement, Guid> _announcementRepository;
|
||||
private readonly IRepository<Survey, Guid> _surveyRepository;
|
||||
private readonly IRepository<SurveyQuestion, Guid> _surveyQuestionRepository;
|
||||
private readonly IRepository<SurveyQuestionOption, Guid> _surveyQuestionOptionRepository;
|
||||
private readonly IRepository<SocialPost, Guid> _socialPostRepository;
|
||||
private readonly IRepository<SocialLocation, Guid> _socialLocationRepository;
|
||||
private readonly IRepository<SocialMedia, Guid> _socialMediaRepository;
|
||||
private readonly IRepository<SocialPollOption, Guid> _socialPollOptionRepository;
|
||||
private readonly IRepository<SocialComment, Guid> _socialCommentRepository;
|
||||
private readonly IRepository<SocialLike, Guid> _socialLikeRepository;
|
||||
private readonly ICurrentTenant _currentTenant;
|
||||
|
||||
public TenantDataSeeder(
|
||||
|
|
@ -322,6 +440,18 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
IRepository<OrganizationUnit, Guid> organizationUnitRepository,
|
||||
IRepository<Department, Guid> departmentRepository,
|
||||
IRepository<JobPosition, Guid> jobPositionRepository,
|
||||
|
||||
IRepository<Announcement, Guid> announcementRepository,
|
||||
IRepository<Survey, Guid> surveyRepository,
|
||||
IRepository<SurveyQuestion, Guid> surveyQuestionRepository,
|
||||
IRepository<SurveyQuestionOption, Guid> surveyQuestionOptionRepository,
|
||||
IRepository<SocialPost, Guid> socialPostRepository,
|
||||
IRepository<SocialLocation, Guid> socialLocationRepository,
|
||||
IRepository<SocialMedia, Guid> socialMediaRepository,
|
||||
IRepository<SocialPollOption, Guid> socialPollOptionRepository,
|
||||
IRepository<SocialComment, Guid> socialCommentRepository,
|
||||
IRepository<SocialLike, Guid> socialLikeRepository,
|
||||
|
||||
OrganizationUnitManager organizationUnitManager,
|
||||
ICurrentTenant currentTenant
|
||||
)
|
||||
|
|
@ -354,6 +484,16 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
_organizationUnitRepository = organizationUnitRepository;
|
||||
_departmentRepository = departmentRepository;
|
||||
_jobPositionRepository = jobPositionRepository;
|
||||
_announcementRepository = announcementRepository;
|
||||
_surveyRepository = surveyRepository;
|
||||
_surveyQuestionRepository = surveyQuestionRepository;
|
||||
_surveyQuestionOptionRepository = surveyQuestionOptionRepository;
|
||||
_socialPostRepository = socialPostRepository;
|
||||
_socialLocationRepository = socialLocationRepository;
|
||||
_socialMediaRepository = socialMediaRepository;
|
||||
_socialPollOptionRepository = socialPollOptionRepository;
|
||||
_socialCommentRepository = socialCommentRepository;
|
||||
_socialLikeRepository = socialLikeRepository;
|
||||
_organizationUnitManager = organizationUnitManager;
|
||||
_currentTenant = currentTenant;
|
||||
}
|
||||
|
|
@ -770,6 +910,204 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
|||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.Announcements)
|
||||
{
|
||||
var exists = await _announcementRepository.AnyAsync(x => x.Title == item.Title);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
await _announcementRepository.InsertAsync(new Announcement
|
||||
{
|
||||
Title = item.Title,
|
||||
Excerpt = item.Excerpt,
|
||||
Content = item.Content,
|
||||
ImageUrl = item.ImageUrl,
|
||||
Category = item.Category,
|
||||
UserId = user != null ? user.Id : null,
|
||||
PublishDate = item.PublishDate,
|
||||
ExpiryDate = item.ExpiryDate,
|
||||
IsPinned = item.IsPinned,
|
||||
ViewCount = item.ViewCount
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var item in items.Surveys)
|
||||
{
|
||||
var exists = await _surveyRepository.AnyAsync(x => x.Title == item.Title);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
await _surveyRepository.InsertAsync(new Survey
|
||||
{
|
||||
Title = item.Title,
|
||||
Description = item.Description,
|
||||
Deadline = item.Deadline,
|
||||
Responses = item.Responses,
|
||||
Status = item.Status,
|
||||
IsAnonymous = item.IsAnonymous
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.SurveyQuestions)
|
||||
{
|
||||
var exists = await _surveyQuestionRepository.AnyAsync(x => x.QuestionText == item.QuestionText);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
var survey = await _surveyRepository.FirstOrDefaultAsync(x => x.Title == item.SurveyTitle);
|
||||
await _surveyQuestionRepository.InsertAsync(new SurveyQuestion
|
||||
{
|
||||
SurveyId = survey != null ? survey.Id : Guid.Empty,
|
||||
QuestionText = item.QuestionText,
|
||||
Type = item.Type,
|
||||
Order = item.Order,
|
||||
IsRequired = item.IsRequired
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.SurveyQuestionOptions)
|
||||
{
|
||||
var surveyQuestion = await _surveyQuestionRepository.FirstOrDefaultAsync(x => x.QuestionText == item.QuestionText);
|
||||
if (surveyQuestion == null)
|
||||
continue;
|
||||
|
||||
var exists = await _surveyQuestionOptionRepository.AnyAsync(x => x.QuestionId == surveyQuestion.Id && x.Text == item.Text);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
await _surveyQuestionOptionRepository.InsertAsync(new SurveyQuestionOption
|
||||
{
|
||||
QuestionId = surveyQuestion.Id,
|
||||
Text = item.Text,
|
||||
Order = item.Order
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.SocialPosts)
|
||||
{
|
||||
var exists = await _socialPostRepository.AnyAsync(x => x.Content == item.Content);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
|
||||
await _socialPostRepository.InsertAsync(new SocialPost
|
||||
{
|
||||
UserId = user != null ? user.Id : null,
|
||||
Content = item.Content,
|
||||
LikeCount = item.LikeCount,
|
||||
IsLiked = item.IsLiked,
|
||||
IsOwnPost = item.IsOwnPost
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.SocialLocations)
|
||||
{
|
||||
var post = await _socialPostRepository.FirstOrDefaultAsync(x => x.Content == item.PostContent);
|
||||
|
||||
if (post == null)
|
||||
continue;
|
||||
|
||||
var exists = await _socialLocationRepository.AnyAsync(x => x.SocialPostId == post.Id && x.Name == item.Name);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
await _socialLocationRepository.InsertAsync(new SocialLocation
|
||||
{
|
||||
SocialPostId = post != null ? post.Id : Guid.Empty,
|
||||
Name = item.Name,
|
||||
Address = item.Address,
|
||||
Lat = item.Lat,
|
||||
Lng = item.Lng,
|
||||
PlaceId = item.PlaceId
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.SocialMedias)
|
||||
{
|
||||
var post = await _socialPostRepository.FirstOrDefaultAsync(x => x.Content == item.PostContent);
|
||||
|
||||
if (post == null)
|
||||
continue;
|
||||
|
||||
var exists = await _socialMediaRepository.AnyAsync(x => x.SocialPostId == post.Id);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
await _socialMediaRepository.InsertAsync(new SocialMedia
|
||||
{
|
||||
SocialPostId = post != null ? post.Id : Guid.Empty,
|
||||
Type = item.Type,
|
||||
Urls = item.Urls,
|
||||
PollQuestion = item.PollQuestion,
|
||||
PollTotalVotes = item.PollTotalVotes,
|
||||
PollEndsAt = item.PollEndsAt,
|
||||
PollUserVoteId = item.PollUserVoteId
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.SocialPollOptions)
|
||||
{
|
||||
var post = await _socialPostRepository.FirstOrDefaultAsync(x => x.Content == item.PostContent);
|
||||
if (post == null)
|
||||
continue;
|
||||
|
||||
var media = await _socialMediaRepository.FirstOrDefaultAsync(x => x.SocialPostId == post.Id && x.PollQuestion == item.PollQuestion);
|
||||
if (media == null)
|
||||
continue;
|
||||
|
||||
var exists = await _socialPollOptionRepository.AnyAsync(x => x.SocialMediaId == media.Id && x.Text == item.Text);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
await _socialPollOptionRepository.InsertAsync(new SocialPollOption
|
||||
{
|
||||
SocialMediaId = media != null ? media.Id : Guid.Empty,
|
||||
Text = item.Text,
|
||||
Votes = item.Votes
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.SocialComments)
|
||||
{
|
||||
var post = await _socialPostRepository.FirstOrDefaultAsync(x => x.Content == item.PostContent);
|
||||
|
||||
if (post == null)
|
||||
continue;
|
||||
|
||||
var exists = await _socialCommentRepository.AnyAsync(x => x.SocialPostId == post.Id && x.Content == item.Content);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
await _socialCommentRepository.InsertAsync(new SocialComment
|
||||
{
|
||||
UserId = user != null ? user.Id : null,
|
||||
SocialPostId = post != null ? post.Id : Guid.Empty,
|
||||
Content = item.Content
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
foreach (var item in items.SocialLikes)
|
||||
{
|
||||
var post = await _socialPostRepository.FirstOrDefaultAsync(x => x.Content == item.PostContent);
|
||||
|
||||
if (post == null)
|
||||
continue;
|
||||
|
||||
var exists = await _socialLikeRepository.AnyAsync(x => x.SocialPostId == post.Id);
|
||||
if (exists)
|
||||
continue;
|
||||
|
||||
var user = await _repositoryUser.FindByNormalizedUserNameAsync(item.UserName);
|
||||
await _socialLikeRepository.InsertAsync(new SocialLike
|
||||
{
|
||||
SocialPostId = post != null ? post.Id : Guid.Empty,
|
||||
UserId = user != null ? user.Id : null
|
||||
}, autoSave: true);
|
||||
}
|
||||
|
||||
//admin kullanının departmen ve pozisyonunu default olarak belirliyoruz
|
||||
var adminUser = await _repositoryUser.FindByNormalizedEmailAsync(PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue);
|
||||
if (adminUser != null)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const ComponentSelector: React.FC<ComponentSelectorProps> = ({
|
|||
</label>
|
||||
<Button
|
||||
variant='solid'
|
||||
size="xs"
|
||||
size="sm"
|
||||
onClick={onRefresh}
|
||||
className="px-3 py-1 bg-blue-500 text-white text-xs rounded hover:bg-blue-600 transition-colors"
|
||||
title="Refresh component list"
|
||||
|
|
|
|||
|
|
@ -528,7 +528,7 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
|||
{/* Sil Butonu */}
|
||||
<Button
|
||||
variant="solid"
|
||||
size="xs"
|
||||
size="sm"
|
||||
className="mr-2 px-3 py-1 rounded bg-red-500 text-white hover:bg-red-600 transition-colors text-sm"
|
||||
onClick={() => {
|
||||
if (selectedComponent) {
|
||||
|
|
@ -551,7 +551,7 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
|||
<div className="p-4 border-t">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="solid"
|
||||
onClick={
|
||||
activeTab === "props"
|
||||
|
|
@ -568,7 +568,7 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
|||
Uygula
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
onClick={
|
||||
activeTab === "props"
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const CopyButton = () => {
|
|||
<Button
|
||||
shape="circle"
|
||||
variant="plain"
|
||||
size="xs"
|
||||
size="sm"
|
||||
onClick={handleCopy}
|
||||
title={translate('::SidePanel.SaveConfig')}
|
||||
icon={<FaSave />}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
|||
`h-${CONTROL_SIZES.lg}`,
|
||||
icon && !children
|
||||
? `w-${CONTROL_SIZES.lg} ${sizeIconClass} text-2xl`
|
||||
: 'px-8 py-2 text-base'
|
||||
: 'px-8 text-base'
|
||||
)
|
||||
break
|
||||
case SIZES.SM:
|
||||
|
|
@ -78,7 +78,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
|||
`h-${CONTROL_SIZES.sm}`,
|
||||
icon && !children
|
||||
? `w-${CONTROL_SIZES.sm} ${sizeIconClass} text-lg`
|
||||
: 'px-3 py-2 text-sm'
|
||||
: 'px-3 text-sm'
|
||||
)
|
||||
break
|
||||
case SIZES.XS:
|
||||
|
|
@ -86,7 +86,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
|||
`h-${CONTROL_SIZES.xs}`,
|
||||
icon && !children
|
||||
? `w-${CONTROL_SIZES.xs} ${sizeIconClass} text-base`
|
||||
: 'px-2 py-1 text-xs'
|
||||
: 'px-2 text-xs'
|
||||
)
|
||||
break
|
||||
default:
|
||||
|
|
@ -94,7 +94,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
|||
`h-${CONTROL_SIZES.md}`,
|
||||
icon && !children
|
||||
? `w-${CONTROL_SIZES.md} ${sizeIconClass} text-xl`
|
||||
: 'px-8 py-2'
|
||||
: 'px-8'
|
||||
)
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
import { BankAccount, BankAccountTypeEnum } from "@/proxy/intranet/models";
|
||||
|
||||
export const mockBanks: BankAccount[] = [
|
||||
{
|
||||
id: "1",
|
||||
accountCode: "BANKA001",
|
||||
bankName: "İş Bankası",
|
||||
branchName: "Levent Şubesi",
|
||||
accountNumber: "1234567890",
|
||||
iban: "TR120006400000112345678901",
|
||||
accountType: BankAccountTypeEnum.Current,
|
||||
currency: "TRY",
|
||||
balance: 125000,
|
||||
overdraftLimit: 50000,
|
||||
dailyTransferLimit: 100000,
|
||||
isActive: true,
|
||||
contactPerson: "Mehmet Ak",
|
||||
phoneNumber: "+90 212 555 1111",
|
||||
creationTime: new Date("2024-01-01"),
|
||||
lastModificationTime: new Date("2024-01-20"),
|
||||
swiftCode: "ISBKTRIS",
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
accountCode: "BANKA002",
|
||||
bankName: "Garanti BBVA",
|
||||
branchName: "Şişli Şubesi",
|
||||
accountNumber: "9876543210",
|
||||
iban: "TR980006200012600006298453",
|
||||
accountType: BankAccountTypeEnum.Deposit,
|
||||
currency: "USD",
|
||||
balance: 15000,
|
||||
overdraftLimit: 0,
|
||||
dailyTransferLimit: 50000,
|
||||
isActive: true,
|
||||
contactPerson: "Ayşe Yıldız",
|
||||
phoneNumber: "+90 212 555 2222",
|
||||
creationTime: new Date("2024-01-01"),
|
||||
lastModificationTime: new Date("2024-01-18"),
|
||||
swiftCode: "TGBATRIS",
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
import { DepartmentDto } from "@/proxy/intranet/models"
|
||||
|
||||
export const mockDepartments: DepartmentDto[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Üretim',
|
||||
description: 'Üretim departmanı',
|
||||
parentDepartmentId: undefined,
|
||||
parentDepartment: undefined,
|
||||
subDepartments: [],
|
||||
managerId: '1',
|
||||
manager: undefined,
|
||||
costCenterId: 'cc-005',
|
||||
costCenter: undefined,
|
||||
budget: 8500000,
|
||||
isActive: true,
|
||||
creationTime: new Date('2022-03-15'),
|
||||
lastModificationTime: new Date('2024-01-15'),
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Bakım',
|
||||
description: 'Bakım departmanı',
|
||||
parentDepartmentId: undefined,
|
||||
parentDepartment: undefined,
|
||||
subDepartments: [],
|
||||
managerId: '7',
|
||||
manager: undefined,
|
||||
costCenterId: 'cc-011',
|
||||
costCenter: undefined,
|
||||
budget: 2200000,
|
||||
isActive: true,
|
||||
creationTime: new Date('2022-03-15'),
|
||||
lastModificationTime: new Date('2024-01-15'),
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Kalite Kontrol',
|
||||
description: 'Kalite kontrol departmanı',
|
||||
parentDepartmentId: '1',
|
||||
parentDepartment: undefined,
|
||||
subDepartments: [],
|
||||
managerId: '5',
|
||||
manager: undefined,
|
||||
costCenterId: 'cc-007',
|
||||
costCenter: undefined,
|
||||
budget: 1200000,
|
||||
isActive: true,
|
||||
creationTime: new Date('2022-03-15'),
|
||||
lastModificationTime: new Date('2024-01-15'),
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Depo',
|
||||
description: 'Depo departmanı',
|
||||
parentDepartmentId: '1',
|
||||
parentDepartment: undefined,
|
||||
subDepartments: [],
|
||||
managerId: '3',
|
||||
manager: undefined,
|
||||
costCenterId: 'cc-008',
|
||||
costCenter: undefined,
|
||||
budget: 2800000,
|
||||
isActive: true,
|
||||
creationTime: new Date('2022-03-15'),
|
||||
lastModificationTime: new Date('2024-01-15'),
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: 'İdari İşler',
|
||||
description: 'İdari işler departmanı',
|
||||
parentDepartmentId: undefined,
|
||||
parentDepartment: undefined,
|
||||
subDepartments: [],
|
||||
managerId: '2',
|
||||
manager: undefined,
|
||||
costCenterId: 'cc-001',
|
||||
costCenter: undefined,
|
||||
budget: 2500000,
|
||||
isActive: true,
|
||||
creationTime: new Date('2022-03-15'),
|
||||
lastModificationTime: new Date('2024-01-15'),
|
||||
},
|
||||
]
|
||||
|
||||
mockDepartments.forEach((dept) => {
|
||||
if (dept.parentDepartmentId) {
|
||||
dept.parentDepartment = mockDepartments.find((d) => d.id === dept.parentDepartmentId)
|
||||
}
|
||||
})
|
||||
|
||||
mockDepartments.forEach((dept) => {
|
||||
if (dept.parentDepartmentId) {
|
||||
const parent = mockDepartments.find((d) => d.id === dept.parentDepartmentId)
|
||||
if (parent) {
|
||||
dept.parentDepartment = parent
|
||||
parent.subDepartments.push(dept) // subDepartments bağlantısı
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -1,602 +0,0 @@
|
|||
import { EmployeeDto } from "@/proxy/intranet/models";
|
||||
import {
|
||||
EmployeeStatusEnum,
|
||||
EmploymentTypeEnum,
|
||||
GenderEnum,
|
||||
MaritalStatusEnum,
|
||||
} from "../types/hr";
|
||||
import { mockBanks } from "./mockBanks";
|
||||
import { mockDepartments } from "./mockDepartments";
|
||||
import { mockJobPositions } from "./mockJobPositions";
|
||||
|
||||
export const mockEmployees: EmployeeDto[] = [
|
||||
{
|
||||
id: "1",
|
||||
code: "EMP-001",
|
||||
firstName: "Ali",
|
||||
lastName: "Öztürk",
|
||||
name: "Ali Öztürk",
|
||||
email: "ali.ozturk@company.com",
|
||||
phoneNumber: "2125550100",
|
||||
mobileNumber: "5325550101",
|
||||
avatar: "https://i.pravatar.cc/150?img=12",
|
||||
nationalId: "12345678901",
|
||||
birthDate: new Date("1988-10-20"),
|
||||
gender: GenderEnum.Male,
|
||||
maritalStatus: MaritalStatusEnum.Married,
|
||||
address: {
|
||||
street: "Kızılay Cd. No:12",
|
||||
city: "Ankara",
|
||||
state: "Ankara",
|
||||
postalCode: "06050",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Ayşe Öztürk",
|
||||
relationship: "Eşi",
|
||||
phoneNumber: "+90 532 555 0100",
|
||||
},
|
||||
hireDate: new Date("2020-01-15"),
|
||||
employmentType: EmploymentTypeEnum.FullTime,
|
||||
jobPositionId: "1",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "1")!,
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((d) => d.id === "1")!,
|
||||
baseSalary: 65000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "1",
|
||||
bankAccount: mockBanks.find((b) => b.id === "1")!,
|
||||
workLocation: "Ankara Merkez",
|
||||
workSchedule: {
|
||||
id: "1",
|
||||
scheduleCode: "STD",
|
||||
name: "Standart Mesai",
|
||||
description: "08:30-17:30 Pazartesi-Cuma",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 40,
|
||||
isFlexible: false,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B001",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2020-01-15"),
|
||||
lastModificationTime: new Date("2024-01-18"),
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
code: "EMP-002",
|
||||
firstName: "Ayşe",
|
||||
lastName: "Kaya",
|
||||
name: "Ayşe Kaya",
|
||||
email: "ayse.kaya@company.com",
|
||||
phoneNumber: "2125550102",
|
||||
mobileNumber: "5325550103",
|
||||
avatar: "https://i.pravatar.cc/150?img=5",
|
||||
nationalId: "12345678902",
|
||||
birthDate: new Date("1990-08-22"),
|
||||
gender: GenderEnum.Female,
|
||||
maritalStatus: MaritalStatusEnum.Single,
|
||||
address: {
|
||||
street: "İnönü Bulvarı No:456",
|
||||
city: "Ankara",
|
||||
state: "Ankara",
|
||||
postalCode: "06000",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Fatma Kaya",
|
||||
relationship: "Anne",
|
||||
phoneNumber: "+90 532 555 0104",
|
||||
},
|
||||
hireDate: new Date("2021-06-01"),
|
||||
employmentType: EmploymentTypeEnum.FullTime,
|
||||
jobPositionId: "2",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "2")!,
|
||||
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((d) => d.id === "1")!,
|
||||
baseSalary: 72000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "2",
|
||||
bankAccount: mockBanks.find((b) => b.id === "2")!,
|
||||
workLocation: "Ankara Şube",
|
||||
workSchedule: {
|
||||
id: "2",
|
||||
scheduleCode: "STD",
|
||||
name: "Standart Mesai",
|
||||
description: "08:30-17:30 Pazartesi-Cuma",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 40,
|
||||
isFlexible: false,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B002",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2021-06-01"),
|
||||
lastModificationTime: new Date("2024-01-18"),
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
code: "EMP-003",
|
||||
firstName: "Mehmet",
|
||||
lastName: "Yılmaz",
|
||||
name: "Mehmet Yılmaz",
|
||||
email: "mehmet.yilmaz@company.com",
|
||||
phoneNumber: "2125550105",
|
||||
mobileNumber: "5325550106",
|
||||
avatar: "https://i.pravatar.cc/150?img=8",
|
||||
nationalId: "12345678903",
|
||||
birthDate: new Date("1987-03-12"),
|
||||
gender: GenderEnum.Male,
|
||||
maritalStatus: MaritalStatusEnum.Married,
|
||||
address: {
|
||||
street: "Cumhuriyet Cad. No:123",
|
||||
city: "İstanbul",
|
||||
state: "İstanbul",
|
||||
postalCode: "34000",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Zeynep Yılmaz",
|
||||
relationship: "Eşi",
|
||||
phoneNumber: "+90 532 555 0107",
|
||||
},
|
||||
hireDate: new Date("2020-02-15"),
|
||||
employmentType: EmploymentTypeEnum.FullTime,
|
||||
jobPositionId: "3",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "3")!,
|
||||
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((d) => d.id === "1")!,
|
||||
baseSalary: 85000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "2",
|
||||
bankAccount: mockBanks.find((b) => b.id === "2")!,
|
||||
workLocation: "İstanbul HQ",
|
||||
workSchedule: {
|
||||
id: "2",
|
||||
scheduleCode: "FLEX",
|
||||
name: "Esnek Çalışma",
|
||||
description: "09:00-18:00 Pazartesi-Cuma",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 40,
|
||||
isFlexible: true,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B003",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2020-02-15"),
|
||||
lastModificationTime: new Date("2024-02-01"),
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
code: "EMP-004",
|
||||
firstName: "Selin",
|
||||
lastName: "Demir",
|
||||
name: "Selin Demir",
|
||||
email: "selin.demir@company.com",
|
||||
phoneNumber: "3125550108",
|
||||
mobileNumber: "5425550109",
|
||||
avatar: "https://i.pravatar.cc/150?img=9",
|
||||
nationalId: "12345678904",
|
||||
birthDate: new Date("1993-05-25"),
|
||||
gender: GenderEnum.Female,
|
||||
maritalStatus: MaritalStatusEnum.Single,
|
||||
address: {
|
||||
street: "Atatürk Bulvarı No:78",
|
||||
city: "Ankara",
|
||||
state: "Ankara",
|
||||
postalCode: "06100",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Ali Demir",
|
||||
relationship: "Baba",
|
||||
phoneNumber: "532 555 0110",
|
||||
},
|
||||
hireDate: new Date("2022-01-10"),
|
||||
employmentType: EmploymentTypeEnum.PartTime,
|
||||
jobPositionId: "4",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "4")!,
|
||||
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((d) => d.id === "1")!,
|
||||
baseSalary: 60000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "3",
|
||||
bankAccount: mockBanks.find((b) => b.id === "3")!,
|
||||
workLocation: "Ankara Şube",
|
||||
workSchedule: {
|
||||
id: "3",
|
||||
scheduleCode: "PT",
|
||||
name: "Yarı Zamanlı",
|
||||
description: "09:00-13:00 Pazartesi-Cuma",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 20,
|
||||
isFlexible: false,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B004",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2022-01-10"),
|
||||
lastModificationTime: new Date("2024-01-20"),
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
code: "EMP-005",
|
||||
firstName: "Ahmet",
|
||||
lastName: "Çelik",
|
||||
name: "Ahmet Çelik",
|
||||
email: "ahmet.celik@company.com",
|
||||
phoneNumber: "2125550111",
|
||||
mobileNumber: "5325550112",
|
||||
avatar: "https://i.pravatar.cc/150?img=33",
|
||||
nationalId: "12345678905",
|
||||
birthDate: new Date("1985-09-10"),
|
||||
gender: GenderEnum.Male,
|
||||
maritalStatus: MaritalStatusEnum.Married,
|
||||
address: {
|
||||
street: "Bağdat Cad. No:25",
|
||||
city: "İstanbul",
|
||||
state: "İstanbul",
|
||||
postalCode: "34728",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Emine Çelik",
|
||||
relationship: "Eşi",
|
||||
phoneNumber: "532 555 0113",
|
||||
},
|
||||
hireDate: new Date("2019-04-01"),
|
||||
employmentType: EmploymentTypeEnum.FullTime,
|
||||
jobPositionId: "5",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "5")!,
|
||||
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((d) => d.id === "1")!,
|
||||
baseSalary: 95000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "4",
|
||||
bankAccount: mockBanks.find((b) => b.id === "4")!,
|
||||
workLocation: "İstanbul HQ",
|
||||
workSchedule: {
|
||||
id: "4",
|
||||
scheduleCode: "STD",
|
||||
name: "Standart Mesai",
|
||||
description: "08:30-17:30 Pazartesi-Cuma",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 40,
|
||||
isFlexible: false,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B005",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2019-04-01"),
|
||||
lastModificationTime: new Date("2024-01-10"),
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
code: "EMP-006",
|
||||
firstName: "Zeynep",
|
||||
lastName: "Arslan",
|
||||
name: "Zeynep Arslan",
|
||||
email: "zeynep.arslan@company.com",
|
||||
phoneNumber: "2165550114",
|
||||
mobileNumber: "5325550115",
|
||||
avatar: "https://i.pravatar.cc/150?img=10",
|
||||
nationalId: "12345678906",
|
||||
birthDate: new Date("1995-01-30"),
|
||||
gender: GenderEnum.Female,
|
||||
maritalStatus: MaritalStatusEnum.Single,
|
||||
address: {
|
||||
street: "Yıldız Mah. No:19",
|
||||
city: "İzmir",
|
||||
state: "İzmir",
|
||||
postalCode: "35000",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Hasan Arslan",
|
||||
relationship: "Baba",
|
||||
phoneNumber: "532 555 0116",
|
||||
},
|
||||
hireDate: new Date("2023-03-20"),
|
||||
employmentType: EmploymentTypeEnum.Intern,
|
||||
jobPositionId: "6",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "6")!,
|
||||
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((d) => d.id === "1")!,
|
||||
baseSalary: 15000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "1",
|
||||
bankAccount: mockBanks.find((b) => b.id === "1")!,
|
||||
workLocation: "İzmir Ofis",
|
||||
workSchedule: {
|
||||
id: "5",
|
||||
scheduleCode: "INT",
|
||||
name: "Staj Programı",
|
||||
description: "09:00-16:00 Pazartesi-Perşembe",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 30,
|
||||
isFlexible: true,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B006",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2023-03-20"),
|
||||
lastModificationTime: new Date("2024-02-15"),
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
code: "EMP-007",
|
||||
firstName: "Burak",
|
||||
lastName: "Koç",
|
||||
name: "Burak Koç",
|
||||
email: "burak.koc@company.com",
|
||||
phoneNumber: "2245550117",
|
||||
mobileNumber: "5325550118",
|
||||
avatar: "https://i.pravatar.cc/150?img=14",
|
||||
nationalId: "12345678907",
|
||||
birthDate: new Date("1991-06-18"),
|
||||
gender: GenderEnum.Male,
|
||||
maritalStatus: MaritalStatusEnum.Married,
|
||||
address: {
|
||||
street: "Osmangazi Mah. No:45",
|
||||
city: "Bursa",
|
||||
state: "Bursa",
|
||||
postalCode: "16000",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Elif Koç",
|
||||
relationship: "Eşi",
|
||||
phoneNumber: "+90 532 555 0119",
|
||||
},
|
||||
hireDate: new Date("2021-07-12"),
|
||||
employmentType: EmploymentTypeEnum.FullTime,
|
||||
jobPositionId: "7",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "7")!,
|
||||
|
||||
departmentId: "2",
|
||||
department: mockDepartments.find((d) => d.id === "2")!,
|
||||
baseSalary: 75000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "3",
|
||||
bankAccount: mockBanks.find((b) => b.id === "3")!,
|
||||
workLocation: "Bursa Depo",
|
||||
workSchedule: {
|
||||
id: "6",
|
||||
scheduleCode: "STD",
|
||||
name: "Standart Mesai",
|
||||
description: "08:00-17:00 Pazartesi-Cumartesi",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 45,
|
||||
isFlexible: false,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B007",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2021-07-12"),
|
||||
lastModificationTime: new Date("2024-01-05"),
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
code: "EMP-008",
|
||||
firstName: "Elif",
|
||||
lastName: "Şahin",
|
||||
name: "Elif Şahin",
|
||||
email: "elif.sahin@company.com",
|
||||
phoneNumber: "2325550120",
|
||||
mobileNumber: "5325550121",
|
||||
avatar: "https://i.pravatar.cc/150?img=20",
|
||||
nationalId: "12345678908",
|
||||
birthDate: new Date("1989-11-05"),
|
||||
gender: GenderEnum.Female,
|
||||
maritalStatus: MaritalStatusEnum.Married,
|
||||
address: {
|
||||
street: "Alsancak Mah. No:88",
|
||||
city: "İzmir",
|
||||
state: "İzmir",
|
||||
postalCode: "35220",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Murat Şahin",
|
||||
relationship: "Eşi",
|
||||
phoneNumber: "+90 532 555 0122",
|
||||
},
|
||||
hireDate: new Date("2018-09-01"),
|
||||
employmentType: EmploymentTypeEnum.FullTime,
|
||||
jobPositionId: "8",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "8")!,
|
||||
|
||||
departmentId: "2",
|
||||
department: mockDepartments.find((d) => d.id === "2")!,
|
||||
baseSalary: 130000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "2",
|
||||
bankAccount: mockBanks.find((b) => b.id === "2")!,
|
||||
workLocation: "İzmir Bölge Ofisi",
|
||||
workSchedule: {
|
||||
id: "7",
|
||||
scheduleCode: "STD",
|
||||
name: "Standart Mesai",
|
||||
description: "08:30-17:30 Pazartesi-Cuma",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 40,
|
||||
isFlexible: false,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B008",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2018-09-01"),
|
||||
lastModificationTime: new Date("2024-01-12"),
|
||||
},
|
||||
{
|
||||
id: "9",
|
||||
code: "EMP-009",
|
||||
firstName: "Canan",
|
||||
lastName: "Öztürk",
|
||||
name: "Canan Öztürk",
|
||||
email: "canan.ozturk@company.com",
|
||||
phoneNumber: "3125550123",
|
||||
mobileNumber: "5325550124",
|
||||
avatar: "https://i.pravatar.cc/150?img=25",
|
||||
nationalId: "12345678909",
|
||||
birthDate: new Date("1992-04-14"),
|
||||
gender: GenderEnum.Female,
|
||||
maritalStatus: MaritalStatusEnum.Single,
|
||||
address: {
|
||||
street: "Bahçelievler Mah. No:55",
|
||||
city: "Ankara",
|
||||
state: "Ankara",
|
||||
postalCode: "06490",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Hüseyin Öztürk",
|
||||
relationship: "Baba",
|
||||
phoneNumber: "+90 532 555 0125",
|
||||
},
|
||||
hireDate: new Date("2020-11-02"),
|
||||
employmentType: EmploymentTypeEnum.FullTime,
|
||||
jobPositionId: "9",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "9")!,
|
||||
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((d) => d.id === "1")!,
|
||||
baseSalary: 50000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "1",
|
||||
bankAccount: mockBanks.find((b) => b.id === "1")!,
|
||||
workLocation: "Ankara Çağrı Merkezi",
|
||||
workSchedule: {
|
||||
id: "8",
|
||||
scheduleCode: "SHIFT",
|
||||
name: "Vardiya",
|
||||
description: "3 vardiya sistemi",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 40,
|
||||
isFlexible: true,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B009",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2020-11-02"),
|
||||
lastModificationTime: new Date("2024-01-18"),
|
||||
},
|
||||
{
|
||||
id: "10",
|
||||
code: "EMP-010",
|
||||
firstName: "Murat",
|
||||
lastName: "Aydın",
|
||||
name: "Murat Aydın",
|
||||
email: "murat.aydin@company.com",
|
||||
phoneNumber: "2125550126",
|
||||
mobileNumber: "5325550127",
|
||||
avatar: "https://i.pravatar.cc/150?img=30",
|
||||
nationalId: "12345678910",
|
||||
birthDate: new Date("1984-12-22"),
|
||||
gender: GenderEnum.Male,
|
||||
maritalStatus: MaritalStatusEnum.Married,
|
||||
address: {
|
||||
street: "Şişli Mah. No:101",
|
||||
city: "İstanbul",
|
||||
state: "İstanbul",
|
||||
postalCode: "34360",
|
||||
country: "Türkiye",
|
||||
},
|
||||
emergencyContact: {
|
||||
name: "Ayten Aydın",
|
||||
relationship: "Eşi",
|
||||
phoneNumber: "+90 532 555 0128",
|
||||
},
|
||||
hireDate: new Date("2017-05-15"),
|
||||
employmentType: EmploymentTypeEnum.FullTime,
|
||||
jobPositionId: "10",
|
||||
jobPosition: mockJobPositions.find((jp) => jp.id === "10")!,
|
||||
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((d) => d.id === "1")!,
|
||||
baseSalary: 250000,
|
||||
currency: "TRY",
|
||||
payrollGroup: "MONTHLY",
|
||||
bankAccountId: "4",
|
||||
bankAccount: mockBanks.find((b) => b.id === "4")!,
|
||||
workLocation: "İstanbul Genel Merkez",
|
||||
workSchedule: {
|
||||
id: "9",
|
||||
scheduleCode: "EXEC",
|
||||
name: "Yönetici Çalışma Programı",
|
||||
description: "Esnek yönetici programı",
|
||||
workingDays: [],
|
||||
totalHoursPerWeek: 50,
|
||||
isFlexible: true,
|
||||
isActive: true,
|
||||
},
|
||||
badgeNumber: "B010",
|
||||
employeeStatus: EmployeeStatusEnum.Active,
|
||||
isActive: true,
|
||||
leaves: [],
|
||||
evaluations: [],
|
||||
trainings: [],
|
||||
disciplinaryActions: [],
|
||||
creationTime: new Date("2017-05-15"),
|
||||
lastModificationTime: new Date("2024-01-22"),
|
||||
},
|
||||
];
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
import { Exam } from "@/types/coordinator";
|
||||
import { generateMockPools } from "./mockPools";
|
||||
|
||||
export const generateMockExam = (): Exam[] => [
|
||||
{
|
||||
id: "exam-1",
|
||||
title: "Grammar Assessment",
|
||||
description: "Comprehensive grammar evaluation",
|
||||
type: "exam",
|
||||
questions: generateMockPools()[0].questions,
|
||||
timeLimit: 30,
|
||||
totalPoints: 25,
|
||||
passingScore: 60,
|
||||
allowReview: true,
|
||||
randomizeQuestions: false,
|
||||
showResults: true,
|
||||
maxAttempts: 1,
|
||||
isActive: true,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
},
|
||||
{
|
||||
id: "assignment-1",
|
||||
title: "Assignment 1",
|
||||
description: "First assignment on grammar topics",
|
||||
type: "assignment",
|
||||
questions: generateMockPools()[0].questions,
|
||||
timeLimit: 30,
|
||||
totalPoints: 25,
|
||||
passingScore: 60,
|
||||
allowReview: true,
|
||||
randomizeQuestions: false,
|
||||
showResults: true,
|
||||
maxAttempts: 1,
|
||||
isActive: true,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
},
|
||||
];
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
import { JobPositionDto } from "@/proxy/intranet/models";
|
||||
import { mockDepartments } from "./mockDepartments";
|
||||
import { JobLevelEnum } from "@/types/hr";
|
||||
|
||||
export const mockJobPositions: JobPositionDto[] = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Software Developer",
|
||||
description: "Responsible for developing and maintaining web applications",
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((dept) => dept.id === "1"),
|
||||
level: JobLevelEnum.Mid,
|
||||
minSalary: 80000,
|
||||
maxSalary: 120000,
|
||||
currency: "USD",
|
||||
requiredSkills: ["JavaScript", "TypeScript", "React", "Node.js"],
|
||||
responsibilities: [
|
||||
"Develop frontend and backend applications",
|
||||
"Write clean and maintainable code",
|
||||
"Participate in code reviews",
|
||||
"Collaborate with team members",
|
||||
],
|
||||
qualifications: [
|
||||
"Bachelor's degree in Computer Science or related field",
|
||||
"3+ years of experience in web development",
|
||||
"Strong knowledge of JavaScript and TypeScript",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-15T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-15T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Project Manager",
|
||||
description: "Lead and manage software development projects",
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((dept) => dept.id === "1"),
|
||||
level: JobLevelEnum.Manager,
|
||||
minSalary: 100000,
|
||||
maxSalary: 150000,
|
||||
currency: "USD",
|
||||
requiredSkills: ["Project Management", "Agile", "Scrum", "Leadership"],
|
||||
responsibilities: [
|
||||
"Plan and execute project timelines",
|
||||
"Coordinate with cross-functional teams",
|
||||
"Manage project budgets and resources",
|
||||
"Ensure project delivery within scope and timeline",
|
||||
],
|
||||
qualifications: [
|
||||
"Bachelor's degree in Business or related field",
|
||||
"5+ years of project management experience",
|
||||
"PMP certification preferred",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-16T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-16T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Quality Assurance Engineer",
|
||||
description: "Ensure software quality through testing and automation",
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((dept) => dept.id === "1"),
|
||||
level: JobLevelEnum.Mid,
|
||||
minSalary: 70000,
|
||||
maxSalary: 100000,
|
||||
currency: "USD",
|
||||
requiredSkills: ["Testing", "Automation", "Selenium", "API Testing"],
|
||||
responsibilities: [
|
||||
"Design and execute test cases",
|
||||
"Automate testing processes",
|
||||
"Report and track bugs",
|
||||
"Collaborate with development team",
|
||||
],
|
||||
qualifications: [
|
||||
"Bachelor's degree in Computer Science or related field",
|
||||
"3+ years of QA experience",
|
||||
"Experience with automation tools",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-17T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-17T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "UX/UI Designer",
|
||||
description: "Design user interfaces and improve user experience",
|
||||
departmentId: "2",
|
||||
department: mockDepartments.find((dept) => dept.id === "2"),
|
||||
level: JobLevelEnum.Mid,
|
||||
minSalary: 75000,
|
||||
maxSalary: 110000,
|
||||
currency: "USD",
|
||||
requiredSkills: [
|
||||
"Figma",
|
||||
"Adobe Creative Suite",
|
||||
"User Research",
|
||||
"Prototyping",
|
||||
],
|
||||
responsibilities: [
|
||||
"Create wireframes and prototypes",
|
||||
"Conduct user research",
|
||||
"Design intuitive user interfaces",
|
||||
"Collaborate with development team",
|
||||
],
|
||||
qualifications: [
|
||||
"Bachelor's degree in Design or related field",
|
||||
"3+ years of UX/UI design experience",
|
||||
"Strong portfolio demonstrating design skills",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-18T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-18T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
name: "Data Analyst",
|
||||
description: "Analyze business data and provide insights",
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((dept) => dept.id === "1"),
|
||||
level: JobLevelEnum.Mid,
|
||||
minSalary: 85000,
|
||||
maxSalary: 125000,
|
||||
currency: "USD",
|
||||
requiredSkills: ["SQL", "Python", "Excel", "Tableau", "Statistics"],
|
||||
responsibilities: [
|
||||
"Analyze large datasets",
|
||||
"Create reports and dashboards",
|
||||
"Identify trends and patterns",
|
||||
"Present findings to stakeholders",
|
||||
],
|
||||
qualifications: [
|
||||
"Bachelor's degree in Statistics, Mathematics, or related field",
|
||||
"3+ years of data analysis experience",
|
||||
"Strong analytical and problem-solving skills",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-19T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-19T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
name: "HR Specialist",
|
||||
description: "Support human resources operations and employee relations",
|
||||
departmentId: "4",
|
||||
department: mockDepartments.find((dept) => dept.id === "4"),
|
||||
level: JobLevelEnum.Mid,
|
||||
minSalary: 60000,
|
||||
maxSalary: 85000,
|
||||
currency: "USD",
|
||||
requiredSkills: [
|
||||
"HR Management",
|
||||
"Recruitment",
|
||||
"Employee Relations",
|
||||
"HRIS",
|
||||
],
|
||||
responsibilities: [
|
||||
"Manage recruitment processes",
|
||||
"Handle employee relations issues",
|
||||
"Maintain HR records and policies",
|
||||
"Support employee onboarding",
|
||||
],
|
||||
qualifications: [
|
||||
"Bachelor's degree in Human Resources or related field",
|
||||
"2+ years of HR experience",
|
||||
"Knowledge of employment laws and regulations",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-20T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-20T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
name: "Sales Associate",
|
||||
description: "Generate sales leads and maintain customer relationships",
|
||||
departmentId: "2",
|
||||
department: mockDepartments.find((dept) => dept.id === "2"),
|
||||
level: JobLevelEnum.Junior,
|
||||
minSalary: 45000,
|
||||
maxSalary: 70000,
|
||||
currency: "USD",
|
||||
requiredSkills: ["Sales", "CRM", "Communication", "Customer Service"],
|
||||
responsibilities: [
|
||||
"Identify and pursue sales opportunities",
|
||||
"Maintain customer relationships",
|
||||
"Prepare sales proposals",
|
||||
"Meet monthly sales targets",
|
||||
],
|
||||
qualifications: [
|
||||
"Bachelor's degree in Business or related field",
|
||||
"1+ years of sales experience",
|
||||
"Excellent communication skills",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-21T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-21T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
name: "Accountant",
|
||||
description: "Manage financial records and ensure compliance",
|
||||
departmentId: "3",
|
||||
department: mockDepartments.find((dept) => dept.id === "3"),
|
||||
level: JobLevelEnum.Mid,
|
||||
minSalary: 55000,
|
||||
maxSalary: 80000,
|
||||
currency: "USD",
|
||||
requiredSkills: [
|
||||
"Accounting",
|
||||
"Excel",
|
||||
"Financial Analysis",
|
||||
"Tax Preparation",
|
||||
],
|
||||
responsibilities: [
|
||||
"Maintain financial records",
|
||||
"Prepare financial statements",
|
||||
"Ensure regulatory compliance",
|
||||
"Assist with budget planning",
|
||||
],
|
||||
qualifications: [
|
||||
"Bachelor's degree in Accounting or Finance",
|
||||
"CPA certification preferred",
|
||||
"3+ years of accounting experience",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-22T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-22T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "9",
|
||||
name: "Customer Support Representative",
|
||||
description: "Provide excellent customer service and technical support",
|
||||
departmentId: "2",
|
||||
department: mockDepartments.find((dept) => dept.id === "2"),
|
||||
level: JobLevelEnum.Entry,
|
||||
minSalary: 35000,
|
||||
maxSalary: 50000,
|
||||
currency: "USD",
|
||||
requiredSkills: [
|
||||
"Customer Service",
|
||||
"Communication",
|
||||
"Problem Solving",
|
||||
"CRM",
|
||||
],
|
||||
responsibilities: [
|
||||
"Respond to customer inquiries",
|
||||
"Resolve technical issues",
|
||||
"Document customer interactions",
|
||||
"Escalate complex issues when needed",
|
||||
],
|
||||
qualifications: [
|
||||
"High school diploma or equivalent",
|
||||
"1+ years of customer service experience",
|
||||
"Strong communication skills",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-23T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-23T08:00:00Z"),
|
||||
},
|
||||
{
|
||||
id: "10",
|
||||
name: "IT Support Specialist",
|
||||
description: "Provide technical support and maintain IT infrastructure",
|
||||
departmentId: "1",
|
||||
department: mockDepartments.find((dept) => dept.id === "1"),
|
||||
level: JobLevelEnum.Junior,
|
||||
minSalary: 50000,
|
||||
maxSalary: 75000,
|
||||
currency: "USD",
|
||||
requiredSkills: [
|
||||
"Windows",
|
||||
"Network Administration",
|
||||
"Hardware",
|
||||
"Troubleshooting",
|
||||
],
|
||||
responsibilities: [
|
||||
"Provide technical support to employees",
|
||||
"Maintain computer systems and networks",
|
||||
"Install and configure software",
|
||||
"Document IT procedures",
|
||||
],
|
||||
qualifications: [
|
||||
"Associate degree in Information Technology or related field",
|
||||
"2+ years of IT support experience",
|
||||
"CompTIA A+ certification preferred",
|
||||
],
|
||||
isActive: true,
|
||||
employees: [],
|
||||
creationTime: new Date("2024-01-24T08:00:00Z"),
|
||||
lastModificationTime: new Date("2024-01-24T08:00:00Z"),
|
||||
},
|
||||
];
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import { QuestionPoolDto } from "@/types/coordinator";
|
||||
|
||||
// Dynamic data - no hardcoded content
|
||||
export const generateMockPools = (): QuestionPoolDto[] => [
|
||||
{
|
||||
id: "pool-1",
|
||||
name: "Grammar Fundamentals",
|
||||
description: "Essential grammar concepts and structures",
|
||||
questions: [
|
||||
{
|
||||
id: "q1",
|
||||
questionType: "multiple-choice",
|
||||
title: "Verb Tenses",
|
||||
content: "Select the appropriate tense form for the given context.",
|
||||
options: [
|
||||
{ id: "opt1", text: "Option A", isCorrect: false },
|
||||
{ id: "opt2", text: "Option B", isCorrect: true },
|
||||
{ id: "opt3", text: "Option C", isCorrect: false },
|
||||
{ id: "opt4", text: "Option D", isCorrect: false },
|
||||
],
|
||||
correctAnswer: "opt2",
|
||||
points: 10,
|
||||
difficulty: "easy",
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
},
|
||||
{
|
||||
id: "q2",
|
||||
questionType: "fill-blank",
|
||||
title: "Articles",
|
||||
content:
|
||||
'Fill in the blank: "I saw _____ elephant at the zoo yesterday."',
|
||||
correctAnswer: "an",
|
||||
points: 15,
|
||||
difficulty: "medium",
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
},
|
||||
],
|
||||
tags: ["grammar", "fundamentals"],
|
||||
creationTime: new Date(),
|
||||
},
|
||||
];
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import { TagItem } from "@/types/coordinator";
|
||||
|
||||
export const generateMockTags = (): TagItem[] => [
|
||||
{
|
||||
id: "tag-1",
|
||||
name: "Grammar",
|
||||
description: "Grammar related questions",
|
||||
color: "#3B82F6",
|
||||
usageCount: 25,
|
||||
creationTime: new Date(),
|
||||
},
|
||||
{
|
||||
id: "tag-2",
|
||||
name: "Vocabulary",
|
||||
description: "Vocabulary building exercises",
|
||||
color: "#10B981",
|
||||
usageCount: 18,
|
||||
creationTime: new Date(),
|
||||
},
|
||||
{
|
||||
id: "tag-3",
|
||||
name: "Reading",
|
||||
description: "Reading comprehension tasks",
|
||||
color: "#F59E0B",
|
||||
usageCount: 12,
|
||||
creationTime: new Date(),
|
||||
},
|
||||
{
|
||||
id: "tag-4",
|
||||
name: "Writing",
|
||||
description: "Writing skill assessments",
|
||||
color: "#EF4444",
|
||||
usageCount: 8,
|
||||
creationTime: new Date(),
|
||||
},
|
||||
];
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import { Exam } from "@/types/coordinator";
|
||||
|
||||
export const generateMockPDFTest = (): Exam => ({
|
||||
id: "test-1",
|
||||
title: "Reading Assessment",
|
||||
description: "Document-based reading evaluation",
|
||||
type: "test",
|
||||
testDocument: {
|
||||
url: "/sample-documents/reading-test.pdf",
|
||||
type: "pdf",
|
||||
name: "reading-assessment.pdf",
|
||||
},
|
||||
answerKeyTemplate: [
|
||||
{
|
||||
id: "ak1",
|
||||
questionNumber: 1,
|
||||
type: "multiple-choice",
|
||||
options: ["A", "B", "C", "D"],
|
||||
points: 10,
|
||||
correctAnswer: "A",
|
||||
},
|
||||
{
|
||||
id: "ak2",
|
||||
questionNumber: 2,
|
||||
type: "true-false",
|
||||
points: 5,
|
||||
correctAnswer: "true",
|
||||
},
|
||||
{
|
||||
id: "ak3",
|
||||
questionNumber: 3,
|
||||
type: "fill-blank",
|
||||
points: 15,
|
||||
correctAnswer: "answer",
|
||||
},
|
||||
],
|
||||
questions: [],
|
||||
timeLimit: 45,
|
||||
totalPoints: 30,
|
||||
passingScore: 70,
|
||||
allowReview: true,
|
||||
randomizeQuestions: false,
|
||||
showResults: true,
|
||||
maxAttempts: 1,
|
||||
isActive: true,
|
||||
creationTime: new Date(),
|
||||
lastModificationTime: new Date(),
|
||||
});
|
||||
|
|
@ -63,6 +63,7 @@ export interface IdentityUserDto extends ExtensibleFullAuditedEntityDto<string>
|
|||
userName?: string
|
||||
name?: string
|
||||
surname?: string
|
||||
fullName?: string
|
||||
email?: string
|
||||
emailConfirmed: boolean
|
||||
phoneNumber?: string
|
||||
|
|
@ -109,10 +110,12 @@ export class ExtensibleFullAuditedEntityDto<
|
|||
|
||||
export interface UserInfoViewModel extends ExtensibleObject {
|
||||
id?: string
|
||||
tenantId?: string
|
||||
concurrencyStamp?: string
|
||||
userName?: string
|
||||
name?: string
|
||||
surname?: string
|
||||
fullName?: string
|
||||
email?: string
|
||||
phoneNumber?: string
|
||||
isActive: boolean
|
||||
|
|
@ -139,6 +142,7 @@ export interface UserInfoViewModel extends ExtensibleObject {
|
|||
lastModificationTime: Date | string
|
||||
workHour?: string
|
||||
departmentId?: string
|
||||
department?: AssignedDepartmentViewModel
|
||||
jobPositionId?: string
|
||||
nationality?: string
|
||||
sskNo?: string
|
||||
|
|
|
|||
137
ui/src/proxy/intranet/models.ts
Normal file
137
ui/src/proxy/intranet/models.ts
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
import { UserInfoViewModel } from "../admin/models"
|
||||
|
||||
export interface IntranetDashboardDto {
|
||||
birthdays: UserInfoViewModel[]
|
||||
documents: DocumentDto[]
|
||||
announcements: AnnouncementDto[]
|
||||
surveys: SurveyDto[]
|
||||
socialPosts: SocialPostDto[]
|
||||
}
|
||||
|
||||
// Doküman (FileItemDto ile uyumlu)
|
||||
export interface DocumentDto {
|
||||
id: string
|
||||
name: string
|
||||
type: string // "file" or "folder"
|
||||
size: number
|
||||
extension: string
|
||||
mimeType: string
|
||||
createdAt: Date
|
||||
modifiedAt: Date
|
||||
path: string
|
||||
parentId: string
|
||||
isReadOnly: boolean
|
||||
childCount: number
|
||||
}
|
||||
|
||||
// Duyuru
|
||||
export interface AnnouncementDto {
|
||||
id: string
|
||||
title: string
|
||||
excerpt: string
|
||||
content: string
|
||||
imageUrl?: string
|
||||
category: string
|
||||
userId: string
|
||||
user: UserInfoViewModel
|
||||
publishDate: Date
|
||||
expiryDate?: Date
|
||||
isPinned: boolean
|
||||
viewCount: number
|
||||
departments?: string[]
|
||||
attachments?: { name: string; url: string; size: string }[]
|
||||
}
|
||||
|
||||
// Anket Cevap
|
||||
export interface SurveyAnswerDto {
|
||||
questionId: string
|
||||
questionType: 'rating' | 'multiple-choice' | 'text' | 'textarea' | 'yes-no'
|
||||
value: string | number | string[]
|
||||
}
|
||||
|
||||
// Anket Cevabı
|
||||
export interface SurveyResponseDto {
|
||||
id: string
|
||||
surveyId: string
|
||||
respondentId?: string // Anonymous ise null
|
||||
submissionTime: Date
|
||||
answers: SurveyAnswerDto[]
|
||||
}
|
||||
|
||||
// Anket Sorusu Seçeneği
|
||||
export interface SurveyQuestionOptionDto {
|
||||
id: string
|
||||
text: string
|
||||
order: number
|
||||
}
|
||||
|
||||
// Anket Sorusu
|
||||
export interface SurveyQuestionDto {
|
||||
id: string
|
||||
surveyId: string
|
||||
questionText: string
|
||||
type: 'rating' | 'multiple-choice' | 'text' | 'textarea' | 'yes-no'
|
||||
order: number
|
||||
isRequired: boolean
|
||||
options?: SurveyQuestionOptionDto[]
|
||||
}
|
||||
|
||||
// Anket
|
||||
export interface SurveyDto {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
creatorId: UserInfoViewModel
|
||||
creationTime: Date
|
||||
deadline: Date
|
||||
questions: SurveyQuestionDto[]
|
||||
responses: number
|
||||
targetAudience: string[]
|
||||
status: 'draft' | 'active' | 'closed'
|
||||
isAnonymous: boolean
|
||||
}
|
||||
|
||||
// Sosyal Duvar - Comment Interface
|
||||
export interface SocialCommentDto {
|
||||
id: string
|
||||
creator: UserInfoViewModel
|
||||
content: string
|
||||
creationTime: Date
|
||||
}
|
||||
|
||||
export interface SocialPollOptionDto {
|
||||
id: string
|
||||
text: string
|
||||
votes: number
|
||||
}
|
||||
|
||||
// Sosyal Duvar - Social Media Interface
|
||||
export interface SocialMediaDto {
|
||||
id?: string
|
||||
type: 'image' | 'video' | 'poll'
|
||||
|
||||
// Ortak alanlar
|
||||
urls?: string[]
|
||||
|
||||
// Anket (poll) ile ilgili alanlar doğrudan burada
|
||||
pollQuestion?: string
|
||||
pollOptions?: SocialPollOptionDto[]
|
||||
pollTotalVotes?: number
|
||||
pollEndsAt?: Date
|
||||
pollUserVoteId?: string
|
||||
}
|
||||
|
||||
// Sosyal Duvar - Ana Interface
|
||||
export interface SocialPostDto {
|
||||
id: string
|
||||
user: UserInfoViewModel
|
||||
content: string
|
||||
locationJson?: string
|
||||
media?: SocialMediaDto
|
||||
likeCount: number
|
||||
isLiked: boolean
|
||||
likeUsers: UserInfoViewModel[]
|
||||
comments: SocialCommentDto[]
|
||||
isOwnPost: boolean
|
||||
creationTime: Date
|
||||
}
|
||||
69
ui/src/proxy/intranet/utils.tsx
Normal file
69
ui/src/proxy/intranet/utils.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import {
|
||||
FaFileAlt,
|
||||
FaFilePdf,
|
||||
FaFileWord,
|
||||
FaFileExcel,
|
||||
FaFilePowerpoint,
|
||||
FaFileImage,
|
||||
FaFileArchive,
|
||||
FaFileCode,
|
||||
} from 'react-icons/fa'
|
||||
|
||||
export const getFileIcon = (extension: string) => {
|
||||
switch (extension.toLowerCase()) {
|
||||
case '.pdf':
|
||||
return <FaFilePdf className="w-4 h-4 text-red-600 dark:text-red-400" />
|
||||
case '.doc':
|
||||
case '.docx':
|
||||
return <FaFileWord className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
||||
case '.xls':
|
||||
case '.xlsx':
|
||||
return <FaFileExcel className="w-4 h-4 text-green-600 dark:text-green-400" />
|
||||
case '.ppt':
|
||||
case '.pptx':
|
||||
return <FaFilePowerpoint className="w-4 h-4 text-orange-600 dark:text-orange-400" />
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
case '.png':
|
||||
case '.gif':
|
||||
return <FaFileImage className="w-4 h-4 text-purple-600 dark:text-purple-400" />
|
||||
case '.zip':
|
||||
case '.rar':
|
||||
return <FaFileArchive className="w-4 h-4 text-yellow-600 dark:text-yellow-400" />
|
||||
case '.txt':
|
||||
return <FaFileCode className="w-4 h-4 text-gray-600 dark:text-gray-400" />
|
||||
default:
|
||||
return <FaFileAlt className="w-4 h-4 text-gray-600 dark:text-gray-400" />
|
||||
}
|
||||
}
|
||||
|
||||
export const getFileType = (extension: string) => {
|
||||
switch (extension.toLowerCase()) {
|
||||
case '.pdf':
|
||||
return '📄 PDF'
|
||||
case '.doc':
|
||||
case '.docx':
|
||||
return '📝 Word'
|
||||
case '.xls':
|
||||
case '.xlsx':
|
||||
return '📊 Excel'
|
||||
case '.ppt':
|
||||
case '.pptx':
|
||||
return '📽️ PowerPoint'
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
return '🖼️ JPEG'
|
||||
case '.png':
|
||||
return '🖼️ PNG'
|
||||
case '.gif':
|
||||
return '🖼️ GIF'
|
||||
case '.zip':
|
||||
return '🗜️ ZIP'
|
||||
case '.rar':
|
||||
return '🗜️ RAR'
|
||||
case '.txt':
|
||||
return '📝 Text'
|
||||
default:
|
||||
return '📄 Dosya'
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +81,9 @@ export const ROUTES_ENUM = {
|
|||
pivot: '/admin/pivot/:listFormCode',
|
||||
},
|
||||
|
||||
participant: {},
|
||||
sqlManager: '/admin/sql-manager',
|
||||
|
||||
accessDenied: '/admin/access-denied',
|
||||
|
||||
coordinator: {
|
||||
classroom: {
|
||||
|
|
@ -237,9 +239,5 @@ export const ROUTES_ENUM = {
|
|||
bank: '/admin/accounting/bank',
|
||||
checkNote: '/admin/accounting/check-note',
|
||||
},
|
||||
|
||||
sqlManager: '/admin/sql-manager',
|
||||
|
||||
accessDenied: '/admin/access-denied',
|
||||
},
|
||||
}
|
||||
|
|
|
|||
17
ui/src/services/intranet.service.ts
Normal file
17
ui/src/services/intranet.service.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { IntranetDashboardDto } from '@/proxy/intranet/models'
|
||||
import apiService, { Config } from './api.service'
|
||||
|
||||
export class IntranetService {
|
||||
apiName = 'Default'
|
||||
|
||||
getDashboard = (config?: Partial<Config>) =>
|
||||
apiService.fetchData<IntranetDashboardDto>(
|
||||
{
|
||||
method: 'GET',
|
||||
url: '/api/app/intranet/intranet-dashboard',
|
||||
},
|
||||
{ apiName: this.apiName, ...config },
|
||||
)
|
||||
}
|
||||
|
||||
export const intranetService = new IntranetService()
|
||||
|
|
@ -52,9 +52,10 @@ const interpolate = (text: string, params: Record<string, string | number>) => {
|
|||
const pattern = new RegExp(`\\{${key}\\}`, 'g')
|
||||
return acc.replace(pattern, String(value))
|
||||
} else {
|
||||
// Named: {{ key }}
|
||||
const pattern = new RegExp(`{{\\s*${key}\\s*}}`, 'g')
|
||||
return acc.replace(pattern, String(value))
|
||||
// Named: {{ key }} or {key}
|
||||
let result = acc.replace(new RegExp(`{{\\s*${key}\\s*}}`, 'g'), String(value))
|
||||
result = result.replace(new RegExp(`(?<!{){${key}}(?!})`, 'g'), String(value))
|
||||
return result
|
||||
}
|
||||
}, text)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const AccessDenied = () => {
|
|||
<p className="text-base">{translate('::AccessDeniedMessage')}</p>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
className="mt-2"
|
||||
variant="default"
|
||||
onClick={() =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { APP_NAME } from '@/constants/app.constant'
|
||||
import IntranetDashboard from './intranet/Dashboard'
|
||||
|
||||
const Dashboard = () => {
|
||||
const { translate } = useLocalization()
|
||||
|
|
@ -12,6 +13,7 @@ const Dashboard = () => {
|
|||
title={translate('::' + 'Dashboard')}
|
||||
defaultTitle={APP_NAME}
|
||||
/>
|
||||
<IntranetDashboard />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ function fallbackRender({ error, resetErrorBoundary }: { error: Error; resetErro
|
|||
<Alert showIcon className="mb-4" type="danger">
|
||||
<h5>{error.name ?? 'Hata!'}</h5>
|
||||
<div>{error.message}</div>
|
||||
<Button size="xs" className="mt-2" variant="default" onClick={resetErrorBoundary}>
|
||||
<Button size="sm" className="mt-2" variant="default" onClick={resetErrorBoundary}>
|
||||
<FaArrowLeft />
|
||||
</Button>
|
||||
</Alert>
|
||||
|
|
|
|||
|
|
@ -746,7 +746,7 @@ const FileManager = () => {
|
|||
<FaBuilding className="text-gray-500 flex-shrink-0" />
|
||||
{isHostContext ? (
|
||||
<Select
|
||||
size="xs"
|
||||
size="sm"
|
||||
isLoading={tenantsLoading}
|
||||
options={[
|
||||
{ value: '', label: 'Host' },
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>((props, ref) => {
|
|||
<div key={item.path} className="flex items-center">
|
||||
{index > 0 && <FaChevronRight className="mx-2 h-4 w-4 text-gray-400" />}
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
onClick={() => onNavigate(item)}
|
||||
className={classNames(
|
||||
'flex items-center px-2 py-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors',
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ const FileUploadModal = forwardRef<HTMLDivElement, FileUploadModalProps>((props,
|
|||
{(file.status === 'pending' || file.status === 'error') && (
|
||||
<Button
|
||||
variant="plain"
|
||||
size="xs"
|
||||
size="sm"
|
||||
icon={<FaTimes />}
|
||||
onClick={() => removeFile(file.id)}
|
||||
disabled={uploading}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ function ChartTabAnnotations(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -118,7 +118,7 @@ function ChartTabAnnotations(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -136,7 +136,7 @@ function ChartTabAnnotations(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={async () => {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ function ChartTabAxis(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaPlus />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -111,7 +111,7 @@ function ChartTabAxis(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -129,7 +129,7 @@ function ChartTabAxis(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={async () => {
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ function ChartTabPanes(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -107,7 +107,7 @@ function ChartTabPanes(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -125,7 +125,7 @@ function ChartTabPanes(props: FormEditProps) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={async () => {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaPlus />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -173,7 +173,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -191,7 +191,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={async () => {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ function FormCustomization({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -89,7 +89,7 @@ function FormCustomization({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -104,7 +104,7 @@ function FormCustomization({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={async (e) => {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ function FormTabCommands() {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -75,7 +75,7 @@ function FormTabCommands() {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -93,7 +93,7 @@ function FormTabCommands() {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={async () => {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ function FormTabDatabaseDelete({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -167,7 +167,7 @@ function FormTabDatabaseDelete({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -187,7 +187,7 @@ function FormTabDatabaseDelete({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ function FormTabDatabaseInsert({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -165,7 +165,7 @@ function FormTabDatabaseInsert({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -185,7 +185,7 @@ function FormTabDatabaseInsert({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
@ -232,7 +232,7 @@ function FormTabDatabaseInsert({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -264,7 +264,7 @@ function FormTabDatabaseInsert({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -283,7 +283,7 @@ function FormTabDatabaseInsert({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ function FormTabDatabaseSelect({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -120,7 +120,7 @@ function FormTabDatabaseSelect({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -140,7 +140,7 @@ function FormTabDatabaseSelect({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ function FormTabDatabaseUpdate({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -167,7 +167,7 @@ function FormTabDatabaseUpdate({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -187,7 +187,7 @@ function FormTabDatabaseUpdate({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ function FormTabEditForm(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -71,7 +71,7 @@ function FormTabEditForm(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -89,7 +89,7 @@ function FormTabEditForm(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ function FormTabExtraFilters(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -71,7 +71,7 @@ function FormTabExtraFilters(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -89,7 +89,7 @@ function FormTabExtraFilters(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ function FormTabSubForm() {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -71,7 +71,7 @@ function FormTabSubForm() {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -89,7 +89,7 @@ function FormTabSubForm() {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ function FormTabWidgets(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -71,7 +71,7 @@ function FormTabWidgets(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={() => {
|
||||
|
|
@ -89,7 +89,7 @@ function FormTabWidgets(props: { listFormCode: string }) {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrash />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ function FormFieldTabConditionalFormatting({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaPlusSquare />}
|
||||
onClick={() => setItemIndex(-1)}
|
||||
|
|
@ -109,7 +109,7 @@ function FormFieldTabConditionalFormatting({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -121,7 +121,7 @@ function FormFieldTabConditionalFormatting({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrashAlt />}
|
||||
onClick={async (e) => {
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ function FormFieldTabValidationRules({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaPlusSquare />}
|
||||
onClick={() => setItemIndex(-1)}
|
||||
|
|
@ -125,7 +125,7 @@ function FormFieldTabValidationRules({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Edit"
|
||||
icon={<FaEdit />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -137,7 +137,7 @@ function FormFieldTabValidationRules({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrashAlt />}
|
||||
onClick={async (e) => {
|
||||
|
|
|
|||
|
|
@ -404,7 +404,7 @@ function FormFields({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileMedical />}
|
||||
onClick={() => {
|
||||
|
|
@ -416,7 +416,7 @@ function FormFields({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Copy"
|
||||
icon={<FaCopy />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -429,7 +429,7 @@ function FormFields({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Tüm Sütunları Ekle"
|
||||
icon={<FaTable />}
|
||||
loading={isAddingAllColumns}
|
||||
|
|
@ -461,7 +461,7 @@ function FormFields({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaMinus />}
|
||||
onClick={async (e) => {
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ function JsonRowOpDialogAxis({
|
|||
shape="circle"
|
||||
type="button"
|
||||
className="mt-7"
|
||||
size="xs"
|
||||
size="sm"
|
||||
icon={<FaMinus />}
|
||||
onClick={() => remove(index)}
|
||||
/>
|
||||
|
|
@ -430,7 +430,7 @@ function JsonRowOpDialogAxis({
|
|||
: null}
|
||||
<div>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
push({
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ function JsonRowOpDialogEditForm({
|
|||
shape="circle"
|
||||
variant="solid"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Options"
|
||||
icon={<FaTag />}
|
||||
onClick={() =>
|
||||
|
|
@ -584,7 +584,7 @@ function JsonRowOpDialogEditForm({
|
|||
shape="circle"
|
||||
variant="solid"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Options"
|
||||
icon={<FaTag />}
|
||||
onClick={() =>
|
||||
|
|
@ -769,7 +769,7 @@ function JsonRowOpDialogEditForm({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaCalendarMinus />}
|
||||
onClick={() => {
|
||||
|
|
@ -780,7 +780,7 @@ function JsonRowOpDialogEditForm({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaCalendarPlus />}
|
||||
onClick={() => {
|
||||
|
|
@ -795,7 +795,7 @@ function JsonRowOpDialogEditForm({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaCalendarPlus />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ function JsonRowOpDialogSubForm({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaCalendarMinus />}
|
||||
onClick={() => {
|
||||
|
|
@ -261,7 +261,7 @@ function JsonRowOpDialogSubForm({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaCalendarPlus />}
|
||||
onClick={() => {
|
||||
|
|
@ -276,7 +276,7 @@ function JsonRowOpDialogSubForm({
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaCalendarPlus />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ const WizardFileManager = () => {
|
|||
<div className="relative">
|
||||
<FaSearch className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400 text-xs pointer-events-none" />
|
||||
<Input
|
||||
size="xs"
|
||||
size="sm"
|
||||
className="pl-6 w-44"
|
||||
placeholder={translate('::App.Platform.Search') || 'Search...'}
|
||||
value={search}
|
||||
|
|
@ -130,7 +130,7 @@ const WizardFileManager = () => {
|
|||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
title={translate('::App.Platform.Refresh') || 'Yenile'}
|
||||
loading={loading}
|
||||
|
|
@ -139,7 +139,7 @@ const WizardFileManager = () => {
|
|||
<FaSync />
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
icon={<FcAcceptDatabase />}
|
||||
onClick={() => setShowDbMigrateDialog(true)}
|
||||
|
|
@ -148,7 +148,7 @@ const WizardFileManager = () => {
|
|||
{translate('::ListForms.ListForm.DbMigrate') || 'DB Migrate'}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="solid"
|
||||
onClick={() => navigate(ROUTES_ENUM.protected.saas.listFormManagement.wizard)}
|
||||
className="flex items-center"
|
||||
|
|
|
|||
|
|
@ -471,7 +471,7 @@ const OrganizationUnits = () => {
|
|||
<div className="flex gap-1">
|
||||
<Button
|
||||
variant="solid"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title={translate('::Abp.Identity.OrganizationUnit.AddUnit')}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
|
|
@ -486,7 +486,7 @@ const OrganizationUnits = () => {
|
|||
|
||||
<Button
|
||||
variant="solid"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title={translate('::Abp.Identity.OrganizationUnit.MoveAllUsers')}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
|
|
@ -576,7 +576,7 @@ const OrganizationUnits = () => {
|
|||
<Button
|
||||
className="mr-auto"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setDeleteRow({
|
||||
id: user.id ?? '',
|
||||
|
|
@ -647,7 +647,7 @@ const OrganizationUnits = () => {
|
|||
<Button
|
||||
className="mr-auto"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setDeleteRow({
|
||||
id: role.id ?? '',
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ const NotificationSettings = () => {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Reset"
|
||||
icon={<FaHistory />}
|
||||
onClick={async (e) => {
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ function UserDetails() {
|
|||
component={Input}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem size="xs" label={translate('::RocketUsername')}>
|
||||
<FormItem size="sm" label={translate('::RocketUsername')}>
|
||||
<Field
|
||||
type="text"
|
||||
name="rocketUsername"
|
||||
|
|
@ -1340,7 +1340,7 @@ function UserDetails() {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Add"
|
||||
icon={<FaFileAlt />}
|
||||
onClick={async (e) => {
|
||||
|
|
@ -1362,7 +1362,7 @@ function UserDetails() {
|
|||
shape="circle"
|
||||
variant="plain"
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title="Delete"
|
||||
icon={<FaTrashAlt />}
|
||||
onClick={() => setConfirmDeleteClaim(claim)}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ function BranchSeed({
|
|||
))}
|
||||
</div>
|
||||
<div>
|
||||
<Button size="xs" variant="solid" onClick={handleRunSeed} loading={isLoading}>
|
||||
<Button size="sm" variant="solid" onClick={handleRunSeed} loading={isLoading}>
|
||||
{isLoading ? 'Seed Çalıştırılıyor...' : 'Seed İşlemini Başlat'}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -908,17 +908,16 @@ GO`,
|
|||
))}
|
||||
</select>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
icon={<FcAcceptDatabase />}
|
||||
onClick={() => setShowDbMigrateConfirmDialog(true)}
|
||||
className="shadow-sm px-2 py-1"
|
||||
title={translate('::App.DbMigrate.StartMessage') || 'Run DB Migration'}
|
||||
>
|
||||
{translate('::ListForms.ListForm.DbMigrate') || 'DB Migrate'}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
icon={<FaFolderOpen />}
|
||||
onClick={handleOpenSqlDataFilesDialog}
|
||||
|
|
@ -931,7 +930,7 @@ GO`,
|
|||
|
||||
<div className="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
icon={<FaCopy />}
|
||||
onClick={handleOpenCopyDialog}
|
||||
|
|
@ -945,7 +944,7 @@ GO`,
|
|||
{translate('::App.Platform.CopySelectedObjects') || 'Copy Selected Objects'}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
icon={<FaFileAlt />}
|
||||
onClick={handleNewQuery}
|
||||
|
|
@ -954,7 +953,7 @@ GO`,
|
|||
{translate('::App.Platform.NewQuery') || 'New Query'}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="solid"
|
||||
color="blue-600"
|
||||
icon={<FaPlay />}
|
||||
|
|
|
|||
|
|
@ -1646,14 +1646,14 @@ const SqlTableDesignerDialog = ({
|
|||
<>
|
||||
<div className="flex items-center justify-between py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button size="xs" variant="solid" color="blue-600" onClick={addFullAuditedColumns}>
|
||||
<Button size="sm" variant="solid" color="blue-600" onClick={addFullAuditedColumns}>
|
||||
{translate('::App.SqlQueryManager.AddFullAuditedColumns')}
|
||||
</Button>
|
||||
<Button size="xs" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
|
||||
<Button size="sm" variant="solid" color="green-600" onClick={addMultiTenantColumns}>
|
||||
{translate('::App.SqlQueryManager.AddMultiTenantColumns')}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="solid"
|
||||
color="amber-600"
|
||||
onClick={importColumnsFromRememberedCreateTable}
|
||||
|
|
@ -1664,7 +1664,7 @@ const SqlTableDesignerDialog = ({
|
|||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="solid"
|
||||
color="red-600"
|
||||
icon={<FaTrash />}
|
||||
|
|
@ -1673,7 +1673,7 @@ const SqlTableDesignerDialog = ({
|
|||
{translate('::App.SqlQueryManager.ClearAllColumns')}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="solid"
|
||||
color="blue-600"
|
||||
icon={<FaPlus />}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ const FormButtons = (props: {
|
|||
<Button
|
||||
key={'toolbarButton-' + i}
|
||||
variant="default"
|
||||
size="xs"
|
||||
size="sm"
|
||||
icon={hasValidIcon ? <IconComp className="text-gray-400" /> : null}
|
||||
onClick={item.options?.onClick}
|
||||
>
|
||||
|
|
@ -161,7 +161,7 @@ const FormButtons = (props: {
|
|||
<Button
|
||||
key={'commandColumnButton-' + i}
|
||||
variant="default"
|
||||
size="xs"
|
||||
size="sm"
|
||||
title={item.hint}
|
||||
onClick={(e: any) => {
|
||||
if (item.onClick) {
|
||||
|
|
@ -180,7 +180,7 @@ const FormButtons = (props: {
|
|||
|
||||
{!isSubForm && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
className="dx-button dx-button-mode-contained dx-button-normal"
|
||||
icon={<FaChevronLeft />}
|
||||
|
|
@ -197,7 +197,7 @@ const FormButtons = (props: {
|
|||
)}
|
||||
{mode != 'new' && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
className="dx-button dx-button-mode-contained dx-button-normal"
|
||||
icon={<FaTrashAlt />}
|
||||
|
|
@ -211,7 +211,7 @@ const FormButtons = (props: {
|
|||
)}
|
||||
{mode != 'new' && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
className="dx-button dx-button-mode-contained dx-button-normal"
|
||||
icon={<FaPlusCircle />}
|
||||
|
|
@ -228,7 +228,7 @@ const FormButtons = (props: {
|
|||
)}
|
||||
{mode == 'view' && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
className="dx-button dx-button-mode-contained dx-button-normal"
|
||||
icon={<FaPencilAlt />}
|
||||
|
|
@ -249,7 +249,7 @@ const FormButtons = (props: {
|
|||
)}
|
||||
{(mode == 'edit' || (onActionView && mode == 'new')) && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
className="dx-button dx-button-mode-contained dx-button-normal"
|
||||
icon={<FaEye />}
|
||||
|
|
@ -270,7 +270,7 @@ const FormButtons = (props: {
|
|||
)}
|
||||
{(mode == 'edit' || mode == 'new') && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
className="dx-button dx-button-mode-contained dx-button-normal"
|
||||
icon={<FaSave />}
|
||||
|
|
@ -281,7 +281,7 @@ const FormButtons = (props: {
|
|||
)}
|
||||
{checkPermission(gridDto?.gridOptions.permissionDto.c) && !isSubForm && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
className="dx-button dx-button-mode-contained dx-button-normal"
|
||||
icon={<FaCog />}
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
|||
<div className="flex items-center justify-end mb-2">
|
||||
<Button
|
||||
variant="solid"
|
||||
size="xs"
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={onAddNote}
|
||||
disabled={!onAddNote}
|
||||
|
|
@ -400,7 +400,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
|||
{user?.id === note.creatorId && (
|
||||
<Button
|
||||
variant="plain"
|
||||
size="xs"
|
||||
size="sm"
|
||||
onClick={() => onDeleteNote?.(note.id as string)}
|
||||
title={translate('::Delete')}
|
||||
className="text-red-400 hover:text-red-600"
|
||||
|
|
@ -430,7 +430,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
|||
<span className="truncate max-w-[150px]">{file.FileName}</span>
|
||||
<Button
|
||||
variant="plain"
|
||||
size="xs"
|
||||
size="sm"
|
||||
onClick={() => onDownloadFile?.(file)}
|
||||
title={translate('::Download')}
|
||||
className="text-blue-500 hover:text-blue-700 ml-1"
|
||||
|
|
@ -452,7 +452,7 @@ export const NoteList: React.FC<NoteListProps> = ({
|
|||
<TabContent value="audit">
|
||||
<div className="flex items-center justify-end mb-2">
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
type="button"
|
||||
onClick={loadAuditLogs}
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ export const NoteModal: React.FC<NoteModalProps> = ({
|
|||
</div>
|
||||
<Button
|
||||
variant="plain"
|
||||
size="xs"
|
||||
size="sm"
|
||||
type="button"
|
||||
onClick={() => removeFile(index)}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ export const NotePanel: React.FC<NotePanelProps> = ({
|
|||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Button variant="plain" size="xs" onClick={onToggle} title="Paneli kapat">
|
||||
<Button variant="plain" size="sm" onClick={onToggle} title="Paneli kapat">
|
||||
<FaTimes />
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
625
ui/src/views/intranet/Dashboard.tsx
Normal file
625
ui/src/views/intranet/Dashboard.tsx
Normal file
|
|
@ -0,0 +1,625 @@
|
|||
import React, { useState, useEffect } from 'react'
|
||||
import { AnimatePresence } from 'framer-motion'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import isBetween from 'dayjs/plugin/isBetween'
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
||||
|
||||
// Widgets
|
||||
import TodayBirthdays from './widgets/TodayBirthdays'
|
||||
import RecentDocuments from './widgets/RecentDocuments'
|
||||
import Surveys from './widgets/Surveys'
|
||||
|
||||
// Modals
|
||||
import SurveyModal from './widgets/SurveyModal'
|
||||
import AnnouncementModal from './widgets/AnnouncementModal'
|
||||
|
||||
// Social Wall
|
||||
import SocialWall from './SocialWall'
|
||||
import { Container } from '@/components/shared'
|
||||
import { usePermission } from '@/utils/hooks/usePermission'
|
||||
import {
|
||||
AnnouncementDto,
|
||||
IntranetDashboardDto,
|
||||
SurveyAnswerDto,
|
||||
SurveyDto,
|
||||
} from '@/proxy/intranet/models'
|
||||
import { intranetService } from '@/services/intranet.service'
|
||||
import Announcements from './widgets/Announcements'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import useLocale from '@/utils/hooks/useLocale'
|
||||
import { currentLocalDate } from '@/utils/dateUtils'
|
||||
import { useStoreState } from '@/store/store'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
dayjs.extend(isBetween)
|
||||
dayjs.extend(localizedFormat)
|
||||
|
||||
const WIDGET_ORDER_KEY = 'dashboard-widget-order'
|
||||
|
||||
const IntranetDashboard: React.FC = () => {
|
||||
const { checkPermission } = usePermission()
|
||||
const [selectedAnnouncement, setSelectedAnnouncement] = useState<AnnouncementDto | null>(null)
|
||||
const [selectedSurvey, setSelectedSurvey] = useState<SurveyDto | null>(null)
|
||||
const [showSurveyModal, setShowSurveyModal] = useState(false)
|
||||
const [isDesignMode, setIsDesignMode] = useState(false)
|
||||
const [widgetOrder, setWidgetOrder] = useState<Record<string, string[]>>({
|
||||
left: [],
|
||||
center: [],
|
||||
right: [],
|
||||
})
|
||||
|
||||
const [intranetDashboard, setIntranetDashboard] = useState<IntranetDashboardDto>()
|
||||
const { translate } = useLocalization()
|
||||
const currentLocale = useLocale()
|
||||
|
||||
const fetchIntranetDashboard = async () => {
|
||||
const dashboard = await intranetService.getDashboard()
|
||||
if (dashboard.data) {
|
||||
setIntranetDashboard(dashboard.data)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchIntranetDashboard()
|
||||
}, [])
|
||||
|
||||
// Drag state'leri birleştirildi
|
||||
const [dragState, setDragState] = useState<{
|
||||
draggedId: string | null
|
||||
targetColumn: string | null
|
||||
targetIndex: number | null
|
||||
}>({
|
||||
draggedId: null,
|
||||
targetColumn: null,
|
||||
targetIndex: null,
|
||||
})
|
||||
|
||||
const handleTakeSurvey = (survey: SurveyDto) => {
|
||||
setSelectedSurvey(survey)
|
||||
setShowSurveyModal(true)
|
||||
}
|
||||
|
||||
const handleSubmitSurvey = (answers: SurveyAnswerDto[]) => {
|
||||
setShowSurveyModal(false)
|
||||
setSelectedSurvey(null)
|
||||
}
|
||||
|
||||
// Widget metadata (component'lar yerine sadece meta bilgiler)
|
||||
const widgetMetadata = [
|
||||
{ id: 'today-birthdays', permission: 'AbpIdentity.Users.Widget', column: 'left' },
|
||||
{ id: 'documents', permission: 'App.Files.Widget', column: 'left' },
|
||||
{ id: 'active-surveys', permission: 'App.Intranet.Survey.Widget', column: 'left' },
|
||||
{ id: 'social-wall', permission: 'App.Intranet.SocialPost.Widget', column: 'center' },
|
||||
{
|
||||
id: 'announcements',
|
||||
permission: 'App.Intranet.Announcement.Widget',
|
||||
column: 'right',
|
||||
},
|
||||
]
|
||||
|
||||
// If permissions arrive after mount, initialize default order when needed
|
||||
const grantedPolicies = useStoreState((state) => state.abpConfig?.config?.auth.grantedPolicies)
|
||||
|
||||
const initializeDefaultOrder = () => {
|
||||
const defaultOrder = {
|
||||
left: widgetMetadata
|
||||
.filter((w) => w.column === 'left' && checkPermission(w.permission))
|
||||
.map((w) => w.id),
|
||||
center: widgetMetadata
|
||||
.filter((w) => w.column === 'center' && checkPermission(w.permission))
|
||||
.map((w) => w.id),
|
||||
right: widgetMetadata
|
||||
.filter((w) => w.column === 'right' && checkPermission(w.permission))
|
||||
.map((w) => w.id),
|
||||
}
|
||||
setWidgetOrder(defaultOrder)
|
||||
}
|
||||
|
||||
// Widget sıralamasını yükle — grantedPolicies gelince çalış ki checkPermission doğru çalışsın
|
||||
useEffect(() => {
|
||||
if (!grantedPolicies) return
|
||||
|
||||
const savedOrder = localStorage.getItem(WIDGET_ORDER_KEY)
|
||||
if (savedOrder) {
|
||||
try {
|
||||
const parsed = JSON.parse(savedOrder) as Record<string, unknown[]>
|
||||
const order: Record<string, string[]> = {
|
||||
left: [...new Set((parsed.left || []) as string[])],
|
||||
center: [...new Set((parsed.center || []) as string[])],
|
||||
right: [...new Set((parsed.right || []) as string[])],
|
||||
}
|
||||
|
||||
// Kaydedilmiş düzende olmayan ama izni olan widgetları varsayılan kolonuna ekle
|
||||
const allAssigned = new Set([...order.left, ...order.center, ...order.right])
|
||||
widgetMetadata.forEach((w) => {
|
||||
if (!allAssigned.has(w.id) && checkPermission(w.permission)) {
|
||||
order[w.column as keyof typeof order].push(w.id)
|
||||
}
|
||||
})
|
||||
|
||||
setWidgetOrder(order)
|
||||
} catch (error) {
|
||||
console.error('Widget order parse error:', error)
|
||||
initializeDefaultOrder()
|
||||
}
|
||||
} else {
|
||||
initializeDefaultOrder()
|
||||
}
|
||||
}, [grantedPolicies])
|
||||
|
||||
// Widget sıralamasını kaydet
|
||||
const saveWidgetOrder = (newOrder: Record<string, string[]>) => {
|
||||
setWidgetOrder(newOrder)
|
||||
localStorage.setItem(WIDGET_ORDER_KEY, JSON.stringify(newOrder))
|
||||
}
|
||||
|
||||
const handleDragStart = (e: React.DragEvent, widgetId: string, column: string) => {
|
||||
setDragState({ draggedId: widgetId, targetColumn: null, targetIndex: null })
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
e.dataTransfer.setData('widgetId', widgetId)
|
||||
e.dataTransfer.setData('sourceColumn', column)
|
||||
}
|
||||
|
||||
const handleDragEnterWidget = (e: React.DragEvent, column: string, index: number) => {
|
||||
// Sadece widget'ın üst kısmına yakınsa indicator göster
|
||||
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
|
||||
const mouseY = e.clientY
|
||||
const widgetTop = rect.top
|
||||
const widgetHeight = rect.height
|
||||
const threshold = widgetHeight * 0.3 // Üst %30'luk alan
|
||||
|
||||
if (mouseY - widgetTop < threshold) {
|
||||
// Üst kısma yakın - indicator göster
|
||||
setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: index }))
|
||||
} else {
|
||||
// Widget'ın ortasında veya altında - indicator gösterme
|
||||
setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: null }))
|
||||
}
|
||||
}
|
||||
|
||||
const handleDragEnterColumn = (column: string) => {
|
||||
setDragState((prev) => ({ ...prev, targetColumn: column, targetIndex: null }))
|
||||
}
|
||||
|
||||
const handleDragLeaveColumn = () => {
|
||||
setDragState((prev) => ({ ...prev, targetColumn: null, targetIndex: null }))
|
||||
}
|
||||
|
||||
const handleDrop = (e: React.DragEvent, targetColumn: string, targetIndex?: number) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
const widgetId = e.dataTransfer.getData('widgetId')
|
||||
const sourceColumn = e.dataTransfer.getData('sourceColumn')
|
||||
|
||||
if (!widgetId || !sourceColumn) return
|
||||
|
||||
const newOrder = { ...widgetOrder }
|
||||
|
||||
// ÖNCE tüm kolonlardan bu widget'ı kaldır (duplicate önleme)
|
||||
Object.keys(newOrder).forEach((col) => {
|
||||
newOrder[col] = newOrder[col].filter((id) => id !== widgetId)
|
||||
})
|
||||
|
||||
// SONRA hedef kolona ekle
|
||||
if (targetIndex !== undefined) {
|
||||
newOrder[targetColumn].splice(targetIndex, 0, widgetId)
|
||||
} else {
|
||||
newOrder[targetColumn].push(widgetId)
|
||||
}
|
||||
|
||||
// Duplicate'leri temizle
|
||||
Object.keys(newOrder).forEach((col) => {
|
||||
newOrder[col] = [...new Set(newOrder[col])]
|
||||
})
|
||||
|
||||
saveWidgetOrder(newOrder)
|
||||
setDragState({ draggedId: null, targetColumn: null, targetIndex: null })
|
||||
}
|
||||
|
||||
const handleDragEnd = () => {
|
||||
setDragState({ draggedId: null, targetColumn: null, targetIndex: null })
|
||||
}
|
||||
|
||||
// Widget component'ını render et
|
||||
const renderWidgetComponent = (widgetId: string) => {
|
||||
switch (widgetId) {
|
||||
case 'today-birthdays':
|
||||
return <TodayBirthdays employees={intranetDashboard?.birthdays || []} />
|
||||
case 'documents':
|
||||
return <RecentDocuments documents={intranetDashboard?.documents || []} />
|
||||
case 'announcements':
|
||||
return (
|
||||
<Announcements
|
||||
announcements={intranetDashboard?.announcements || []}
|
||||
onAnnouncementClick={setSelectedAnnouncement}
|
||||
/>
|
||||
)
|
||||
case 'active-surveys':
|
||||
return (
|
||||
<Surveys
|
||||
surveys={intranetDashboard?.surveys || []}
|
||||
onTakeSurvey={handleTakeSurvey}
|
||||
/>
|
||||
)
|
||||
case 'social-wall':
|
||||
return <SocialWall posts={intranetDashboard?.socialPosts || []} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Widget'ları render et
|
||||
const renderWidgets = (column: 'left' | 'center' | 'right') => {
|
||||
const columnWidgets = widgetOrder[column] || []
|
||||
|
||||
// Duplicate'leri filtrele
|
||||
const uniqueWidgets = [...new Set(columnWidgets)]
|
||||
|
||||
return uniqueWidgets
|
||||
.map((widgetId, index) => {
|
||||
const metadata = widgetMetadata.find((w) => w.id === widgetId)
|
||||
if (!metadata || !checkPermission(metadata.permission)) return null
|
||||
|
||||
const isDragging = dragState.draggedId === widgetId
|
||||
const isDropTarget = dragState.targetColumn === column && dragState.targetIndex === index
|
||||
|
||||
return (
|
||||
<div key={`${column}-${widgetId}-${index}`} className="relative group">
|
||||
{/* Drop indicator - SADECE widget'ların arasına (üst %30'luk alana) gelince göster */}
|
||||
{isDesignMode && isDropTarget && !isDragging && (
|
||||
<div className="absolute -top-5 left-0 right-0 z-20 animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
{/* Çizgi */}
|
||||
<div className="h-2 bg-gradient-to-r from-transparent via-blue-500 to-transparent rounded-full shadow-lg" />
|
||||
{/* Badge */}
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-gradient-to-r from-blue-500 to-blue-600 text-white text-xs px-4 py-2 rounded-full whitespace-nowrap shadow-xl font-semibold flex items-center gap-2 border-2 border-white dark:border-gray-800">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 14l-7 7m0 0l-7-7m7 7V3"
|
||||
/>
|
||||
</svg>
|
||||
<span>Buraya Bırak</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
draggable={isDesignMode}
|
||||
onDragStart={(e) => {
|
||||
if (isDesignMode) {
|
||||
handleDragStart(e, widgetId, column)
|
||||
// Drag ghost image'i gizle
|
||||
const ghost = document.createElement('div')
|
||||
ghost.style.opacity = '0'
|
||||
e.dataTransfer.setDragImage(ghost, 0, 0)
|
||||
}
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
if (!isDesignMode) return
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
// Throttle: Sadece düzenli aralıklarla güncelle
|
||||
const now = Date.now()
|
||||
if (
|
||||
!e.currentTarget.dataset.lastUpdate ||
|
||||
now - parseInt(e.currentTarget.dataset.lastUpdate) > 150
|
||||
) {
|
||||
e.currentTarget.dataset.lastUpdate = now.toString()
|
||||
handleDragEnterWidget(e, column, index)
|
||||
}
|
||||
}}
|
||||
onDragLeave={(e) => {
|
||||
// Widget'tan çıkınca indicator'ı kaldır
|
||||
if (isDesignMode) {
|
||||
setDragState((prev) => ({
|
||||
...prev,
|
||||
targetColumn: prev.targetColumn,
|
||||
targetIndex: null,
|
||||
}))
|
||||
}
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
if (!isDesignMode) return
|
||||
e.stopPropagation()
|
||||
|
||||
// Drop pozisyonunu hesapla
|
||||
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
|
||||
const mouseY = e.clientY
|
||||
const widgetTop = rect.top
|
||||
const widgetHeight = rect.height
|
||||
const threshold = widgetHeight * 0.3
|
||||
|
||||
if (mouseY - widgetTop < threshold) {
|
||||
// Üst kısma bırak - mevcut index'e ekle
|
||||
handleDrop(e, column, index)
|
||||
} else {
|
||||
// Alt kısma bırak - sonraki index'e ekle
|
||||
handleDrop(e, column, index + 1)
|
||||
}
|
||||
}}
|
||||
onDragEnd={handleDragEnd}
|
||||
className={`
|
||||
relative
|
||||
${
|
||||
isDesignMode
|
||||
? `border-2 border-dashed rounded-lg cursor-move
|
||||
${
|
||||
isDragging
|
||||
? 'border-blue-400 opacity-70 bg-blue-50/30 dark:bg-blue-900/10'
|
||||
: 'border-gray-300 dark:border-gray-600 hover:border-blue-400 dark:hover:border-blue-500 hover:shadow-md'
|
||||
}
|
||||
transition-all duration-300 ease-out`
|
||||
: 'border-0 transition-none'
|
||||
}
|
||||
`}
|
||||
style={{
|
||||
touchAction: 'none',
|
||||
transition:
|
||||
'border-color 0.3s ease-out, opacity 0.3s ease-out, box-shadow 0.3s ease-out',
|
||||
willChange: isDragging ? 'opacity' : 'auto',
|
||||
}}
|
||||
>
|
||||
{/* Dragging overlay - daha minimal */}
|
||||
{isDesignMode && isDragging && (
|
||||
<div className="absolute inset-0 bg-white/60 dark:bg-gray-900/40 rounded-lg z-10 flex items-center justify-center backdrop-blur-[1px] transition-opacity duration-300">
|
||||
<div className="bg-gradient-to-r from-blue-500 to-blue-600 text-white px-4 py-2 rounded-lg font-medium shadow-lg flex items-center gap-2">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"
|
||||
/>
|
||||
</svg>
|
||||
<span>Taşınıyor</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`${isDesignMode ? 'p-1.5' : ''} transition-all duration-500 ease-out`}
|
||||
>
|
||||
{renderWidgetComponent(widgetId)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<div className="mx-auto space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Design Mode Toggle */}
|
||||
<div className="flex items-center gap-2">
|
||||
<label
|
||||
htmlFor="design-mode-toggle"
|
||||
className="text-sm font-medium text-gray-700 dark:text-gray-300 cursor-pointer"
|
||||
>
|
||||
🎨 {translate('::App.Platform.Intranet.Dashboard.DesignMode')}
|
||||
</label>
|
||||
<button
|
||||
id="design-mode-toggle"
|
||||
onClick={() => setIsDesignMode(!isDesignMode)}
|
||||
className={`
|
||||
relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
|
||||
${isDesignMode ? 'bg-blue-600' : 'bg-gray-300 dark:bg-gray-600'}
|
||||
`}
|
||||
role="switch"
|
||||
aria-checked={isDesignMode}
|
||||
>
|
||||
<span
|
||||
className={`
|
||||
inline-block h-4 w-4 transform rounded-full bg-white transition-transform
|
||||
${isDesignMode ? 'translate-x-6' : 'translate-x-1'}
|
||||
`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Reset Button - Sadece design mode aktifken görünsün */}
|
||||
{isDesignMode && (
|
||||
<button
|
||||
onClick={() => {
|
||||
localStorage.removeItem(WIDGET_ORDER_KEY)
|
||||
initializeDefaultOrder()
|
||||
}}
|
||||
className="text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors flex items-center gap-1"
|
||||
title="Widget düzenini varsayılana döndür"
|
||||
>
|
||||
🔄 {translate('::App.Platform.Intranet.Dashboard.Reset')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||||
<span className="font-medium">{translate('::AI.Welcome')},</span>{' '}
|
||||
{currentLocalDate(new Date(), currentLocale || 'tr')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12">
|
||||
<div
|
||||
className={`lg:col-span-3 space-y-6 min-h-[100px] rounded-xl p-1
|
||||
${
|
||||
isDesignMode && dragState.targetColumn === 'left' && dragState.targetIndex === null
|
||||
? 'bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-blue-300 dark:ring-blue-600 shadow-lg'
|
||||
: 'bg-transparent'
|
||||
}
|
||||
transition-all duration-700 ease-out
|
||||
`}
|
||||
onDragOver={(e) => {
|
||||
if (!isDesignMode) return
|
||||
e.preventDefault()
|
||||
// Throttle: Sadece her 150ms'de bir güncelle
|
||||
const now = Date.now()
|
||||
const target = e.currentTarget as HTMLElement
|
||||
if (
|
||||
!target.dataset.lastColumnUpdate ||
|
||||
now - parseInt(target.dataset.lastColumnUpdate) > 150
|
||||
) {
|
||||
target.dataset.lastColumnUpdate = now.toString()
|
||||
handleDragEnterColumn('left')
|
||||
}
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
if (isDesignMode) handleDragLeaveColumn()
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
if (!isDesignMode) return
|
||||
e.stopPropagation()
|
||||
const columnWidgets = widgetOrder['left'] || []
|
||||
handleDrop(e, 'left', columnWidgets.length)
|
||||
}}
|
||||
>
|
||||
{renderWidgets('left')}
|
||||
{isDesignMode &&
|
||||
dragState.targetColumn === 'left' &&
|
||||
widgetOrder['left']?.length === 0 && (
|
||||
<div className="flex items-center justify-center h-40 border-2 border-dashed border-blue-300 dark:border-blue-600 rounded-xl bg-blue-50/50 dark:bg-blue-900/10 transition-all duration-500 ease-out">
|
||||
<p className="text-blue-600 dark:text-blue-400 font-medium flex items-center gap-2">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
|
||||
/>
|
||||
</svg>
|
||||
<span>Widget'ı buraya bırakın</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`lg:col-span-6 space-y-6 min-h-[100px] rounded-xl p-1
|
||||
${
|
||||
isDesignMode &&
|
||||
dragState.targetColumn === 'center' &&
|
||||
dragState.targetIndex === null
|
||||
? 'bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-blue-300 dark:ring-blue-600 shadow-lg'
|
||||
: 'bg-transparent'
|
||||
}
|
||||
transition-all duration-700 ease-out
|
||||
`}
|
||||
onDragOver={(e) => {
|
||||
if (!isDesignMode) return
|
||||
e.preventDefault()
|
||||
const now = Date.now()
|
||||
const target = e.currentTarget as HTMLElement
|
||||
if (
|
||||
!target.dataset.lastColumnUpdate ||
|
||||
now - parseInt(target.dataset.lastColumnUpdate) > 150
|
||||
) {
|
||||
target.dataset.lastColumnUpdate = now.toString()
|
||||
handleDragEnterColumn('center')
|
||||
}
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
if (isDesignMode) handleDragLeaveColumn()
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
if (!isDesignMode) return
|
||||
e.stopPropagation()
|
||||
const columnWidgets = widgetOrder['center'] || []
|
||||
handleDrop(e, 'center', columnWidgets.length)
|
||||
}}
|
||||
>
|
||||
{renderWidgets('center')}
|
||||
{isDesignMode &&
|
||||
dragState.targetColumn === 'center' &&
|
||||
widgetOrder['center']?.length === 0 && (
|
||||
<div className="flex items-center justify-center rounded-xl bg-blue-50/50 dark:bg-blue-900/10 transition-all duration-500 ease-out">
|
||||
<p className="text-blue-600 dark:text-blue-400 font-medium flex items-center gap-2">
|
||||
<span>Widget'ı buraya bırakın</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`lg:col-span-3 space-y-6 min-h-[100px] rounded-xl p-1
|
||||
${
|
||||
isDesignMode && dragState.targetColumn === 'right' && dragState.targetIndex === null
|
||||
? 'bg-blue-50/80 dark:bg-blue-900/20 ring-2 ring-blue-300 dark:ring-blue-600 shadow-lg'
|
||||
: 'bg-transparent'
|
||||
}
|
||||
transition-all duration-700 ease-out
|
||||
`}
|
||||
onDragOver={(e) => {
|
||||
if (!isDesignMode) return
|
||||
e.preventDefault()
|
||||
const now = Date.now()
|
||||
const target = e.currentTarget as HTMLElement
|
||||
if (
|
||||
!target.dataset.lastColumnUpdate ||
|
||||
now - parseInt(target.dataset.lastColumnUpdate) > 150
|
||||
) {
|
||||
target.dataset.lastColumnUpdate = now.toString()
|
||||
handleDragEnterColumn('right')
|
||||
}
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
if (isDesignMode) handleDragLeaveColumn()
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
if (!isDesignMode) return
|
||||
e.stopPropagation()
|
||||
const columnWidgets = widgetOrder['right'] || []
|
||||
handleDrop(e, 'right', columnWidgets.length)
|
||||
}}
|
||||
>
|
||||
{renderWidgets('right')}
|
||||
{isDesignMode &&
|
||||
dragState.targetColumn === 'right' &&
|
||||
widgetOrder['right']?.length === 0 && (
|
||||
<div className="flex items-center justify-center h-40 border-2 border-dashed border-blue-300 dark:border-blue-600 rounded-xl bg-blue-50/50 dark:bg-blue-900/10 transition-all duration-500 ease-out">
|
||||
<p className="text-blue-600 dark:text-blue-400 font-medium flex items-center gap-2">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
|
||||
/>
|
||||
</svg>
|
||||
<span>Widget'ı buraya bırakın</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{showSurveyModal && selectedSurvey && (
|
||||
<SurveyModal
|
||||
survey={selectedSurvey}
|
||||
onClose={() => setShowSurveyModal(false)}
|
||||
onSubmit={handleSubmitSurvey}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<AnimatePresence>
|
||||
{selectedAnnouncement && (
|
||||
<AnnouncementModal
|
||||
announcement={selectedAnnouncement}
|
||||
onClose={() => setSelectedAnnouncement(null)}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default IntranetDashboard
|
||||
466
ui/src/views/intranet/SocialWall/CreatePost.tsx
Normal file
466
ui/src/views/intranet/SocialWall/CreatePost.tsx
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
import React, { useState, useRef } from 'react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import classNames from 'classnames'
|
||||
import EmojiPicker, { EmojiClickData } from 'emoji-picker-react'
|
||||
import {
|
||||
FaChartBar,
|
||||
FaSmile,
|
||||
FaTimes,
|
||||
FaImages,
|
||||
FaMapMarkerAlt
|
||||
} from 'react-icons/fa'
|
||||
import MediaManager from './MediaManager'
|
||||
import LocationPicker from './LocationPicker'
|
||||
import { SocialMediaDto } from '@/proxy/intranet/models'
|
||||
|
||||
interface CreatePostProps {
|
||||
onCreatePost: (post: {
|
||||
content: string
|
||||
location?: string
|
||||
media?: {
|
||||
type: 'mixed' | 'poll'
|
||||
mediaItems?: SocialMediaDto[]
|
||||
poll?: {
|
||||
question: string
|
||||
options: Array<{ text: string }>
|
||||
}
|
||||
}
|
||||
}) => void
|
||||
}
|
||||
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { useStoreState } from '@/store/store'
|
||||
import { Avatar } from '@/components/ui'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
const CreatePost: React.FC<CreatePostProps> = ({ onCreatePost }) => {
|
||||
const { translate } = useLocalization();
|
||||
const [content, setContent] = useState('')
|
||||
const [mediaType, setMediaType] = useState<'media' | 'poll' | null>(null)
|
||||
const [mediaItems, setMediaItems] = useState<SocialMediaDto[]>([])
|
||||
const [location, setLocation] = useState<string | null>(null)
|
||||
const [pollQuestion, setPollQuestion] = useState('')
|
||||
const [pollOptions, setPollOptions] = useState(['', ''])
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
||||
const [showMediaManager, setShowMediaManager] = useState(false)
|
||||
const [showLocationPicker, setShowLocationPicker] = useState(false)
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||
const emojiPickerRef = useRef<HTMLDivElement>(null)
|
||||
const { user, tenant } = useStoreState((state) => state.auth)
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (!content.trim() && mediaItems.length === 0 && !mediaType) return
|
||||
|
||||
let media = undefined
|
||||
|
||||
if (mediaType === 'media' && mediaItems.length > 0) {
|
||||
media = {
|
||||
type: 'mixed' as const,
|
||||
mediaItems
|
||||
}
|
||||
} else if (mediaType === 'poll' && pollQuestion && pollOptions.filter(o => o.trim()).length >= 2) {
|
||||
media = {
|
||||
type: 'poll' as const,
|
||||
poll: {
|
||||
question: pollQuestion,
|
||||
options: pollOptions.filter(o => o.trim()).map(text => ({ text }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCreatePost({
|
||||
content,
|
||||
media,
|
||||
location: location || undefined
|
||||
})
|
||||
|
||||
// Reset form
|
||||
setContent('')
|
||||
setMediaType(null)
|
||||
setMediaItems([])
|
||||
setLocation(null)
|
||||
setPollQuestion('')
|
||||
setPollOptions(['', ''])
|
||||
setIsExpanded(false)
|
||||
setShowEmojiPicker(false)
|
||||
}
|
||||
|
||||
const handleEmojiClick = (emojiData: EmojiClickData) => {
|
||||
const emoji = emojiData.emoji
|
||||
const textarea = textareaRef.current
|
||||
if (!textarea) return
|
||||
|
||||
const start = textarea.selectionStart
|
||||
const end = textarea.selectionEnd
|
||||
const text = content
|
||||
const before = text.substring(0, start)
|
||||
const after = text.substring(end)
|
||||
|
||||
setContent(before + emoji + after)
|
||||
|
||||
// Set cursor position after emoji
|
||||
setTimeout(() => {
|
||||
textarea.selectionStart = textarea.selectionEnd = start + emoji.length
|
||||
textarea.focus()
|
||||
}, 0)
|
||||
}
|
||||
|
||||
const addPollOption = () => {
|
||||
if (pollOptions.length < 6) {
|
||||
setPollOptions([...pollOptions, ''])
|
||||
}
|
||||
}
|
||||
|
||||
const removePollOption = (index: number) => {
|
||||
if (pollOptions.length > 2) {
|
||||
setPollOptions(pollOptions.filter((_, i) => i !== index))
|
||||
}
|
||||
}
|
||||
|
||||
const updatePollOption = (index: number, value: string) => {
|
||||
const newOptions = [...pollOptions]
|
||||
newOptions[index] = value
|
||||
setPollOptions(newOptions)
|
||||
}
|
||||
|
||||
const clearMedia = () => {
|
||||
setMediaType(null)
|
||||
setMediaItems([])
|
||||
setPollQuestion('')
|
||||
setPollOptions(['', ''])
|
||||
}
|
||||
|
||||
const removeMediaItem = (id: string | undefined) => {
|
||||
if (!id) return
|
||||
setMediaItems(mediaItems.filter((m) => m.id !== id))
|
||||
}
|
||||
|
||||
// Close emoji picker when clicking outside
|
||||
React.useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (emojiPickerRef.current && !emojiPickerRef.current.contains(event.target as Node)) {
|
||||
setShowEmojiPicker(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (showEmojiPicker) {
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
}, [showEmojiPicker])
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 mb-6">
|
||||
<form onSubmit={handleSubmit}>
|
||||
{/* Text Input */}
|
||||
<div className="flex gap-3 mb-4">
|
||||
<Avatar size={32} shape="circle" src={AVATAR_URL(user.id, tenant.tenantId)} />
|
||||
<div className="flex-1">
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
onFocus={() => setIsExpanded(true)}
|
||||
placeholder={translate('::App.Platform.Intranet.SocialWall.CreatePost.Placeholder')}
|
||||
className={classNames(
|
||||
'w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none transition-all',
|
||||
isExpanded ? 'min-h-[120px]' : 'min-h-[48px]'
|
||||
)}
|
||||
rows={isExpanded ? 4 : 1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Media Preview */}
|
||||
<AnimatePresence>
|
||||
{mediaType === 'media' && mediaItems.length > 0 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
className="mb-4"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{translate('::App.Platform.Intranet.SocialWall.CreatePost.MediaTitle')} ({mediaItems.length})
|
||||
</h4>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
clearMedia()
|
||||
}}
|
||||
className="text-sm text-red-600 hover:text-red-700 font-medium"
|
||||
title={translate('::App.Platform.Intranet.SocialWall.CreatePost.RemoveAllMediaTitle')}
|
||||
>
|
||||
{translate('::Cancel')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
{mediaItems.map((item) => (
|
||||
<div key={item.id} className="relative group">
|
||||
{item.type === 'image' ? (
|
||||
<img
|
||||
src={item.urls?.[0]}
|
||||
alt="Preview"
|
||||
className="w-full h-24 object-cover rounded-lg"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-24 bg-gray-900 rounded-lg relative">
|
||||
<video src={item.urls?.[0]} className="w-full h-full object-cover rounded-lg" />
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="w-10 h-10 bg-black bg-opacity-50 rounded-full flex items-center justify-center">
|
||||
<div className="w-0 h-0 border-t-8 border-t-transparent border-l-12 border-l-white border-b-8 border-b-transparent ml-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeMediaItem(item.id)}
|
||||
className="absolute -top-2 -right-2 p-1 bg-red-600 text-white rounded-full opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<FaTimes className="w-4 h-4" />
|
||||
</button>
|
||||
<div className="absolute bottom-1 left-1 px-2 py-0.5 bg-black bg-opacity-70 text-white text-xs rounded">
|
||||
{item.type === 'image' ? '📷' : '🎥'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowMediaManager(true)}
|
||||
className="h-24 border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg flex items-center justify-center hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors"
|
||||
>
|
||||
<FaImages className="w-6 h-6 text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{location && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
className="mb-4"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">{translate('::App.Platform.Intranet.SocialWall.CreatePost.Location')}</h4>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLocation(null)}
|
||||
className="text-sm text-red-600 hover:text-red-700 font-medium"
|
||||
title={translate('::App.Platform.Intranet.SocialWall.CreatePost.RemoveLocationTitle')}
|
||||
>
|
||||
{translate('::Cancel')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700">
|
||||
<div className="flex items-start gap-2">
|
||||
<FaMapMarkerAlt className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-gray-100 mb-1">
|
||||
{JSON.parse(location).name}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
|
||||
{JSON.parse(location).address}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{mediaType === 'poll' && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
className="mb-4"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">{translate('::App.Platform.Intranet.SocialWall.CreatePost.Poll')}</h4>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
clearMedia()
|
||||
}}
|
||||
className="text-sm text-red-600 hover:text-red-700 font-medium"
|
||||
title={translate('::App.Platform.Intranet.SocialWall.CreatePost.RemovePollTitle')}
|
||||
>
|
||||
{translate('::Cancel')}
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
value={pollQuestion}
|
||||
onChange={(e) => setPollQuestion(e.target.value)}
|
||||
placeholder={translate('::App.Platform.Intranet.SocialWall.CreatePost.PollQuestionPlaceholder')}
|
||||
className="w-full px-4 py-2 mb-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
{pollOptions.map((option, index) => (
|
||||
<div key={index} className="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
value={option}
|
||||
onChange={(e) => updatePollOption(index, e.target.value)}
|
||||
placeholder={translate('::App.Platform.Intranet.SocialWall.CreatePost.PollOptionPlaceholder')}
|
||||
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{pollOptions.length > 2 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removePollOption(index)}
|
||||
className="p-2 text-gray-500 hover:text-red-600"
|
||||
>
|
||||
<FaTimes className="w-5 h-5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{pollOptions.length < 6 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={addPollOption}
|
||||
className="mt-2 text-sm text-blue-600 hover:text-blue-700 font-medium"
|
||||
>
|
||||
+ {translate('::App.Platform.Intranet.SocialWall.CreatePost.AddOption')}
|
||||
</button>
|
||||
)}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Actions */}
|
||||
<AnimatePresence>
|
||||
{isExpanded && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
className="flex items-center justify-between pt-3 border-t border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div className="flex gap-2 relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (mediaType === 'media' && mediaItems.length > 0) {
|
||||
// Eğer zaten medya varsa, yöneticiyi aç
|
||||
setShowMediaManager(true)
|
||||
} else {
|
||||
// Başka bir tip seçiliyse temizle ve medya modunu aç
|
||||
clearMedia()
|
||||
setMediaType('media')
|
||||
setShowMediaManager(true)
|
||||
}
|
||||
}}
|
||||
className={classNames(
|
||||
'p-2 rounded-full transition-colors',
|
||||
mediaType === 'media'
|
||||
? 'bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400'
|
||||
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'
|
||||
)}
|
||||
title={mediaType === 'media' ? translate('::App.Platform.Intranet.SocialWall.CreatePost.EditMediaTitle') : translate('::App.Platform.Intranet.SocialWall.CreatePost.AddMediaTitle')}
|
||||
>
|
||||
<FaImages className="w-5 h-5" />
|
||||
{mediaType === 'media' && mediaItems.length > 0 && (
|
||||
<span className="absolute -top-1 -right-1 w-4 h-4 bg-blue-600 text-white text-xs rounded-full flex items-center justify-center">
|
||||
{mediaItems.length}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
// Başka bir tip seçiliyse temizle
|
||||
if (mediaType !== 'poll') {
|
||||
clearMedia()
|
||||
}
|
||||
setMediaType(mediaType === 'poll' ? null : 'poll')
|
||||
}}
|
||||
className={classNames(
|
||||
'p-2 rounded-full transition-colors',
|
||||
mediaType === 'poll'
|
||||
? 'bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400'
|
||||
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'
|
||||
)}
|
||||
title={mediaType === 'poll' ? translate('::App.Platform.Intranet.SocialWall.CreatePost.RemovePollTitle') : translate('::App.Platform.Intranet.SocialWall.CreatePost.AddPollTitle')}
|
||||
>
|
||||
<FaChartBar className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
|
||||
className="p-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors"
|
||||
title={translate('::App.Platform.Intranet.SocialWall.CreatePost.AddEmojiTitle')}
|
||||
>
|
||||
<FaSmile className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowLocationPicker(true)}
|
||||
className={classNames(
|
||||
'p-2 rounded-full transition-colors',
|
||||
location
|
||||
? 'bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-400'
|
||||
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'
|
||||
)}
|
||||
title={location ? translate('::App.Platform.Intranet.SocialWall.CreatePost.EditLocationTitle') : translate('::App.Platform.Intranet.SocialWall.CreatePost.AddLocationTitle')}
|
||||
>
|
||||
<FaMapMarkerAlt className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
{/* Emoji Picker */}
|
||||
{showEmojiPicker && (
|
||||
<div ref={emojiPickerRef} className="absolute bottom-12 left-0 z-50">
|
||||
<EmojiPicker
|
||||
onEmojiClick={handleEmojiClick}
|
||||
autoFocusSearch={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!content.trim() && mediaItems.length === 0 && !mediaType}
|
||||
className="px-6 py-2 bg-blue-600 text-white font-medium rounded-full hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{translate('::App.Platform.Intranet.SocialWall.CreatePost.Submit')}
|
||||
</button>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</form>
|
||||
|
||||
{/* Media Manager Modal */}
|
||||
<AnimatePresence>
|
||||
{showMediaManager && (
|
||||
<MediaManager
|
||||
media={mediaItems}
|
||||
onChange={setMediaItems}
|
||||
onClose={() => setShowMediaManager(false)}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Location Picker Modal */}
|
||||
<AnimatePresence>
|
||||
{showLocationPicker && (
|
||||
<LocationPicker
|
||||
onSelect={setLocation}
|
||||
onClose={() => setShowLocationPicker(false)}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreatePost
|
||||
109
ui/src/views/intranet/SocialWall/LocationMap.tsx
Normal file
109
ui/src/views/intranet/SocialWall/LocationMap.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import React from 'react'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { FaExternalLinkAlt, FaMapMarkerAlt } from 'react-icons/fa'
|
||||
|
||||
interface LocationData {
|
||||
id: string
|
||||
name: string
|
||||
address: string
|
||||
lat: number
|
||||
lng: number
|
||||
placeId?: string
|
||||
}
|
||||
|
||||
interface LocationMapProps {
|
||||
location: string // JSON string
|
||||
className?: string
|
||||
showDirections?: boolean
|
||||
}
|
||||
|
||||
const LocationMap: React.FC<LocationMapProps> = ({
|
||||
location,
|
||||
className = '',
|
||||
showDirections = true
|
||||
}) => {
|
||||
const locationData: LocationData = JSON.parse(location)
|
||||
|
||||
const handleOpenGoogleMaps = () => {
|
||||
const url = `https://www.google.com/maps/search/?api=1&query=${locationData.lat},${locationData.lng}&query_place_id=${locationData.placeId || ''}`
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
|
||||
// Google Maps Static API URL (gerçek uygulamada API key eklenecek)
|
||||
const getMapImageUrl = () => {
|
||||
const { lat, lng } = locationData
|
||||
const zoom = 15
|
||||
const size = '600x300'
|
||||
const marker = `color:red|${lat},${lng}`
|
||||
|
||||
// Production'da gerçek API key kullanılacak
|
||||
// const apiKey = 'YOUR_GOOGLE_MAPS_API_KEY'
|
||||
// return `https://maps.googleapis.com/maps/api/staticmap?center=${lat},${lng}&zoom=${zoom}&size=${size}&markers=${marker}&key=${apiKey}`
|
||||
|
||||
// Demo için OpenStreetMap kullanıyoruz
|
||||
return `https://www.openstreetmap.org/export/embed.html?bbox=${lng - 0.01},${lat - 0.01},${lng + 0.01},${lat + 0.01}&layer=mapnik&marker=${lat},${lng}`
|
||||
}
|
||||
|
||||
const { translate } = useLocalization();
|
||||
return (
|
||||
<div className={`relative rounded-lg overflow-hidden bg-gray-200 dark:bg-gray-700 ${className}`}>
|
||||
{/* Map Container */}
|
||||
<div className="relative w-full h-64 group">
|
||||
{/* OpenStreetMap iframe for demo */}
|
||||
<iframe
|
||||
title={`Map of ${locationData.name}`}
|
||||
src={getMapImageUrl()}
|
||||
className="w-full h-full border-0"
|
||||
allowFullScreen
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
{/* Overlay with location info */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent pointer-events-none" />
|
||||
|
||||
{/* Location Info */}
|
||||
<div className="absolute bottom-0 left-0 right-0 p-4 text-white pointer-events-none">
|
||||
<div className="flex items-start gap-2">
|
||||
<FaMapMarkerAlt className="w-5 h-5 mt-0.5 flex-shrink-0" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-bold text-lg mb-1 drop-shadow-lg">{locationData.name}</h3>
|
||||
<p className="text-sm text-white/90 drop-shadow-md line-clamp-2">
|
||||
{locationData.address}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Click to open overlay - invisible but clickable */}
|
||||
<button
|
||||
onClick={handleOpenGoogleMaps}
|
||||
className="absolute inset-0 w-full h-full cursor-pointer group"
|
||||
aria-label={translate('::App.Platform.Intranet.SocialWall.LocationMap.OpenInGoogleMaps')}
|
||||
>
|
||||
<span className="sr-only">{translate('::App.Platform.Intranet.SocialWall.LocationMap.OpenInGoogleMaps')}</span>
|
||||
</button>
|
||||
|
||||
{/* Hover Effect */}
|
||||
<div className="absolute inset-0 bg-blue-600/0 group-hover:bg-blue-600/10 transition-colors duration-200" />
|
||||
</div>
|
||||
|
||||
{/* Directions Button */}
|
||||
{showDirections && (
|
||||
<div className="p-3 bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700">
|
||||
<button
|
||||
onClick={handleOpenGoogleMaps}
|
||||
className="w-full flex items-center justify-center gap-2 px-4 py-2.5 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
<FaExternalLinkAlt className="w-5 h-5" />
|
||||
<span>{translate('::App.Platform.Intranet.SocialWall.LocationMap.OpenInGoogleMaps')}</span>
|
||||
</button>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-2 text-center">
|
||||
{translate('::App.Platform.Intranet.SocialWall.LocationMap.ClickForDirections')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LocationMap
|
||||
388
ui/src/views/intranet/SocialWall/LocationPicker.tsx
Normal file
388
ui/src/views/intranet/SocialWall/LocationPicker.tsx
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { motion } from 'framer-motion'
|
||||
import { FaTimes, FaSearch, FaMapMarkerAlt } from 'react-icons/fa'
|
||||
import classNames from 'classnames'
|
||||
|
||||
interface LocationPickerProps {
|
||||
onSelect: (location: string) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
interface LocationData {
|
||||
id: string
|
||||
name: string
|
||||
address: string
|
||||
lat: number
|
||||
lng: number
|
||||
placeId?: string
|
||||
}
|
||||
|
||||
// Google Maps API key - .env dosyasından alınmalı
|
||||
const GOOGLE_API_KEY = import.meta.env.VITE_GOOGLE_MAPS_API_KEY || ''
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
google: any
|
||||
initGoogleMaps?: () => void
|
||||
}
|
||||
}
|
||||
|
||||
const LocationPicker: React.FC<LocationPickerProps> = ({ onSelect, onClose }) => {
|
||||
const { translate } = useLocalization();
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [locations, setLocations] = useState<LocationData[]>([])
|
||||
const [selectedLocation, setSelectedLocation] = useState<LocationData | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [isGoogleLoaded, setIsGoogleLoaded] = useState(false)
|
||||
const searchInputRef = useRef<HTMLInputElement>(null)
|
||||
const autocompleteServiceRef = useRef<any>(null)
|
||||
const placesServiceRef = useRef<any>(null)
|
||||
const debounceTimerRef = useRef<NodeJS.Timeout>()
|
||||
const scriptLoadedRef = useRef(false)
|
||||
|
||||
// Google Maps SDK'yı yükle
|
||||
useEffect(() => {
|
||||
if (scriptLoadedRef.current) return
|
||||
|
||||
const loadGoogleMaps = () => {
|
||||
if (window.google && window.google.maps && window.google.maps.places) {
|
||||
setIsGoogleLoaded(true)
|
||||
autocompleteServiceRef.current = new window.google.maps.places.AutocompleteService()
|
||||
const mapDiv = document.createElement('div')
|
||||
const map = new window.google.maps.Map(mapDiv)
|
||||
placesServiceRef.current = new window.google.maps.places.PlacesService(map)
|
||||
return
|
||||
}
|
||||
|
||||
if (!GOOGLE_API_KEY) {
|
||||
setError(translate('::App.Platform.Intranet.SocialWall.LocationPicker.ApiKeyError'))
|
||||
return
|
||||
}
|
||||
|
||||
// Script zaten yüklendiyse sadece bekle
|
||||
const existingScript = document.querySelector('script[src*="maps.googleapis.com"]')
|
||||
if (existingScript) {
|
||||
const checkInterval = setInterval(() => {
|
||||
if (window.google && window.google.maps && window.google.maps.places) {
|
||||
clearInterval(checkInterval)
|
||||
setIsGoogleLoaded(true)
|
||||
autocompleteServiceRef.current = new window.google.maps.places.AutocompleteService()
|
||||
const mapDiv = document.createElement('div')
|
||||
const map = new window.google.maps.Map(mapDiv)
|
||||
placesServiceRef.current = new window.google.maps.places.PlacesService(map)
|
||||
}
|
||||
}, 100)
|
||||
return
|
||||
}
|
||||
|
||||
// Yeni script ekle
|
||||
const script = document.createElement('script')
|
||||
script.src = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}&libraries=places&language=tr`
|
||||
script.async = true
|
||||
script.defer = true
|
||||
|
||||
script.onload = () => {
|
||||
if (window.google && window.google.maps && window.google.maps.places) {
|
||||
setIsGoogleLoaded(true)
|
||||
autocompleteServiceRef.current = new window.google.maps.places.AutocompleteService()
|
||||
const mapDiv = document.createElement('div')
|
||||
const map = new window.google.maps.Map(mapDiv)
|
||||
placesServiceRef.current = new window.google.maps.places.PlacesService(map)
|
||||
}
|
||||
}
|
||||
|
||||
script.onerror = () => {
|
||||
setError(translate('::App.Platform.Intranet.SocialWall.LocationPicker.GoogleMapsLoadError'))
|
||||
}
|
||||
|
||||
document.head.appendChild(script)
|
||||
scriptLoadedRef.current = true
|
||||
}
|
||||
|
||||
loadGoogleMaps()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
searchInputRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
// Google Places Autocomplete ile konum arama
|
||||
useEffect(() => {
|
||||
if (debounceTimerRef.current) {
|
||||
clearTimeout(debounceTimerRef.current)
|
||||
}
|
||||
|
||||
if (searchQuery.trim() === '') {
|
||||
setLocations([])
|
||||
setError(null)
|
||||
return
|
||||
}
|
||||
|
||||
if (!isGoogleLoaded) {
|
||||
return
|
||||
}
|
||||
|
||||
debounceTimerRef.current = setTimeout(async () => {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
// Google Places Autocomplete Service kullan (CORS yok)
|
||||
autocompleteServiceRef.current.getPlacePredictions(
|
||||
{
|
||||
input: searchQuery,
|
||||
componentRestrictions: { country: 'tr' },
|
||||
language: 'tr'
|
||||
},
|
||||
async (predictions: any, status: any) => {
|
||||
if (status === window.google.maps.places.PlacesServiceStatus.ZERO_RESULTS) {
|
||||
setLocations([])
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (status !== window.google.maps.places.PlacesServiceStatus.OK) {
|
||||
setError(translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchFailed'))
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (!predictions || predictions.length === 0) {
|
||||
setLocations([])
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
// Her bir prediction için detaylı bilgi al
|
||||
const detailedLocations: LocationData[] = []
|
||||
let completed = 0
|
||||
|
||||
predictions.forEach((prediction: any) => {
|
||||
placesServiceRef.current.getDetails(
|
||||
{
|
||||
placeId: prediction.place_id,
|
||||
fields: ['name', 'formatted_address', 'geometry', 'place_id']
|
||||
},
|
||||
(place: any, placeStatus: any) => {
|
||||
completed++
|
||||
|
||||
if (placeStatus === window.google.maps.places.PlacesServiceStatus.OK && place) {
|
||||
detailedLocations.push({
|
||||
id: place.place_id,
|
||||
name: place.name,
|
||||
address: place.formatted_address,
|
||||
lat: place.geometry.location.lat(),
|
||||
lng: place.geometry.location.lng(),
|
||||
placeId: place.place_id
|
||||
})
|
||||
}
|
||||
|
||||
// Tüm istekler tamamlandıysa state'i güncelle
|
||||
if (completed === predictions.length) {
|
||||
setLocations(detailedLocations)
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
} catch (err) {
|
||||
console.error('Location search error:', err)
|
||||
setError(translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchError'))
|
||||
setIsLoading(false)
|
||||
}
|
||||
}, 500) // 500ms debounce
|
||||
|
||||
return () => {
|
||||
if (debounceTimerRef.current) {
|
||||
clearTimeout(debounceTimerRef.current)
|
||||
}
|
||||
}
|
||||
}, [searchQuery, isGoogleLoaded])
|
||||
|
||||
const handleSelect = (location: LocationData) => {
|
||||
setSelectedLocation(location)
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (selectedLocation) {
|
||||
onSelect(JSON.stringify(selectedLocation))
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.9 }}
|
||||
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">{translate('::App.Platform.Intranet.SocialWall.LocationPicker.AddLocation')}</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors"
|
||||
>
|
||||
<FaTimes className="w-5 h-5 text-gray-500 dark:text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="relative">
|
||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder={translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchPlaceholder')}
|
||||
disabled={!isGoogleLoaded}
|
||||
className="w-full pl-10 pr-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100 dark:disabled:bg-gray-600 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
{!isGoogleLoaded && (
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-2">
|
||||
{translate('::App.Platform.Intranet.SocialWall.LocationPicker.LoadingGoogleMaps')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Location List */}
|
||||
<div className="flex-1 overflow-y-auto p-4">
|
||||
{!isGoogleLoaded ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-500 dark:text-gray-400">{translate('::App.Platform.Intranet.SocialWall.LocationPicker.LoadingGoogleMaps')}</p>
|
||||
</div>
|
||||
) : isLoading ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-500 dark:text-gray-400">{translate('::App.Platform.Intranet.SocialWall.LocationPicker.SearchingLocations')}</p>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="text-center py-12">
|
||||
<FaMapMarkerAlt className="w-16 h-16 mx-auto mb-4 text-red-400" />
|
||||
<p className="text-red-500 dark:text-red-400">{error}</p>
|
||||
</div>
|
||||
) : searchQuery.trim() === '' ? (
|
||||
<div className="text-center py-12">
|
||||
<FaSearch className="w-16 h-16 mx-auto mb-4 text-gray-400" />
|
||||
<p className="text-gray-500 dark:text-gray-400">
|
||||
{translate('::App.Platform.Intranet.SocialWall.LocationPicker.TypeToSearch')}
|
||||
</p>
|
||||
<p className="text-sm text-gray-400 dark:text-gray-500 mt-2">
|
||||
{translate('::App.Platform.Intranet.SocialWall.LocationPicker.Example')}
|
||||
</p>
|
||||
</div>
|
||||
) : locations.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<FaMapMarkerAlt className="w-16 h-16 mx-auto mb-4 text-gray-400" />
|
||||
<p className="text-gray-500 dark:text-gray-400">
|
||||
{translate('::App.Platform.Intranet.SocialWall.LocationPicker.NotFound')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{locations.map((location) => (
|
||||
<button
|
||||
key={location.id}
|
||||
onClick={() => handleSelect(location)}
|
||||
className={classNames(
|
||||
'w-full text-left p-3 rounded-lg transition-all hover:bg-gray-50 dark:hover:bg-gray-700',
|
||||
selectedLocation?.id === location.id
|
||||
? 'bg-blue-50 dark:bg-blue-900/30 border-2 border-blue-500'
|
||||
: 'border-2 border-transparent'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="mt-1">
|
||||
<FaMapMarkerAlt
|
||||
className={classNames(
|
||||
'w-5 h-5',
|
||||
selectedLocation?.id === location.id
|
||||
? 'text-blue-600'
|
||||
: 'text-gray-400'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3
|
||||
className={classNames(
|
||||
'font-semibold mb-1',
|
||||
selectedLocation?.id === location.id
|
||||
? 'text-blue-600 dark:text-blue-400'
|
||||
: 'text-gray-900 dark:text-gray-100'
|
||||
)}
|
||||
>
|
||||
{location.name}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
|
||||
{location.address}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-500 mt-1">
|
||||
{location.lat.toFixed(4)}, {location.lng.toFixed(4)}
|
||||
</p>
|
||||
</div>
|
||||
{selectedLocation?.id === location.id && (
|
||||
<div className="mt-1">
|
||||
<div className="w-5 h-5 bg-blue-600 rounded-full flex items-center justify-center">
|
||||
<svg className="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex items-center justify-between p-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-750">
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{selectedLocation ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<FaMapMarkerAlt className="w-4 h-4 text-blue-600" />
|
||||
<span className="font-medium text-gray-900 dark:text-gray-100">
|
||||
{selectedLocation.name}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
<span>{translate('::App.Platform.Intranet.SocialWall.LocationPicker.SelectLocation')}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
{translate('::Cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirm}
|
||||
disabled={!selectedLocation}
|
||||
className="px-6 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{translate('::ListForms.Wizard.Add')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LocationPicker
|
||||
67
ui/src/views/intranet/SocialWall/MediaLightbox.tsx
Normal file
67
ui/src/views/intranet/SocialWall/MediaLightbox.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import React from 'react'
|
||||
import Lightbox from 'yet-another-react-lightbox'
|
||||
import 'yet-another-react-lightbox/styles.css'
|
||||
import Video from 'yet-another-react-lightbox/plugins/video'
|
||||
import Zoom from 'yet-another-react-lightbox/plugins/zoom'
|
||||
import Counter from 'yet-another-react-lightbox/plugins/counter'
|
||||
import 'yet-another-react-lightbox/plugins/counter.css'
|
||||
import { SocialMediaDto } from '@/proxy/intranet/models'
|
||||
|
||||
interface MediaLightboxProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
media: SocialMediaDto
|
||||
startIndex?: number
|
||||
}
|
||||
|
||||
const MediaLightbox: React.FC<MediaLightboxProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
media,
|
||||
startIndex = 0
|
||||
}) => {
|
||||
const slides = React.useMemo(() => {
|
||||
if (media.type === 'video' && media.urls && media.urls.length > 0) {
|
||||
return [
|
||||
{
|
||||
type: 'video' as const,
|
||||
sources: [
|
||||
{
|
||||
src: media.urls[0],
|
||||
type: 'video/mp4'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const urls = media.urls || []
|
||||
return urls.map((url) => ({
|
||||
src: url
|
||||
}))
|
||||
}, [media])
|
||||
|
||||
return (
|
||||
<Lightbox
|
||||
open={isOpen}
|
||||
close={onClose}
|
||||
slides={slides}
|
||||
index={startIndex}
|
||||
plugins={[Video, Zoom, Counter]}
|
||||
counter={{ container: { style: { top: 'unset', bottom: 0 } } }}
|
||||
zoom={{
|
||||
maxZoomPixelRatio: 3,
|
||||
scrollToZoom: true
|
||||
}}
|
||||
video={{
|
||||
controls: true,
|
||||
autoPlay: false
|
||||
}}
|
||||
styles={{
|
||||
container: { backgroundColor: 'rgba(0, 0, 0, 0.95)' }
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default MediaLightbox
|
||||
240
ui/src/views/intranet/SocialWall/MediaManager.tsx
Normal file
240
ui/src/views/intranet/SocialWall/MediaManager.tsx
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { motion } from 'framer-motion'
|
||||
import { FaTimes, FaLink, FaUpload } from 'react-icons/fa'
|
||||
import classNames from 'classnames'
|
||||
import { SocialMediaDto } from '@/proxy/intranet/models'
|
||||
|
||||
interface MediaManagerProps {
|
||||
media: SocialMediaDto[]
|
||||
onChange: (media: SocialMediaDto[]) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const MediaManager: React.FC<MediaManagerProps> = ({ media, onChange, onClose }) => {
|
||||
const { translate } = useLocalization();
|
||||
const [activeTab, setActiveTab] = useState<'upload' | 'url'>('upload')
|
||||
const [urlInput, setUrlInput] = useState('')
|
||||
const [mediaType, setMediaType] = useState<'image' | 'video'>('image')
|
||||
|
||||
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = e.target.files
|
||||
if (!files) return
|
||||
|
||||
const newMedia: SocialMediaDto[] = Array.from(files).map((file) => ({
|
||||
id: Math.random().toString(36).substr(2, 9),
|
||||
type: file.type.startsWith('video/') ? 'video' : 'image',
|
||||
urls: [URL.createObjectURL(file)],
|
||||
file
|
||||
}))
|
||||
|
||||
onChange([...media, ...newMedia])
|
||||
e.target.value = ''
|
||||
}
|
||||
|
||||
const handleUrlAdd = () => {
|
||||
if (!urlInput.trim()) return
|
||||
|
||||
const newMedia: SocialMediaDto = {
|
||||
id: Math.random().toString(36).substr(2, 9),
|
||||
type: mediaType,
|
||||
urls: [urlInput]
|
||||
}
|
||||
|
||||
onChange([...media, newMedia])
|
||||
setUrlInput('')
|
||||
}
|
||||
|
||||
const removeMedia = (id: string | undefined) => {
|
||||
if (!id) return
|
||||
onChange(media.filter((m) => m.id !== id))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.9 }}
|
||||
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-3xl max-h-[90vh] overflow-hidden"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">{translate('::App.Platform.Intranet.SocialWall.MediaManager.AddMedia')}</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-full transition-colors"
|
||||
>
|
||||
<FaTimes className="w-5 h-5 text-gray-500 dark:text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex border-b border-gray-200 dark:border-gray-700 px-4">
|
||||
<button
|
||||
onClick={() => setActiveTab('upload')}
|
||||
className={classNames(
|
||||
'px-4 py-3 font-medium border-b-2 transition-colors',
|
||||
activeTab === 'upload'
|
||||
? 'border-blue-600 text-blue-600'
|
||||
: 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FaUpload className="w-5 h-5" />
|
||||
<span>{translate('::App.Platform.Intranet.SocialWall.MediaManager.SelectFromComputer')}</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('url')}
|
||||
className={classNames(
|
||||
'px-4 py-3 font-medium border-b-2 transition-colors',
|
||||
activeTab === 'url'
|
||||
? 'border-blue-600 text-blue-600'
|
||||
: 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FaLink className="w-5 h-5" />
|
||||
<span>{translate('::App.Platform.Intranet.SocialWall.MediaManager.AddByUrl')}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-4 overflow-y-auto max-h-[calc(90vh-240px)]">
|
||||
{activeTab === 'upload' ? (
|
||||
<div>
|
||||
<label className="block">
|
||||
<div className="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-8 text-center hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors cursor-pointer">
|
||||
<FaUpload className="w-12 h-12 mx-auto mb-4 text-gray-400" />
|
||||
<p className="text-gray-700 dark:text-gray-300 font-medium mb-1">
|
||||
{translate('::App.Platform.Intranet.SocialWall.MediaManager.ClickToSelectFile')}
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{translate('::App.Platform.Intranet.SocialWall.MediaManager.ImageOrVideoFormats')}
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*,video/*"
|
||||
multiple
|
||||
onChange={handleFileSelect}
|
||||
className="hidden"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{translate('::App.Platform.Intranet.SocialWall.MediaManager.MediaType')}
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setMediaType('image')}
|
||||
className={classNames(
|
||||
'flex-1 py-2 px-4 rounded-lg font-medium transition-colors',
|
||||
mediaType === 'image'
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
||||
)}
|
||||
>
|
||||
{translate('::App.Platform.Intranet.SocialWall.MediaManager.Image')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setMediaType('video')}
|
||||
className={classNames(
|
||||
'flex-1 py-2 px-4 rounded-lg font-medium transition-colors',
|
||||
mediaType === 'video'
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'
|
||||
)}
|
||||
>
|
||||
{translate('::App.Platform.Intranet.SocialWall.MediaManager.Video')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="url"
|
||||
value={urlInput}
|
||||
onChange={(e) => setUrlInput(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleUrlAdd()}
|
||||
placeholder={mediaType === 'image' ? translate('::App.Platform.Intranet.SocialWall.MediaManager.EnterImageUrl') : translate('::App.Platform.Intranet.SocialWall.MediaManager.EnterVideoUrl')}
|
||||
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUrlAdd}
|
||||
disabled={!urlInput.trim()}
|
||||
className="px-6 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{translate('::ListForms.Wizard.Add')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Media Preview */}
|
||||
{media.length > 0 && (
|
||||
<div className="mt-6">
|
||||
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
|
||||
{translate('::App.Platform.Intranet.SocialWall.MediaManager.AddedMedia')} ({media.length})
|
||||
</h3>
|
||||
<div className="grid grid-cols-4 gap-3">
|
||||
{media.map((item) => (
|
||||
<div key={item.id} className="relative group">
|
||||
{item.type === 'image' ? (
|
||||
<img
|
||||
src={item.urls?.[0]}
|
||||
alt="Media preview"
|
||||
className="w-full h-24 object-cover rounded-lg"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-24 bg-gray-900 rounded-lg flex items-center justify-center">
|
||||
<video src={item.urls?.[0]} className="w-full h-full object-cover rounded-lg" />
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="w-10 h-10 bg-black bg-opacity-50 rounded-full flex items-center justify-center">
|
||||
<div className="w-0 h-0 border-t-8 border-t-transparent border-l-12 border-l-white border-b-8 border-b-transparent ml-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={() => removeMedia(item.id)}
|
||||
className="absolute -top-2 -right-2 p-1 bg-red-600 text-white rounded-full opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<FaTimes className="w-4 h-4" />
|
||||
</button>
|
||||
<div className="absolute bottom-1 left-1 px-2 py-0.5 bg-black bg-opacity-70 text-white text-xs rounded">
|
||||
{item.type === 'image' ? translate('::App.Platform.Intranet.SocialWall.MediaManager.ImageIcon') : translate('::App.Platform.Intranet.SocialWall.MediaManager.VideoIcon')}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex items-center justify-end gap-2 p-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
{translate('::Cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={onClose}
|
||||
disabled={media.length === 0}
|
||||
className="px-6 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{translate('::App.Platform.Intranet.SocialWall.MediaManager.Done', { count: media.length })}
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MediaManager
|
||||
409
ui/src/views/intranet/SocialWall/PostItem.tsx
Normal file
409
ui/src/views/intranet/SocialWall/PostItem.tsx
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import classNames from 'classnames'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import 'dayjs/locale/tr'
|
||||
import { FaHeart, FaRegHeart, FaRegCommentAlt, FaTrash, FaPaperPlane } from 'react-icons/fa'
|
||||
import MediaLightbox from './MediaLightbox'
|
||||
import LocationMap from './LocationMap'
|
||||
import UserProfileCard from './UserProfileCard'
|
||||
import { SocialPostDto } from '@/proxy/intranet/models'
|
||||
import { useStoreState } from '@/store/store'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
import { Avatar } from '@/components/ui'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
dayjs.locale('tr')
|
||||
|
||||
interface PostItemProps {
|
||||
post: SocialPostDto
|
||||
onLike: (postId: string) => void
|
||||
onComment: (postId: string, content: string) => void
|
||||
onDelete: (postId: string) => void
|
||||
onVote: (postId: string, optionId: string) => void
|
||||
}
|
||||
|
||||
const PostItem: React.FC<PostItemProps> = ({ post, onLike, onComment, onDelete, onVote }) => {
|
||||
const { translate } = useLocalization()
|
||||
const [showComments, setShowComments] = useState(false)
|
||||
const [commentText, setCommentText] = useState('')
|
||||
const [showAllImages, setShowAllImages] = useState(false)
|
||||
const [lightboxOpen, setLightboxOpen] = useState(false)
|
||||
const [lightboxIndex, setLightboxIndex] = useState(0)
|
||||
const [showUserCard, setShowUserCard] = useState(false)
|
||||
const [hoveredCommentAuthor, setHoveredCommentAuthor] = useState<string | null>(null)
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const { user } = useStoreState((state) => state.auth)
|
||||
|
||||
// Intersection Observer for video autoplay/pause
|
||||
useEffect(() => {
|
||||
const video = videoRef.current
|
||||
if (!video) return
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
// Video ekranda görünür - oynat
|
||||
video.play().catch((err) => {
|
||||
console.log('Video autoplay failed:', err)
|
||||
})
|
||||
} else {
|
||||
// Video ekrandan çıktı - durdur
|
||||
video.pause()
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
threshold: 0.5, // Video %50 görünür olduğunda oynat
|
||||
},
|
||||
)
|
||||
|
||||
observer.observe(video)
|
||||
|
||||
return () => {
|
||||
observer.disconnect()
|
||||
}
|
||||
}, [post.media?.type])
|
||||
|
||||
const handleSubmitComment = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (commentText.trim()) {
|
||||
onComment(post.id, commentText)
|
||||
setCommentText('')
|
||||
}
|
||||
}
|
||||
|
||||
const getImageLayout = (images: string[]) => {
|
||||
const count = images.length
|
||||
if (count === 1) return 'single'
|
||||
if (count === 2) return 'double'
|
||||
if (count === 3) return 'triple'
|
||||
return 'multiple'
|
||||
}
|
||||
|
||||
const renderMedia = () => {
|
||||
if (!post.media) return null
|
||||
|
||||
switch (post.media.type) {
|
||||
case 'image':
|
||||
if (post.media.urls && post.media.urls.length > 0) {
|
||||
const layout = getImageLayout(post.media.urls)
|
||||
const displayImages = showAllImages ? post.media.urls : post.media.urls.slice(0, 4)
|
||||
const hasMore = post.media.urls.length > 4
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={classNames('mt-3 rounded-lg overflow-hidden', {
|
||||
'grid gap-1': layout !== 'single',
|
||||
'grid-cols-2': layout === 'double' || layout === 'multiple',
|
||||
'grid-cols-3': layout === 'triple',
|
||||
})}
|
||||
>
|
||||
{displayImages.map((url, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={classNames('relative', {
|
||||
'col-span-2': layout === 'triple' && index === 0,
|
||||
'aspect-video': layout === 'single',
|
||||
'aspect-square': layout !== 'single',
|
||||
})}
|
||||
>
|
||||
<img
|
||||
src={url}
|
||||
alt={`Post image ${index + 1}`}
|
||||
className="w-full h-full object-cover cursor-pointer hover:opacity-90 transition-opacity"
|
||||
onClick={() => {
|
||||
setLightboxIndex(index)
|
||||
setLightboxOpen(true)
|
||||
}}
|
||||
/>
|
||||
{hasMore && index === 3 && !showAllImages && post.media?.urls && (
|
||||
<div
|
||||
className="absolute inset-0 bg-black bg-opacity-60 flex items-center justify-center cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setShowAllImages(true)
|
||||
}}
|
||||
>
|
||||
<span className="text-white text-2xl font-bold">
|
||||
+{post.media.urls.length - 4}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<MediaLightbox
|
||||
isOpen={lightboxOpen}
|
||||
onClose={() => setLightboxOpen(false)}
|
||||
media={{ type: 'image', urls: post.media.urls }}
|
||||
startIndex={lightboxIndex}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
break
|
||||
|
||||
case 'video':
|
||||
if (post.media.urls && post.media.urls.length > 0) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="mt-3 rounded-lg overflow-hidden cursor-pointer relative group"
|
||||
onClick={() => setLightboxOpen(true)}
|
||||
>
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={post.media.urls[0]}
|
||||
className="w-full max-h-96 object-cover"
|
||||
controls
|
||||
playsInline
|
||||
muted
|
||||
loop
|
||||
/>
|
||||
</div>
|
||||
<MediaLightbox
|
||||
isOpen={lightboxOpen}
|
||||
onClose={() => setLightboxOpen(false)}
|
||||
media={{ type: 'video', urls: [post.media.urls[0]] }}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
break
|
||||
|
||||
case 'poll':
|
||||
if (post.media.pollQuestion && post.media.pollOptions) {
|
||||
const isExpired = post.media.pollEndsAt ? new Date() > post.media.pollEndsAt : false
|
||||
const hasVoted = !!post.media.pollUserVoteId
|
||||
const totalVotes = post.media.pollTotalVotes || 0
|
||||
const pollUserVoteId = post.media.pollUserVoteId
|
||||
|
||||
return (
|
||||
<div className="mt-3 p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
||||
<h4 className="font-medium text-gray-900 dark:text-gray-100 mb-3">
|
||||
{post.media.pollQuestion}
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
{post.media.pollOptions.map((option) => {
|
||||
const percentage = totalVotes > 0 ? (option.votes / totalVotes) * 100 : 0
|
||||
const isSelected = pollUserVoteId === option.id
|
||||
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
onClick={() => !hasVoted && !isExpired && onVote(post.id, option.id)}
|
||||
disabled={hasVoted || isExpired}
|
||||
className={classNames(
|
||||
'w-full text-left p-3 rounded-lg relative overflow-hidden transition-all',
|
||||
{
|
||||
'bg-blue-100 dark:bg-blue-900 border-2 border-blue-500': isSelected,
|
||||
'bg-white dark:bg-gray-600 hover:bg-gray-50 dark:hover:bg-gray-500':
|
||||
!isSelected && !hasVoted && !isExpired,
|
||||
'bg-white dark:bg-gray-600 cursor-not-allowed': hasVoted || isExpired,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{hasVoted && (
|
||||
<div
|
||||
className="absolute inset-y-0 left-0 bg-blue-200 dark:bg-blue-800 transition-all"
|
||||
style={{ width: `${percentage}%` }}
|
||||
/>
|
||||
)}
|
||||
<div className="relative z-10 flex justify-between items-center">
|
||||
<span className="font-medium text-gray-900 dark:text-gray-100">
|
||||
{option.text}
|
||||
</span>
|
||||
{hasVoted && (
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">
|
||||
{percentage.toFixed(0)}%
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-3 text-sm text-gray-600 dark:text-gray-400">
|
||||
{totalVotes} oy •{' '}
|
||||
{isExpired
|
||||
? 'Sona erdi'
|
||||
: post.media.pollEndsAt
|
||||
? dayjs(post.media.pollEndsAt).fromNow() + ' bitiyor'
|
||||
: ''}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 mb-4"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="relative"
|
||||
onMouseEnter={() => setShowUserCard(true)}
|
||||
onMouseLeave={() => setShowUserCard(false)}
|
||||
>
|
||||
<Avatar size={32} shape="circle" src={AVATAR_URL(post.user.id, post.user.tenantId)} />
|
||||
<AnimatePresence>
|
||||
{showUserCard && (
|
||||
<UserProfileCard
|
||||
user={{
|
||||
id: post.user.id || '',
|
||||
name: post.user.fullName || '',
|
||||
title: post.user.jobPositions?.[0]?.name || '',
|
||||
email: post.user.email,
|
||||
phoneNumber: post.user.phoneNumber,
|
||||
department: post.user.departments?.[0]?.name,
|
||||
tenantId: post.user.tenantId || '',
|
||||
}}
|
||||
position="bottom"
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm text-gray-900 dark:text-gray-100">{post.user.fullName}</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{post.user.jobPositions?.[0]?.name || ''} • {dayjs(post.creationTime).fromNow()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{post.isOwnPost && (
|
||||
<button
|
||||
onClick={() => onDelete(post.id)}
|
||||
className="p-2 text-gray-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-full transition-colors"
|
||||
title={translate('::App.Platform.Intranet.SocialWall.PostItem.DeletePost')}
|
||||
>
|
||||
<FaTrash className="w-5 h-5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="mb-3">
|
||||
<p className="text-gray-800 dark:text-gray-200 whitespace-pre-wrap">{post.content}</p>
|
||||
{renderMedia()}
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-6 pt-3 border-t border-gray-100 dark:border-gray-700">
|
||||
<button
|
||||
onClick={() => onLike(post.id)}
|
||||
className={classNames(
|
||||
'flex items-center gap-2 transition-colors',
|
||||
post.isLiked
|
||||
? 'text-red-600 hover:text-red-700'
|
||||
: 'text-gray-600 dark:text-gray-400 hover:text-red-600',
|
||||
)}
|
||||
>
|
||||
{post.isLiked ? <FaHeart className="w-5 h-5" /> : <FaRegHeart className="w-5 h-5" />}
|
||||
<span className="text-sm font-medium">{post.likeCount}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setShowComments(!showComments)}
|
||||
className="flex items-center gap-2 text-gray-600 dark:text-gray-400 hover:text-blue-600 transition-colors"
|
||||
>
|
||||
<FaRegCommentAlt className="w-5 h-5" />
|
||||
<span className="text-sm font-medium">{post.comments.length}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Comments Section */}
|
||||
<AnimatePresence>
|
||||
{showComments && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: 'auto', opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
className="mt-4 pt-4 border-t border-gray-100 dark:border-gray-700"
|
||||
>
|
||||
{/* Comment Form */}
|
||||
<form onSubmit={handleSubmitComment} className="mb-4">
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
value={commentText}
|
||||
onChange={(e) => setCommentText(e.target.value)}
|
||||
placeholder={translate(
|
||||
'::App.Platform.Intranet.SocialWall.PostItem.CommentPlaceholder',
|
||||
)}
|
||||
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-full bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!commentText.trim()}
|
||||
className="p-2 bg-blue-600 text-white rounded-full hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
<FaPaperPlane className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Comments List */}
|
||||
<div className="space-y-3">
|
||||
{post.comments.map((comment) => (
|
||||
<div key={comment.id} className="flex gap-3">
|
||||
<div
|
||||
className="relative"
|
||||
onMouseEnter={() => setHoveredCommentAuthor(comment.id)}
|
||||
onMouseLeave={() => setHoveredCommentAuthor(null)}
|
||||
>
|
||||
<Avatar
|
||||
size={32}
|
||||
shape="circle"
|
||||
src={AVATAR_URL(comment.creator.id, comment.creator.tenantId)}
|
||||
/>
|
||||
<AnimatePresence>
|
||||
{hoveredCommentAuthor === comment.id && (
|
||||
<UserProfileCard
|
||||
user={{
|
||||
id: comment.creator.id || '',
|
||||
name: comment.creator.fullName || '',
|
||||
title: comment.creator.jobPositions?.[0]?.name || '',
|
||||
tenantId: comment.creator.tenantId || '',
|
||||
}}
|
||||
position="bottom"
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="bg-gray-100 dark:bg-gray-700 rounded-lg px-4 py-2">
|
||||
<h4 className="font-semibold text-sm text-gray-900 dark:text-gray-100">
|
||||
{comment.creator.fullName}
|
||||
</h4>
|
||||
<p className="text-sm text-gray-800 dark:text-gray-200">{comment.content}</p>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1 ml-4">
|
||||
{dayjs(comment.creationTime).fromNow()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostItem
|
||||
100
ui/src/views/intranet/SocialWall/UserProfileCard.tsx
Normal file
100
ui/src/views/intranet/SocialWall/UserProfileCard.tsx
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import React from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { FaEnvelope, FaPhone, FaBriefcase, FaMapMarkerAlt } from 'react-icons/fa'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
import { Avatar } from '@/components/ui'
|
||||
|
||||
interface UserProfileCardProps {
|
||||
user: {
|
||||
id: string
|
||||
name: string
|
||||
title: string
|
||||
email?: string
|
||||
phoneNumber?: string
|
||||
department?: string
|
||||
location?: string
|
||||
tenantId: string
|
||||
}
|
||||
position?: 'top' | 'bottom'
|
||||
}
|
||||
|
||||
const UserProfileCard: React.FC<UserProfileCardProps> = ({ user, position = 'bottom' }) => {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95, y: position === 'bottom' ? -10 : 10 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: position === 'bottom' ? -10 : 10 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
className={`absolute left-0 ${
|
||||
position === 'bottom' ? 'top-full mt-2' : 'bottom-full mb-2'
|
||||
} z-50 w-72 bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 p-4`}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start gap-3 mb-3 pb-3 border-b border-gray-200 dark:border-gray-700">
|
||||
<Avatar size={48} shape="circle" src={AVATAR_URL(user.id, user.tenantId)} />
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-bold text-gray-900 dark:text-gray-100 text-lg mb-1">
|
||||
{user.name}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1">
|
||||
<FaBriefcase className="w-4 h-4" />
|
||||
{user.title}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Info */}
|
||||
<div className="space-y-2">
|
||||
{user.email && (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<FaEnvelope className="w-4 h-4 text-gray-400 flex-shrink-0" />
|
||||
<a
|
||||
href={`mailto:${user.email}`}
|
||||
className="text-blue-600 dark:text-blue-400 hover:underline truncate"
|
||||
>
|
||||
{user.email}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{user.phoneNumber && (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<FaPhone className="w-4 h-4 text-gray-400 flex-shrink-0" />
|
||||
<a
|
||||
href={`tel:${user.phoneNumber}`}
|
||||
className="text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400"
|
||||
>
|
||||
{user.phoneNumber}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{user.department && (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<FaBriefcase className="w-4 h-4 text-gray-400 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300">{user.department}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{user.location && (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<FaMapMarkerAlt className="w-4 h-4 text-gray-400 flex-shrink-0" />
|
||||
<span className="text-gray-700 dark:text-gray-300">{user.location}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Arrow indicator */}
|
||||
<div
|
||||
className={`absolute left-6 ${
|
||||
position === 'bottom' ? '-top-2' : '-bottom-2'
|
||||
} w-4 h-4 bg-white dark:bg-gray-800 border-l border-t border-gray-200 dark:border-gray-700 transform ${
|
||||
position === 'bottom' ? 'rotate-45' : '-rotate-135'
|
||||
}`}
|
||||
/>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserProfileCard
|
||||
222
ui/src/views/intranet/SocialWall/index.tsx
Normal file
222
ui/src/views/intranet/SocialWall/index.tsx
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { AnimatePresence } from 'framer-motion'
|
||||
import PostItem from './PostItem'
|
||||
import CreatePost from './CreatePost'
|
||||
import { SocialMediaDto, SocialPostDto } from '@/proxy/intranet/models'
|
||||
import { useStoreState } from '@/store/store'
|
||||
import { IdentityUserDto } from '@/proxy/admin/models'
|
||||
|
||||
const SocialWall: React.FC<{ posts: SocialPostDto[] }> = ({ posts }) => {
|
||||
// const [posts, setPosts] = useState<SocialPost[]>(mockSocialPosts)
|
||||
const [filter, setFilter] = useState<'all' | 'mine'>('all')
|
||||
const { translate } = useLocalization()
|
||||
|
||||
// Ali Öztürk'ü "Siz" kullanıcısı olarak kullan
|
||||
const { user } = useStoreState((state) => state.auth)
|
||||
const currentUserAuthor: IdentityUserDto = {
|
||||
id: user?.id || '0',
|
||||
userName: user.userName || 'unknown',
|
||||
name: user?.name || 'Siz',
|
||||
email: user?.email || '',
|
||||
isActive: true,
|
||||
emailConfirmed: true,
|
||||
phoneNumberConfirmed: true,
|
||||
lockoutEnabled: false,
|
||||
}
|
||||
|
||||
const handleCreatePost = (postData: {
|
||||
content: string
|
||||
location?: string
|
||||
media?: {
|
||||
type: 'mixed' | 'poll'
|
||||
mediaItems?: SocialMediaDto[]
|
||||
poll?: {
|
||||
question: string
|
||||
options: Array<{ text: string }>
|
||||
}
|
||||
}
|
||||
}) => {
|
||||
let mediaForPost = undefined
|
||||
|
||||
if (postData.media) {
|
||||
if (postData.media.type === 'mixed' && postData.media.mediaItems) {
|
||||
// Convert MediaItems to post format
|
||||
const images = postData.media.mediaItems.filter((m) => m.type === 'image')
|
||||
const videos = postData.media.mediaItems.filter((m) => m.type === 'video')
|
||||
|
||||
if (images.length > 0 && videos.length === 0) {
|
||||
mediaForPost = {
|
||||
type: 'image' as const,
|
||||
urls: images.map((i) => i.urls?.[0]).filter((url) => url !== undefined) as string[],
|
||||
}
|
||||
} else if (videos.length > 0 && images.length === 0) {
|
||||
mediaForPost = {
|
||||
type: 'video' as const,
|
||||
urls: videos[0].urls || [],
|
||||
}
|
||||
} else if (images.length > 0 || videos.length > 0) {
|
||||
// Mixed media - use first image for now
|
||||
mediaForPost = {
|
||||
type: 'image' as const,
|
||||
urls: images.map((i) => i.urls?.[0]).filter((url) => url !== undefined) as string[],
|
||||
}
|
||||
}
|
||||
} else if (postData.media.type === 'poll' && postData.media.poll) {
|
||||
mediaForPost = {
|
||||
type: 'poll' as const,
|
||||
pollQuestion: postData.media.poll.question,
|
||||
pollOptions: postData.media.poll.options.map((opt, index) => ({
|
||||
id: `opt-${index}`,
|
||||
text: opt.text,
|
||||
votes: 0,
|
||||
})),
|
||||
pollTotalVotes: 0,
|
||||
pollEndsAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const newPost: SocialPostDto = {
|
||||
id: Date.now().toString(),
|
||||
user: currentUserAuthor,
|
||||
content: postData.content,
|
||||
creationTime: new Date(),
|
||||
media: mediaForPost,
|
||||
locationJson: postData.location,
|
||||
likeCount: 0,
|
||||
isLiked: false,
|
||||
likeUsers: [],
|
||||
comments: [],
|
||||
isOwnPost: true,
|
||||
}
|
||||
|
||||
// setPosts([newPost, ...posts])
|
||||
}
|
||||
|
||||
const handleLike = (postId: string) => {
|
||||
// setPosts(
|
||||
// posts.map((post) => {
|
||||
// if (post.id === postId) {
|
||||
// return {
|
||||
// ...post,
|
||||
// likeCount: post.isLiked ? post.likeCount - 1 : post.likeCount + 1,
|
||||
// isLiked: !post.isLiked
|
||||
// }
|
||||
// }
|
||||
// return post
|
||||
// })
|
||||
// )
|
||||
}
|
||||
|
||||
const handleComment = (postId: string, content: string) => {
|
||||
// setPosts(
|
||||
// posts.map((post) => {
|
||||
// if (post.id === postId) {
|
||||
// const commentAuthor = currentUserAuthor
|
||||
// const newComment = {
|
||||
// id: Date.now().toString(),
|
||||
// creator: commentAuthor,
|
||||
// content,
|
||||
// creationTime: new Date()
|
||||
// }
|
||||
// return {
|
||||
// ...post,
|
||||
// comments: [...post.comments, newComment]
|
||||
// }
|
||||
// }
|
||||
// return post
|
||||
// })
|
||||
// )
|
||||
}
|
||||
|
||||
const handleDelete = (postId: string) => {
|
||||
if (window.confirm(translate('::App.Platform.Intranet.SocialWall.DeleteConfirm'))) {
|
||||
// setPosts(posts.filter((post) => post.id !== postId))
|
||||
}
|
||||
}
|
||||
|
||||
const handleVote = (postId: string, optionId: string) => {
|
||||
// setPosts(
|
||||
// posts.map((post) => {
|
||||
// if (post.id === postId && post.media?.type === 'poll' && post.media.pollOptions) {
|
||||
// // If user already voted, don't allow voting again
|
||||
// if (post.media.pollUserVoteId) {
|
||||
// return post
|
||||
// }
|
||||
// return {
|
||||
// ...post,
|
||||
// media: {
|
||||
// ...post.media,
|
||||
// pollOptions: post.media.pollOptions.map((opt) =>
|
||||
// opt.id === optionId ? { ...opt, votes: opt.votes + 1 } : opt
|
||||
// ),
|
||||
// pollTotalVotes: (post.media.pollTotalVotes || 0) + 1,
|
||||
// pollUserVoteId: optionId
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return post
|
||||
// })
|
||||
// )
|
||||
}
|
||||
|
||||
const filteredPosts = filter === 'mine' ? posts.filter((post) => post.isOwnPost) : posts
|
||||
|
||||
return (
|
||||
<div className="mx-auto px-4">
|
||||
{/* Filter Tabs */}
|
||||
<div className="flex gap-4 mb-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<button
|
||||
onClick={() => setFilter('all')}
|
||||
className={`pb-3 px-1 border-b-2 transition-colors font-medium ${
|
||||
filter === 'all'
|
||||
? 'border-blue-600 text-blue-600'
|
||||
: 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
|
||||
}`}
|
||||
>
|
||||
{translate('::App.Platform.Intranet.SocialWall.AllPosts')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('mine')}
|
||||
className={`pb-3 px-1 border-b-2 transition-colors font-medium ${
|
||||
filter === 'mine'
|
||||
? 'border-blue-600 text-blue-600'
|
||||
: 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
|
||||
}`}
|
||||
>
|
||||
{translate('::App.Platform.Intranet.SocialWall.MyPosts')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Create Post */}
|
||||
<CreatePost onCreatePost={handleCreatePost} />
|
||||
|
||||
{/* Posts Feed */}
|
||||
<AnimatePresence>
|
||||
{filteredPosts.length > 0 ? (
|
||||
filteredPosts.map((post) => (
|
||||
<PostItem
|
||||
key={post.id}
|
||||
post={post}
|
||||
onLike={handleLike}
|
||||
onComment={handleComment}
|
||||
onDelete={handleDelete}
|
||||
onVote={handleVote}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-gray-500 dark:text-gray-400 text-lg">
|
||||
{filter === 'mine'
|
||||
? translate('::App.Platform.Intranet.SocialWall.NoMyPosts')
|
||||
: translate('::App.Platform.Intranet.SocialWall.NoPosts')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SocialWall
|
||||
211
ui/src/views/intranet/widgets/AnnouncementModal.tsx
Normal file
211
ui/src/views/intranet/widgets/AnnouncementModal.tsx
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import React from 'react'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { motion } from 'framer-motion'
|
||||
import { FaTimes, FaEye, FaClipboard } from 'react-icons/fa'
|
||||
import { AnnouncementDto } from '@/proxy/intranet/models'
|
||||
import useLocale from '@/utils/hooks/useLocale'
|
||||
import { currentLocalDate } from '@/utils/dateUtils'
|
||||
import Avatar from '@/components/ui/Avatar/Avatar'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
|
||||
interface AnnouncementModalProps {
|
||||
announcement: AnnouncementDto
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const AnnouncementModal: React.FC<AnnouncementModalProps> = ({
|
||||
announcement,
|
||||
onClose,
|
||||
}) => {
|
||||
const { translate } = useLocalization()
|
||||
const currentLocale = useLocale()
|
||||
const getCategoryColor = (category: string) => {
|
||||
const colors: Record<string, string> = {
|
||||
general: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300',
|
||||
hr: 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300',
|
||||
it: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
||||
event: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||
urgent: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300',
|
||||
}
|
||||
return colors[category] || colors.general
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black/50 z-40"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 overflow-y-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-3xl w-full"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<span
|
||||
className={`px-3 py-1 text-xs font-medium rounded-full ${getCategoryColor(announcement.category)}`}
|
||||
>
|
||||
{announcement.category === 'general' &&
|
||||
`📢 ${translate('::App.Platform.Intranet.AnnouncementDetailModal.Category.General')}`}
|
||||
{announcement.category === 'hr' &&
|
||||
`👥 ${translate('::App.Platform.Intranet.AnnouncementDetailModal.Category.HR')}`}
|
||||
{announcement.category === 'it' &&
|
||||
`💻 ${translate('::App.Platform.Intranet.AnnouncementDetailModal.Category.IT')}`}
|
||||
{announcement.category === 'event' &&
|
||||
`🎉 ${translate('::App.Platform.Intranet.AnnouncementDetailModal.Category.Event')}`}
|
||||
{announcement.category === 'urgent' &&
|
||||
`🚨 ${translate('::App.Platform.Intranet.AnnouncementDetailModal.Category.Urgent')}`}
|
||||
</span>
|
||||
{announcement.isPinned && (
|
||||
<span className="text-yellow-500 text-sm">
|
||||
📌 {translate('::App.Platform.Intranet.AnnouncementDetailModal.Pinned')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
{announcement.title}
|
||||
</h2>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
<FaTimes className="w-6 h-6 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Author Info */}
|
||||
<div className="flex items-center gap-3 mt-4">
|
||||
<Avatar
|
||||
size={32}
|
||||
shape="circle"
|
||||
src={AVATAR_URL(announcement.user.id, announcement.user.tenantId)}
|
||||
/>
|
||||
<div>
|
||||
<p className="font-semibold text-gray-900 dark:text-white">
|
||||
{announcement.user.fullName}
|
||||
</p>
|
||||
<div className="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400">
|
||||
<span>{currentLocalDate(announcement.publishDate, currentLocale || 'tr')}</span>
|
||||
<span>•</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<FaEye className="w-4 h-4" />
|
||||
{announcement.viewCount}{' '}
|
||||
{translate('::App.Platform.Intranet.AnnouncementDetailModal.Views')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6 max-h-[60vh] overflow-y-auto">
|
||||
{/* Image if exists */}
|
||||
{announcement.imageUrl && (
|
||||
<img
|
||||
src={announcement.imageUrl}
|
||||
alt={announcement.title}
|
||||
className="w-full rounded-lg mb-6"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Full Content */}
|
||||
<div className="prose prose-sm dark:prose-invert max-w-none">
|
||||
<p className="text-gray-700 dark:text-gray-300 whitespace-pre-line">
|
||||
{announcement.content}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Attachments */}
|
||||
{announcement.attachments && announcement.attachments.length > 0 && (
|
||||
<div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-3 flex items-center gap-2">
|
||||
<FaClipboard className="w-5 h-5" />
|
||||
{translate('::App.Platform.Intranet.AnnouncementDetailModal.Attachments')} (
|
||||
{announcement.attachments.length})
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
{announcement.attachments.map((attachment, idx) => (
|
||||
<a
|
||||
key={idx}
|
||||
href={attachment.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
<FaClipboard className="w-5 h-5 text-gray-400" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-white truncate">
|
||||
{attachment.name}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{attachment.size}
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-sm text-blue-600 dark:text-blue-400">
|
||||
{translate('::App.Platform.Intranet.AnnouncementDetailModal.Download')}
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Departments */}
|
||||
{announcement.departments && announcement.departments.length > 0 && (
|
||||
<div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-3">
|
||||
{translate('::App.Platform.Intranet.AnnouncementDetailModal.TargetDepartments')}
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{announcement.departments?.map((dept, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
className="px-3 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 text-sm rounded-full"
|
||||
>
|
||||
{dept}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Expiry Date */}
|
||||
{announcement.expiryDate && (
|
||||
<div className="mt-6 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
<span className="font-medium">
|
||||
{translate('::App.Platform.Intranet.AnnouncementDetailModal.ExpiryDate')}:
|
||||
</span>{' '}
|
||||
{currentLocalDate(announcement.expiryDate, currentLocale || 'tr')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="p-6 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/50">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
|
||||
>
|
||||
{translate('::App.Platform.Close')}
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnnouncementModal
|
||||
105
ui/src/views/intranet/widgets/Announcements.tsx
Normal file
105
ui/src/views/intranet/widgets/Announcements.tsx
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import React from 'react'
|
||||
import { FaBell, FaClipboardCheck, FaEye } from 'react-icons/fa'
|
||||
import dayjs from 'dayjs'
|
||||
import { AnnouncementDto } from '@/proxy/intranet/models'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { Avatar } from '@/components/ui'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
|
||||
interface AnnouncementsProps {
|
||||
announcements: AnnouncementDto[]
|
||||
onAnnouncementClick: (announcement: AnnouncementDto) => void
|
||||
}
|
||||
|
||||
const Announcements: React.FC<AnnouncementsProps> = ({ announcements, onAnnouncementClick }) => {
|
||||
const pinnedAnnouncements = announcements.filter((a) => a.isPinned).slice(0, 3)
|
||||
const { translate } = useLocalization()
|
||||
|
||||
const getCategoryColor = (category: string) => {
|
||||
const colors: Record<string, string> = {
|
||||
general: 'bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300',
|
||||
hr: 'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300',
|
||||
it: 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300',
|
||||
event: 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300',
|
||||
urgent: 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300',
|
||||
}
|
||||
return colors[category] || colors.general
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
||||
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-base font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<FaBell className="w-5 h-5" />
|
||||
{translate('::App.Platform.Intranet.Widgets.Announcements.Title')}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{pinnedAnnouncements.map((announcement) => (
|
||||
<div
|
||||
key={announcement.id}
|
||||
onClick={() => onAnnouncementClick(announcement)}
|
||||
className="p-6 hover:bg-gray-50 dark:hover:bg-gray-700/50 cursor-pointer transition-colors"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<h3 className="text-base font-semibold text-gray-900 dark:text-white">
|
||||
{announcement.title}
|
||||
</h3>
|
||||
{announcement.category && (
|
||||
<span
|
||||
className={`px-2 py-1 text-center text-xs rounded-full ${getCategoryColor(announcement.category)}`}
|
||||
>
|
||||
{(() => {
|
||||
const key = `::App.Platform.Intranet.Widgets.Announcements.Category.${announcement.category.charAt(0).toUpperCase() + announcement.category.slice(1)}`
|
||||
const translated = translate(key)
|
||||
return translated === key ? announcement.category : translated
|
||||
})()}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
|
||||
{announcement.excerpt}
|
||||
</p>
|
||||
<div className="flex items-center gap-2 mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
<Avatar
|
||||
size={24}
|
||||
shape="circle"
|
||||
src={AVATAR_URL(announcement.user.id, announcement.user.tenantId)}
|
||||
/>
|
||||
<span>{announcement.user.fullName}</span>
|
||||
<span>•</span>
|
||||
<span>{dayjs(announcement.publishDate).fromNow()}</span>
|
||||
<span>•</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<FaEye className="w-3 h-3" />
|
||||
{announcement.viewCount}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{pinnedAnnouncements.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-full mb-4">
|
||||
<FaClipboardCheck className="w-8 h-8 text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">
|
||||
{translate('::App.Platform.Intranet.Widgets.Announcements.NoActive')}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{translate('::App.Platform.Intranet.Widgets.Announcements.WillAppearHere')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Announcements
|
||||
84
ui/src/views/intranet/widgets/RecentDocuments.tsx
Normal file
84
ui/src/views/intranet/widgets/RecentDocuments.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import React from 'react'
|
||||
import { FaFileAlt, FaDownload } from 'react-icons/fa'
|
||||
import dayjs from 'dayjs'
|
||||
import { DocumentDto } from '@/proxy/intranet/models'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { getFileIcon, getFileType } from '@/proxy/intranet/utils'
|
||||
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
if (bytes === 0) return '0 B'
|
||||
const k = 1024
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`
|
||||
}
|
||||
|
||||
const RecentDocuments: React.FC<{ documents: DocumentDto[] }> = ({ documents }) => {
|
||||
const { translate } = useLocalization();
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
|
||||
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-base font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<FaFileAlt className="w-5 h-5" />
|
||||
{translate('::App.Platform.Intranet.Widgets.RecentDocuments.Title')}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{documents.length > 0 ? (
|
||||
documents.slice(0, 3).map((doc) => (
|
||||
<div
|
||||
key={doc.id}
|
||||
className="p-4 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="p-2 bg-blue-100 dark:bg-blue-900/30 rounded-lg">
|
||||
{getFileIcon(doc.extension)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-medium text-gray-900 dark:text-white truncate">
|
||||
{doc.name}
|
||||
</h4>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
||||
{getFileType(doc.extension)}
|
||||
<span className="mx-1">•</span>
|
||||
{formatFileSize(doc.size)}
|
||||
</p>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1 flex items-center gap-2">
|
||||
<span>{dayjs(doc.modifiedAt).fromNow()}</span>
|
||||
{doc.isReadOnly && (
|
||||
<>
|
||||
<span>•</span>
|
||||
<span className="text-orange-500">🔒 {translate('::App.Platform.Intranet.Widgets.RecentDocuments.ReadOnly')}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
const link = document.createElement('a')
|
||||
link.href = `/cdn/${doc.path}`
|
||||
link.download = doc.name
|
||||
link.click()
|
||||
}}
|
||||
className="p-2 hover:bg-blue-100 dark:hover:bg-blue-900/30 rounded-lg transition-colors group"
|
||||
title={translate('::App.Platform.Intranet.Widgets.RecentDocuments.Download')}
|
||||
>
|
||||
<FaDownload className="w-5 h-5 text-gray-600 dark:text-gray-400 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="p-4 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
{translate('::App.Platform.Intranet.Widgets.RecentDocuments.NoDocuments')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RecentDocuments
|
||||
275
ui/src/views/intranet/widgets/SurveyModal.tsx
Normal file
275
ui/src/views/intranet/widgets/SurveyModal.tsx
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
import React, { useState } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { FaTimes } from 'react-icons/fa'
|
||||
import { SurveyAnswerDto, SurveyDto, SurveyQuestionDto } from '@/proxy/intranet/models'
|
||||
|
||||
interface SurveyModalProps {
|
||||
survey: SurveyDto
|
||||
onClose: () => void
|
||||
onSubmit: (answers: SurveyAnswerDto[]) => void
|
||||
}
|
||||
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit }) => {
|
||||
const { translate } = useLocalization();
|
||||
const [answers, setAnswers] = useState<{ [questionId: string]: any }>({})
|
||||
const [errors, setErrors] = useState<{ [questionId: string]: string }>({})
|
||||
|
||||
const handleAnswerChange = (questionId: string, value: any) => {
|
||||
setAnswers((prev) => ({
|
||||
...prev,
|
||||
[questionId]: value,
|
||||
}))
|
||||
|
||||
// Clear error when user provides an answer
|
||||
if (errors[questionId]) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[questionId]: '',
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const validateAnswers = (): boolean => {
|
||||
const newErrors: { [questionId: string]: string } = {}
|
||||
|
||||
survey.questions.forEach((question) => {
|
||||
if (question.isRequired && (!answers[question.id] || answers[question.id] === '')) {
|
||||
newErrors[question.id] = translate('::App.Platform.Intranet.SurveyModal.RequiredField')
|
||||
}
|
||||
})
|
||||
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
if (!validateAnswers()) {
|
||||
return
|
||||
}
|
||||
|
||||
const surveyAnswers: SurveyAnswerDto[] = survey.questions.map((question) => ({
|
||||
questionId: question.id,
|
||||
questionType: question.type,
|
||||
value:
|
||||
answers[question.id] ||
|
||||
(question.type === 'multiple-choice' ? '' : question.type === 'rating' ? 0 : ''),
|
||||
}))
|
||||
|
||||
onSubmit(surveyAnswers)
|
||||
}
|
||||
|
||||
const renderQuestion = (question: SurveyQuestionDto, index: number) => {
|
||||
const questionNumber = index + 1
|
||||
const hasError = !!errors[question.id]
|
||||
|
||||
switch (question.type) {
|
||||
case 'rating':
|
||||
return (
|
||||
<div key={question.id} className="space-y-2">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||
</label>
|
||||
{hasError && (
|
||||
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'multiple-choice':
|
||||
return (
|
||||
<div key={question.id} className="space-y-2">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
{question.options?.map((option) => (
|
||||
<label
|
||||
key={option.id}
|
||||
className={`flex items-center gap-3 p-3 border rounded-lg cursor-pointer transition-colors ${
|
||||
answers[question.id] === option.id
|
||||
? 'bg-blue-50 border-blue-500 dark:bg-blue-900/20 dark:border-blue-400'
|
||||
: 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name={`question-${question.id}`}
|
||||
value={option.id}
|
||||
checked={answers[question.id] === option.id}
|
||||
onChange={(e) => handleAnswerChange(question.id, e.target.value)}
|
||||
className="w-4 h-4 text-blue-600"
|
||||
/>
|
||||
<span className="text-sm text-gray-900 dark:text-white">{option.text}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
{hasError && (
|
||||
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'yes-no':
|
||||
return (
|
||||
<div key={question.id} className="space-y-2">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||
</label>
|
||||
<div className="flex gap-4">
|
||||
{['yes', 'no'].map((value) => (
|
||||
<label
|
||||
key={value}
|
||||
className={`flex items-center gap-2 px-4 py-2 border rounded-lg cursor-pointer transition-colors ${
|
||||
answers[question.id] === value
|
||||
? 'bg-blue-50 border-blue-500 dark:bg-blue-900/20 dark:border-blue-400'
|
||||
: 'border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name={`question-${question.id}`}
|
||||
value={value}
|
||||
checked={answers[question.id] === value}
|
||||
onChange={(e) => handleAnswerChange(question.id, e.target.value)}
|
||||
className="w-4 h-4 text-blue-600"
|
||||
/>
|
||||
<span className="text-sm text-gray-900 dark:text-white">
|
||||
{value === 'yes' ? 'Evet' : 'Hayır'}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
{hasError && (
|
||||
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'text':
|
||||
return (
|
||||
<div key={question.id} className="space-y-2">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={answers[question.id] || ''}
|
||||
onChange={(e) => handleAnswerChange(question.id, e.target.value)}
|
||||
className={`w-full px-4 py-2 border rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 ${
|
||||
hasError ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'
|
||||
}`}
|
||||
placeholder={translate('::App.Platform.Intranet.SurveyModal.AnswerPlaceholder')}
|
||||
/>
|
||||
{hasError && (
|
||||
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
case 'textarea':
|
||||
return (
|
||||
<div key={question.id} className="space-y-2">
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||
</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
value={answers[question.id] || ''}
|
||||
onChange={(e) => handleAnswerChange(question.id, e.target.value)}
|
||||
className={`w-full px-4 py-2 border rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 ${
|
||||
hasError ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'
|
||||
}`}
|
||||
placeholder={translate('::App.Platform.Intranet.SurveyModal.CommentPlaceholder')}
|
||||
/>
|
||||
{hasError && (
|
||||
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black/50 z-40"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="p-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between sticky top-0 bg-white dark:bg-gray-800 z-10">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
{survey.title}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">{survey.description}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
<FaTimes className="w-5 h-5 text-gray-500" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="p-6 space-y-6">
|
||||
<div className="space-y-6">
|
||||
{survey.questions
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map((question, index) => renderQuestion(question, index))}
|
||||
</div>
|
||||
|
||||
{!survey.isAnonymous && (
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3">
|
||||
<p className="text-sm text-blue-700 dark:text-blue-300">
|
||||
ℹ️ Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız kaydedilecektir.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{survey.isAnonymous && (
|
||||
<div className="bg-green-50 dark:bg-green-900/20 rounded-lg p-3">
|
||||
<p className="text-sm text-green-700 dark:text-green-300">
|
||||
✅ Bu anket anonimdir. Kimlik bilgileriniz kaydedilmeyecektir.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
{translate('::Cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
|
||||
>
|
||||
{translate('::App.Platform.Intranet.SurveyModal.Submit')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</motion.div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SurveyModal
|
||||
149
ui/src/views/intranet/widgets/Surveys.tsx
Normal file
149
ui/src/views/intranet/widgets/Surveys.tsx
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import React from 'react'
|
||||
import { FaClipboardCheck, FaQuestionCircle, FaUsers, FaClock, FaArrowRight } from 'react-icons/fa'
|
||||
import dayjs from 'dayjs'
|
||||
import { SurveyDto } from '@/proxy/intranet/models'
|
||||
import useLocale from '@/utils/hooks/useLocale'
|
||||
import { currentLocalDate } from '@/utils/dateUtils'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
|
||||
interface SurveysProps {
|
||||
surveys?: SurveyDto[]
|
||||
onTakeSurvey: (survey: SurveyDto) => void
|
||||
}
|
||||
|
||||
const Surveys: React.FC<SurveysProps> = ({ surveys, onTakeSurvey }) => {
|
||||
const currentLocale = useLocale()
|
||||
const { translate } = useLocalization()
|
||||
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-white to-gray-50 dark:from-gray-800 dark:to-gray-850 rounded-xl shadow-lg border border-gray-200/50 dark:border-gray-700/50 overflow-hidden">
|
||||
{/* Header with gradient */}
|
||||
|
||||
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-base font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
<FaClipboardCheck className="w-5 h-5" />
|
||||
{translate('::App.Platform.Intranet.Widgets.ActiveSurveys.Title')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="p-6 space-y-4">
|
||||
{surveys?.map((survey, index) => {
|
||||
const daysLeft = dayjs(survey.deadline).diff(dayjs(), 'day')
|
||||
const urgency = daysLeft <= 3 ? 'urgent' : daysLeft <= 7 ? 'warning' : 'normal'
|
||||
|
||||
return (
|
||||
<div
|
||||
key={survey.id}
|
||||
onClick={() => onTakeSurvey(survey)}
|
||||
className="group relative p-5 rounded-xl bg-white dark:bg-gray-750 border border-gray-200 dark:border-gray-600 hover:border-purple-300 dark:hover:border-purple-500 cursor-pointer transition-all duration-300 hover:shadow-lg hover:-translate-y-1"
|
||||
>
|
||||
{/* Background gradient on hover */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-50 to-pink-50 dark:from-purple-900/10 dark:to-pink-900/10 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
||||
|
||||
<div className="relative">
|
||||
{/* Survey Title */}
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<h4 className="text-base font-semibold text-gray-900 dark:text-white group-hover:text-purple-700 dark:group-hover:text-purple-300 transition-colors">
|
||||
{survey.title}
|
||||
</h4>
|
||||
<div
|
||||
className={`px-2 py-1 text-center rounded-full text-xs font-medium ${
|
||||
urgency === 'urgent'
|
||||
? 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'
|
||||
: urgency === 'warning'
|
||||
? 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300'
|
||||
: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300'
|
||||
}`}
|
||||
>
|
||||
{daysLeft > 0 ? translate('::App.Platform.Intranet.Widgets.ActiveSurveys.DaysLeft', { count: daysLeft }) : translate('::App.Platform.Intranet.Widgets.ActiveSurveys.LastDay')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Survey Stats */}
|
||||
<div className="grid grid-cols-3 gap-4 mb-4">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<div className="p-1.5 bg-blue-100 dark:bg-blue-900/30 rounded-lg">
|
||||
<FaQuestionCircle className="w-3 h-3 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">{translate('::App.Platform.Intranet.Widgets.ActiveSurveys.Questions')}</p>
|
||||
<p className="font-semibold text-gray-900 dark:text-white">
|
||||
{survey.questions.length}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<div className="p-1.5 bg-green-100 dark:bg-green-900/30 rounded-lg">
|
||||
<FaUsers className="w-3 h-3 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">{translate('::App.Platform.Intranet.Widgets.ActiveSurveys.Responses')}</p>
|
||||
<p className="font-semibold text-gray-900 dark:text-white">
|
||||
{survey.responses}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<div className="p-1.5 bg-purple-100 dark:bg-purple-900/30 rounded-lg">
|
||||
<FaClock className="w-3 h-3 text-purple-600 dark:text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">{translate('::App.Platform.Intranet.Widgets.ActiveSurveys.Duration')}</p>
|
||||
<p className="font-semibold text-gray-900 dark:text-white">~5dk</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress Bar */}
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between text-xs mb-1">
|
||||
<span className="text-gray-600 dark:text-gray-400">{translate('::App.Platform.Intranet.Widgets.ActiveSurveys.CompletionRate')}</span>
|
||||
<span className="text-gray-800 dark:text-gray-200 font-medium">
|
||||
{Math.round((survey.responses / 100) * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2">
|
||||
<div
|
||||
className="bg-gradient-to-r from-purple-500 to-pink-500 h-2 rounded-full transition-all duration-500"
|
||||
style={{ width: `${Math.min((survey.responses / 100) * 100, 100)}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Deadline */}
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
<FaClock className="inline w-3 h-3 mr-1" />
|
||||
{currentLocalDate(survey.deadline, currentLocale || 'tr')}
|
||||
</p>
|
||||
|
||||
{/* Action Button */}
|
||||
<button className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white text-sm font-medium rounded-lg transition-all duration-300 transform group-hover:scale-[1.02] shadow-sm hover:shadow-md">
|
||||
{translate('::App.Platform.Intranet.Widgets.ActiveSurveys.FillSurvey')}
|
||||
<FaArrowRight className="w-3 h-3 transition-transform group-hover:translate-x-1" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{surveys?.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-full mb-4">
|
||||
<FaClipboardCheck className="w-8 h-8 text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">
|
||||
{translate('::App.Platform.Intranet.Widgets.ActiveSurveys.NoActive')}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{translate('::App.Platform.Intranet.Widgets.ActiveSurveys.WillAppearHere')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Surveys
|
||||
58
ui/src/views/intranet/widgets/TodayBirthdays.tsx
Normal file
58
ui/src/views/intranet/widgets/TodayBirthdays.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import React from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { UserInfoViewModel } from '@/proxy/admin/models'
|
||||
import { Avatar } from '@/components/ui'
|
||||
import { AVATAR_URL } from '@/constants/app.constant'
|
||||
|
||||
const TodayBirthdays: React.FC<{ employees: UserInfoViewModel[] }> = ({ employees }) => {
|
||||
const today = dayjs()
|
||||
const { translate } = useLocalization();
|
||||
|
||||
console.log('TodayBirthdays rendered with employees:', employees)
|
||||
return (
|
||||
<div className="bg-gradient-to-br from-pink-50 to-purple-50 dark:from-pink-900/20 dark:to-purple-900/20 rounded-lg shadow-sm border border-pink-200 dark:border-pink-800">
|
||||
<div className="p-4 border-b border-pink-200 dark:border-pink-700">
|
||||
<h2 className="text-base font-semibold text-gray-900 dark:text-white flex items-center gap-2">
|
||||
🎂 {translate('::App.Platform.Intranet.Widgets.TodayBirthdays.Title')}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="p-2 space-y-3">
|
||||
{employees.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
{employees.map((birthday, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-2 p-2"
|
||||
>
|
||||
<Avatar
|
||||
size={48}
|
||||
shape="circle"
|
||||
src={AVATAR_URL(birthday.id, birthday.tenantId)}
|
||||
alt={birthday.fullName}
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
{birthday.fullName}
|
||||
</p>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">
|
||||
{translate('::App.Platform.Intranet.Widgets.TodayBirthdays.Age', { age: today.diff(dayjs(birthday.birthDate), 'year') })} 🎉
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-500 mt-1">
|
||||
{birthday.department?.name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 text-center py-4">
|
||||
{translate('::App.Platform.Intranet.Widgets.TodayBirthdays.NoBirthday')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TodayBirthdays
|
||||
|
|
@ -341,7 +341,7 @@ const Chart = (props: ChartProps) => {
|
|||
className="p-1 pl-6 pr-2 border border-1 outline-none text-xs text-gray-700 dark:text-gray-200 placeholder-gray-400 rounded"
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant={'default'}
|
||||
className="text-sm flex items-center gap-1"
|
||||
onClick={async () => {
|
||||
|
|
@ -353,7 +353,7 @@ const Chart = (props: ChartProps) => {
|
|||
<FaSyncAlt className="w-3 h-3" /> {translate('::App.Platform.Refresh')}
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="default"
|
||||
className="text-sm flex items-center gap-1"
|
||||
onClick={() => setOpenDrawer(true)}
|
||||
|
|
@ -364,7 +364,7 @@ const Chart = (props: ChartProps) => {
|
|||
|
||||
{checkPermission(gridDto?.gridOptions.permissionDto.u) && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant={'default'}
|
||||
className="text-sm"
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ const ChartDrawer = ({
|
|||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant="plain"
|
||||
icon={<FaMinus />}
|
||||
className="text-red-500 hover:bg-red-100"
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ const List: React.FC = () => {
|
|||
gridDto?.gridOptions?.schedulerOptionDto?.textExpr &&
|
||||
gridDto?.gridOptions?.schedulerOptionDto?.startDateExpr && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant={viewMode === 'scheduler' ? 'solid' : 'default'}
|
||||
onClick={() => setLayout('scheduler')}
|
||||
onMouseEnter={() => preload.scheduler()}
|
||||
|
|
@ -173,7 +173,7 @@ const List: React.FC = () => {
|
|||
gridDto?.gridOptions?.ganttOptionDto?.parentIdExpr &&
|
||||
gridDto?.gridOptions?.ganttOptionDto?.titleExpr && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant={viewMode === 'gantt' ? 'solid' : 'default'}
|
||||
onClick={() => setLayout('gantt')}
|
||||
onMouseEnter={() => preload.gantt()}
|
||||
|
|
@ -185,7 +185,7 @@ const List: React.FC = () => {
|
|||
{gridDto?.gridOptions?.layoutDto.tree &&
|
||||
gridDto?.gridOptions?.treeOptionDto?.parentIdExpr && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant={viewMode === 'tree' ? 'solid' : 'default'}
|
||||
onClick={() => setLayout('tree')}
|
||||
onMouseEnter={() => preload.tree()}
|
||||
|
|
@ -195,7 +195,7 @@ const List: React.FC = () => {
|
|||
)}
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant={viewMode === 'grid' ? 'solid' : 'default'}
|
||||
onClick={() => setLayout('grid')}
|
||||
onMouseEnter={() => preload.grid()}
|
||||
|
|
@ -205,7 +205,7 @@ const List: React.FC = () => {
|
|||
|
||||
{gridDto.gridOptions.layoutDto.pivot && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant={viewMode === 'pivot' ? 'solid' : 'default'}
|
||||
onClick={() => setLayout('pivot')}
|
||||
onMouseEnter={() => preload.pivot()}
|
||||
|
|
@ -216,7 +216,7 @@ const List: React.FC = () => {
|
|||
|
||||
{gridDto.gridOptions.layoutDto.chart && (
|
||||
<Button
|
||||
size="xs"
|
||||
size="sm"
|
||||
variant={viewMode === 'chart' ? 'solid' : 'default'}
|
||||
onClick={() => setLayout('chart')}
|
||||
onMouseEnter={() => preload.chart()}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue