mockIntranet kaldırıldı
Intranet Dashboard düzeltildi.
This commit is contained in:
parent
264f802265
commit
988a8d0bfb
22 changed files with 422 additions and 3646 deletions
|
|
@ -17,5 +17,7 @@ public class IntranetDashboardDto
|
||||||
public List<MealDto> Meals { get; set; } = [];
|
public List<MealDto> Meals { get; set; } = [];
|
||||||
public List<LeaveDto> Leaves { get; set; } = [];
|
public List<LeaveDto> Leaves { get; set; } = [];
|
||||||
public List<OvertimeDto> Overtimes { get; set; } = [];
|
public List<OvertimeDto> Overtimes { get; set; } = [];
|
||||||
|
public List<SurveyDto> Surveys { get; set; } = [];
|
||||||
|
public List<SocialPostDto> SocialPosts { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.Intranet;
|
||||||
|
|
||||||
|
public class SocialPostDto : FullAuditedEntityDto<Guid>
|
||||||
|
{
|
||||||
|
public Guid? EmployeeId { get; set; }
|
||||||
|
public EmployeeDto? Employee { 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? EmployeeId { get; set; }
|
||||||
|
public EmployeeDto? Employee { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SocialLikeDto : FullAuditedEntityDto<Guid>
|
||||||
|
{
|
||||||
|
public Guid SocialPostId { get; set; }
|
||||||
|
public Guid? EmployeeId { get; set; }
|
||||||
|
public EmployeeDto? Employee { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Kurs.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? EmployeeId { 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; }
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ using Kurs.Platform.Entities;
|
||||||
using Kurs.Platform.FileManagement;
|
using Kurs.Platform.FileManagement;
|
||||||
using Kurs.Platform.Intranet;
|
using Kurs.Platform.Intranet;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
@ -35,6 +36,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
private readonly IRepository<Meal, Guid> _mealRepository;
|
private readonly IRepository<Meal, Guid> _mealRepository;
|
||||||
private readonly IRepository<Leave, Guid> _leaveRepository;
|
private readonly IRepository<Leave, Guid> _leaveRepository;
|
||||||
private readonly IRepository<Overtime, Guid> _overtimeRepository;
|
private readonly IRepository<Overtime, Guid> _overtimeRepository;
|
||||||
|
private readonly IRepository<Survey, Guid> _surveyRepository;
|
||||||
|
private readonly IRepository<SocialPost, Guid> _socialPostRepository;
|
||||||
|
|
||||||
public IntranetAppService(
|
public IntranetAppService(
|
||||||
ICurrentTenant currentTenant,
|
ICurrentTenant currentTenant,
|
||||||
|
|
@ -52,7 +55,9 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
IRepository<ShuttleRoute, Guid> shuttleRouteRepository,
|
IRepository<ShuttleRoute, Guid> shuttleRouteRepository,
|
||||||
IRepository<Meal, Guid> mealRepository,
|
IRepository<Meal, Guid> mealRepository,
|
||||||
IRepository<Leave, Guid> leaveRepository,
|
IRepository<Leave, Guid> leaveRepository,
|
||||||
IRepository<Overtime, Guid> overtimeRepository
|
IRepository<Overtime, Guid> overtimeRepository,
|
||||||
|
IRepository<Survey, Guid> surveyRepository,
|
||||||
|
IRepository<SocialPost, Guid> socialPostRepository
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_currentTenant = currentTenant;
|
_currentTenant = currentTenant;
|
||||||
|
|
@ -71,6 +76,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
_mealRepository = mealRepository;
|
_mealRepository = mealRepository;
|
||||||
_leaveRepository = leaveRepository;
|
_leaveRepository = leaveRepository;
|
||||||
_overtimeRepository = overtimeRepository;
|
_overtimeRepository = overtimeRepository;
|
||||||
|
_surveyRepository = surveyRepository;
|
||||||
|
_socialPostRepository = socialPostRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IntranetDashboardDto> GetIntranetDashboardAsync()
|
public async Task<IntranetDashboardDto> GetIntranetDashboardAsync()
|
||||||
|
|
@ -88,10 +95,34 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
ShuttleRoutes = await GetShuttleRoutesAsync(),
|
ShuttleRoutes = await GetShuttleRoutesAsync(),
|
||||||
Meals = await GetMealsAsync(),
|
Meals = await GetMealsAsync(),
|
||||||
Leaves = await GetLeavesAsync(),
|
Leaves = await GetLeavesAsync(),
|
||||||
Overtimes = await GetOvertimesAsync()
|
Overtimes = await GetOvertimesAsync(),
|
||||||
|
Surveys = await GetSurveysAsync(),
|
||||||
|
SocialPosts = await GetSocialPostsAsync()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<List<SocialPostDto>> GetSocialPostsAsync()
|
||||||
|
{
|
||||||
|
var socialPosts = await _socialPostRepository
|
||||||
|
.WithDetailsAsync(e => e.Employee, e => e.Location, e => e.Media, e => e.Comments, e => e.Likes)
|
||||||
|
.ContinueWith(t => t.Result.ToList());
|
||||||
|
|
||||||
|
return ObjectMapper.Map<List<SocialPost>, List<SocialPostDto>>(socialPosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<SurveyDto>> GetSurveysAsync()
|
||||||
|
{
|
||||||
|
var queryable = await _surveyRepository.GetQueryableAsync();
|
||||||
|
|
||||||
|
var surveys = queryable
|
||||||
|
.Where(s => s.Status == "active")
|
||||||
|
.Include(s => s.Questions)
|
||||||
|
.ThenInclude(q => q.Options)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return ObjectMapper.Map<List<Survey>, List<SurveyDto>>(surveys);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<List<OvertimeDto>> GetOvertimesAsync()
|
private async Task<List<OvertimeDto>> GetOvertimesAsync()
|
||||||
{
|
{
|
||||||
var today = DateTime.Now;
|
var today = DateTime.Now;
|
||||||
|
|
|
||||||
|
|
@ -28,5 +28,17 @@ public class IntranetAutoMapperProfile : Profile
|
||||||
CreateMap<ShuttleRoute, ShuttleRouteDto>()
|
CreateMap<ShuttleRoute, ShuttleRouteDto>()
|
||||||
.ForMember(dest => dest.Route, opt => opt.Ignore());
|
.ForMember(dest => dest.Route, opt => opt.Ignore());
|
||||||
|
|
||||||
|
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>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ public class SocialPost : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
public Guid? TenantId { get; set; }
|
public Guid? TenantId { get; set; }
|
||||||
|
|
||||||
public Guid? EmployeeId { get; set; }
|
public Guid? EmployeeId { get; set; }
|
||||||
public Employee? Employee { get; set; }
|
public Employee Employee { get; set; }
|
||||||
|
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
|
@ -19,8 +19,8 @@ public class SocialPost : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
public bool IsOwnPost { get; set; }
|
public bool IsOwnPost { get; set; }
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
public SocialLocation? Location { get; set; }
|
public SocialLocation Location { get; set; }
|
||||||
public SocialMedia? Media { get; set; }
|
public SocialMedia Media { get; set; }
|
||||||
public ICollection<SocialComment> Comments { get; set; }
|
public ICollection<SocialComment> Comments { get; set; }
|
||||||
public ICollection<SocialLike> Likes { get; set; }
|
public ICollection<SocialLike> Likes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Kurs.Platform.Migrations
|
namespace Kurs.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20251029074530_Initial")]
|
[Migration("20251029185945_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -28,7 +28,8 @@ export interface IntranetDashboardDto {
|
||||||
meals: MealDto[]
|
meals: MealDto[]
|
||||||
leaves: LeaveDto[]
|
leaves: LeaveDto[]
|
||||||
overtimes: OvertimeDto[]
|
overtimes: OvertimeDto[]
|
||||||
// surveys: Survey[]
|
surveys: SurveyDto[]
|
||||||
|
socialPosts: SocialPostDto[]
|
||||||
// priorityTasks: TaskDto[]
|
// priorityTasks: TaskDto[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,3 +324,97 @@ export interface LeaveDto {
|
||||||
creationTime: Date
|
creationTime: Date
|
||||||
lastModificationTime: Date
|
lastModificationTime: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Anket Cevap
|
||||||
|
export interface SurveyAnswerDto {
|
||||||
|
questionId: string
|
||||||
|
questionType: 'rating' | 'multiple-choice' | 'text' | 'textarea' | 'yes-no'
|
||||||
|
value: string | number | string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sosyal Duvar - Comment Interface
|
||||||
|
export interface SocialCommentDto {
|
||||||
|
id: string
|
||||||
|
creator: EmployeeDto
|
||||||
|
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
|
||||||
|
employee: EmployeeDto
|
||||||
|
content: string
|
||||||
|
locationJson?: string
|
||||||
|
media?: SocialMediaDto
|
||||||
|
likeCount: number
|
||||||
|
isLiked: boolean
|
||||||
|
likeUsers: EmployeeDto[]
|
||||||
|
comments: SocialCommentDto[]
|
||||||
|
isOwnPost: boolean
|
||||||
|
creationTime: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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: EmployeeDto
|
||||||
|
creationTime: Date
|
||||||
|
deadline: Date
|
||||||
|
questions: SurveyQuestionDto[]
|
||||||
|
responses: number
|
||||||
|
targetAudience: string[]
|
||||||
|
status: 'draft' | 'active' | 'closed'
|
||||||
|
isAnonymous: boolean
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import { EmployeeDto } from "@/proxy/intranet/models"
|
import {
|
||||||
|
EmployeeDto,
|
||||||
|
SurveyQuestionDto,
|
||||||
|
} from '@/proxy/intranet/models'
|
||||||
|
|
||||||
// Görev
|
// Görev
|
||||||
export interface Task {
|
export interface Task {
|
||||||
|
|
@ -16,102 +19,3 @@ export interface Task {
|
||||||
attachments?: { name: string; url: string }[]
|
attachments?: { name: string; url: string }[]
|
||||||
comments: number
|
comments: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anket
|
|
||||||
export interface Survey {
|
|
||||||
id: string
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
creatorId: EmployeeDto
|
|
||||||
creationTime: Date
|
|
||||||
deadline: Date
|
|
||||||
questions: SurveyQuestion[]
|
|
||||||
responses: number
|
|
||||||
targetAudience: string[]
|
|
||||||
status: 'draft' | 'active' | 'closed'
|
|
||||||
isAnonymous: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anket Sorusu
|
|
||||||
export interface SurveyQuestion {
|
|
||||||
id: string
|
|
||||||
surveyId: string
|
|
||||||
questionText: string
|
|
||||||
type: 'rating' | 'multiple-choice' | 'text' | 'textarea' | 'yes-no'
|
|
||||||
order: number
|
|
||||||
isRequired: boolean
|
|
||||||
options?: SurveyQuestionOption[]
|
|
||||||
ratingConfig?: {
|
|
||||||
min: number
|
|
||||||
max: number
|
|
||||||
labels?: { [key: number]: string }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anket Sorusu Seçeneği
|
|
||||||
export interface SurveyQuestionOption {
|
|
||||||
id: string
|
|
||||||
text: string
|
|
||||||
order: number
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anket Cevabı
|
|
||||||
export interface SurveyResponse {
|
|
||||||
id: string
|
|
||||||
surveyId: string
|
|
||||||
respondentId?: string // Anonymous ise null
|
|
||||||
submissionTime: Date
|
|
||||||
answers: SurveyAnswer[]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anket Cevap
|
|
||||||
export interface SurveyAnswer {
|
|
||||||
questionId: string
|
|
||||||
questionType: 'rating' | 'multiple-choice' | 'text' | 'textarea' | 'yes-no'
|
|
||||||
value: string | number | string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sosyal Duvar - Ana Interface
|
|
||||||
export interface SocialPost {
|
|
||||||
id: string
|
|
||||||
creator: EmployeeDto
|
|
||||||
content: string
|
|
||||||
locationJson?: string
|
|
||||||
media?: SocialMedia
|
|
||||||
likeCount: number
|
|
||||||
isLiked: boolean
|
|
||||||
likeUsers: EmployeeDto[]
|
|
||||||
comments: SocialComment[]
|
|
||||||
isOwnPost: boolean
|
|
||||||
creationTime: Date
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sosyal Duvar - Social Media Interface
|
|
||||||
export interface SocialMedia {
|
|
||||||
id?: string
|
|
||||||
type: 'image' | 'video' | 'poll'
|
|
||||||
|
|
||||||
// Ortak alanlar
|
|
||||||
urls?: string[]
|
|
||||||
|
|
||||||
// Anket (poll) ile ilgili alanlar doğrudan burada
|
|
||||||
pollQuestion?: string
|
|
||||||
pollOptions?: SocialPollOption[]
|
|
||||||
pollTotalVotes?: number
|
|
||||||
pollEndsAt?: Date
|
|
||||||
pollUserVoteId?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SocialPollOption {
|
|
||||||
id: string
|
|
||||||
text: string
|
|
||||||
votes: number
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sosyal Duvar - Comment Interface
|
|
||||||
export interface SocialComment {
|
|
||||||
id: string
|
|
||||||
creator: EmployeeDto
|
|
||||||
content: string
|
|
||||||
creationTime: Date
|
|
||||||
}
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -15,11 +15,12 @@ import {
|
||||||
FaMapMarkerAlt,
|
FaMapMarkerAlt,
|
||||||
FaBriefcase,
|
FaBriefcase,
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
import { EmployeeDto, HrOrganizationChart as OrgChart } from '../../../types/hr'
|
import { HrOrganizationChart as OrgChart } from '../../../types/hr'
|
||||||
import { mockEmployees } from '../../../mocks/mockEmployees'
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
||||||
import { mockDepartments } from '../../../mocks/mockDepartments'
|
import { mockDepartments } from '../../../mocks/mockDepartments'
|
||||||
import Widget from '../../../components/common/Widget'
|
import Widget from '../../../components/common/Widget'
|
||||||
import { Container } from '@/components/shared'
|
import { Container } from '@/components/shared'
|
||||||
|
import { EmployeeDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
// Dinamik organizasyon verisi oluşturma fonksiyonu
|
// Dinamik organizasyon verisi oluşturma fonksiyonu
|
||||||
const generateOrganizationData = (): OrgChart[] => {
|
const generateOrganizationData = (): OrgChart[] => {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -29,10 +29,10 @@ import AnnouncementDetailModal from './modals/AnnouncementDetailModal'
|
||||||
|
|
||||||
// Social Wall
|
// Social Wall
|
||||||
import SocialWall from './SocialWall'
|
import SocialWall from './SocialWall'
|
||||||
import { Survey, SurveyAnswer } from '@/types/intranet'
|
import { SurveyDto } from '@/types/intranet'
|
||||||
import { Container } from '@/components/shared'
|
import { Container } from '@/components/shared'
|
||||||
import { usePermission } from '@/utils/hooks/usePermission'
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
import { AnnouncementDto, IntranetDashboardDto } from '@/proxy/intranet/models'
|
import { AnnouncementDto, IntranetDashboardDto, SurveyAnswerDto } from '@/proxy/intranet/models'
|
||||||
import { intranetService } from '@/services/intranet.service'
|
import { intranetService } from '@/services/intranet.service'
|
||||||
import Announcements from './widgets/Announcements'
|
import Announcements from './widgets/Announcements'
|
||||||
import ShuttleRoute from './widgets/ShuttleRoute'
|
import ShuttleRoute from './widgets/ShuttleRoute'
|
||||||
|
|
@ -46,7 +46,7 @@ const WIDGET_ORDER_KEY = 'dashboard-widget-order'
|
||||||
const IntranetDashboard: React.FC = () => {
|
const IntranetDashboard: React.FC = () => {
|
||||||
const { checkPermission } = usePermission()
|
const { checkPermission } = usePermission()
|
||||||
const [selectedAnnouncement, setSelectedAnnouncement] = useState<AnnouncementDto | null>(null)
|
const [selectedAnnouncement, setSelectedAnnouncement] = useState<AnnouncementDto | null>(null)
|
||||||
const [selectedSurvey, setSelectedSurvey] = useState<Survey | null>(null)
|
const [selectedSurvey, setSelectedSurvey] = useState<SurveyDto | null>(null)
|
||||||
const [showSurveyModal, setShowSurveyModal] = useState(false)
|
const [showSurveyModal, setShowSurveyModal] = useState(false)
|
||||||
const [showLeaveModal, setShowLeaveModal] = useState(false)
|
const [showLeaveModal, setShowLeaveModal] = useState(false)
|
||||||
const [showOvertimeModal, setShowOvertimeModal] = useState(false)
|
const [showOvertimeModal, setShowOvertimeModal] = useState(false)
|
||||||
|
|
@ -81,12 +81,12 @@ const IntranetDashboard: React.FC = () => {
|
||||||
targetIndex: null,
|
targetIndex: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleTakeSurvey = (survey: Survey) => {
|
const handleTakeSurvey = (survey: SurveyDto) => {
|
||||||
setSelectedSurvey(survey)
|
setSelectedSurvey(survey)
|
||||||
setShowSurveyModal(true)
|
setShowSurveyModal(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmitSurvey = (answers: SurveyAnswer[]) => {
|
const handleSubmitSurvey = (answers: SurveyAnswerDto[]) => {
|
||||||
setShowSurveyModal(false)
|
setShowSurveyModal(false)
|
||||||
setSelectedSurvey(null)
|
setSelectedSurvey(null)
|
||||||
}
|
}
|
||||||
|
|
@ -299,11 +299,15 @@ const IntranetDashboard: React.FC = () => {
|
||||||
onNewOvertime={() => setShowOvertimeModal(true)}
|
onNewOvertime={() => setShowOvertimeModal(true)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
case 'active-surveys':
|
case 'active-surveys':
|
||||||
return <ActiveSurveys onTakeSurvey={handleTakeSurvey} />
|
return (
|
||||||
|
<ActiveSurveys
|
||||||
|
surveys={intranetDashboard?.surveys || []}
|
||||||
|
onTakeSurvey={handleTakeSurvey}
|
||||||
|
/>
|
||||||
|
)
|
||||||
case 'social-wall':
|
case 'social-wall':
|
||||||
return <SocialWall />
|
return <SocialWall posts={intranetDashboard?.socialPosts || []} />
|
||||||
case 'priority-tasks':
|
case 'priority-tasks':
|
||||||
return <PriorityTasks />
|
return <PriorityTasks />
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
import MediaManager from './MediaManager'
|
import MediaManager from './MediaManager'
|
||||||
import LocationPicker from './LocationPicker'
|
import LocationPicker from './LocationPicker'
|
||||||
import { SocialMedia } from '@/types/intranet'
|
import { SocialMediaDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
interface CreatePostProps {
|
interface CreatePostProps {
|
||||||
onCreatePost: (post: {
|
onCreatePost: (post: {
|
||||||
|
|
@ -19,7 +19,7 @@ interface CreatePostProps {
|
||||||
location?: string
|
location?: string
|
||||||
media?: {
|
media?: {
|
||||||
type: 'mixed' | 'poll'
|
type: 'mixed' | 'poll'
|
||||||
mediaItems?: SocialMedia[]
|
mediaItems?: SocialMediaDto[]
|
||||||
poll?: {
|
poll?: {
|
||||||
question: string
|
question: string
|
||||||
options: Array<{ text: string }>
|
options: Array<{ text: string }>
|
||||||
|
|
@ -31,7 +31,7 @@ interface CreatePostProps {
|
||||||
const CreatePost: React.FC<CreatePostProps> = ({ onCreatePost }) => {
|
const CreatePost: React.FC<CreatePostProps> = ({ onCreatePost }) => {
|
||||||
const [content, setContent] = useState('')
|
const [content, setContent] = useState('')
|
||||||
const [mediaType, setMediaType] = useState<'media' | 'poll' | null>(null)
|
const [mediaType, setMediaType] = useState<'media' | 'poll' | null>(null)
|
||||||
const [mediaItems, setMediaItems] = useState<SocialMedia[]>([])
|
const [mediaItems, setMediaItems] = useState<SocialMediaDto[]>([])
|
||||||
const [location, setLocation] = useState<string | null>(null)
|
const [location, setLocation] = useState<string | null>(null)
|
||||||
const [pollQuestion, setPollQuestion] = useState('')
|
const [pollQuestion, setPollQuestion] = useState('')
|
||||||
const [pollOptions, setPollOptions] = useState(['', ''])
|
const [pollOptions, setPollOptions] = useState(['', ''])
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ import Video from 'yet-another-react-lightbox/plugins/video'
|
||||||
import Zoom from 'yet-another-react-lightbox/plugins/zoom'
|
import Zoom from 'yet-another-react-lightbox/plugins/zoom'
|
||||||
import Counter from 'yet-another-react-lightbox/plugins/counter'
|
import Counter from 'yet-another-react-lightbox/plugins/counter'
|
||||||
import 'yet-another-react-lightbox/plugins/counter.css'
|
import 'yet-another-react-lightbox/plugins/counter.css'
|
||||||
import { SocialMedia } from '@/types/intranet'
|
import { SocialMediaDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
interface MediaLightboxProps {
|
interface MediaLightboxProps {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
media: SocialMedia
|
media: SocialMediaDto
|
||||||
startIndex?: number
|
startIndex?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import React, { useState } from 'react'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { FaTimes, FaLink, FaUpload } from 'react-icons/fa'
|
import { FaTimes, FaLink, FaUpload } from 'react-icons/fa'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { SocialMedia } from '@/types/intranet'
|
import { SocialMediaDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
interface MediaManagerProps {
|
interface MediaManagerProps {
|
||||||
media: SocialMedia[]
|
media: SocialMediaDto[]
|
||||||
onChange: (media: SocialMedia[]) => void
|
onChange: (media: SocialMediaDto[]) => void
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ const MediaManager: React.FC<MediaManagerProps> = ({ media, onChange, onClose })
|
||||||
const files = e.target.files
|
const files = e.target.files
|
||||||
if (!files) return
|
if (!files) return
|
||||||
|
|
||||||
const newMedia: SocialMedia[] = Array.from(files).map((file) => ({
|
const newMedia: SocialMediaDto[] = Array.from(files).map((file) => ({
|
||||||
id: Math.random().toString(36).substr(2, 9),
|
id: Math.random().toString(36).substr(2, 9),
|
||||||
type: file.type.startsWith('video/') ? 'video' : 'image',
|
type: file.type.startsWith('video/') ? 'video' : 'image',
|
||||||
urls: [URL.createObjectURL(file)],
|
urls: [URL.createObjectURL(file)],
|
||||||
|
|
@ -33,7 +33,7 @@ const MediaManager: React.FC<MediaManagerProps> = ({ media, onChange, onClose })
|
||||||
const handleUrlAdd = () => {
|
const handleUrlAdd = () => {
|
||||||
if (!urlInput.trim()) return
|
if (!urlInput.trim()) return
|
||||||
|
|
||||||
const newMedia: SocialMedia = {
|
const newMedia: SocialMediaDto = {
|
||||||
id: Math.random().toString(36).substr(2, 9),
|
id: Math.random().toString(36).substr(2, 9),
|
||||||
type: mediaType,
|
type: mediaType,
|
||||||
urls: [urlInput]
|
urls: [urlInput]
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@ import {
|
||||||
import MediaLightbox from './MediaLightbox'
|
import MediaLightbox from './MediaLightbox'
|
||||||
import LocationMap from './LocationMap'
|
import LocationMap from './LocationMap'
|
||||||
import UserProfileCard from './UserProfileCard'
|
import UserProfileCard from './UserProfileCard'
|
||||||
import { SocialPost } from '@/types/intranet'
|
import { SocialPostDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
dayjs.locale('tr')
|
dayjs.locale('tr')
|
||||||
|
|
||||||
interface PostItemProps {
|
interface PostItemProps {
|
||||||
post: SocialPost
|
post: SocialPostDto
|
||||||
onLike: (postId: string) => void
|
onLike: (postId: string) => void
|
||||||
onComment: (postId: string, content: string) => void
|
onComment: (postId: string, content: string) => void
|
||||||
onDelete: (postId: string) => void
|
onDelete: (postId: string) => void
|
||||||
|
|
@ -258,22 +258,22 @@ const PostItem: React.FC<PostItemProps> = ({ post, onLike, onComment, onDelete,
|
||||||
onMouseLeave={() => setShowUserCard(false)}
|
onMouseLeave={() => setShowUserCard(false)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={post.creator.avatar || 'https://i.pravatar.cc/150?img=1'}
|
src={post.employee.avatar || 'https://i.pravatar.cc/150?img=1'}
|
||||||
alt={post.creator.fullName}
|
alt={post.employee.fullName}
|
||||||
className="w-12 h-12 rounded-full object-cover cursor-pointer ring-2 ring-transparent hover:ring-blue-500 transition-all"
|
className="w-12 h-12 rounded-full object-cover cursor-pointer ring-2 ring-transparent hover:ring-blue-500 transition-all"
|
||||||
/>
|
/>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{showUserCard && (
|
{showUserCard && (
|
||||||
<UserProfileCard
|
<UserProfileCard
|
||||||
user={{
|
user={{
|
||||||
id: post.creator.id,
|
id: post.employee.id,
|
||||||
name: post.creator.fullName,
|
name: post.employee.fullName,
|
||||||
avatar: post.creator.avatar || 'https://i.pravatar.cc/150?img=1',
|
avatar: post.employee.avatar || 'https://i.pravatar.cc/150?img=1',
|
||||||
title: post.creator.jobPosition?.name || 'Çalışan',
|
title: post.employee.jobPosition?.name || 'Çalışan',
|
||||||
email: post.creator.email,
|
email: post.employee.email,
|
||||||
phone: post.creator.phone,
|
phone: post.employee.phone,
|
||||||
department: post.creator.department?.name,
|
department: post.employee.department?.name,
|
||||||
location: post.creator.workLocation
|
location: post.employee.workLocation
|
||||||
}}
|
}}
|
||||||
position="bottom"
|
position="bottom"
|
||||||
/>
|
/>
|
||||||
|
|
@ -282,10 +282,10 @@ const PostItem: React.FC<PostItemProps> = ({ post, onLike, onComment, onDelete,
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-semibold text-gray-900 dark:text-gray-100">
|
<h3 className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
{post.creator.fullName}
|
{post.employee.fullName}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||||
{post.creator.jobPosition?.name || 'Çalışan'} • {dayjs(post.creationTime).fromNow()}
|
{post.employee.jobPosition?.name || 'Çalışan'} • {dayjs(post.creationTime).fromNow()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { AnimatePresence } from 'framer-motion'
|
import { AnimatePresence } from 'framer-motion'
|
||||||
import PostItem from './PostItem'
|
import PostItem from './PostItem'
|
||||||
import { SocialMedia } from '@/types/intranet'
|
|
||||||
import CreatePost from './CreatePost'
|
import CreatePost from './CreatePost'
|
||||||
import { SocialPost } from '@/types/intranet'
|
// import { mockSocialPosts } from '@/mocks/mockIntranet'
|
||||||
import { EmployeeDto } from '@/types/hr'
|
|
||||||
import { mockSocialPosts } from '@/mocks/mockIntranet'
|
|
||||||
import { mockEmployees } from '@/mocks/mockEmployees'
|
import { mockEmployees } from '@/mocks/mockEmployees'
|
||||||
|
import { EmployeeDto, SocialMediaDto, SocialPostDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
const SocialWall: React.FC = () => {
|
const SocialWall: React.FC<{ posts: SocialPostDto[] }> = ({ posts }) => {
|
||||||
const [posts, setPosts] = useState<SocialPost[]>(mockSocialPosts)
|
// const [posts, setPosts] = useState<SocialPost[]>(mockSocialPosts)
|
||||||
const [filter, setFilter] = useState<'all' | 'mine'>('all')
|
const [filter, setFilter] = useState<'all' | 'mine'>('all')
|
||||||
|
|
||||||
// Ali Öztürk'ü "Siz" kullanıcısı olarak kullan
|
// Ali Öztürk'ü "Siz" kullanıcısı olarak kullan
|
||||||
|
|
@ -20,7 +18,7 @@ const SocialWall: React.FC = () => {
|
||||||
location?: string
|
location?: string
|
||||||
media?: {
|
media?: {
|
||||||
type: 'mixed' | 'poll'
|
type: 'mixed' | 'poll'
|
||||||
mediaItems?: SocialMedia[]
|
mediaItems?: SocialMediaDto[]
|
||||||
poll?: {
|
poll?: {
|
||||||
question: string
|
question: string
|
||||||
options: Array<{ text: string }>
|
options: Array<{ text: string }>
|
||||||
|
|
@ -32,24 +30,24 @@ const SocialWall: React.FC = () => {
|
||||||
if (postData.media) {
|
if (postData.media) {
|
||||||
if (postData.media.type === 'mixed' && postData.media.mediaItems) {
|
if (postData.media.type === 'mixed' && postData.media.mediaItems) {
|
||||||
// Convert MediaItems to post format
|
// Convert MediaItems to post format
|
||||||
const images = postData.media.mediaItems.filter(m => m.type === 'image')
|
const images = postData.media.mediaItems.filter((m) => m.type === 'image')
|
||||||
const videos = postData.media.mediaItems.filter(m => m.type === 'video')
|
const videos = postData.media.mediaItems.filter((m) => m.type === 'video')
|
||||||
|
|
||||||
if (images.length > 0 && videos.length === 0) {
|
if (images.length > 0 && videos.length === 0) {
|
||||||
mediaForPost = {
|
mediaForPost = {
|
||||||
type: 'image' as const,
|
type: 'image' as const,
|
||||||
urls: images.map(i => i.urls?.[0]).filter(url => url !== undefined) as string[]
|
urls: images.map((i) => i.urls?.[0]).filter((url) => url !== undefined) as string[],
|
||||||
}
|
}
|
||||||
} else if (videos.length > 0 && images.length === 0) {
|
} else if (videos.length > 0 && images.length === 0) {
|
||||||
mediaForPost = {
|
mediaForPost = {
|
||||||
type: 'video' as const,
|
type: 'video' as const,
|
||||||
urls: videos[0].urls || []
|
urls: videos[0].urls || [],
|
||||||
}
|
}
|
||||||
} else if (images.length > 0 || videos.length > 0) {
|
} else if (images.length > 0 || videos.length > 0) {
|
||||||
// Mixed media - use first image for now
|
// Mixed media - use first image for now
|
||||||
mediaForPost = {
|
mediaForPost = {
|
||||||
type: 'image' as const,
|
type: 'image' as const,
|
||||||
urls: images.map(i => i.urls?.[0]).filter(url => url !== undefined) as string[]
|
urls: images.map((i) => i.urls?.[0]).filter((url) => url !== undefined) as string[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (postData.media.type === 'poll' && postData.media.poll) {
|
} else if (postData.media.type === 'poll' && postData.media.poll) {
|
||||||
|
|
@ -59,17 +57,17 @@ const SocialWall: React.FC = () => {
|
||||||
pollOptions: postData.media.poll.options.map((opt, index) => ({
|
pollOptions: postData.media.poll.options.map((opt, index) => ({
|
||||||
id: `opt-${index}`,
|
id: `opt-${index}`,
|
||||||
text: opt.text,
|
text: opt.text,
|
||||||
votes: 0
|
votes: 0,
|
||||||
})),
|
})),
|
||||||
pollTotalVotes: 0,
|
pollTotalVotes: 0,
|
||||||
pollEndsAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
|
pollEndsAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const newPost: SocialPost = {
|
const newPost: SocialPostDto = {
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
creator: currentUserAuthor,
|
employee: currentUserAuthor,
|
||||||
content: postData.content,
|
content: postData.content,
|
||||||
creationTime: new Date(),
|
creationTime: new Date(),
|
||||||
media: mediaForPost,
|
media: mediaForPost,
|
||||||
|
|
@ -78,79 +76,77 @@ const SocialWall: React.FC = () => {
|
||||||
isLiked: false,
|
isLiked: false,
|
||||||
likeUsers: [],
|
likeUsers: [],
|
||||||
comments: [],
|
comments: [],
|
||||||
isOwnPost: true
|
isOwnPost: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
setPosts([newPost, ...posts])
|
// setPosts([newPost, ...posts])
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLike = (postId: string) => {
|
const handleLike = (postId: string) => {
|
||||||
setPosts(
|
// setPosts(
|
||||||
posts.map((post) => {
|
// posts.map((post) => {
|
||||||
if (post.id === postId) {
|
// if (post.id === postId) {
|
||||||
return {
|
// return {
|
||||||
...post,
|
// ...post,
|
||||||
likeCount: post.isLiked ? post.likeCount - 1 : post.likeCount + 1,
|
// likeCount: post.isLiked ? post.likeCount - 1 : post.likeCount + 1,
|
||||||
isLiked: !post.isLiked
|
// isLiked: !post.isLiked
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return post
|
// return post
|
||||||
})
|
// })
|
||||||
)
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleComment = (postId: string, content: string) => {
|
const handleComment = (postId: string, content: string) => {
|
||||||
setPosts(
|
// setPosts(
|
||||||
posts.map((post) => {
|
// posts.map((post) => {
|
||||||
if (post.id === postId) {
|
// if (post.id === postId) {
|
||||||
const commentAuthor = currentUserAuthor
|
// const commentAuthor = currentUserAuthor
|
||||||
|
// const newComment = {
|
||||||
const newComment = {
|
// id: Date.now().toString(),
|
||||||
id: Date.now().toString(),
|
// creator: commentAuthor,
|
||||||
creator: commentAuthor,
|
// content,
|
||||||
content,
|
// creationTime: new Date()
|
||||||
creationTime: new Date()
|
// }
|
||||||
}
|
// return {
|
||||||
return {
|
// ...post,
|
||||||
...post,
|
// comments: [...post.comments, newComment]
|
||||||
comments: [...post.comments, newComment]
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// return post
|
||||||
return post
|
// })
|
||||||
})
|
// )
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDelete = (postId: string) => {
|
const handleDelete = (postId: string) => {
|
||||||
if (window.confirm('Bu gönderiyi silmek istediğinizden emin misiniz?')) {
|
if (window.confirm('Bu gönderiyi silmek istediğinizden emin misiniz?')) {
|
||||||
setPosts(posts.filter((post) => post.id !== postId))
|
// setPosts(posts.filter((post) => post.id !== postId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleVote = (postId: string, optionId: string) => {
|
const handleVote = (postId: string, optionId: string) => {
|
||||||
setPosts(
|
// setPosts(
|
||||||
posts.map((post) => {
|
// posts.map((post) => {
|
||||||
if (post.id === postId && post.media?.type === 'poll' && post.media.pollOptions) {
|
// if (post.id === postId && post.media?.type === 'poll' && post.media.pollOptions) {
|
||||||
// If user already voted, don't allow voting again
|
// // If user already voted, don't allow voting again
|
||||||
if (post.media.pollUserVoteId) {
|
// if (post.media.pollUserVoteId) {
|
||||||
return post
|
// return post
|
||||||
}
|
// }
|
||||||
|
// return {
|
||||||
return {
|
// ...post,
|
||||||
...post,
|
// media: {
|
||||||
media: {
|
// ...post.media,
|
||||||
...post.media,
|
// pollOptions: post.media.pollOptions.map((opt) =>
|
||||||
pollOptions: post.media.pollOptions.map((opt) =>
|
// opt.id === optionId ? { ...opt, votes: opt.votes + 1 } : opt
|
||||||
opt.id === optionId ? { ...opt, votes: opt.votes + 1 } : opt
|
// ),
|
||||||
),
|
// pollTotalVotes: (post.media.pollTotalVotes || 0) + 1,
|
||||||
pollTotalVotes: (post.media.pollTotalVotes || 0) + 1,
|
// pollUserVoteId: optionId
|
||||||
pollUserVoteId: optionId
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// return post
|
||||||
return post
|
// })
|
||||||
})
|
// )
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredPosts = filter === 'mine' ? posts.filter((post) => post.isOwnPost) : posts
|
const filteredPosts = filter === 'mine' ? posts.filter((post) => post.isOwnPost) : posts
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { FaTimes } from 'react-icons/fa'
|
import { FaTimes } from 'react-icons/fa'
|
||||||
import { Survey, SurveyQuestion, SurveyAnswer } from '@/types/intranet'
|
import { SurveyAnswerDto, SurveyDto, SurveyQuestionDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
interface SurveyModalProps {
|
interface SurveyModalProps {
|
||||||
survey: Survey
|
survey: SurveyDto
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onSubmit: (answers: SurveyAnswer[]) => void
|
onSubmit: (answers: SurveyAnswerDto[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit }) => {
|
const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit }) => {
|
||||||
|
|
@ -14,51 +14,52 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
const [errors, setErrors] = useState<{ [questionId: string]: string }>({})
|
const [errors, setErrors] = useState<{ [questionId: string]: string }>({})
|
||||||
|
|
||||||
const handleAnswerChange = (questionId: string, value: any) => {
|
const handleAnswerChange = (questionId: string, value: any) => {
|
||||||
setAnswers(prev => ({
|
setAnswers((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[questionId]: value
|
[questionId]: value,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Clear error when user provides an answer
|
// Clear error when user provides an answer
|
||||||
if (errors[questionId]) {
|
if (errors[questionId]) {
|
||||||
setErrors(prev => ({
|
setErrors((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[questionId]: ''
|
[questionId]: '',
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const validateAnswers = (): boolean => {
|
const validateAnswers = (): boolean => {
|
||||||
const newErrors: { [questionId: string]: string } = {}
|
const newErrors: { [questionId: string]: string } = {}
|
||||||
|
|
||||||
survey.questions.forEach(question => {
|
survey.questions.forEach((question) => {
|
||||||
if (question.isRequired && (!answers[question.id] || answers[question.id] === '')) {
|
if (question.isRequired && (!answers[question.id] || answers[question.id] === '')) {
|
||||||
newErrors[question.id] = 'Bu alan zorunludur'
|
newErrors[question.id] = 'Bu alan zorunludur'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
setErrors(newErrors)
|
setErrors(newErrors)
|
||||||
return Object.keys(newErrors).length === 0
|
return Object.keys(newErrors).length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (!validateAnswers()) {
|
if (!validateAnswers()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const surveyAnswers: SurveyAnswer[] = survey.questions.map(question => ({
|
const surveyAnswers: SurveyAnswerDto[] = survey.questions.map((question) => ({
|
||||||
questionId: question.id,
|
questionId: question.id,
|
||||||
questionType: question.type,
|
questionType: question.type,
|
||||||
value: answers[question.id] || (question.type === 'multiple-choice' ? '' :
|
value:
|
||||||
question.type === 'rating' ? 0 : '')
|
answers[question.id] ||
|
||||||
|
(question.type === 'multiple-choice' ? '' : question.type === 'rating' ? 0 : ''),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
onSubmit(surveyAnswers)
|
onSubmit(surveyAnswers)
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderQuestion = (question: SurveyQuestion, index: number) => {
|
const renderQuestion = (question: SurveyQuestionDto, index: number) => {
|
||||||
const questionNumber = index + 1
|
const questionNumber = index + 1
|
||||||
const hasError = !!errors[question.id]
|
const hasError = !!errors[question.id]
|
||||||
|
|
||||||
|
|
@ -69,34 +70,9 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||||
</label>
|
</label>
|
||||||
<div className="flex gap-2 flex-wrap">
|
{hasError && (
|
||||||
{Array.from({ length: question.ratingConfig!.max - question.ratingConfig!.min + 1 }, (_, i) => {
|
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||||
const value = question.ratingConfig!.min + i
|
)}
|
||||||
const label = question.ratingConfig!.labels?.[value] || value.toString()
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
key={value}
|
|
||||||
className={`flex flex-col items-center gap-1 px-3 py-2 border rounded-lg cursor-pointer transition-colors ${
|
|
||||||
answers[question.id] === value
|
|
||||||
? 'bg-blue-100 border-blue-500 dark:bg-blue-900/30 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, parseInt(e.target.value))}
|
|
||||||
className="sr-only"
|
|
||||||
/>
|
|
||||||
<span className="font-medium text-gray-900 dark:text-white">{value}</span>
|
|
||||||
<span className="text-xs text-gray-500 dark:text-gray-400 text-center">{label}</span>
|
|
||||||
</label>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -107,7 +83,7 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||||
</label>
|
</label>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{question.options?.map(option => (
|
{question.options?.map((option) => (
|
||||||
<label
|
<label
|
||||||
key={option.id}
|
key={option.id}
|
||||||
className={`flex items-center gap-3 p-3 border rounded-lg cursor-pointer transition-colors ${
|
className={`flex items-center gap-3 p-3 border rounded-lg cursor-pointer transition-colors ${
|
||||||
|
|
@ -128,7 +104,9 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
{hasError && (
|
||||||
|
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -139,7 +117,7 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
{questionNumber}. {question.questionText} {question.isRequired && '*'}
|
||||||
</label>
|
</label>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
{['yes', 'no'].map(value => (
|
{['yes', 'no'].map((value) => (
|
||||||
<label
|
<label
|
||||||
key={value}
|
key={value}
|
||||||
className={`flex items-center gap-2 px-4 py-2 border rounded-lg cursor-pointer transition-colors ${
|
className={`flex items-center gap-2 px-4 py-2 border rounded-lg cursor-pointer transition-colors ${
|
||||||
|
|
@ -162,7 +140,9 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
{hasError && (
|
||||||
|
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -181,7 +161,9 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
}`}
|
}`}
|
||||||
placeholder="Cevabınızı yazın..."
|
placeholder="Cevabınızı yazın..."
|
||||||
/>
|
/>
|
||||||
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
{hasError && (
|
||||||
|
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -200,7 +182,9 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
}`}
|
}`}
|
||||||
placeholder="Yorumlarınızı buraya yazabilirsiniz..."
|
placeholder="Yorumlarınızı buraya yazabilirsiniz..."
|
||||||
/>
|
/>
|
||||||
{hasError && <p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>}
|
{hasError && (
|
||||||
|
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -231,9 +215,7 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">
|
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||||
{survey.title}
|
{survey.title}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">{survey.description}</p>
|
||||||
{survey.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
|
|
@ -253,8 +235,7 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
{!survey.isAnonymous && (
|
{!survey.isAnonymous && (
|
||||||
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-3">
|
<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">
|
<p className="text-sm text-blue-700 dark:text-blue-300">
|
||||||
ℹ️ Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız
|
ℹ️ Bu anket isim belirtilerek doldurulmaktadır. Yanıtlarınız kaydedilecektir.
|
||||||
kaydedilecektir.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FaClipboardCheck, FaQuestionCircle, FaUsers, FaClock, FaArrowRight } from 'react-icons/fa'
|
import { FaClipboardCheck, FaQuestionCircle, FaUsers, FaClock, FaArrowRight } from 'react-icons/fa'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { mockSurveys } from '../../../mocks/mockIntranet'
|
import { SurveyDto } from '@/proxy/intranet/models'
|
||||||
import { Survey } from '@/types/intranet'
|
// import { mockSurveys } from '../../../mocks/mockIntranet'
|
||||||
|
|
||||||
interface ActiveSurveysProps {
|
interface ActiveSurveysProps {
|
||||||
onTakeSurvey: (survey: Survey) => void
|
surveys?: SurveyDto[]
|
||||||
|
onTakeSurvey: (survey: SurveyDto) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActiveSurveys: React.FC<ActiveSurveysProps> = ({ onTakeSurvey }) => {
|
const ActiveSurveys: React.FC<ActiveSurveysProps> = ({ surveys, onTakeSurvey }) => {
|
||||||
const activeSurveys = mockSurveys.filter((s) => s.status === 'active').slice(0, 3)
|
|
||||||
|
|
||||||
return (
|
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">
|
<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 */}
|
{/* Header with gradient */}
|
||||||
|
|
@ -23,7 +22,7 @@ const ActiveSurveys: React.FC<ActiveSurveysProps> = ({ onTakeSurvey }) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-6 space-y-4">
|
<div className="p-6 space-y-4">
|
||||||
{activeSurveys.map((survey, index) => {
|
{surveys?.map((survey, index) => {
|
||||||
const daysLeft = dayjs(survey.deadline).diff(dayjs(), 'day')
|
const daysLeft = dayjs(survey.deadline).diff(dayjs(), 'day')
|
||||||
const urgency = daysLeft <= 3 ? 'urgent' : daysLeft <= 7 ? 'warning' : 'normal'
|
const urgency = daysLeft <= 3 ? 'urgent' : daysLeft <= 7 ? 'warning' : 'normal'
|
||||||
|
|
||||||
|
|
@ -124,7 +123,7 @@ const ActiveSurveys: React.FC<ActiveSurveysProps> = ({ onTakeSurvey }) => {
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{activeSurveys.length === 0 && (
|
{surveys?.length === 0 && (
|
||||||
<div className="text-center py-12">
|
<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">
|
<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" />
|
<FaClipboardCheck className="w-8 h-8 text-gray-400" />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue