Classroom Search
This commit is contained in:
parent
0b60f006d0
commit
51f2c976bf
6 changed files with 102 additions and 14 deletions
|
|
@ -0,0 +1,7 @@
|
|||
using Volo.Abp.Application.Dtos;
|
||||
|
||||
public class ClassroomFilterInputDto : PagedAndSortedResultRequestDto
|
||||
{
|
||||
public string Search { get; set; }
|
||||
public string Status { get; set; }
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ namespace Kurs.Platform.Classrooms;
|
|||
public interface IClassroomAppService : IApplicationService
|
||||
{
|
||||
Task<ClassroomDto> GetAsync(Guid id);
|
||||
Task<PagedResultDto<ClassroomDto>> GetListAsync(PagedAndSortedResultRequestDto input);
|
||||
Task<PagedResultDto<ClassroomDto>> GetListAsync(ClassroomFilterInputDto input);
|
||||
Task<ClassroomDto> CreateAsync(ClassroomDto input);
|
||||
Task<ClassroomDto> UpdateAsync(Guid id, ClassroomDto input);
|
||||
Task DeleteAsync(Guid id);
|
||||
|
|
|
|||
|
|
@ -34,9 +34,39 @@ public class ClassroomAppService : PlatformAppService, IClassroomAppService
|
|||
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();
|
||||
|
||||
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 items = query
|
||||
.OrderBy(x => x.ScheduledStartTime)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { PagedAndSortedResultRequestDto } from '../abp'
|
||||
|
||||
export type RoleState = 'role-selection' | 'dashboard' | 'classroom'
|
||||
|
||||
export type Role = 'teacher' | 'student' | 'observer'
|
||||
|
|
@ -131,3 +133,8 @@ export interface ScreenShareRequestDto {
|
|||
userName: string
|
||||
isActive: boolean
|
||||
}
|
||||
|
||||
export interface ClassroomFilterInputDto extends PagedAndSortedResultRequestDto {
|
||||
search: string
|
||||
status: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ClassroomDto } from '@/proxy/classroom/models'
|
||||
import { ClassroomDto, ClassroomFilterInputDto } from '@/proxy/classroom/models'
|
||||
import apiService from './api.service'
|
||||
import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy'
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ export const getClassroomById = (id: string) =>
|
|||
url: `/api/app/classroom/${id}`,
|
||||
})
|
||||
|
||||
export const getClassrooms = (input: PagedAndSortedResultRequestDto) =>
|
||||
export const getClassrooms = (input: ClassroomFilterInputDto) =>
|
||||
apiService.fetchData<PagedResultDto<ClassroomDto>>({
|
||||
method: 'GET',
|
||||
url: `/api/app/classroom`,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import {
|
|||
FaEye,
|
||||
FaHourglassEnd,
|
||||
FaDoorOpen,
|
||||
FaSearch,
|
||||
FaFilter,
|
||||
} from 'react-icons/fa'
|
||||
|
||||
import { ClassroomDto } from '@/proxy/classroom/models'
|
||||
|
|
@ -70,12 +72,24 @@ const ClassList: React.FC = () => {
|
|||
const [showEditModal, setShowEditModal] = 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 {
|
||||
const result = await getClassrooms({
|
||||
sorting,
|
||||
skipCount,
|
||||
maxResultCount,
|
||||
search,
|
||||
status,
|
||||
})
|
||||
|
||||
const items = (result.data.items || []).map((item) => ({
|
||||
|
|
@ -93,8 +107,8 @@ const ClassList: React.FC = () => {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
getClassroomList()
|
||||
}, [])
|
||||
getClassroomList(0, 1000, '', searchTerm, statusFilter)
|
||||
}, [searchTerm, statusFilter])
|
||||
|
||||
const handleCreateClass = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
|
@ -166,13 +180,13 @@ const ClassList: React.FC = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const canJoinClass = (actualStartTime: string) => {
|
||||
const actualed = new Date(actualStartTime)
|
||||
const now = new Date()
|
||||
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
|
||||
return now >= tenMinutesBefore && now <= twoHoursAfter
|
||||
}
|
||||
// const canJoinClass = (actualStartTime: string) => {
|
||||
// const actualed = new Date(actualStartTime)
|
||||
// const now = new Date()
|
||||
// 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
|
||||
// return now >= tenMinutesBefore && now <= twoHoursAfter
|
||||
// }
|
||||
|
||||
const widgets = () => {
|
||||
return {
|
||||
|
|
@ -342,6 +356,36 @@ const ClassList: React.FC = () => {
|
|||
</motion.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 Açık</option>
|
||||
<option value="Passive">Pasif</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scheduled Classes */}
|
||||
<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">
|
||||
|
|
|
|||
Loading…
Reference in a new issue