Merge pull request #16 from Since-3/feature/course-request-limit

added limit for requesting the courses
This commit is contained in:
Rajpreet0 2025-12-13 17:29:30 +01:00 • committed by GitHub
commit 8a41262309
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 172 additions and 21 deletions

View file

@ -12,6 +12,7 @@ import { prisma } from "@/src/lib/prisma";
import { getUserData, isManager } from "@/src/lib/auth/getUser";
import { courseSchema, type CourseFormData } from "../schemas/course-schema";
import { revalidatePath } from "next/cache";
import { Prisma } from "@prisma/client";
/**
* Erstellt einen neuen Kurs in der Datenbank
@ -132,20 +133,23 @@ export async function getAllCourses() {
}
/**
* Ruft alle Kurse des angemeldeten Managers ab
* Fetches all courses created by the logged-in manager
*
* Diese Funktion gibt alle Kurse zurück, die vom aktuell angemeldeten Manager
* erstellt wurden, unabhängig vom Status (Entwurf oder veröffentlicht).
* Die Kurse werden nach Erstellungsdatum absteigend sortiert (neueste zuerst).
* This function returns all courses created by the currently logged-in manager,
* regardless of status (draft or published). Supports optional date range filtering
* to implement efficient loading for large datasets.
*
* @returns Ein Objekt mit success-Flag und einem Array von Kursen des Managers
* @param options - Optional filtering parameters
* @param options.dateFrom - Start date for filtering (inclusive)
* @param options.dateTo - End date for filtering (inclusive)
* @returns An object with success flag and an array of the manager's courses
*/
export async function getMyCourses() {
export async function getMyCourses(options?: { dateFrom?: Date; dateTo?: Date }) {
try {
// Aktuellen Benutzer abrufen
// Step 1: Get current user
const userData = await getUserData();
// Überprüfen, ob Benutzer angemeldet und ein Manager ist
// Step 2: Check if user is logged in and is a manager
if (!userData || !isManager(userData)) {
return {
success: false,
@ -154,13 +158,31 @@ export async function getMyCourses() {
};
}
// Alle Kurse abrufen, die von diesem Manager erstellt wurden
// Step 3: Build the where clause with optional date filtering
const whereClause: Prisma.CourseWhereInput = {
createdBy: userData.id, // Filter by manager ID
};
// Add date range filter if provided
if (options?.dateFrom || options?.dateTo) {
whereClause.date = {};
if (options.dateFrom) {
// Filter courses from this date onwards (inclusive)
whereClause.date.gte = options.dateFrom;
}
if (options.dateTo) {
// Filter courses up to this date (inclusive)
whereClause.date.lte = options.dateTo;
}
}
// Step 4: Fetch courses created by this manager with optional date filtering
const courses = await prisma.course.findMany({
where: {
createdBy: userData.id, // Filtert nach Manager-ID
},
where: whereClause,
orderBy: {
createdAt: "desc", // Neueste Kurse zuerst
date: "asc", // Sort by course date ascending (oldest first)
},
});

View file

@ -5,7 +5,7 @@ import { Button } from "@/src/components/ui/button";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
import CourseListItem from "../components/CourseListItem";
import { Filter } from "lucide-react";
import { Filter, ChevronUp, ChevronDown } from "lucide-react";
import DeleteDialog from "@/src/components/layout/DeleteDialog";
type Course = {
@ -42,24 +42,119 @@ const CourseListView = () => {
const [courseToDelete, setCourseToDelete] = useState<{ id: string; name: string } | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
const loadCourses = async () => {
const result = await getMyCourses();
// Date range state for infinite scroll
const [loadedDateFrom, setLoadedDateFrom] = useState<Date | null>(null);
const [loadedDateTo, setLoadedDateTo] = useState<Date | null>(null);
const [isLoadingOlder, setIsLoadingOlder] = useState(false);
const [isLoadingNewer, setIsLoadingNewer] = useState(false);
const [hasOlderCourses, setHasOlderCourses] = useState(true);
const [hasNewerCourses, setHasNewerCourses] = useState(true);
/**
* Load courses with optional date range
* @param options - Optional date range parameters
* @param mode - Loading mode: 'initial', 'older', or 'newer'
*/
const loadCourses = async (
options?: { dateFrom?: Date; dateTo?: Date },
mode: "initial" | "older" | "newer" = "initial"
) => {
const result = await getMyCourses(options);
if (result.success) {
setCourses(result.courses);
if (mode === "initial") {
// Replace all courses on initial load
setCourses(result.courses);
} else if (mode === "older") {
// Prepend older courses to the beginning
setCourses((prev) => [...result.courses, ...prev]);
} else if (mode === "newer") {
// Append newer courses to the end
setCourses((prev) => [...prev, ...result.courses]);
}
// Check if there are more courses to load
if (mode === "older" && result.courses.length === 0) {
setHasOlderCourses(false);
}
if (mode === "newer" && result.courses.length === 0) {
setHasNewerCourses(false);
}
} else {
toast.error(result.error || "Fehler beim Laden der Kurse");
}
};
/**
* Initial load: Load courses from -2 weeks to +6 weeks from today
*/
useEffect(() => {
const fetchCourses = async () => {
setIsLoading(true);
await loadCourses();
// Calculate default date range: -2 weeks to +6 weeks from today
const today = new Date();
today.setHours(0, 0, 0, 0);
const twoWeeksAgo = new Date(today);
twoWeeksAgo.setDate(today.getDate() - 14); // -2 weeks
const sixWeeksLater = new Date(today);
sixWeeksLater.setDate(today.getDate() + 42); // +6 weeks
// Store the loaded date range
setLoadedDateFrom(twoWeeksAgo);
setLoadedDateTo(sixWeeksLater);
await loadCourses({ dateFrom: twoWeeksAgo, dateTo: sixWeeksLater }, "initial");
setIsLoading(false);
};
fetchCourses();
}, []);
/**
* Load older courses (extend the date range backwards by 4 weeks)
*/
const loadOlderCourses = async () => {
if (!loadedDateFrom || isLoadingOlder) return;
setIsLoadingOlder(true);
// Calculate new date range: 4 weeks before the current loadedDateFrom
const fourWeeksEarlier = new Date(loadedDateFrom);
fourWeeksEarlier.setDate(loadedDateFrom.getDate() - 28); // -4 weeks
const oneDayBeforeLoadedFrom = new Date(loadedDateFrom);
oneDayBeforeLoadedFrom.setDate(loadedDateFrom.getDate() - 1); // -1 day to avoid duplicates
await loadCourses({ dateFrom: fourWeeksEarlier, dateTo: oneDayBeforeLoadedFrom }, "older");
// Update the loaded date range
setLoadedDateFrom(fourWeeksEarlier);
setIsLoadingOlder(false);
};
/**
* Load newer courses (extend the date range forwards by 4 weeks)
*/
const loadNewerCourses = async () => {
if (!loadedDateTo || isLoadingNewer) return;
setIsLoadingNewer(true);
// Calculate new date range: 4 weeks after the current loadedDateTo
const oneDayAfterLoadedTo = new Date(loadedDateTo);
oneDayAfterLoadedTo.setDate(loadedDateTo.getDate() + 1); // +1 day to avoid duplicates
const fourWeeksLater = new Date(loadedDateTo);
fourWeeksLater.setDate(loadedDateTo.getDate() + 28); // +4 weeks
await loadCourses({ dateFrom: oneDayAfterLoadedTo, dateTo: fourWeeksLater }, "newer");
// Update the loaded date range
setLoadedDateTo(fourWeeksLater);
setIsLoadingNewer(false);
};
const requestDelete = (courseId: string, courseName: string) => {
setCourseToDelete({ id: courseId, name: courseName });
setDeleteDialogOpen(true);
@ -78,7 +173,10 @@ const CourseListView = () => {
if (result.success) {
toast.success(result.message);
loadCourses();
// Reload courses with the current date range
if (loadedDateFrom && loadedDateTo) {
await loadCourses({ dateFrom: loadedDateFrom, dateTo: loadedDateTo }, "initial");
}
} else {
toast.error(result.error || "Fehler beim Löschen");
}
@ -106,7 +204,7 @@ const CourseListView = () => {
return course < today;
};
// Filter courses
// Filter courses (client-side filtering for status, sport, etc.)
const filteredCourses = courses.filter((course) => {
if (filterStatus !== "all" && course.status !== filterStatus) return false;
if (filterSport !== "all" && course.sport !== filterSport) return false;
@ -114,7 +212,7 @@ const CourseListView = () => {
// Hide past courses unless showPastCourses is enabled
if (!showPastCourses && isPastCourse(course.date)) return false;
// Date range filter
// Date range filter (additional client-side filter on top of server-side)
if (dateFrom) {
const fromDate = new Date(dateFrom);
fromDate.setHours(0, 0, 0, 0);
@ -295,6 +393,21 @@ const CourseListView = () => {
</div>
)}
{/* Load Older Courses Button */}
{hasOlderCourses && (
<div className="mb-4 flex justify-center">
<Button
variant="outline"
onClick={loadOlderCourses}
disabled={isLoadingOlder}
className="flex items-center gap-2"
>
<ChevronUp size={18} />
{isLoadingOlder ? "Lädt..." : "Ältere Kurse laden"}
</Button>
</div>
)}
{filteredCourses.length === 0 ? (
<div className="text-center py-12 bg-gray-50 rounded-lg">
<p className="text-xl text-gray-600">
@ -347,6 +460,22 @@ const CourseListView = () => {
})}
</div>
)}
{/* Load Newer Courses Button */}
{hasNewerCourses && filteredCourses.length > 0 && (
<div className="mt-4 flex justify-center">
<Button
variant="outline"
onClick={loadNewerCourses}
disabled={isLoadingNewer}
className="flex items-center gap-2"
>
<ChevronDown size={18} />
{isLoadingNewer ? "Lädt..." : "Neuere Kurse laden"}
</Button>
</div>
)}
<DeleteDialog
open={deleteDialogOpen}
onOpenChange={setDeleteDialogOpen}