add certificate disbale function
All checks were successful
Build & Push Docker Image / docker (push) Successful in 1m49s
All checks were successful
Build & Push Docker Image / docker (push) Successful in 1m49s
This commit is contained in:
parent
cbf39f2feb
commit
368a49fb0c
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Certificate\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CertificationSettingsController extends Controller
|
||||
{
|
||||
public function update(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'show_course_certificate' => ['sometimes', 'boolean'],
|
||||
'show_course_marksheet' => ['sometimes', 'boolean'],
|
||||
]);
|
||||
|
||||
$system = Setting::where('type', 'system')->firstOrFail();
|
||||
$fields = $system->fields ?? [];
|
||||
|
||||
foreach ($validated as $key => $value) {
|
||||
$fields[$key] = (bool) $value;
|
||||
}
|
||||
|
||||
$system->update(['fields' => $fields]);
|
||||
|
||||
return back()->with('success', 'Certification settings updated successfully.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,15 @@
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Certificate\Http\Controllers\CertificateController;
|
||||
use Modules\Certificate\Http\Controllers\CertificationSettingsController;
|
||||
use Modules\Certificate\Http\Controllers\CertificateTemplateController;
|
||||
use Modules\Certificate\Http\Controllers\MarksheetTemplateController;
|
||||
|
||||
Route::middleware(['auth', 'verified'])->group(function () {
|
||||
// Admin routes for managing certificate templates
|
||||
Route::middleware(['role:admin'])->prefix('dashboard/certification')->group(function () {
|
||||
Route::post('settings', [CertificationSettingsController::class, 'update'])->name('certification.settings.update');
|
||||
|
||||
Route::resource('certificate', CertificateTemplateController::class)->except(['show', 'create', 'update'])->names('certificate.templates');
|
||||
Route::get('certificate/create-certificate', [CertificateTemplateController::class, 'create'])->name('certificate.templates.create');
|
||||
Route::post('certificate/{id}', [CertificateTemplateController::class, 'update'])->name('certificate.templates.update');
|
||||
|
||||
@ -60,6 +60,15 @@ class StudentController extends Controller
|
||||
|
||||
public function show_course(int $id, string $tab)
|
||||
{
|
||||
$system = app('system_settings');
|
||||
$fields = $system?->fields ?? [];
|
||||
$showCourseCertificate = $fields['show_course_certificate'] ?? true;
|
||||
$showCourseMarksheet = $fields['show_course_marksheet'] ?? true;
|
||||
|
||||
if ($tab === 'certificate' && !$showCourseCertificate && !$showCourseMarksheet) {
|
||||
return redirect()->route('student.course.show', ['id' => $id, 'tab' => 'modules']);
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$course = $this->studentService->getEnrolledCourse($id, $user);
|
||||
$props = $this->studentService->getEnrolledCourseOverview($id, $tab, $user);
|
||||
|
||||
@ -185,15 +185,20 @@ class StudentService extends MediaService
|
||||
|
||||
function getEnrolledCourseOverview(string $course_id, string $tab, User $user): array
|
||||
{
|
||||
$system = app('system_settings');
|
||||
$fields = $system?->fields ?? [];
|
||||
$showCourseCertificate = $fields['show_course_certificate'] ?? true;
|
||||
$showCourseMarksheet = $fields['show_course_marksheet'] ?? true;
|
||||
|
||||
return [
|
||||
'modules' => $tab === 'modules' ? $this->getCourseModules($course_id) : null,
|
||||
'live_classes' => $tab === 'live_classes' ? $this->getCourseLiveClasses($course_id) : null,
|
||||
'assignments' => $tab === 'assignments' ? $this->getCourseAssignments($course_id, $user) : null,
|
||||
'quizzes' => $tab === 'quizzes' ? $this->getCourseSectionQuizzes($course_id, $user) : null,
|
||||
'resources' => $tab === 'resources' ? $this->getCourseLessonResources($course_id) : null,
|
||||
'certificateTemplate' => $tab === 'certificate' ? $this->certificate->getActiveCertificateTemplate('course') : null,
|
||||
'marksheetTemplate' => $tab === 'certificate' ? $this->certificate->getActiveMarksheetTemplate('course') : null,
|
||||
'studentMarks' => $tab === 'certificate' ? $this->calculateStudentMarks($course_id, $user->id) : null,
|
||||
'certificateTemplate' => $tab === 'certificate' && $showCourseCertificate ? $this->certificate->getActiveCertificateTemplate('course') : null,
|
||||
'marksheetTemplate' => $tab === 'certificate' && $showCourseMarksheet ? $this->certificate->getActiveMarksheetTemplate('course') : null,
|
||||
'studentMarks' => $tab === 'certificate' && $showCourseMarksheet ? $this->calculateStudentMarks($course_id, $user->id) : null,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -27,8 +27,9 @@ interface ContentListProps {
|
||||
|
||||
const ContentList = ({ completedContents, courseCompletion }: ContentListProps) => {
|
||||
const { props } = usePage<CoursePlayerProps>();
|
||||
const { course, zoomConfig, section, watchHistory, translate, direction } = props;
|
||||
const { course, zoomConfig, section, watchHistory, translate, direction, system } = props;
|
||||
const { button, common, frontend } = translate;
|
||||
const showCourseCertificate = system?.fields?.show_course_certificate ?? true;
|
||||
|
||||
// Get live classes from course data
|
||||
const liveClasses = course.live_classes || [];
|
||||
@ -115,7 +116,7 @@ const ContentList = ({ completedContents, courseCompletion }: ContentListProps)
|
||||
<Link
|
||||
href={route('student.course.show', {
|
||||
id: course.id,
|
||||
tab: 'certificate',
|
||||
tab: showCourseCertificate ? 'certificate' : 'modules',
|
||||
})}
|
||||
>
|
||||
<Button className="w-full" variant="secondary" size="lg" disabled={courseCompletion.percentage !== '100.00'}>
|
||||
|
||||
@ -1,24 +1,61 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import DashboardLayout from '@/layouts/dashboard/layout';
|
||||
import { SharedData } from '@/types/global';
|
||||
import { Head, Link } from '@inertiajs/react';
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { Award, Plus } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import CertificateCard from './partials/certificate-card';
|
||||
|
||||
interface CertificatePageProps extends SharedData {
|
||||
templates: CertificateTemplate[];
|
||||
}
|
||||
|
||||
const CertificateIndex = ({ templates }: CertificatePageProps) => {
|
||||
const CertificateIndex = ({ templates, system }: CertificatePageProps) => {
|
||||
const examTemplates = templates.filter((template) => template.type === 'exam');
|
||||
const courseTemplates = templates.filter((template) => template.type === 'course');
|
||||
|
||||
const initialCourseCertificateEnabled = system?.fields?.show_course_certificate ?? true;
|
||||
const [courseCertificateEnabled, setCourseCertificateEnabled] = useState<boolean>(initialCourseCertificateEnabled);
|
||||
|
||||
useEffect(() => {
|
||||
setCourseCertificateEnabled(initialCourseCertificateEnabled);
|
||||
}, [initialCourseCertificateEnabled]);
|
||||
|
||||
const handleCourseCertificateToggle = (checked: boolean) => {
|
||||
const previous = courseCertificateEnabled;
|
||||
setCourseCertificateEnabled(checked);
|
||||
router.post(
|
||||
route('certification.settings.update'),
|
||||
{ show_course_certificate: checked },
|
||||
{ preserveScroll: true, preserveState: true, onError: () => setCourseCertificateEnabled(previous) },
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title="Certificate Templates" />
|
||||
|
||||
<div className="space-y-6">
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<Label htmlFor="show_course_certificate" className="text-base font-semibold">
|
||||
Course Certificate (Student)
|
||||
</Label>
|
||||
<p className="text-muted-foreground text-sm">Enable/disable course certificates for students.</p>
|
||||
</div>
|
||||
|
||||
<Switch
|
||||
id="show_course_certificate"
|
||||
checked={courseCertificateEnabled}
|
||||
onCheckedChange={handleCourseCertificateToggle}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold">Certificate Templates</h2>
|
||||
|
||||
@ -1,24 +1,57 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import DashboardLayout from '@/layouts/dashboard/layout';
|
||||
import { SharedData } from '@/types/global';
|
||||
import { Head, Link } from '@inertiajs/react';
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { ClipboardList, Plus } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import MarkSheetCard from './partials/marksheet-card';
|
||||
|
||||
interface MarksheetPageProps extends SharedData {
|
||||
templates: MarksheetTemplate[];
|
||||
}
|
||||
|
||||
const MarksheetIndex = ({ templates }: MarksheetPageProps) => {
|
||||
const MarksheetIndex = ({ templates, system }: MarksheetPageProps) => {
|
||||
const examTemplates = templates.filter((template) => template.type === 'exam');
|
||||
const courseTemplates = templates.filter((template) => template.type === 'course');
|
||||
|
||||
const initialCourseMarksheetEnabled = system?.fields?.show_course_marksheet ?? true;
|
||||
const [courseMarksheetEnabled, setCourseMarksheetEnabled] = useState<boolean>(initialCourseMarksheetEnabled);
|
||||
|
||||
useEffect(() => {
|
||||
setCourseMarksheetEnabled(initialCourseMarksheetEnabled);
|
||||
}, [initialCourseMarksheetEnabled]);
|
||||
|
||||
const handleCourseMarksheetToggle = (checked: boolean) => {
|
||||
const previous = courseMarksheetEnabled;
|
||||
setCourseMarksheetEnabled(checked);
|
||||
router.post(
|
||||
route('certification.settings.update'),
|
||||
{ show_course_marksheet: checked },
|
||||
{ preserveScroll: true, preserveState: true, onError: () => setCourseMarksheetEnabled(previous) },
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title="Certificate & Marksheet Templates" />
|
||||
|
||||
<div className="space-y-6">
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<Label htmlFor="show_course_marksheet" className="text-base font-semibold">
|
||||
Course Marksheet (Student)
|
||||
</Label>
|
||||
<p className="text-muted-foreground text-sm">Enable/disable course marksheets for students.</p>
|
||||
</div>
|
||||
|
||||
<Switch id="show_course_marksheet" checked={courseMarksheetEnabled} onCheckedChange={handleCourseMarksheetToggle} />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold">Marksheet Templates</h2>
|
||||
|
||||
@ -15,7 +15,11 @@ import CourseQuizzes from './tabs-content/course-quizzes';
|
||||
import CourseResources from './tabs-content/course-resources';
|
||||
|
||||
const Course = (props: StudentCourseProps) => {
|
||||
const { tab, course, watchHistory, completion } = props;
|
||||
const { tab, course, watchHistory, completion, system } = props;
|
||||
|
||||
const showCourseCertificate = system?.fields?.show_course_certificate ?? true;
|
||||
const showCourseMarksheet = system?.fields?.show_course_marksheet ?? true;
|
||||
const showCertificateTab = showCourseCertificate || showCourseMarksheet;
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
@ -38,10 +42,14 @@ const Course = (props: StudentCourseProps) => {
|
||||
value: 'resources',
|
||||
label: 'Ressourcen',
|
||||
},
|
||||
...(showCertificateTab
|
||||
? [
|
||||
{
|
||||
value: 'certificate',
|
||||
label: 'Zertifikat',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
const renderContent = () => {
|
||||
|
||||
@ -10,18 +10,34 @@ import { Award, ClipboardList, Lock } from 'lucide-react';
|
||||
|
||||
const CourseCertificate = () => {
|
||||
const { props } = usePage<StudentCourseProps>();
|
||||
const { course, watchHistory, completion, certificateTemplate, marksheetTemplate, studentMarks, auth } = props;
|
||||
const { course, watchHistory, completion, certificateTemplate, marksheetTemplate, studentMarks, auth, system } = props;
|
||||
|
||||
const showCourseCertificate = system?.fields?.show_course_certificate ?? true;
|
||||
const showCourseMarksheet = system?.fields?.show_course_marksheet ?? true;
|
||||
|
||||
if (!showCourseCertificate && !showCourseMarksheet) {
|
||||
return (
|
||||
<Alert>
|
||||
<Lock className="h-4 w-4" />
|
||||
<AlertTitle>Zertifikatbereich deaktiviert</AlertTitle>
|
||||
<AlertDescription>Für diesen Kurs sind Zertifikat und Notenblatt aktuell deaktiviert.</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
// Check if course is completed
|
||||
const isCompleted = watchHistory?.completion_date || completion?.completion === 100;
|
||||
|
||||
if (!isCompleted) {
|
||||
const title = showCourseCertificate && showCourseMarksheet ? 'Zertifikat & Notenblatt gesperrt' : showCourseCertificate ? 'Zertifikat gesperrt' : 'Notenblatt gesperrt';
|
||||
const unlockText = showCourseCertificate && showCourseMarksheet ? 'Schließe alle Kursmodule ab, um dein Zertifikat und Notenblatt freizuschalten.' : showCourseCertificate ? 'Schließe alle Kursmodule ab, um dein Zertifikat freizuschalten.' : 'Schließe alle Kursmodule ab, um dein Notenblatt freizuschalten.';
|
||||
|
||||
return (
|
||||
<Alert>
|
||||
<Lock className="h-4 w-4" />
|
||||
<AlertTitle>Zertifikat & Notenblatt gesperrt</AlertTitle>
|
||||
<AlertTitle>{title}</AlertTitle>
|
||||
<AlertDescription>
|
||||
Schließe alle Kursmodule ab, um dein Zertifikat und Notenblatt freizuschalten. Dein aktueller Fortschritt: {completion?.completion || 0}%
|
||||
{unlockText} Dein aktueller Fortschritt: {completion?.completion || 0}%
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
@ -32,14 +48,20 @@ const CourseCertificate = () => {
|
||||
: format(new Date(), 'MMMM d, yyyy');
|
||||
|
||||
// Check if both are unavailable
|
||||
if (!certificateTemplate && !marksheetTemplate) {
|
||||
const certificateAvailable = showCourseCertificate && !!certificateTemplate;
|
||||
const marksheetAvailable = showCourseMarksheet && !!marksheetTemplate;
|
||||
|
||||
if (!certificateAvailable && !marksheetAvailable) {
|
||||
const title = showCourseCertificate && showCourseMarksheet ? 'Kein Zertifikat oder Notenblatt verfügbar' : showCourseCertificate ? 'Kein Zertifikat verfügbar' : 'Kein Notenblatt verfügbar';
|
||||
const description = showCourseCertificate && showCourseMarksheet ? 'Der Dozent hat für diesen Kurs noch keine Zertifikate oder Notenblätter eingerichtet.' : showCourseCertificate ? 'Der Dozent hat für diesen Kurs noch keine Zertifikate eingerichtet.' : 'Der Dozent hat für diesen Kurs noch keine Notenblätter eingerichtet.';
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<Card>
|
||||
<CardContent className="flex flex-col items-center justify-center p-12 text-center">
|
||||
<Award className="text-muted-foreground mb-4 h-16 w-16" />
|
||||
<h3 className="mb-2 text-xl font-semibold">Kein Zertifikat oder Notenblatt verfügbar</h3>
|
||||
<p className="text-muted-foreground">Der Dozent hat für diesen Kurs noch keine Zertifikate oder Notenblätter eingerichtet.</p>
|
||||
<h3 className="mb-2 text-xl font-semibold">{title}</h3>
|
||||
<p className="text-muted-foreground">{description}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
@ -48,18 +70,23 @@ const CourseCertificate = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs defaultValue="certificate" className="w-full">
|
||||
<TabsList className="mb-6 grid h-11 w-full grid-cols-2">
|
||||
<Tabs defaultValue={showCourseCertificate ? 'certificate' : 'marksheet'} className="w-full">
|
||||
<TabsList className={`mb-6 grid h-11 w-full ${showCourseCertificate && showCourseMarksheet ? 'grid-cols-2' : 'grid-cols-1'}`}>
|
||||
{showCourseCertificate && (
|
||||
<TabsTrigger value="certificate" className="flex h-9 cursor-pointer items-center gap-2">
|
||||
<Award className="h-4 w-4" />
|
||||
Zertifikat
|
||||
</TabsTrigger>
|
||||
)}
|
||||
{showCourseMarksheet && (
|
||||
<TabsTrigger value="marksheet" className="flex h-9 cursor-pointer items-center gap-2">
|
||||
<ClipboardList className="h-4 w-4" />
|
||||
Notenblatt
|
||||
</TabsTrigger>
|
||||
)}
|
||||
</TabsList>
|
||||
|
||||
{showCourseCertificate && (
|
||||
<TabsContent value="certificate">
|
||||
{!certificateTemplate ? (
|
||||
<Card>
|
||||
@ -78,7 +105,9 @@ const CourseCertificate = () => {
|
||||
/>
|
||||
)}
|
||||
</TabsContent>
|
||||
)}
|
||||
|
||||
{showCourseMarksheet && (
|
||||
<TabsContent value="marksheet">
|
||||
{!marksheetTemplate || !studentMarks ? (
|
||||
<Card>
|
||||
@ -102,6 +131,7 @@ const CourseCertificate = () => {
|
||||
/>
|
||||
)}
|
||||
</TabsContent>
|
||||
)}
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
|
||||
2
resources/js/types/settings.d.ts
vendored
2
resources/js/types/settings.d.ts
vendored
@ -39,6 +39,8 @@ interface SystemFields {
|
||||
show_course_cart?: boolean;
|
||||
show_student_exams?: boolean;
|
||||
show_student_wishlist?: boolean;
|
||||
show_course_certificate?: boolean;
|
||||
show_course_marksheet?: boolean;
|
||||
}
|
||||
|
||||
interface GoogleAuthFields {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user