lang bugfix
All checks were successful
Build & Push Docker Image / docker (push) Successful in 2m9s

This commit is contained in:
Ahmed Darrazi 2025-12-19 00:11:10 +01:00
parent 6ba9651f34
commit 5b4470a323
6 changed files with 78 additions and 78 deletions

View File

@ -49,7 +49,7 @@ const Certificate = ({ course, watchHistory }: { course: Course; watchHistory: W
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = `${studentName}_${courseName}_Certificate.png`; a.download = `${studentName}_${courseName}_Zertifikat.png`;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
document.body.removeChild(a); document.body.removeChild(a);
@ -86,7 +86,7 @@ const Certificate = ({ course, watchHistory }: { course: Course; watchHistory: W
pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height); pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height);
// Save the PDF // Save the PDF
pdf.save(`${studentName}_${courseName}_Certificate.pdf`); pdf.save(`${studentName}_${courseName}_Zertifikat.pdf`);
toast.success(frontend.pdf_certificate_saved); toast.success(frontend.pdf_certificate_saved);
}; };
@ -253,21 +253,21 @@ const Certificate = ({ course, watchHistory }: { course: Course; watchHistory: W
<RadioGroupItem className="cursor-pointer" value="png" id="png" /> <RadioGroupItem className="cursor-pointer" value="png" id="png" />
<Label htmlFor="png" className="flex cursor-pointer items-center gap-2"> <Label htmlFor="png" className="flex cursor-pointer items-center gap-2">
<FileImage className="h-4 w-4" /> <FileImage className="h-4 w-4" />
PNG Image PNG-Bild
</Label> </Label>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<RadioGroupItem className="cursor-pointer" value="pdf" id="pdf" /> <RadioGroupItem className="cursor-pointer" value="pdf" id="pdf" />
<Label htmlFor="pdf" className="flex cursor-pointer items-center gap-2"> <Label htmlFor="pdf" className="flex cursor-pointer items-center gap-2">
<FileText className="h-4 w-4" /> <FileText className="h-4 w-4" />
PDF Document PDF-Dokument
</Label> </Label>
</div> </div>
</RadioGroup> </RadioGroup>
<Button variant="outline" className="w-full" onClick={handleDownloadCertificate}> <Button variant="outline" className="w-full" onClick={handleDownloadCertificate}>
<Download className="mr-2 h-4 w-4" /> <Download className="mr-2 h-4 w-4" />
Download as {downloadFormat.toUpperCase()} Herunterladen als {downloadFormat.toUpperCase()}
</Button> </Button>
</div> </div>
</Card> </Card>

View File

@ -68,13 +68,13 @@ const DynamicCertificate = ({ template, courseName, studentName, completionDate
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = `${studentName}_${courseName}_Certificate.png`; a.download = `${studentName}_${courseName}_Zertifikat.png`;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
document.body.removeChild(a); document.body.removeChild(a);
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
toast.success('Certificate saved as PNG!'); toast.success('Zertifikat als PNG gespeichert!');
}, 'image/png'); }, 'image/png');
}; };
@ -111,7 +111,7 @@ const DynamicCertificate = ({ template, courseName, studentName, completionDate
pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height); pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height);
pdf.save(`${studentName}_${courseName}_Certificate.pdf`); pdf.save(`${studentName}_${courseName}_Certificate.pdf`);
toast.success('Certificate saved as PDF!'); toast.success('Zertifikat als PDF gespeichert!');
}; };
const wrapText = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number, maxWidth: number, lineHeight: number) => { const wrapText = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number, maxWidth: number, lineHeight: number) => {
@ -228,7 +228,7 @@ const DynamicCertificate = ({ template, courseName, studentName, completionDate
// Completion date // Completion date
ctx.font = `18px ${template_data.fontFamily}`; ctx.font = `18px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.secondaryColor; ctx.fillStyle = template_data.secondaryColor;
ctx.fillText(`Completed on: ${completionDate}`, dimensions.width / 2, currentY); ctx.fillText(`Abgeschlossen am: ${completionDate}`, dimensions.width / 2, currentY);
currentY += 60; currentY += 60;
// Footer // Footer
@ -352,7 +352,7 @@ const DynamicCertificate = ({ template, courseName, studentName, completionDate
color: template_data.secondaryColor, color: template_data.secondaryColor,
}} }}
> >
Completed on: {completionDate} Abgeschlossen am: {completionDate}
</p> </p>
</div> </div>
</div> </div>
@ -382,21 +382,21 @@ const DynamicCertificate = ({ template, courseName, studentName, completionDate
<RadioGroupItem className="cursor-pointer" value="png" id="png" /> <RadioGroupItem className="cursor-pointer" value="png" id="png" />
<Label htmlFor="png" className="flex cursor-pointer items-center gap-2"> <Label htmlFor="png" className="flex cursor-pointer items-center gap-2">
<FileImage className="h-4 w-4" /> <FileImage className="h-4 w-4" />
PNG Image PNG-Bild
</Label> </Label>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<RadioGroupItem className="cursor-pointer" value="pdf" id="pdf" /> <RadioGroupItem className="cursor-pointer" value="pdf" id="pdf" />
<Label htmlFor="pdf" className="flex cursor-pointer items-center gap-2"> <Label htmlFor="pdf" className="flex cursor-pointer items-center gap-2">
<FileText className="h-4 w-4" /> <FileText className="h-4 w-4" />
PDF Document PDF-Dokument
</Label> </Label>
</div> </div>
</RadioGroup> </RadioGroup>
<Button variant="outline" className="w-full" onClick={handleDownloadCertificate}> <Button variant="outline" className="w-full" onClick={handleDownloadCertificate}>
<Download className="mr-2 h-4 w-4" /> <Download className="mr-2 h-4 w-4" />
Download as {downloadFormat.toUpperCase()} Herunterladen als {downloadFormat.toUpperCase()}
</Button> </Button>
</div> </div>
</Card> </Card>

View File

@ -69,13 +69,13 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = `${studentName}_${courseName}_Marksheet.png`; a.download = `${studentName}_${courseName}_Notenblatt.png`;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
document.body.removeChild(a); document.body.removeChild(a);
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
toast.success('Marksheet saved as PNG!'); toast.success('Notenblatt als PNG gespeichert!');
}, 'image/png'); }, 'image/png');
}; };
@ -110,9 +110,9 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
const imgData = canvas.toDataURL('image/png'); const imgData = canvas.toDataURL('image/png');
pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height); pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height);
pdf.save(`${studentName}_${courseName}_Marksheet.pdf`); pdf.save(`${studentName}_${courseName}_Notenblatt.pdf`);
toast.success('Marksheet saved as PDF!'); toast.success('Notenblatt als PDF gespeichert!');
}; };
const drawMarksheet = async ( const drawMarksheet = async (
@ -174,7 +174,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
// Column 1 - Student Name // Column 1 - Student Name
ctx.font = `16px ${template_data.fontFamily}`; ctx.font = `16px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.secondaryColor; ctx.fillStyle = template_data.secondaryColor;
ctx.fillText('Student Name', col1X, currentY); ctx.fillText('Name des Studierenden', col1X, currentY);
ctx.font = `bold 20px ${template_data.fontFamily}`; ctx.font = `bold 20px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.primaryColor; ctx.fillStyle = template_data.primaryColor;
@ -183,7 +183,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
// Column 2 - Course // Column 2 - Course
ctx.font = `16px ${template_data.fontFamily}`; ctx.font = `16px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.secondaryColor; ctx.fillStyle = template_data.secondaryColor;
ctx.fillText('Course', col2X, currentY); ctx.fillText('Kurs', col2X, currentY);
ctx.font = `bold 20px ${template_data.fontFamily}`; ctx.font = `bold 20px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.primaryColor; ctx.fillStyle = template_data.primaryColor;
@ -215,7 +215,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
// Column 1 - Completion Date // Column 1 - Completion Date
ctx.font = `16px ${template_data.fontFamily}`; ctx.font = `16px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.secondaryColor; ctx.fillStyle = template_data.secondaryColor;
ctx.fillText('Completion Date', col1X, currentY); ctx.fillText('Abschlussdatum', col1X, currentY);
ctx.font = `18px ${template_data.fontFamily}`; ctx.font = `18px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.primaryColor; ctx.fillStyle = template_data.primaryColor;
@ -224,7 +224,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
// Column 2 - Overall Grade // Column 2 - Overall Grade
ctx.font = `16px ${template_data.fontFamily}`; ctx.font = `16px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.secondaryColor; ctx.fillStyle = template_data.secondaryColor;
ctx.fillText('Overall Grade', col2X, currentY); ctx.fillText('Gesamtnote', col2X, currentY);
ctx.font = `bold 20px ${template_data.fontFamily}`; ctx.font = `bold 20px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.primaryColor; ctx.fillStyle = template_data.primaryColor;
@ -236,7 +236,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
ctx.textAlign = 'left'; ctx.textAlign = 'left';
ctx.font = `bold 22px ${template_data.fontFamily}`; ctx.font = `bold 22px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.primaryColor; ctx.fillStyle = template_data.primaryColor;
ctx.fillText('Exam Type', leftMargin, currentY); ctx.fillText('Prüfungsart', leftMargin, currentY);
currentY += 35; currentY += 35;
// Table Header Background // Table Header Background
@ -247,9 +247,9 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
// Table Headers // Table Headers
ctx.font = `bold 18px ${template_data.fontFamily}`; ctx.font = `bold 18px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.primaryColor; ctx.fillStyle = template_data.primaryColor;
ctx.fillText('Exam Type', leftMargin + 15, currentY + 28); ctx.fillText('Prüfungsart', leftMargin + 15, currentY + 28);
ctx.textAlign = 'right'; ctx.textAlign = 'right';
ctx.fillText('Total Marks', rightMargin - 15, currentY + 28); ctx.fillText('Gesamtpunkte', rightMargin - 15, currentY + 28);
// Table Border // Table Border
ctx.strokeStyle = template_data.borderColor; ctx.strokeStyle = template_data.borderColor;
@ -264,7 +264,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
ctx.textAlign = 'left'; ctx.textAlign = 'left';
ctx.font = `18px ${template_data.fontFamily}`; ctx.font = `18px ${template_data.fontFamily}`;
ctx.fillStyle = template_data.secondaryColor; ctx.fillStyle = template_data.secondaryColor;
ctx.fillText('Assignment', leftMargin + 15, currentY + 28); ctx.fillText('Aufgabe', leftMargin + 15, currentY + 28);
ctx.textAlign = 'right'; ctx.textAlign = 'right';
ctx.font = `bold 18px ${template_data.fontFamily}`; ctx.font = `bold 18px ${template_data.fontFamily}`;
@ -378,7 +378,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
color: template_data.secondaryColor, color: template_data.secondaryColor,
}} }}
> >
Student Name Name des Studierenden
</p> </p>
<p <p
className="text-lg font-semibold" className="text-lg font-semibold"
@ -396,7 +396,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
color: template_data.secondaryColor, color: template_data.secondaryColor,
}} }}
> >
Course Kurs
</p> </p>
<p <p
className="text-lg font-semibold" className="text-lg font-semibold"
@ -414,7 +414,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
color: template_data.secondaryColor, color: template_data.secondaryColor,
}} }}
> >
Completion Date Abschlussdatum
</p> </p>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Calendar <Calendar
@ -440,7 +440,7 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
color: template_data.secondaryColor, color: template_data.secondaryColor,
}} }}
> >
Overall Grade Gesamtnote
</p> </p>
<p <p
className="text-2xl font-bold" className="text-2xl font-bold"
@ -569,21 +569,21 @@ const DynamicMarksheet = ({ template, courseName, studentName, completionDate, s
<RadioGroupItem className="cursor-pointer" value="png" id="marksheet-png" /> <RadioGroupItem className="cursor-pointer" value="png" id="marksheet-png" />
<Label htmlFor="marksheet-png" className="flex cursor-pointer items-center gap-2"> <Label htmlFor="marksheet-png" className="flex cursor-pointer items-center gap-2">
<FileImage className="h-4 w-4" /> <FileImage className="h-4 w-4" />
PNG Image PNG-Bild
</Label> </Label>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<RadioGroupItem className="cursor-pointer" value="pdf" id="marksheet-pdf" /> <RadioGroupItem className="cursor-pointer" value="pdf" id="marksheet-pdf" />
<Label htmlFor="marksheet-pdf" className="flex cursor-pointer items-center gap-2"> <Label htmlFor="marksheet-pdf" className="flex cursor-pointer items-center gap-2">
<FileText className="h-4 w-4" /> <FileText className="h-4 w-4" />
PDF Document PDF-Dokument
</Label> </Label>
</div> </div>
</RadioGroup> </RadioGroup>
<Button variant="outline" className="w-full" onClick={handleDownloadMarksheet}> <Button variant="outline" className="w-full" onClick={handleDownloadMarksheet}>
<Download className="mr-2 h-4 w-4" /> <Download className="mr-2 h-4 w-4" />
Download as {downloadFormat.toUpperCase()} Herunterladen als {downloadFormat.toUpperCase()}
</Button> </Button>
</div> </div>
</Card> </Card>

View File

@ -49,7 +49,7 @@ const Certificate = () => {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = `${studentName}_${courseName}_Certificate.png`; a.download = `${studentName}_${courseName}_Zertifikat.png`;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
document.body.removeChild(a); document.body.removeChild(a);
@ -86,7 +86,7 @@ const Certificate = () => {
pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height); pdf.addImage(imgData, 'PNG', 0, 0, dimensions.width, dimensions.height);
// Save the PDF // Save the PDF
pdf.save(`${studentName}_${courseName}_Certificate.pdf`); pdf.save(`${studentName}_${courseName}_Zertifikat.pdf`);
toast.success(frontend.pdf_certificate_saved); toast.success(frontend.pdf_certificate_saved);
}; };
@ -255,21 +255,21 @@ const Certificate = () => {
<RadioGroupItem className="cursor-pointer" value="png" id="png" /> <RadioGroupItem className="cursor-pointer" value="png" id="png" />
<Label htmlFor="png" className="flex cursor-pointer items-center gap-2"> <Label htmlFor="png" className="flex cursor-pointer items-center gap-2">
<FileImage className="h-4 w-4" /> <FileImage className="h-4 w-4" />
PNG Image PNG-Bild
</Label> </Label>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<RadioGroupItem className="cursor-pointer" value="pdf" id="pdf" /> <RadioGroupItem className="cursor-pointer" value="pdf" id="pdf" />
<Label htmlFor="pdf" className="flex cursor-pointer items-center gap-2"> <Label htmlFor="pdf" className="flex cursor-pointer items-center gap-2">
<FileText className="h-4 w-4" /> <FileText className="h-4 w-4" />
PDF Document PDF-Dokument
</Label> </Label>
</div> </div>
</RadioGroup> </RadioGroup>
<Button variant="outline" className="w-full" onClick={handleDownloadCertificate}> <Button variant="outline" className="w-full" onClick={handleDownloadCertificate}>
<Download className="mr-2 h-4 w-4" /> <Download className="mr-2 h-4 w-4" />
Download as {downloadFormat.toUpperCase()} Herunterladen als {downloadFormat.toUpperCase()}
</Button> </Button>
</div> </div>
</Card> </Card>

View File

@ -32,7 +32,7 @@ const Course = (props: StudentCourseProps) => {
}, },
{ {
value: 'quizzes', value: 'quizzes',
label: 'Tests', label: 'Quizze',
}, },
{ {
value: 'resources', value: 'resources',

View File

@ -74,7 +74,7 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
<Award className={`h-8 w-8 ${isPassed ? 'text-green-500' : 'text-red-500'}`} /> <Award className={`h-8 w-8 ${isPassed ? 'text-green-500' : 'text-red-500'}`} />
<div> <div>
<p className="text-2xl font-bold">{percentage}%</p> <p className="text-2xl font-bold">{percentage}%</p>
<p className="text-muted-foreground text-xs">Total Score</p> <p className="text-muted-foreground text-xs">Gesamtpunktzahl</p>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@ -114,7 +114,7 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
{/* Questions and Answers */} {/* Questions and Answers */}
<div className="mt-6 space-y-4"> <div className="mt-6 space-y-4">
<h3 className="text-lg font-semibold">Quiz Questions</h3> <h3 className="text-lg font-semibold">Quizfragen</h3>
{quiz.quiz_questions && {quiz.quiz_questions &&
quiz.quiz_questions.map((question, index) => { quiz.quiz_questions.map((question, index) => {
@ -136,7 +136,7 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
</div> </div>
<Badge variant={isCorrect ? 'default' : 'destructive'} className="ml-2"> <Badge variant={isCorrect ? 'default' : 'destructive'} className="ml-2">
{isCorrect ? <CheckCircle2 className="mr-1 h-3 w-3" /> : <XCircle className="mr-1 h-3 w-3" />} {isCorrect ? <CheckCircle2 className="mr-1 h-3 w-3" /> : <XCircle className="mr-1 h-3 w-3" />}
{isCorrect ? 'Correct' : 'Wrong'} {isCorrect ? 'Richtig' : 'Falsch'}
</Badge> </Badge>
</div> </div>
@ -163,7 +163,7 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
<div className="flex gap-2"> <div className="flex gap-2">
{isUserAnswer && ( {isUserAnswer && (
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
Your Answer Deine Antwort
</Badge> </Badge>
)} )}
{isCorrectAnswer && <CheckCircle2 className="h-4 w-4 text-green-600" />} {isCorrectAnswer && <CheckCircle2 className="h-4 w-4 text-green-600" />}
@ -178,13 +178,13 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
{/* Boolean type */} {/* Boolean type */}
{question.type === 'boolean' && ( {question.type === 'boolean' && (
<div className="space-y-2"> <div className="space-y-2">
{['True', 'False'].map((option) => { {[{ value: 'True', label: 'Wahr' }, { value: 'False', label: 'Falsch' }].map((option) => {
const isUserAnswer = userAnswers.includes(option); const isUserAnswer = userAnswers.includes(option.value);
const isCorrectAnswer = correctAnswers.includes(option); const isCorrectAnswer = correctAnswers.includes(option.value);
return ( return (
<div <div
key={option} key={option.value}
className={`rounded-md border p-3 ${ className={`rounded-md border p-3 ${
isCorrectAnswer isCorrectAnswer
? 'border-green-500 bg-green-50 dark:bg-green-950/20' ? 'border-green-500 bg-green-50 dark:bg-green-950/20'
@ -194,11 +194,11 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
}`} }`}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span>{option}</span> <span>{option.label}</span>
<div className="flex gap-2"> <div className="flex gap-2">
{isUserAnswer && ( {isUserAnswer && (
<Badge variant="outline" className="text-xs"> <Badge variant="outline" className="text-xs">
Your Answer Deine Antwort
</Badge> </Badge>
)} )}
{isCorrectAnswer && <CheckCircle2 className="h-4 w-4 text-green-600" />} {isCorrectAnswer && <CheckCircle2 className="h-4 w-4 text-green-600" />}