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

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

View File

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

View File

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

View File

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

View File

@ -32,7 +32,7 @@ const Course = (props: StudentCourseProps) => {
},
{
value: 'quizzes',
label: 'Tests',
label: 'Quizze',
},
{
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'}`} />
<div>
<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>
</CardContent>
</Card>
@ -114,7 +114,7 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
{/* Questions and Answers */}
<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.map((question, index) => {
@ -136,7 +136,7 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
</div>
<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 ? 'Correct' : 'Wrong'}
{isCorrect ? 'Richtig' : 'Falsch'}
</Badge>
</div>
@ -163,7 +163,7 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
<div className="flex gap-2">
{isUserAnswer && (
<Badge variant="outline" className="text-xs">
Your Answer
Deine Antwort
</Badge>
)}
{isCorrectAnswer && <CheckCircle2 className="h-4 w-4 text-green-600" />}
@ -178,13 +178,13 @@ const QuizResultDialog = ({ quiz, submission }: Props) => {
{/* Boolean type */}
{question.type === 'boolean' && (
<div className="space-y-2">
{['True', 'False'].map((option) => {
const isUserAnswer = userAnswers.includes(option);
const isCorrectAnswer = correctAnswers.includes(option);
{[{ value: 'True', label: 'Wahr' }, { value: 'False', label: 'Falsch' }].map((option) => {
const isUserAnswer = userAnswers.includes(option.value);
const isCorrectAnswer = correctAnswers.includes(option.value);
return (
<div
key={option}
key={option.value}
className={`rounded-md border p-3 ${
isCorrectAnswer
? '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">
<span>{option}</span>
<span>{option.label}</span>
<div className="flex gap-2">
{isUserAnswer && (
<Badge variant="outline" className="text-xs">
Your Answer
Deine Antwort
</Badge>
)}
{isCorrectAnswer && <CheckCircle2 className="h-4 w-4 text-green-600" />}