Classroom Çıkan kişinin adını yazdık

This commit is contained in:
Sedat ÖZTÜRK 2025-09-01 09:57:54 +03:00
parent 9db84f137e
commit 0d2e3f715c
4 changed files with 65 additions and 54 deletions

View file

@ -1,12 +1,12 @@
import React from 'react'; import React from 'react'
import { motion } from 'framer-motion'; import { motion } from 'framer-motion'
import { FaUserTimes, FaExclamationTriangle } from 'react-icons/fa'; import { FaUserTimes, FaExclamationTriangle } from 'react-icons/fa'
interface KickParticipantModalProps { interface KickParticipantModalProps {
participant: { id: string; name: string } | null; participant: { id: string; name: string } | null
isOpen: boolean; isOpen: boolean
onClose: () => void; onClose: () => void
onConfirm: (participantId: string) => void; onConfirm: (participantId: string, participantName: string) => void
} }
export const KickParticipantModal: React.FC<KickParticipantModalProps> = ({ export const KickParticipantModal: React.FC<KickParticipantModalProps> = ({
@ -15,12 +15,12 @@ export const KickParticipantModal: React.FC<KickParticipantModalProps> = ({
onClose, onClose,
onConfirm, onConfirm,
}) => { }) => {
if (!isOpen || !participant) return null; if (!isOpen || !participant) return null
const handleConfirm = () => { const handleConfirm = () => {
onConfirm(participant.id); onConfirm(participant.id, participant.name)
onClose(); onClose()
}; }
return ( return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
@ -42,7 +42,8 @@ export const KickParticipantModal: React.FC<KickParticipantModalProps> = ({
<div className="mb-6"> <div className="mb-6">
<p className="text-gray-700 mb-2"> <p className="text-gray-700 mb-2">
<strong>"{participant.name}"</strong> adlı katılımcıyı sınıftan çıkarmak istediğinizden emin misiniz? <strong>"{participant.name}"</strong> adlı katılımcıyı sınıftan çıkarmak
istediğinizden emin misiniz?
</p> </p>
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3"> <div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
<div className="flex items-start"> <div className="flex items-start">
@ -77,5 +78,5 @@ export const KickParticipantModal: React.FC<KickParticipantModalProps> = ({
</div> </div>
</motion.div> </motion.div>
</div> </div>
); )
}; }

View file

@ -17,7 +17,7 @@ interface ParticipantsPanelProps {
participants: ClassroomParticipantDto[] participants: ClassroomParticipantDto[]
attendanceRecords: ClassroomAttendanceDto[] attendanceRecords: ClassroomAttendanceDto[]
onMuteParticipant: (participantId: string, isMuted: boolean, isTeacher: boolean) => void onMuteParticipant: (participantId: string, isMuted: boolean, isTeacher: boolean) => void
onKickParticipant: (participantId: string) => void onKickParticipant: (participantId: string, participantName: string) => void
onApproveHandRaise: (participantId: string) => void onApproveHandRaise: (participantId: string) => void
onDismissHandRaise: (participantId: string) => void onDismissHandRaise: (participantId: string) => void
onClose: () => void onClose: () => void
@ -129,20 +129,19 @@ const ParticipantsPanel: React.FC<ParticipantsPanelProps> = ({
</div> </div>
<span className="text-gray-900">{participant.name}</span> <span className="text-gray-900">{participant.name}</span>
{/* Hand Raise Indicator & Teacher Control */} {/* Hand Raise Indicator & Teacher Control */}
{participant.isHandRaised && ( {participant.isHandRaised &&
user.role === 'teacher' && !participant.isTeacher ? ( (user.role === 'teacher' && !participant.isTeacher ? (
<button <button
onClick={() => onDismissHandRaise(participant.id)} onClick={() => onDismissHandRaise(participant.id)}
className="ml-2 p-1 rounded bg-yellow-100 hover:bg-yellow-200" className="ml-2 p-1 rounded bg-yellow-100 hover:bg-yellow-200"
title="El kaldırmayı kaldır" title="El kaldırmayı kaldır"
> >
<FaHandPaper className="text-yellow-600" /> <FaHandPaper className="text-yellow-600" />
</button> </button>
) : ( ) : (
<FaHandPaper className="text-yellow-600 ml-2" title="Parmak kaldırdı" /> <FaHandPaper className="text-yellow-600 ml-2" title="Parmak kaldırdı" />
) ))}
)}
</div> </div>
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1">
@ -177,7 +176,7 @@ const ParticipantsPanel: React.FC<ParticipantsPanelProps> = ({
{/* Kick Button (Teacher Only) */} {/* Kick Button (Teacher Only) */}
{user.role === 'teacher' && !participant.isTeacher && ( {user.role === 'teacher' && !participant.isTeacher && (
<button <button
onClick={() => onKickParticipant(participant.id)} onClick={() => onKickParticipant(participant.id, participant.name)}
className="p-1 text-red-600 hover:bg-red-50 rounded transition-colors" className="p-1 text-red-600 hover:bg-red-50 rounded transition-colors"
title="Sınıftan Çıkar" title="Sınıftan Çıkar"
> >

View file

@ -1,8 +1,4 @@
import { import { ClassroomAttendanceDto, ClassroomChatDto, HandRaiseDto } from '@/proxy/classroom/models'
ClassroomAttendanceDto,
ClassroomChatDto,
HandRaiseDto,
} from '@/proxy/classroom/models'
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { store } from '@/store/store' import { store } from '@/store/store'
import * as signalR from '@microsoft/signalr' import * as signalR from '@microsoft/signalr'
@ -22,7 +18,11 @@ export class SignalRService {
isTeacher: boolean, isTeacher: boolean,
isActive: boolean, isActive: boolean,
) => void ) => void
private onParticipantLeft?: (payload: { userId: string; sessionId: string }) => void private onParticipantLeft?: (payload: {
userId: string
sessionId: string
userName: string
}) => void
private onChatMessage?: (message: ClassroomChatDto) => void private onChatMessage?: (message: ClassroomChatDto) => void
private onParticipantMuted?: (userId: string, isMuted: boolean) => void private onParticipantMuted?: (userId: string, isMuted: boolean) => void
private onHandRaiseReceived?: (studentId: string) => void private onHandRaiseReceived?: (studentId: string) => void
@ -59,9 +59,12 @@ export class SignalRService {
}, },
) )
this.connection.on('ParticipantLeft', (payload: { userId: string; sessionId: string }) => { this.connection.on(
this.onParticipantLeft?.(payload) 'ParticipantLeft',
}) (payload: { userId: string; sessionId: string; userName: string }) => {
this.onParticipantLeft?.(payload)
},
)
this.connection.on('ChatMessage', (message: any) => { this.connection.on('ChatMessage', (message: any) => {
this.onChatMessage?.(message) this.onChatMessage?.(message)
@ -178,6 +181,7 @@ export class SignalRService {
this.onParticipantLeft?.({ this.onParticipantLeft?.({
userId: store.getState().auth.user.id, userId: store.getState().auth.user.id,
sessionId: this.currentSessionId, sessionId: this.currentSessionId,
userName: store.getState().auth.user.name,
}) })
} }
@ -241,7 +245,7 @@ export class SignalRService {
const { auth } = store.getState() const { auth } = store.getState()
if (!this.isConnected) { if (!this.isConnected) {
this.onParticipantLeft?.({ userId: auth.user.id, sessionId }) this.onParticipantLeft?.({ userId: auth.user.id, sessionId, userName: auth.user.name })
return return
} }
@ -422,10 +426,10 @@ export class SignalRService {
} }
} }
async kickParticipant(sessionId: string, participantId: string): Promise<void> { async kickParticipant(sessionId: string, participantId: string, userName: string): Promise<void> {
if (!this.isConnected) { if (!this.isConnected) {
setTimeout(() => { setTimeout(() => {
this.onParticipantLeft?.({ userId: participantId, sessionId }) this.onParticipantLeft?.({ userId: participantId, sessionId, userName })
}, 100) }, 100)
return return
} }
@ -502,7 +506,9 @@ export class SignalRService {
this.onParticipantJoined = callback this.onParticipantJoined = callback
} }
setParticipantLeaveHandler(callback: (payload: { userId: string; sessionId: string }) => void) { setParticipantLeaveHandler(
callback: (payload: { userId: string; sessionId: string; userName: string }) => void,
) {
this.onParticipantLeft = callback this.onParticipantLeft = callback
} }

View file

@ -363,14 +363,15 @@ const RoomDetail: React.FC = () => {
localStream?.getTracks().forEach((t) => t.stop()) localStream?.getTracks().forEach((t) => t.stop())
}) })
signalRServiceRef.current.setParticipantLeaveHandler(({ userId, sessionId }) => { signalRServiceRef.current.setParticipantLeaveHandler(({ userId, sessionId, userName }) => {
if (userId !== user.id) { if (userId !== user.id) {
const leftUser = participants.find((p) => p.id === userId) toast.push(
const leftName = leftUser ? leftUser.name : 'Bilinmeyen' <Notification
title={`Katılımcı ayrıldı: ${userName ?? 'Bilinmeyen'}`}
toast.push(<Notification title={`Katılımcı ayrıldı: ${leftName}`} type="warning" />, { type="warning"
placement: 'top-center', />,
}) { placement: 'top-center' },
)
} }
// peer connectionı kapat // peer connectionı kapat
@ -584,10 +585,14 @@ const RoomDetail: React.FC = () => {
} }
} }
const handleKickParticipant = async (participantId: string) => { const handleKickParticipant = async (participantId: string, participantName: string) => {
if (signalRServiceRef.current && user.role === 'teacher') { if (signalRServiceRef.current && user.role === 'teacher') {
try { try {
await signalRServiceRef.current.kickParticipant(classSession.id, participantId) await signalRServiceRef.current.kickParticipant(
classSession.id,
participantId,
participantName,
)
setAttendanceRecords((prev) => setAttendanceRecords((prev) =>
prev.map((r) => { prev.map((r) => {
if (r.studentId === participantId && !r.leaveTime) { if (r.studentId === participantId && !r.leaveTime) {