# Berkas tugas dan pratinjau

Guru memilih **Upload File (Dokumen, Foto, Audio, Video)** pada langkah
**Soal & Konten**. Siswa dan guru dapat memilih **Pratinjau** untuk membuka
penampil besar, lalu **Unduh Berkas** jika ingin menyimpan salinan. Pratinjau
tidak menulis berkas ke folder Downloads, tetapi tetap memakai jaringan dan
memori/cache sementara browser.

## Format dan batas

| Format | Pratinjau |
| --- | --- |
| PDF | Penampil PDF browser; tersedia tautan tab baru untuk browser yang tidak mendukung PDF tertanam |
| JPG/JPEG, PNG, GIF, WebP | Gambar |
| MP3, WAV, OGG, M4A | Pemutar audio browser |
| MP4, MOV, WebM | Pemutar video; dukungan codec bergantung pada browser/perangkat |
| DOCX | Isi, gambar, dan tabel melalui Mammoth, disanitasi DOMPurify, diisolasi dalam iframe tanpa skrip |
| TXT, CSV | Teks, hingga 500.000 karakter |
| DOC, XLS/XLSX, PPT/PPTX | Berkas tetap dapat dikirim/diunduh; penampil menjelaskan format belum didukung dan menyarankan PDF/DOCX |

DOCX bukan reproduksi tata letak Word yang persis. Batas pratinjau DOCX 20 MiB.
MOV tidak otomatis dikonversi. Jika codec tidak didukung, gunakan MP4 dengan
video H.264 dan audio AAC. Tidak diperlukan FFmpeg, worker, migrasi, atau
konverter Office pada server.

Penyimpanan lokal dibatasi 20 MiB (20 x 1024 x 1024 byte), ditampilkan sebagai
20 MB. Untuk **Google Drive saya** (bawaan 100 MiB), OAuth, dan folder tujuan,
lihat [Google Drive untuk tugas](GOOGLE-DRIVE-ASSIGNMENTS.md). Batas PHP
`upload_max_filesize` dan `post_max_size` harus mencukupi; mengubah batas PHP
tidak mengubah batas aplikasi. Pemeriksaan browser memvalidasi ekstensi dan
ukuran. Server memeriksa pasangan ekstensi/MIME dan ukuran secara mandiri.

## Privasi dan akses

Rute privat tetap `/submissions/{submission}/download` dan
`/submissions/{submission}/preview`. Hanya siswa pemilik, guru pemilik materi,
atau admin yang dapat mengaksesnya. Berkas Drive diteruskan server memakai
otorisasi yang sama; tidak dibuat publik atau dikirim ke viewer pihak ketiga.

Pratinjau hanya melayani daftar format pasif yang diizinkan, memakai
`nosniff`, `private, no-store`, dan mempertahankan byte range untuk media.
HTML/SVG dan format tak didukung tidak boleh dilayani inline. DOCX dibaca di
browser, akses berkas eksternal dinonaktifkan, HTML dibersihkan, serta frame
memblokir skrip, form, navigasi tautan, dan sumber jaringan eksternal.

## Notifikasi dan konfirmasi

`ActionDialogHost` dipasang sekali pada root aplikasi. Semua bekas pemanggilan
`window.confirm`/`confirm` memakai `await confirmAction(...)`; aksi hanya
berjalan jika pengguna mengonfirmasi. Batal, Escape, dan navigasi halaman
membatalkan konfirmasi. Klik berulang tidak membuat permintaan ganda.
Dialog `<dialog>` diberi CSS aplikasi dan memakai fokus/modalitas browser.
Tiga validasi bekas `alert()` menggunakan toast Sonner.

## Verifikasi

```text
php artisan test --compact --filter="AssignmentFileSubmissionTest|GoogleDriveAssignmentTest|SubmissionPreviewTest"
npm run typecheck
npm run build
node --test tests/js/submission-preview.test.mjs
```

Tes MIME/range menggunakan signature kontainer, bukan bukti playback video.
Uji penerimaan browser/HP dan produksi tetap mencakup: baca PDF beberapa
halaman, lihat gambar, putar/seek audio dan video nyata, baca DOCX bertabel,
unduh hanya setelah klik, akses ditolak untuk akun lain, serta Batal/Escape/
konfirmasi pada Minta Revisi, hapus, dan voting. Periksa tampilan sempit dan
tema terang/gelap. Build lokal tidak membuktikan deployment atau playback.
