Chat ve Attandace özellikleri veritabanıından yükleniyor

This commit is contained in:
Sedat ÖZTÜRK 2025-08-29 14:41:47 +03:00
parent cf3cb50e1a
commit f3b953da4f
6 changed files with 104 additions and 64 deletions

View file

@ -17,15 +17,18 @@ public class ClassroomAppService : PlatformAppService, IClassroomAppService
private readonly IRepository<Classroom, Guid> _classSessionRepository;
private readonly IRepository<ClassroomParticipant, Guid> _participantRepository;
private readonly IRepository<ClassroomAttandance, Guid> _attendanceRepository;
private readonly IRepository<ClassroomChat, Guid> _chatRepository;
public ClassroomAppService(
IRepository<Classroom, Guid> classSessionRepository,
IRepository<ClassroomParticipant, Guid> participantRepository,
IRepository<ClassroomAttandance, Guid> attendanceRepository)
IRepository<ClassroomAttandance, Guid> attendanceRepository,
IRepository<ClassroomChat, Guid> chatRepository)
{
_classSessionRepository = classSessionRepository;
_participantRepository = participantRepository;
_attendanceRepository = attendanceRepository;
_chatRepository = chatRepository;
}
public async Task<ClassroomDto> GetAsync(Guid id)
@ -271,4 +274,37 @@ public class ClassroomAppService : PlatformAppService, IClassroomAppService
return ObjectMapper.Map<List<ClassroomAttandance>, List<ClassroomAttendanceDto>>(attendanceRecords);
}
public async Task<List<ClassroomParticipantDto>> GetParticipantAsync(Guid sessionId)
{
var classSession = await _classSessionRepository.GetAsync(sessionId);
if (classSession.TeacherId != CurrentUser.Id)
{
throw new UnauthorizedAccessException("Only the teacher can view participant");
}
var participantRecords = await _participantRepository.GetListAsync(
x => x.SessionId == sessionId
);
return ObjectMapper.Map<List<ClassroomParticipant>, List<ClassroomParticipantDto>>(participantRecords);
}
public async Task<List<ClassroomChatDto>> GetChatAsync(Guid sessionId)
{
var classSession = await _classSessionRepository.GetAsync(sessionId);
if (classSession.TeacherId != CurrentUser.Id)
{
throw new UnauthorizedAccessException("Only the teacher can view chat");
}
var chatRecords = await _chatRepository.GetListAsync(
x => x.SessionId == sessionId
);
return ObjectMapper.Map<List<ClassroomChat>, List<ClassroomChatDto>>(chatRecords);
}
}

View file

@ -144,6 +144,7 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
</select>
)}
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-3">
{messages.length === 0 ? (

View file

@ -66,12 +66,13 @@ export type messageType = 'public' | 'private' | 'announcement'
export interface ClassroomChatDto {
id: string
sessionId: string
senderId: string
senderName: string
message: string
timestamp: string
isTeacher: boolean
recipientId?: string // Özel mesaj için
recipientId?: string
recipientName?: string
messageType: messageType
}

View file

@ -1,4 +1,10 @@
import { ClassroomDto, ClassroomFilterInputDto } from '@/proxy/classroom/models'
import {
ClassroomAttendanceDto,
ClassroomChatDto,
ClassroomDto,
ClassroomFilterInputDto,
ClassroomParticipantDto,
} from '@/proxy/classroom/models'
import apiService from './api.service'
import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy'
@ -46,3 +52,21 @@ export const endClassroom = (id: string) =>
method: 'PUT',
url: `/api/app/classroom/${id}/end-class`,
})
export const getClassroomAttandances = (id: string) =>
apiService.fetchData<ClassroomAttendanceDto[]>({
method: 'GET',
url: `/api/app/classroom/attendance/${id}`,
})
export const getClassroomParticipants = (id: string) =>
apiService.fetchData<ClassroomParticipantDto[]>({
method: 'GET',
url: `/api/app/classroom/participant/${id}`,
})
export const getClassroomChats = (id: string) =>
apiService.fetchData<ClassroomChatDto[]>({
method: 'GET',
url: `/api/app/classroom/chat/${id}`,
})

View file

@ -7,7 +7,6 @@ import { useNavigate } from 'react-router-dom'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { Helmet } from 'react-helmet'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Container } from '@/components/shared'
const Dashboard: React.FC = () => {
const navigate = useNavigate()
@ -31,7 +30,7 @@ const Dashboard: React.FC = () => {
title={translate('::' + 'App.Classroom')}
defaultTitle="Kurs Platform"
></Helmet>
<Container>
<div className="flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
@ -80,7 +79,7 @@ const Dashboard: React.FC = () => {
</motion.button>
</div>
</motion.div>
</Container>
</div>
</>
)
}

View file

@ -53,7 +53,11 @@ import { ParticipantGrid } from '@/components/classroom/ParticipantGrid'
import { ScreenSharePanel } from '@/components/classroom/Panels/ScreenSharePanel'
import { KickParticipantModal } from '@/components/classroom/KickParticipantModal'
import { useParams } from 'react-router-dom'
import { getClassroomById } from '@/services/classroom.service'
import {
getClassroomAttandances,
getClassroomById,
getClassroomChats,
} from '@/services/classroom.service'
import { showDbDateAsIs } from '@/utils/dateUtils'
import { useNavigate } from 'react-router-dom'
import { endClassroom } from '@/services/classroom.service'
@ -162,6 +166,22 @@ const RoomDetail: React.FC = () => {
},
]
const fetchClassAttendances = async () => {
if (!params?.id) return
const attResult = await getClassroomAttandances(params.id)
if (attResult && attResult.data) {
setAttendanceRecords(attResult.data)
}
}
const fetchClassChats = async () => {
if (!params?.id) return
const chatResult = await getClassroomChats(params.id)
if (chatResult && chatResult.data) {
setChatMessages(chatResult.data || [])
}
}
const fetchClassDetails = async () => {
const classEntity = await getClassroomById(params?.id ?? '')
if (classEntity) {
@ -172,6 +192,8 @@ const RoomDetail: React.FC = () => {
useEffect(() => {
fetchClassDetails()
fetchClassChats()
fetchClassAttendances()
}, [])
useEffect(() => {
@ -558,52 +580,6 @@ const RoomDetail: React.FC = () => {
)
}
// // Demo: Simulate student joining
// const simulateStudentJoin = () => {
// const studentNames = ['Ahmet Yılmaz', 'Fatma Demir', 'Mehmet Kaya', 'Ayşe Özkan', 'Ali Çelik']
// const availableNames = studentNames.filter((name) => !participants.some((p) => p.name === name))
// if (availableNames.length === 0) {
// alert('Tüm demo öğrenciler zaten sınıfta!')
// return
// }
// const randomName = availableNames[Math.floor(Math.random() * availableNames.length)]
// const studentId = crypto.randomUUID()
// setParticipants((prev) => {
// return [
// ...prev,
// {
// id: studentId,
// name: randomName,
// isTeacher: false,
// isAudioMuted: classSettings.autoMuteNewParticipants,
// isVideoMuted: classSettings.defaultCameraState === 'off',
// },
// ]
// })
// // Add attendance record
// setAttendanceRecords((prev: any) => {
// // Check if student already has an active attendance record
// const existingRecord = prev.find((r: any) => r.studentId === studentId && !r.leaveTime)
// if (existingRecord) return prev
// return [
// ...prev,
// {
// id: crypto.randomUUID(),
// sessionId: classSession.id,
// studentId,
// studentName: randomName,
// joinTime: new Date().toISOString(),
// totalDurationMinutes: 0,
// },
// ]
// })
// }
const handleSettingsChange = (newSettings: Partial<ClassroomSettingsDto>) => {
setClassSettings((prev) => ({ ...prev, ...newSettings }))
}
@ -921,17 +897,20 @@ const RoomDetail: React.FC = () => {
<FaUsers className="inline mr-1" size={14} />
Katılımcılar
</button>
<button
onClick={() => setParticipantsActiveTab('attendance')}
className={`flex-1 px-3 py-2 text-sm font-medium rounded-md transition-all ${
participantsActiveTab === 'attendance'
? 'bg-white text-blue-600 shadow-sm'
: 'text-gray-600 hover:text-gray-900'
}`}
>
<FaClipboardList className="inline mr-1" size={14} />
Katılım Raporu
</button>
{user.role === 'teacher' && (
<button
onClick={() => setParticipantsActiveTab('attendance')}
className={`flex-1 px-3 py-2 text-sm font-medium rounded-md transition-all ${
participantsActiveTab === 'attendance'
? 'bg-white text-blue-600 shadow-sm'
: 'text-gray-600 hover:text-gray-900'
}`}
>
<FaClipboardList className="inline mr-1" size={14} />
Katılım Raporu
</button>
)}
</div>
</div>