Survey Widget
This commit is contained in:
parent
06558a1284
commit
a3f86a6fdc
12 changed files with 227 additions and 21 deletions
|
|
@ -6,4 +6,5 @@ namespace Sozsoft.Platform.Intranet;
|
||||||
public interface IIntranetAppService : IApplicationService
|
public interface IIntranetAppService : IApplicationService
|
||||||
{
|
{
|
||||||
Task<IntranetDashboardDto> GetIntranetDashboardAsync();
|
Task<IntranetDashboardDto> GetIntranetDashboardAsync();
|
||||||
|
Task UpdateSurveyResponseAsync(SubmitSurveyInput input);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ public class SurveyDto : FullAuditedEntityDto<Guid>
|
||||||
public bool IsAnonymous { get; set; }
|
public bool IsAnonymous { get; set; }
|
||||||
|
|
||||||
public List<SurveyQuestionDto> Questions { get; set; }
|
public List<SurveyQuestionDto> Questions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Mevcut kullanıcının bu ankete verdiği cevap. Anonim anketlerde veya henüz cevaplanmadıysa null.</summary>
|
||||||
|
public SurveyResponseDto MyResponse { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SurveyQuestionDto : FullAuditedEntityDto<Guid>
|
public class SurveyQuestionDto : FullAuditedEntityDto<Guid>
|
||||||
|
|
@ -50,3 +53,16 @@ public class SurveyAnswerDto : FullAuditedEntityDto<Guid>
|
||||||
public string QuestionType { get; set; }
|
public string QuestionType { get; set; }
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SubmitSurveyInput
|
||||||
|
{
|
||||||
|
public Guid SurveyId { get; set; }
|
||||||
|
public List<SubmitSurveyAnswerInput> Answers { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SubmitSurveyAnswerInput
|
||||||
|
{
|
||||||
|
public Guid QuestionId { get; set; }
|
||||||
|
public string QuestionType { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Identity;
|
using Volo.Abp.Identity;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
using Volo.Abp.Uow;
|
using Volo.Abp.Uow;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Intranet;
|
namespace Sozsoft.Platform.Intranet;
|
||||||
|
|
||||||
|
|
@ -32,6 +33,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
private readonly IRepository<JobPosition, Guid> _jobPositionRepository;
|
private readonly IRepository<JobPosition, Guid> _jobPositionRepository;
|
||||||
private readonly IRepository<Announcement, Guid> _announcementRepository;
|
private readonly IRepository<Announcement, Guid> _announcementRepository;
|
||||||
private readonly IRepository<Survey, Guid> _surveyRepository;
|
private readonly IRepository<Survey, Guid> _surveyRepository;
|
||||||
|
private readonly IRepository<SurveyResponse, Guid> _surveyResponseRepository;
|
||||||
|
private readonly IRepository<SurveyAnswer, Guid> _surveyAnswerRepository;
|
||||||
private readonly IRepository<SocialPost, Guid> _socialPostRepository;
|
private readonly IRepository<SocialPost, Guid> _socialPostRepository;
|
||||||
|
|
||||||
public IntranetAppService(
|
public IntranetAppService(
|
||||||
|
|
@ -45,6 +48,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
IRepository<JobPosition, Guid> jobPositionRepository,
|
IRepository<JobPosition, Guid> jobPositionRepository,
|
||||||
IRepository<Announcement, Guid> announcementRepository,
|
IRepository<Announcement, Guid> announcementRepository,
|
||||||
IRepository<Survey, Guid> surveyRepository,
|
IRepository<Survey, Guid> surveyRepository,
|
||||||
|
IRepository<SurveyResponse, Guid> surveyResponseRepository,
|
||||||
|
IRepository<SurveyAnswer, Guid> surveyAnswerRepository,
|
||||||
IRepository<SocialPost, Guid> socialPostRepository
|
IRepository<SocialPost, Guid> socialPostRepository
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
@ -57,6 +62,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
_jobPositionRepository = jobPositionRepository;
|
_jobPositionRepository = jobPositionRepository;
|
||||||
_announcementRepository = announcementRepository;
|
_announcementRepository = announcementRepository;
|
||||||
_surveyRepository = surveyRepository;
|
_surveyRepository = surveyRepository;
|
||||||
|
_surveyResponseRepository = surveyResponseRepository;
|
||||||
|
_surveyAnswerRepository = surveyAnswerRepository;
|
||||||
_socialPostRepository = socialPostRepository;
|
_socialPostRepository = socialPostRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,12 +195,40 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
|
|
||||||
var surveys = await AsyncExecuter.ToListAsync(
|
var surveys = await AsyncExecuter.ToListAsync(
|
||||||
queryable
|
queryable
|
||||||
|
.AsNoTracking()
|
||||||
.Where(s => s.Status == "active")
|
.Where(s => s.Status == "active")
|
||||||
.Include(s => s.Questions)
|
.Include(s => s.Questions)
|
||||||
.ThenInclude(q => q.Options)
|
.ThenInclude(q => q.Options)
|
||||||
);
|
);
|
||||||
|
|
||||||
return ObjectMapper.Map<List<Survey>, List<SurveyDto>>(surveys);
|
var dtos = ObjectMapper.Map<List<Survey>, List<SurveyDto>>(surveys);
|
||||||
|
|
||||||
|
// Tüm anketler için mevcut kullanıcının cevabını çek (anonim dahil — CreatorId ile)
|
||||||
|
if (CurrentUser.IsAuthenticated)
|
||||||
|
{
|
||||||
|
var allSurveyIds = surveys.Select(s => s.Id).ToList();
|
||||||
|
|
||||||
|
if (allSurveyIds.Any())
|
||||||
|
{
|
||||||
|
var rq = await _surveyResponseRepository.GetQueryableAsync();
|
||||||
|
var myResponses = await AsyncExecuter.ToListAsync(
|
||||||
|
rq.AsNoTracking()
|
||||||
|
.Where(r => allSurveyIds.Contains(r.SurveyId)
|
||||||
|
&& (r.UserId == CurrentUser.Id || r.CreatorId == CurrentUser.Id))
|
||||||
|
.Include(r => r.Answers)
|
||||||
|
);
|
||||||
|
|
||||||
|
var responseMap = myResponses.ToDictionary(r => r.SurveyId);
|
||||||
|
|
||||||
|
for (var i = 0; i < surveys.Count; i++)
|
||||||
|
{
|
||||||
|
if (responseMap.TryGetValue(surveys[i].Id, out var myResponse))
|
||||||
|
dtos[i].MyResponse = ObjectMapper.Map<SurveyResponse, SurveyResponseDto>(myResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dtos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<SocialPostDto>> GetSocialPostsAsync()
|
private async Task<List<SocialPostDto>> GetSocialPostsAsync()
|
||||||
|
|
@ -346,5 +381,79 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
_ => "application/octet-stream"
|
_ => "application/octet-stream"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task UpdateSurveyResponseAsync(SubmitSurveyInput input)
|
||||||
|
{
|
||||||
|
var survey = await _surveyRepository.GetAsync(input.SurveyId);
|
||||||
|
|
||||||
|
SurveyResponse? response = null;
|
||||||
|
|
||||||
|
if (CurrentUser.IsAuthenticated)
|
||||||
|
{
|
||||||
|
var responseQueryable = await _surveyResponseRepository.GetQueryableAsync();
|
||||||
|
|
||||||
|
response = await AsyncExecuter.FirstOrDefaultAsync(
|
||||||
|
responseQueryable.Where(r =>
|
||||||
|
r.SurveyId == input.SurveyId &&
|
||||||
|
(r.UserId == CurrentUser.Id || r.CreatorId == CurrentUser.Id))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
var answerQueryable = await _surveyAnswerRepository.GetQueryableAsync();
|
||||||
|
|
||||||
|
var existingAnswers = await AsyncExecuter.ToListAsync(
|
||||||
|
answerQueryable.Where(a => a.ResponseId == response.Id)
|
||||||
|
);
|
||||||
|
|
||||||
|
var existingAnswerMap = existingAnswers.ToDictionary(x => x.QuestionId);
|
||||||
|
|
||||||
|
foreach (var inputAnswer in input.Answers)
|
||||||
|
{
|
||||||
|
if (existingAnswerMap.TryGetValue(inputAnswer.QuestionId, out var existingAnswer))
|
||||||
|
{
|
||||||
|
existingAnswer.Value = inputAnswer.Value ?? string.Empty;
|
||||||
|
existingAnswer.QuestionType = inputAnswer.QuestionType;
|
||||||
|
|
||||||
|
await _surveyAnswerRepository.UpdateAsync(existingAnswer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _surveyAnswerRepository.InsertAsync(new SurveyAnswer(Guid.NewGuid())
|
||||||
|
{
|
||||||
|
ResponseId = response.Id,
|
||||||
|
QuestionId = inputAnswer.QuestionId,
|
||||||
|
QuestionType = inputAnswer.QuestionType,
|
||||||
|
Value = inputAnswer.Value ?? string.Empty
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.SubmissionTime = Clock.Now;
|
||||||
|
await _surveyResponseRepository.UpdateAsync(response);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newResponse = new SurveyResponse(Guid.NewGuid())
|
||||||
|
{
|
||||||
|
SurveyId = input.SurveyId,
|
||||||
|
UserId = survey.IsAnonymous ? null : CurrentUser.Id,
|
||||||
|
SubmissionTime = Clock.Now,
|
||||||
|
Answers = input.Answers.Select(a => new SurveyAnswer(Guid.NewGuid())
|
||||||
|
{
|
||||||
|
QuestionId = a.QuestionId,
|
||||||
|
QuestionType = a.QuestionType,
|
||||||
|
Value = a.Value ?? string.Empty
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
await _surveyResponseRepository.InsertAsync(newResponse);
|
||||||
|
|
||||||
|
survey.Responses++;
|
||||||
|
await _surveyRepository.UpdateAsync(survey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11970,6 +11970,12 @@
|
||||||
"tr": "Anketi Gönder",
|
"tr": "Anketi Gönder",
|
||||||
"en": "Submit Survey"
|
"en": "Submit Survey"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.Platform.Intranet.SurveyModal.Update",
|
||||||
|
"tr": "Anketi Güncelle",
|
||||||
|
"en": "Update Survey"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "App.Platform.Intranet.SocialWall.LocationMap.OpenInGoogleMaps",
|
"key": "App.Platform.Intranet.SocialWall.LocationMap.OpenInGoogleMaps",
|
||||||
|
|
|
||||||
|
|
@ -2913,17 +2913,16 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.Survey)),
|
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.Survey)),
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 500, true, true, true, true, false, true),
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 700, 500, true, true, true, true, false, true),
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
||||||
{
|
{
|
||||||
new() {
|
new() {
|
||||||
Order=1, ColCount=2, ColSpan=1, ItemType="group", Items =[
|
Order=1, ColCount=3, ColSpan=1, ItemType="group", Items =[
|
||||||
new EditingFormItemDto { Order = 1, DataField = "Title", ColSpan=2, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
new EditingFormItemDto { Order = 1, DataField = "Title", ColSpan=3, IsRequired = true, EditorType2 = EditorTypes.dxTextBox },
|
||||||
new EditingFormItemDto { Order = 2, DataField = "Deadline", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxDateBox },
|
new EditingFormItemDto { Order = 2, DataField = "Deadline", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxDateBox },
|
||||||
new EditingFormItemDto { Order = 3, DataField = "Responses", ColSpan=1, EditorType2 = EditorTypes.dxNumberBox },
|
new EditingFormItemDto { Order = 3, DataField = "Status", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
||||||
new EditingFormItemDto { Order = 4, DataField = "Status", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
|
new EditingFormItemDto { Order = 4, DataField = "IsAnonymous", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxCheckBox },
|
||||||
new EditingFormItemDto { Order = 5, DataField = "IsAnonymous", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxCheckBox },
|
|
||||||
]}
|
]}
|
||||||
}),
|
}),
|
||||||
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
|
FormFieldsDefaultValueJson = JsonSerializer.Serialize(new FieldsDefaultValue[]
|
||||||
|
|
@ -3109,7 +3108,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.SurveyQuestion)),
|
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.SurveyQuestion)),
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 400, true, true, true, true, false, true),
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 400, true, true, true, true, false, false),
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
||||||
{
|
{
|
||||||
|
|
@ -3280,7 +3279,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.SurveyResponse)),
|
DeleteCommand = DefaultDeleteCommand(nameof(TableNameEnum.SurveyResponse)),
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||||
PagerOptionJson = DefaultPagerOptionJson,
|
PagerOptionJson = DefaultPagerOptionJson,
|
||||||
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 400, true, true, true, true, false, true),
|
EditingOptionJson = DefaultEditingOptionJson(listFormName, 500, 400, true, true, true, true, false, false),
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||||
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
EditingFormJson = JsonSerializer.Serialize(new List<EditingFormDto>()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Volo.Abp.Domain.Entities.Auditing;
|
using Volo.Abp.Domain.Entities.Auditing;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
|
|
@ -59,6 +58,13 @@ public class SurveyResponse : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
public DateTime SubmissionTime { get; set; }
|
public DateTime SubmissionTime { get; set; }
|
||||||
|
|
||||||
public ICollection<SurveyAnswer> Answers { get; set; }
|
public ICollection<SurveyAnswer> Answers { get; set; }
|
||||||
|
|
||||||
|
public SurveyResponse(Guid id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SurveyResponse() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SurveyAnswer : FullAuditedEntity<Guid>, IMultiTenant
|
public class SurveyAnswer : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
|
|
@ -73,4 +79,11 @@ public class SurveyAnswer : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
|
|
||||||
public string QuestionType { get; set; } // rating | multiple-choice | text | textarea | yes-no
|
public string QuestionType { get; set; } // rating | multiple-choice | text | textarea | yes-no
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
public SurveyAnswer(Guid id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SurveyAnswer() { }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1351,7 +1351,7 @@
|
||||||
"Title": "Çalışan Memnuniyet Anketi 2024",
|
"Title": "Çalışan Memnuniyet Anketi 2024",
|
||||||
"Description": "Yıllık çalışan memnuniyeti ve bağlılık araştırması",
|
"Description": "Yıllık çalışan memnuniyeti ve bağlılık araştırması",
|
||||||
"Deadline": "2024-10-31T00:00:00",
|
"Deadline": "2024-10-31T00:00:00",
|
||||||
"Responses": 45,
|
"Responses": 0,
|
||||||
"Status": "active",
|
"Status": "active",
|
||||||
"IsAnonymous": true
|
"IsAnonymous": true
|
||||||
},
|
},
|
||||||
|
|
@ -1359,7 +1359,7 @@
|
||||||
"Title": "Eğitim İhtiyaç Analizi",
|
"Title": "Eğitim İhtiyaç Analizi",
|
||||||
"Description": "2025 yılı eğitim planlaması için ihtiyaç tespiti",
|
"Description": "2025 yılı eğitim planlaması için ihtiyaç tespiti",
|
||||||
"Deadline": "2024-11-15T00:00:00",
|
"Deadline": "2024-11-15T00:00:00",
|
||||||
"Responses": 28,
|
"Responses": 0,
|
||||||
"Status": "active",
|
"Status": "active",
|
||||||
"IsAnonymous": false
|
"IsAnonymous": false
|
||||||
},
|
},
|
||||||
|
|
@ -1367,7 +1367,7 @@
|
||||||
"Title": "Kafeterya Memnuniyet Anketi",
|
"Title": "Kafeterya Memnuniyet Anketi",
|
||||||
"Description": "Yemek kalitesi ve servis değerlendirmesi",
|
"Description": "Yemek kalitesi ve servis değerlendirmesi",
|
||||||
"Deadline": "2024-09-30T00:00:00",
|
"Deadline": "2024-09-30T00:00:00",
|
||||||
"Responses": 62,
|
"Responses": 0,
|
||||||
"Status": "passive",
|
"Status": "passive",
|
||||||
"IsAnonymous": true
|
"IsAnonymous": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ export interface SurveyDto {
|
||||||
targetAudience: string[]
|
targetAudience: string[]
|
||||||
status: 'draft' | 'active' | 'closed'
|
status: 'draft' | 'active' | 'closed'
|
||||||
isAnonymous: boolean
|
isAnonymous: boolean
|
||||||
|
myResponse?: SurveyResponseDto
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sosyal Duvar - Comment Interface
|
// Sosyal Duvar - Comment Interface
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,20 @@ export class IntranetService {
|
||||||
},
|
},
|
||||||
{ apiName: this.apiName, ...config },
|
{ apiName: this.apiName, ...config },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
updateSurveyResponse = (
|
||||||
|
surveyId: string,
|
||||||
|
answers: { questionId: string; questionType: string; value: string }[],
|
||||||
|
config?: Partial<Config>,
|
||||||
|
) =>
|
||||||
|
apiService.fetchData<void>(
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/app/intranet/update-survey-response',
|
||||||
|
data: { surveyId, answers },
|
||||||
|
},
|
||||||
|
{ apiName: this.apiName, ...config },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const intranetService = new IntranetService()
|
export const intranetService = new IntranetService()
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,21 @@ const IntranetDashboard: React.FC = () => {
|
||||||
setShowSurveyModal(true)
|
setShowSurveyModal(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmitSurvey = (answers: SurveyAnswerDto[]) => {
|
const handleSubmitSurvey = async (answers: SurveyAnswerDto[]) => {
|
||||||
|
if (!selectedSurvey) return
|
||||||
|
try {
|
||||||
|
await intranetService.updateSurveyResponse(
|
||||||
|
selectedSurvey.id,
|
||||||
|
answers.map((a) => ({
|
||||||
|
questionId: a.questionId,
|
||||||
|
questionType: a.questionType,
|
||||||
|
value: a.value !== undefined && a.value !== null ? String(a.value) : '',
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
await fetchIntranetDashboard()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Survey submit error', e)
|
||||||
|
}
|
||||||
setShowSurveyModal(false)
|
setShowSurveyModal(false)
|
||||||
setSelectedSurvey(null)
|
setSelectedSurvey(null)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,19 @@ interface SurveyModalProps {
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit }) => {
|
const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit }) => {
|
||||||
const { translate } = useLocalization();
|
const { translate } = useLocalization();
|
||||||
const [answers, setAnswers] = useState<{ [questionId: string]: any }>({})
|
const isUpdate = !!survey.myResponse
|
||||||
|
|
||||||
|
const [answers, setAnswers] = useState<{ [questionId: string]: any }>(() => {
|
||||||
|
if (survey.myResponse?.answers) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
survey.myResponse.answers.map((a) => [
|
||||||
|
a.questionId,
|
||||||
|
a.questionType === 'rating' ? Number(a.value) : a.value,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
})
|
||||||
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) => {
|
||||||
|
|
@ -34,8 +46,12 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
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) {
|
||||||
newErrors[question.id] = translate('::App.Platform.Intranet.SurveyModal.RequiredField')
|
const val = answers[question.id]
|
||||||
|
const isEmpty = val === undefined || val === null || val === '' || (question.type === 'rating' && Number(val) === 0)
|
||||||
|
if (isEmpty) {
|
||||||
|
newErrors[question.id] = translate('::App.Platform.Intranet.SurveyModal.RequiredField')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -72,6 +88,22 @@ 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">
|
||||||
|
{[1, 2, 3, 4, 5].map((star) => (
|
||||||
|
<button
|
||||||
|
key={star}
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleAnswerChange(question.id, star)}
|
||||||
|
className={`text-2xl transition-colors ${
|
||||||
|
(answers[question.id] ?? 0) >= star
|
||||||
|
? 'text-yellow-400'
|
||||||
|
: 'text-gray-300 dark:text-gray-600 hover:text-yellow-300'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
★
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
{hasError && (
|
{hasError && (
|
||||||
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
<p className="text-sm text-red-600 dark:text-red-400">{errors[question.id]}</p>
|
||||||
)}
|
)}
|
||||||
|
|
@ -97,8 +129,8 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name={`question-${question.id}`}
|
name={`question-${question.id}`}
|
||||||
value={option.id}
|
value={option.text}
|
||||||
checked={answers[question.id] === option.id}
|
checked={answers[question.id] === option.text}
|
||||||
onChange={(e) => handleAnswerChange(question.id, e.target.value)}
|
onChange={(e) => handleAnswerChange(question.id, e.target.value)}
|
||||||
className="w-4 h-4 text-blue-600"
|
className="w-4 h-4 text-blue-600"
|
||||||
/>
|
/>
|
||||||
|
|
@ -262,7 +294,9 @@ const SurveyModal: React.FC<SurveyModalProps> = ({ survey, onClose, onSubmit })
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
|
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')}
|
{isUpdate
|
||||||
|
? translate('::App.Platform.Intranet.SurveyModal.Update')
|
||||||
|
: translate('::App.Platform.Intranet.SurveyModal.Submit')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ const TodayBirthdays: React.FC<{ employees: UserInfoViewModel[] }> = ({ employee
|
||||||
const today = dayjs()
|
const today = dayjs()
|
||||||
const { translate } = useLocalization();
|
const { translate } = useLocalization();
|
||||||
|
|
||||||
console.log('TodayBirthdays rendered with employees:', employees)
|
|
||||||
return (
|
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="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">
|
<div className="p-4 border-b border-pink-200 dark:border-pink-700">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue