Classroom endpoints
This commit is contained in:
parent
b1f82e2f91
commit
6365a0672f
9 changed files with 113 additions and 111 deletions
|
|
@ -3924,21 +3924,21 @@
|
||||||
{
|
{
|
||||||
"key": "admin.classroom.dashboard",
|
"key": "admin.classroom.dashboard",
|
||||||
"path": "/admin/classroom/dashboard",
|
"path": "/admin/classroom/dashboard",
|
||||||
"componentPath": "@/views/classroom/DashboardPage",
|
"componentPath": "@/views/classroom/Dashboard",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": ["App.Classroom.Dashboard"]
|
"authority": ["App.Classroom.Dashboard"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.classroom.classes",
|
"key": "admin.classroom.classes",
|
||||||
"path": "/admin/classroom/classes",
|
"path": "/admin/classroom/classes",
|
||||||
"componentPath": "@/views/classroom/ClassListPage",
|
"componentPath": "@/views/classroom/ClassList",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": ["App.Classroom.List"]
|
"authority": ["App.Classroom.List"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.classroom.classroom",
|
"key": "admin.classroom.classroom",
|
||||||
"path": "/admin/classroom/room/:id",
|
"path": "/admin/classroom/room/:id",
|
||||||
"componentPath": "@/views/classroom/RoomPage",
|
"componentPath": "@/views/classroom/RoomDetail",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": ["App.Classroom.RoomDetail"]
|
"authority": ["App.Classroom.RoomDetail"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
|
||||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||||
}, {
|
}, {
|
||||||
"url": "index.html",
|
"url": "index.html",
|
||||||
"revision": "0.9qu602jrc3g"
|
"revision": "0.lcv915aoevo"
|
||||||
}], {});
|
}], {});
|
||||||
workbox.cleanupOutdatedCaches();
|
workbox.cleanupOutdatedCaches();
|
||||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
|
|
||||||
54
ui/src/services/classroom.service.ts
Normal file
54
ui/src/services/classroom.service.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { ClassroomDto } from '@/proxy/classroom/models'
|
||||||
|
import apiService from './api.service'
|
||||||
|
|
||||||
|
export const getClassroomById = (id: string) =>
|
||||||
|
apiService.fetchData<ClassroomDto>({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/api/app/classroom`,
|
||||||
|
params: { id },
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getClassrooms = () =>
|
||||||
|
apiService.fetchData<ClassroomDto[]>({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/api/app/classroom`,
|
||||||
|
})
|
||||||
|
|
||||||
|
// export const getChartOptions = (chartCode: string) =>
|
||||||
|
// apiService.fetchData<ChartDto>({
|
||||||
|
// method: 'GET',
|
||||||
|
// url: `/api/app/charts/chart-options`,
|
||||||
|
// params: { chartCode },
|
||||||
|
// })
|
||||||
|
|
||||||
|
// export const getChartSelect = (chartCode: string, filter?: string) =>
|
||||||
|
// apiService.fetchData({
|
||||||
|
// method: 'GET',
|
||||||
|
// url: `/api/app/chart-select/select`,
|
||||||
|
// params: { chartCode, filter },
|
||||||
|
// })
|
||||||
|
|
||||||
|
// export const putCharts = (input: ChartEditDto) =>
|
||||||
|
// apiService.fetchData({
|
||||||
|
// method: 'PUT',
|
||||||
|
// url: `/api/app/charts/${input.id}`,
|
||||||
|
// data: input,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// export const deleteChartJsonItem = (
|
||||||
|
// id: string,
|
||||||
|
// chartCode: string,
|
||||||
|
// index: number,
|
||||||
|
// fieldName: string,
|
||||||
|
// ) =>
|
||||||
|
// apiService.fetchData({
|
||||||
|
// method: 'DELETE',
|
||||||
|
// url: `/api/app/charts/chart-json-item?id=${id}&chartCode=${chartCode}&index=${index}&fieldName=${fieldName}`,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// export const putChartJsonItem = (input: ChartJsonItemRowDto) =>
|
||||||
|
// apiService.fetchData({
|
||||||
|
// method: 'PUT',
|
||||||
|
// url: `/api/app/charts/chart-json-item`,
|
||||||
|
// data: input,
|
||||||
|
// })
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import {
|
import {
|
||||||
|
|
@ -11,24 +11,15 @@ import {
|
||||||
FaTrash,
|
FaTrash,
|
||||||
FaEye,
|
FaEye,
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
|
|
||||||
import { ClassroomDto } from '@/proxy/classroom/models'
|
import { ClassroomDto } from '@/proxy/classroom/models'
|
||||||
import { initialScheduledClasses } from '@/proxy/classroom/data'
|
import { initialScheduledClasses } from '@/proxy/classroom/data'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
|
import { Container } from '@/components/shared'
|
||||||
|
import { getClassrooms } from '@/services/classroom.service'
|
||||||
|
|
||||||
interface ClassListProps {
|
const ClassList: React.FC = () => {
|
||||||
onCreateClass: (classData: Partial<ClassroomDto>) => void
|
|
||||||
onJoinClass: (classSession: ClassroomDto) => void
|
|
||||||
onEditClass: (classId: string, classData: Partial<ClassroomDto>) => void
|
|
||||||
onDeleteClass: (classId: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ClassList: React.FC<ClassListProps> = ({
|
|
||||||
onCreateClass,
|
|
||||||
onJoinClass,
|
|
||||||
onEditClass,
|
|
||||||
onDeleteClass,
|
|
||||||
}) => {
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { user } = useStoreState((state) => state.auth)
|
const { user } = useStoreState((state) => state.auth)
|
||||||
|
|
||||||
|
|
@ -106,7 +97,6 @@ export const ClassList: React.FC<ClassListProps> = ({
|
||||||
participantCount: 0,
|
participantCount: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
onCreateClass(newClass)
|
|
||||||
setScheduledClasses((prev) => [...prev, newClass as ClassroomDto])
|
setScheduledClasses((prev) => [...prev, newClass as ClassroomDto])
|
||||||
setShowCreateModal(false)
|
setShowCreateModal(false)
|
||||||
setFormData({
|
setFormData({
|
||||||
|
|
@ -145,7 +135,6 @@ export const ClassList: React.FC<ClassListProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
setScheduledClasses((prev) => prev.map((c) => (c.id === editingClass.id ? updatedClass : c)))
|
setScheduledClasses((prev) => prev.map((c) => (c.id === editingClass.id ? updatedClass : c)))
|
||||||
onEditClass(editingClass.id, formData)
|
|
||||||
setShowEditModal(false)
|
setShowEditModal(false)
|
||||||
setEditingClass(null)
|
setEditingClass(null)
|
||||||
resetForm()
|
resetForm()
|
||||||
|
|
@ -155,7 +144,6 @@ export const ClassList: React.FC<ClassListProps> = ({
|
||||||
if (!deletingClass) return
|
if (!deletingClass) return
|
||||||
|
|
||||||
setScheduledClasses((prev) => prev.filter((c) => c.id !== deletingClass.id))
|
setScheduledClasses((prev) => prev.filter((c) => c.id !== deletingClass.id))
|
||||||
onDeleteClass(deletingClass.id)
|
|
||||||
setShowDeleteModal(false)
|
setShowDeleteModal(false)
|
||||||
setDeletingClass(null)
|
setDeletingClass(null)
|
||||||
}
|
}
|
||||||
|
|
@ -221,7 +209,6 @@ export const ClassList: React.FC<ClassListProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
setScheduledClasses((prev) => prev.map((c) => (c.id === classSession.id ? updatedClass : c)))
|
setScheduledClasses((prev) => prev.map((c) => (c.id === classSession.id ? updatedClass : c)))
|
||||||
onJoinClass(updatedClass)
|
|
||||||
// Sınıf başlatıldığında classroom ekranına yönlendir
|
// Sınıf başlatıldığında classroom ekranına yönlendir
|
||||||
if (updatedClass.id) {
|
if (updatedClass.id) {
|
||||||
navigate(ROUTES_ENUM.protected.admin.classroom.classroom.replace(':id', updatedClass.id))
|
navigate(ROUTES_ENUM.protected.admin.classroom.classroom.replace(':id', updatedClass.id))
|
||||||
|
|
@ -239,7 +226,7 @@ export const ClassList: React.FC<ClassListProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Container>
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div className="mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
|
|
@ -385,7 +372,6 @@ export const ClassList: React.FC<ClassListProps> = ({
|
||||||
user.role === 'teacher' && classSession.teacherId === user.id
|
user.role === 'teacher' && classSession.teacherId === user.id
|
||||||
? handleStartClass(classSession)
|
? handleStartClass(classSession)
|
||||||
: (() => {
|
: (() => {
|
||||||
onJoinClass(classSession)
|
|
||||||
if (classSession.id)
|
if (classSession.id)
|
||||||
navigate(
|
navigate(
|
||||||
ROUTES_ENUM.protected.admin.classroom.classroom.replace(
|
ROUTES_ENUM.protected.admin.classroom.classroom.replace(
|
||||||
|
|
@ -931,6 +917,8 @@ export const ClassList: React.FC<ClassListProps> = ({
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default ClassList
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { ClassList } from '@/components/classroom/ClassList'
|
|
||||||
import { Container } from '@/components/shared'
|
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
const ClassListPage: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<ClassList
|
|
||||||
onCreateClass={() => {}}
|
|
||||||
onJoinClass={() => {}}
|
|
||||||
onEditClass={() => {}}
|
|
||||||
onDeleteClass={() => {}}
|
|
||||||
/>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ClassListPage
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { RoleSelector } from './RoleSelector'
|
|
||||||
import { useClassroomLogic } from '@/utils/hooks/useClassroomLogic'
|
import { useClassroomLogic } from '@/utils/hooks/useClassroomLogic'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
import { Room } from './Room'
|
import { Container } from '@/components/shared'
|
||||||
|
import { RoleSelector } from '@/components/classroom/RoleSelector'
|
||||||
|
import RoomDetail from './RoomDetail'
|
||||||
|
|
||||||
export function Dashboard() {
|
const Dashboard: React.FC = () => {
|
||||||
const { roleState, currentClass, handleRoleSelect, handleLeaveClass } = useClassroomLogic()
|
const { roleState, currentClass, handleRoleSelect, handleLeaveClass } = useClassroomLogic()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
|
@ -16,21 +17,13 @@ export function Dashboard() {
|
||||||
}
|
}
|
||||||
}, [roleState, navigate])
|
}, [roleState, navigate])
|
||||||
|
|
||||||
// Render edilecek içerik
|
return (
|
||||||
const renderContent = () => {
|
<Container>
|
||||||
switch (roleState) {
|
{roleState === 'role-selection' && <RoleSelector onRoleSelect={handleRoleSelect} />}
|
||||||
case 'role-selection':
|
|
||||||
return <RoleSelector onRoleSelect={handleRoleSelect} />
|
|
||||||
|
|
||||||
case 'classroom':
|
{roleState === 'classroom' && currentClass && <RoomDetail />}
|
||||||
return currentClass ? (
|
</Container>
|
||||||
<Room classSession={currentClass} onLeaveClass={handleLeaveClass} />
|
)
|
||||||
) : null
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderContent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Dashboard
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import { Dashboard } from '@/components/classroom/Dashboard'
|
|
||||||
import { Container } from '@/components/shared'
|
|
||||||
|
|
||||||
const DashboardPage: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<Dashboard />
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DashboardPage
|
|
||||||
|
|
@ -36,9 +36,6 @@ import {
|
||||||
FaUser,
|
FaUser,
|
||||||
FaBars,
|
FaBars,
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
import { ParticipantGrid } from './ParticipantGrid'
|
|
||||||
import { KickParticipantModal } from './KickParticipantModal'
|
|
||||||
import { ScreenSharePanel } from './Panels/ScreenSharePanel'
|
|
||||||
import { SignalRService } from '@/services/classroom/signalr'
|
import { SignalRService } from '@/services/classroom/signalr'
|
||||||
import { WebRTCService } from '@/services/classroom/webrtc'
|
import { WebRTCService } from '@/services/classroom/webrtc'
|
||||||
import {
|
import {
|
||||||
|
|
@ -52,11 +49,11 @@ import {
|
||||||
VideoLayoutDto,
|
VideoLayoutDto,
|
||||||
} from '@/proxy/classroom/models'
|
} from '@/proxy/classroom/models'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
|
import { ParticipantGrid } from '@/components/classroom/ParticipantGrid'
|
||||||
interface RoomProps {
|
import { ScreenSharePanel } from '@/components/classroom/Panels/ScreenSharePanel'
|
||||||
classSession: ClassroomDto
|
import { KickParticipantModal } from '@/components/classroom/KickParticipantModal'
|
||||||
onLeaveClass: () => void
|
import { useParams } from 'react-router-dom'
|
||||||
}
|
import { getClassroomById } from '@/services/classroom.service'
|
||||||
|
|
||||||
type SidePanelType =
|
type SidePanelType =
|
||||||
| 'chat'
|
| 'chat'
|
||||||
|
|
@ -67,8 +64,25 @@ type SidePanelType =
|
||||||
| 'settings'
|
| 'settings'
|
||||||
| null
|
| null
|
||||||
|
|
||||||
export const Room: React.FC<RoomProps> = ({ classSession, onLeaveClass }) => {
|
const newClassSession: ClassroomDto = {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
teacherId: '',
|
||||||
|
teacherName: '',
|
||||||
|
startTime: '',
|
||||||
|
scheduledStartTime: '',
|
||||||
|
endTime: '',
|
||||||
|
isActive: false,
|
||||||
|
isScheduled: false,
|
||||||
|
participantCount: 0,
|
||||||
|
settings: undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
const RoomDetail: React.FC = () => {
|
||||||
|
const params = useParams()
|
||||||
const { user } = useStoreState((state) => state.auth)
|
const { user } = useStoreState((state) => state.auth)
|
||||||
|
|
||||||
|
const [classSession, setClassSession] = useState<ClassroomDto>(newClassSession)
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||||
const [participants, setParticipants] = useState<ClassParticipantDto[]>([])
|
const [participants, setParticipants] = useState<ClassParticipantDto[]>([])
|
||||||
const [localStream, setLocalStream] = useState<MediaStream>()
|
const [localStream, setLocalStream] = useState<MediaStream>()
|
||||||
|
|
@ -152,7 +166,7 @@ export const Room: React.FC<RoomProps> = ({ classSession, onLeaveClass }) => {
|
||||||
|
|
||||||
// Apply class settings
|
// Apply class settings
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (classSession.settings) {
|
if (classSession?.settings) {
|
||||||
setClassSettings(classSession.settings)
|
setClassSettings(classSession.settings)
|
||||||
const selectedLayout =
|
const selectedLayout =
|
||||||
layouts.find((l) => l.id === classSession.settings!.defaultLayout) || layouts[0]
|
layouts.find((l) => l.id === classSession.settings!.defaultLayout) || layouts[0]
|
||||||
|
|
@ -164,7 +178,7 @@ export const Room: React.FC<RoomProps> = ({ classSession, onLeaveClass }) => {
|
||||||
setIsVideoEnabled(classSession.settings.defaultCameraState === 'on')
|
setIsVideoEnabled(classSession.settings.defaultCameraState === 'on')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [classSession.settings, user.role])
|
}, [classSession?.settings, user.role])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
|
|
@ -176,6 +190,12 @@ export const Room: React.FC<RoomProps> = ({ classSession, onLeaveClass }) => {
|
||||||
|
|
||||||
const initializeServices = async () => {
|
const initializeServices = async () => {
|
||||||
try {
|
try {
|
||||||
|
//ClassEntity
|
||||||
|
const classEntity = await getClassroomById(params?.id ?? '')
|
||||||
|
if (classEntity) {
|
||||||
|
setClassSession(classEntity.data)
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize SignalR
|
// Initialize SignalR
|
||||||
signalRServiceRef.current = new SignalRService()
|
signalRServiceRef.current = new SignalRService()
|
||||||
await signalRServiceRef.current.start()
|
await signalRServiceRef.current.start()
|
||||||
|
|
@ -281,7 +301,6 @@ export const Room: React.FC<RoomProps> = ({ classSession, onLeaveClass }) => {
|
||||||
|
|
||||||
const handleLeaveCall = async () => {
|
const handleLeaveCall = async () => {
|
||||||
await cleanup()
|
await cleanup()
|
||||||
onLeaveClass()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSendMessage = async (e: React.FormEvent) => {
|
const handleSendMessage = async (e: React.FormEvent) => {
|
||||||
|
|
@ -1770,7 +1789,7 @@ export const Room: React.FC<RoomProps> = ({ classSession, onLeaveClass }) => {
|
||||||
{/* Left Side - Meeting Info */}
|
{/* Left Side - Meeting Info */}
|
||||||
<div className="flex items-center space-x-4 text-white absolute left-0">
|
<div className="flex items-center space-x-4 text-white absolute left-0">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span className="text-sm font-medium truncate">{classSession.name}</span>
|
<span className="text-sm font-medium truncate">{classSession?.name}</span>
|
||||||
<div className="w-px h-4 bg-gray-600"></div>
|
<div className="w-px h-4 bg-gray-600"></div>
|
||||||
<span className="text-sm text-gray-300">
|
<span className="text-sm text-gray-300">
|
||||||
{new Date().toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' })}
|
{new Date().toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' })}
|
||||||
|
|
@ -1996,3 +2015,5 @@ export const Room: React.FC<RoomProps> = ({ classSession, onLeaveClass }) => {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default RoomDetail
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { Room } from '@/components/classroom/Room'
|
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
const RoomPage: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<Room
|
|
||||||
classSession={{
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
teacherId: '',
|
|
||||||
teacherName: '',
|
|
||||||
startTime: '',
|
|
||||||
scheduledStartTime: '',
|
|
||||||
isActive: false,
|
|
||||||
isScheduled: false,
|
|
||||||
participantCount: 0,
|
|
||||||
}}
|
|
||||||
onLeaveClass={() => {}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RoomPage
|
|
||||||
Loading…
Reference in a new issue