Classroom Search

This commit is contained in:
Sedat ÖZTÜRK 2025-08-28 15:28:42 +03:00
parent 0b60f006d0
commit 51f2c976bf
6 changed files with 102 additions and 14 deletions

View file

@ -0,0 +1,7 @@
using Volo.Abp.Application.Dtos;
public class ClassroomFilterInputDto : PagedAndSortedResultRequestDto
{
public string Search { get; set; }
public string Status { get; set; }
}

View file

@ -9,7 +9,7 @@ namespace Kurs.Platform.Classrooms;
public interface IClassroomAppService : IApplicationService public interface IClassroomAppService : IApplicationService
{ {
Task<ClassroomDto> GetAsync(Guid id); Task<ClassroomDto> GetAsync(Guid id);
Task<PagedResultDto<ClassroomDto>> GetListAsync(PagedAndSortedResultRequestDto input); Task<PagedResultDto<ClassroomDto>> GetListAsync(ClassroomFilterInputDto input);
Task<ClassroomDto> CreateAsync(ClassroomDto input); Task<ClassroomDto> CreateAsync(ClassroomDto input);
Task<ClassroomDto> UpdateAsync(Guid id, ClassroomDto input); Task<ClassroomDto> UpdateAsync(Guid id, ClassroomDto input);
Task DeleteAsync(Guid id); Task DeleteAsync(Guid id);

View file

@ -34,9 +34,39 @@ public class ClassroomAppService : PlatformAppService, IClassroomAppService
return ObjectMapper.Map<Classroom, ClassroomDto>(classSession); return ObjectMapper.Map<Classroom, ClassroomDto>(classSession);
} }
public async Task<PagedResultDto<ClassroomDto>> GetListAsync(PagedAndSortedResultRequestDto input) public async Task<PagedResultDto<ClassroomDto>> GetListAsync(ClassroomFilterInputDto input)
{ {
var query = await _classSessionRepository.GetQueryableAsync(); var query = await _classSessionRepository.GetQueryableAsync();
if (!string.IsNullOrWhiteSpace(input.Search))
{
query = query.Where(x =>
x.Name.Contains(input.Search) ||
x.Description.Contains(input.Search) ||
x.Subject.Contains(input.Search) ||
x.TeacherName.Contains(input.Search)
);
}
if (!string.IsNullOrWhiteSpace(input.Status))
{
switch (input.Status)
{
case "Active":
query = query.Where(x => x.ActualStartTime == null && x.ActualEndTime == null);
break;
case "Open":
query = query.Where(x => x.ActualStartTime != null && x.ActualEndTime == null);
break;
case "Passive":
query = query.Where(x => x.ActualEndTime != null);
break;
}
}
var totalCount = query.Count(); var totalCount = query.Count();
var items = query var items = query
.OrderBy(x => x.ScheduledStartTime) .OrderBy(x => x.ScheduledStartTime)

View file

@ -1,3 +1,5 @@
import { PagedAndSortedResultRequestDto } from '../abp'
export type RoleState = 'role-selection' | 'dashboard' | 'classroom' export type RoleState = 'role-selection' | 'dashboard' | 'classroom'
export type Role = 'teacher' | 'student' | 'observer' export type Role = 'teacher' | 'student' | 'observer'
@ -131,3 +133,8 @@ export interface ScreenShareRequestDto {
userName: string userName: string
isActive: boolean isActive: boolean
} }
export interface ClassroomFilterInputDto extends PagedAndSortedResultRequestDto {
search: string
status: string
}

View file

@ -1,4 +1,4 @@
import { ClassroomDto } from '@/proxy/classroom/models' import { ClassroomDto, ClassroomFilterInputDto } from '@/proxy/classroom/models'
import apiService from './api.service' import apiService from './api.service'
import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy' import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy'
@ -8,7 +8,7 @@ export const getClassroomById = (id: string) =>
url: `/api/app/classroom/${id}`, url: `/api/app/classroom/${id}`,
}) })
export const getClassrooms = (input: PagedAndSortedResultRequestDto) => export const getClassrooms = (input: ClassroomFilterInputDto) =>
apiService.fetchData<PagedResultDto<ClassroomDto>>({ apiService.fetchData<PagedResultDto<ClassroomDto>>({
method: 'GET', method: 'GET',
url: `/api/app/classroom`, url: `/api/app/classroom`,

View file

@ -13,6 +13,8 @@ import {
FaEye, FaEye,
FaHourglassEnd, FaHourglassEnd,
FaDoorOpen, FaDoorOpen,
FaSearch,
FaFilter,
} from 'react-icons/fa' } from 'react-icons/fa'
import { ClassroomDto } from '@/proxy/classroom/models' import { ClassroomDto } from '@/proxy/classroom/models'
@ -70,12 +72,24 @@ const ClassList: React.FC = () => {
const [showEditModal, setShowEditModal] = useState(false) const [showEditModal, setShowEditModal] = useState(false)
const [showDeleteModal, setShowDeleteModal] = useState(false) const [showDeleteModal, setShowDeleteModal] = useState(false)
const getClassroomList = async (skipCount = 0, maxResultCount = 1000, sorting = '') => { // Filter/search state
const [searchTerm, setSearchTerm] = useState('')
const [statusFilter, setStatusFilter] = useState('')
const getClassroomList = async (
skipCount = 0,
maxResultCount = 1000,
sorting = '',
search = '',
status = '',
) => {
try { try {
const result = await getClassrooms({ const result = await getClassrooms({
sorting, sorting,
skipCount, skipCount,
maxResultCount, maxResultCount,
search,
status,
}) })
const items = (result.data.items || []).map((item) => ({ const items = (result.data.items || []).map((item) => ({
@ -93,8 +107,8 @@ const ClassList: React.FC = () => {
} }
useEffect(() => { useEffect(() => {
getClassroomList() getClassroomList(0, 1000, '', searchTerm, statusFilter)
}, []) }, [searchTerm, statusFilter])
const handleCreateClass = async (e: React.FormEvent) => { const handleCreateClass = async (e: React.FormEvent) => {
e.preventDefault() e.preventDefault()
@ -166,13 +180,13 @@ const ClassList: React.FC = () => {
} }
} }
const canJoinClass = (actualStartTime: string) => { // const canJoinClass = (actualStartTime: string) => {
const actualed = new Date(actualStartTime) // const actualed = new Date(actualStartTime)
const now = new Date() // const now = new Date()
const tenMinutesBefore = new Date(actualed.getTime() - 10 * 60 * 1000) //10 dakika öncesine kadar // const tenMinutesBefore = new Date(actualed.getTime() - 10 * 60 * 1000) //10 dakika öncesine kadar
const twoHoursAfter = new Date(actualed.getTime() + 2 * 60 * 60 * 1000) // 2 saat sonrasına kadar // const twoHoursAfter = new Date(actualed.getTime() + 2 * 60 * 60 * 1000) // 2 saat sonrasına kadar
return now >= tenMinutesBefore && now <= twoHoursAfter // return now >= tenMinutesBefore && now <= twoHoursAfter
} // }
const widgets = () => { const widgets = () => {
return { return {
@ -342,6 +356,36 @@ const ClassList: React.FC = () => {
</motion.div> </motion.div>
</div> </div>
{/* Filter Bar */}
<div className="bg-white rounded-lg border border-slate-200 p-6 mb-6 shadow-sm">
<div className="flex flex-col lg:flex-row gap-4">
<div className="flex-1 relative">
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-slate-400" />
<input
type="text"
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Search class"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div className="flex items-center gap-4">
<FaFilter className="w-5 h-5 text-slate-500" />
<select
className="ml-2 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
style={{ minWidth: 120 }}
>
<option value="">All Status</option>
<option value="Active">Aktif</option>
<option value="Open">Katılıma ık</option>
<option value="Passive">Pasif</option>
</select>
</div>
</div>
</div>
{/* Scheduled Classes */} {/* Scheduled Classes */}
<div className="bg-white rounded-lg shadow-md"> <div className="bg-white rounded-lg shadow-md">
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 p-4 sm:px-6 border-b border-gray-200"> <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 p-4 sm:px-6 border-b border-gray-200">