feat: Permettre à l'élève de rendre un devoir avec réponse texte et pièces jointes
L'élève peut désormais répondre à un devoir via un éditeur WYSIWYG, joindre des fichiers (PDF, JPEG, PNG, DOCX), sauvegarder un brouillon et soumettre définitivement son rendu. Le système détecte automatiquement les soumissions en retard par rapport à la date d'échéance. Côté enseignant, une page dédiée affiche la liste complète des élèves avec leur statut (soumis, en retard, brouillon, non rendu), le détail de chaque rendu avec ses pièces jointes téléchargeables, et les statistiques de rendus par classe.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
const ACCEPTED_TYPES = ['application/pdf', 'image/jpeg', 'image/png'];
|
||||
const DEFAULT_ACCEPTED_TYPES = ['application/pdf', 'image/jpeg', 'image/png'];
|
||||
const DEFAULT_ACCEPT_ATTR = '.pdf,.jpg,.jpeg,.png';
|
||||
const DEFAULT_HINT = 'PDF, JPEG ou PNG — 10 Mo max par fichier';
|
||||
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 Mo
|
||||
|
||||
interface UploadedFile {
|
||||
@@ -13,12 +15,20 @@
|
||||
existingFiles = [],
|
||||
onUpload,
|
||||
onDelete,
|
||||
disabled = false
|
||||
disabled = false,
|
||||
acceptedTypes = DEFAULT_ACCEPTED_TYPES,
|
||||
acceptAttr = DEFAULT_ACCEPT_ATTR,
|
||||
hint = DEFAULT_HINT,
|
||||
showDelete = true
|
||||
}: {
|
||||
existingFiles?: UploadedFile[];
|
||||
onUpload: (file: File) => Promise<UploadedFile>;
|
||||
onDelete: (fileId: string) => Promise<void>;
|
||||
disabled?: boolean;
|
||||
acceptedTypes?: string[];
|
||||
acceptAttr?: string;
|
||||
hint?: string;
|
||||
showDelete?: boolean;
|
||||
} = $props();
|
||||
|
||||
let files = $state<UploadedFile[]>(existingFiles);
|
||||
@@ -43,8 +53,8 @@
|
||||
}
|
||||
|
||||
function validateFile(file: File): string | null {
|
||||
if (!ACCEPTED_TYPES.includes(file.type)) {
|
||||
return `Type de fichier non accepté : ${file.type}. Types autorisés : PDF, JPEG, PNG.`;
|
||||
if (!acceptedTypes.includes(file.type)) {
|
||||
return `Type de fichier non accepté : ${file.type}.`;
|
||||
}
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
return `Le fichier dépasse la taille maximale de 10 Mo (${formatFileSize(file.size)}).`;
|
||||
@@ -105,7 +115,7 @@
|
||||
<span class="file-icon">{getFileIcon(file.mimeType)}</span>
|
||||
<span class="file-name">{file.filename}</span>
|
||||
<span class="file-size">{formatFileSize(file.fileSize)}</span>
|
||||
{#if !disabled}
|
||||
{#if !disabled && showDelete}
|
||||
<button
|
||||
type="button"
|
||||
class="file-remove"
|
||||
@@ -139,13 +149,13 @@
|
||||
<input
|
||||
bind:this={fileInput}
|
||||
type="file"
|
||||
accept=".pdf,.jpg,.jpeg,.png"
|
||||
accept={acceptAttr}
|
||||
onchange={handleFileSelect}
|
||||
class="file-input-hidden"
|
||||
aria-hidden="true"
|
||||
tabindex="-1"
|
||||
/>
|
||||
<p class="upload-hint">PDF, JPEG ou PNG — 10 Mo max par fichier</p>
|
||||
<p class="upload-hint">{hint}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user