Compare commits
No commits in common. "8b0c8071cd241393bef1655c80bc53866339b468" and "6e992c0487b84c034ffcf8fa485ad46ea4cd1791" have entirely different histories.
8b0c8071cd
...
6e992c0487
|
|
@ -30,15 +30,17 @@ class FileMaterialRequest(BaseModel):
|
|||
file_name: str
|
||||
question_type: str
|
||||
question_count: int
|
||||
start_page: int = 24
|
||||
start_page: int = 10
|
||||
|
||||
class FeedbackRequest(BaseModel):
|
||||
user_answer: str
|
||||
expected_answer: str
|
||||
question_text: str
|
||||
|
||||
feedback_cache = {}
|
||||
|
||||
def potong_konten(text: str, max_chars: int = 5000):
|
||||
return text[:max_chars] if len(text) > max_chars else text
|
||||
|
||||
@app.post("/generate-from-file/")
|
||||
async def generate_from_file(request: FileMaterialRequest):
|
||||
try:
|
||||
|
|
@ -60,135 +62,55 @@ async def generate_from_file(request: FileMaterialRequest):
|
|||
if not text.strip():
|
||||
raise HTTPException(status_code=400, detail="Isi file kosong atau tidak terbaca.")
|
||||
|
||||
# ==== PROMPT INSTRUKSI BERDASARKAN JENIS SOAL ====
|
||||
if request.question_type == "multiple_choice":
|
||||
soal_instruksi = f"""
|
||||
1. Ambil satu paragraf panjang dari teks.
|
||||
2. Buat **{mc_count} soal pilihan ganda** dari paragraf tersebut.
|
||||
3. Soal harus berdasarkan informasi yang tertulis langsung dalam paragraf (eksplisit).
|
||||
4. Hindari soal yang:
|
||||
- Mengandung simpulan di luar isi
|
||||
- Menambahkan tokoh, nama, atau kejadian di luar paragraf
|
||||
- Jawabannya tidak bisa ditemukan dari paragraf
|
||||
5. Topik soal mencakup:
|
||||
- Ide pokok paragraf
|
||||
- Kalimat utama
|
||||
- Sinonim atau antonim (jika ada kata yang mendukung)
|
||||
- Informasi eksplisit dari kalimat dalam paragraf
|
||||
6. Gunakan bahasa sederhana, sesuai siswa SD kelas 3–4.
|
||||
7. Bobot setiap soal **1–2**, berdasarkan tingkat kesulitan.
|
||||
8. Gunakan format berikut:
|
||||
content_bersih = potong_konten(text.strip())
|
||||
|
||||
**Paragraf:**
|
||||
"[Isi paragraf]"
|
||||
prompt = f"""
|
||||
Buat soal latihan berdasarkan teks materi berikut untuk siswa SD kelas 3.
|
||||
|
||||
**Instruksi:**
|
||||
1. Buat total {request.question_count} soal dengan rincian:
|
||||
- Soal pilihan ganda: {mc_count}
|
||||
- Soal isian: {essay_count}
|
||||
|
||||
2. **Untuk soal isian:**
|
||||
- Ambil kutipan **minimal dua kalimat yang saling terhubung** dari teks sebagai dasar soal
|
||||
- Awali dengan: **Bacalah kutipan berikut: "[kutipan]"**
|
||||
- Buat pertanyaan berdasarkan kutipan tersebut
|
||||
- Sertakan **jawaban singkat**
|
||||
- Beri bobot antara **3–5** sesuai kompleksitas
|
||||
|
||||
3. **Untuk soal pilihan ganda (jika ada):**
|
||||
- Ambil kutipan **1 kalimat yang relevan** dari teks
|
||||
- Buat pertanyaan dan 4 pilihan jawaban (A–D)
|
||||
- Beri jawaban benar dan bobot antara **1–2** sesuai tingkat kesulitan
|
||||
|
||||
4. Gunakan bahasa sederhana dan sesuai dengan siswa SD kelas 3.
|
||||
|
||||
5. Jangan menambahkan informasi di luar teks materi.
|
||||
|
||||
**Format Output:**
|
||||
""" + ("""
|
||||
**Soal Pilihan Ganda:**
|
||||
1. [Pertanyaan]
|
||||
A. ...
|
||||
B. ...
|
||||
C. ...
|
||||
D. ...
|
||||
Jawaban: ...
|
||||
Bobot: ...
|
||||
|
||||
---
|
||||
|
||||
✅ Contoh soal:
|
||||
|
||||
**Paragraf:**
|
||||
"Pak Budi memelihara ayam, bebek, dan kambing. Setiap pagi, ia memberi makan ternaknya dengan penuh kasih sayang."
|
||||
|
||||
**Contoh Soal Pilihan Ganda:**
|
||||
1. Apa hewan yang dipelihara Pak Budi?
|
||||
A. Kucing
|
||||
B. Ayam dan kambing
|
||||
C. Anjing dan bebek
|
||||
D. Ikan dan sapi
|
||||
Jawaban: B
|
||||
Bobot: 1
|
||||
|
||||
2. Apa sinonim dari kata 'ternak' dalam paragraf tersebut?
|
||||
A. Hewan peliharaan
|
||||
B. Sayuran
|
||||
C. Alat tani
|
||||
D. Makanan ternak
|
||||
Jawaban: A
|
||||
Bobot: 2
|
||||
""".strip()
|
||||
|
||||
elif request.question_type == "essay":
|
||||
soal_instruksi = f"""
|
||||
1. Ambil satu paragraf panjang dari teks.
|
||||
2. Buat **{essay_count} soal isian** dari paragraf tersebut.
|
||||
3. Soal harus berasal dari informasi eksplisit yang tertulis dalam paragraf.
|
||||
4. Hindari soal yang:
|
||||
- Mengandung penalaran atau simpulan dari luar isi
|
||||
- Mengandung tokoh, tempat, atau kejadian tambahan
|
||||
5. Topik soal mencakup:
|
||||
- Ide pokok paragraf
|
||||
- Kalimat penting
|
||||
- Karakter tokoh
|
||||
- Sinonim atau antonim (jika tersedia dalam paragraf)
|
||||
6. Gunakan bahasa sederhana, sesuai siswa SD kelas 3–4.
|
||||
7. **Ingat: Bobot soal isian HARUS antara 3 hingga 5. Tidak boleh menggunakan bobot 1 atau 2.**
|
||||
8. Gunakan format berikut:
|
||||
|
||||
**Paragraf:**
|
||||
"[Isi paragraf]"
|
||||
1. Bacalah kutipan berikut: "[2 kalimat atau lebih dari teks]"
|
||||
Pertanyaan: [Pertanyaan]
|
||||
A. [Opsi A]
|
||||
B. [Opsi B]
|
||||
C. [Opsi C]
|
||||
D. [Opsi D]
|
||||
Jawaban: [Huruf Opsi]
|
||||
Bobot: [1 atau 2]
|
||||
""" if mc_count > 0 else "") + ("""
|
||||
|
||||
**Soal Isian:**
|
||||
1. [Pertanyaan]
|
||||
Jawaban: ...
|
||||
Bobot: [3, 4, atau 5 saja — bukan angka lain]
|
||||
1. Bacalah kutipan berikut: "[2 kalimat atau lebih dari teks]". [Pertanyaan]
|
||||
Jawaban: [Jawaban]
|
||||
Bobot: [3 - 5]
|
||||
""" if essay_count > 0 else "") + f"""
|
||||
|
||||
---
|
||||
|
||||
✅ Contoh soal (ikuti strukturnya):
|
||||
|
||||
**Paragraf:**
|
||||
"Pak Ali menanam padi di sawah setiap musim tanam. Ia bekerja keras agar panennya berhasil."
|
||||
|
||||
**Contoh Soal Isian:**
|
||||
1. Apa yang ditanam Pak Ali di sawah?
|
||||
Jawaban: Padi
|
||||
Bobot: 3
|
||||
|
||||
2. Mengapa Pak Ali bekerja keras?
|
||||
Jawaban: Agar panennya berhasil
|
||||
Bobot: 4
|
||||
|
||||
3. Apa sinonim dari kata 'bekerja keras' dalam paragraf tersebut?
|
||||
Jawaban: Rajin atau tekun
|
||||
Bobot: 5
|
||||
""".strip()
|
||||
|
||||
# ==== FINAL PROMPT ====
|
||||
prompt = f"""
|
||||
Kamu adalah asisten guru SD kelas 3 dan 4.
|
||||
|
||||
Tugasmu adalah membuat soal literasi dari teks di bawah ini. Soal HARUS berdasarkan isi teks, tanpa menambahkan informasi dari luar.
|
||||
|
||||
---
|
||||
|
||||
### 🎯 Tujuan:
|
||||
Buat **{request.question_count} soal literasi** berdasarkan **satu paragraf** untuk masing-masing jenis soal.
|
||||
|
||||
---
|
||||
|
||||
### 📌 Instruksi Umum:
|
||||
- Gunakan paragraf berbeda untuk soal pilihan ganda dan isian (jika kedua jenis digunakan).
|
||||
- Jangan menambahkan nama tokoh, tempat, atau kejadian di luar isi paragraf.
|
||||
- Bahasa harus sederhana dan mudah dipahami siswa SD.
|
||||
|
||||
---
|
||||
|
||||
### 📌 Instruksi Soal:
|
||||
{soal_instruksi}
|
||||
|
||||
---
|
||||
|
||||
### 📚 Teks:
|
||||
{text.strip()}
|
||||
**Materi:**
|
||||
{content_bersih}
|
||||
""".strip()
|
||||
|
||||
logging.info("Mengirim prompt ke Ollama...")
|
||||
|
|
@ -224,17 +146,15 @@ async def generate_feedback(request: FeedbackRequest):
|
|||
try:
|
||||
user_answer = request.user_answer.strip()
|
||||
expected_answer = request.expected_answer.strip()
|
||||
question_text = request.question_text.strip()
|
||||
|
||||
prompt_hash = hashlib.sha256(f"{user_answer}|{expected_answer}|{question_text}".encode()).hexdigest()
|
||||
prompt_hash = hashlib.sha256(f"{user_answer}|{expected_answer}".encode()).hexdigest()
|
||||
if prompt_hash in feedback_cache:
|
||||
logging.info("Feedback dari cache.")
|
||||
return {"feedback": feedback_cache[prompt_hash]}
|
||||
|
||||
prompt = f"""
|
||||
Kamu adalah asisten pengajar untuk siswa SD kelas 3. Berikut ini adalah soal isian, jawaban siswa, dan jawaban ideal.
|
||||
Kamu adalah asisten pengajar untuk siswa SD kelas 3. Siswa memberikan jawaban berikut untuk soal isian.
|
||||
|
||||
**Soal:** {question_text}
|
||||
**Jawaban Siswa:** {user_answer}
|
||||
**Jawaban Ideal:** {expected_answer}
|
||||
|
||||
|
|
@ -242,7 +162,7 @@ Beri feedback singkat dan membangun, maksimal 2 kalimat. Gunakan bahasa yang mud
|
|||
"""
|
||||
|
||||
logging.info("Mengirim prompt feedback ke Ollama...")
|
||||
async with httpx.AsyncClient(timeout=300) as client:
|
||||
async with httpx.AsyncClient(timeout=60) as client:
|
||||
response = await client.post(OLLAMA_URL, json={
|
||||
"model": "llama3.1:latest",
|
||||
"prompt": prompt,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
from docx import Document
|
||||
import fitz
|
||||
|
||||
def read_text_from_file(filepath: str, start_page: int = 0, max_chars: int = 5000) -> str:
|
||||
def read_text_from_file(filepath: str, start_page: int = 0, max_chars: int = 3000) -> str:
|
||||
ext = os.path.splitext(filepath)[1].lower()
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -245,10 +245,9 @@ public function submitAssessment($id)
|
|||
$correctWeight += $weight;
|
||||
} else {
|
||||
try {
|
||||
$apiResponse = Http::timeout(300)->post('http://127.0.0.1:8010/generate-feedback/', [
|
||||
$apiResponse = Http::timeout(10)->post('http://127.0.0.1:8010/generate-feedback/', [
|
||||
'user_answer' => $answer->answer_text,
|
||||
'expected_answer' => $bestMatch,
|
||||
'question_text' => $question->question_text,
|
||||
]);
|
||||
|
||||
if ($apiResponse->ok()) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
`@extends('php/teacher/home')
|
||||
@section('content')
|
||||
<div style="padding: 20px; max-width: 68%; margin-left:5px; ">
|
||||
<div style="border: 1px solid #ccc; padding: 10px 10px 10px 10px; border-radius: 5px;margin-bottom:40px">
|
||||
<div class="form-group">
|
||||
<form method="POST" action="" enctype="multipart/form-data">
|
||||
<div class="form-group" style="margin-bottom: 20px;">
|
||||
<input type="text" name="title" class='form-control' placeholder="Tittle" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<style>
|
||||
.ck-content > p{
|
||||
height: 300px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<textarea name="editor" id="editor" class="form-control"></textarea>
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 20px;">
|
||||
<input type="text" name="title" class='form-control' placeholder="Tittle" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/34.2.0/classic/ckeditor.js">
|
||||
<script ></script>
|
||||
<script type="text/javascript">
|
||||
ClassicEditor
|
||||
.create(document.querySelector('#editor'), {
|
||||
ckfinder: {
|
||||
uploadUrl: '{{route('uploadimage').'?_token='.csrf_token()}}',
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
|
|
@ -370,7 +370,7 @@
|
|||
<div class="col-span-12 lg:col-span-9 space-y-4">
|
||||
|
||||
{{-- ===================== PILIHAN GANDA ===================== --}}
|
||||
<h4 class="text-lg font-semibold mb-2">Bagian 1: Pilihan Ganda</h4>
|
||||
<h4 class="text-lg font-semibold mb-2">Soal Pilihan Ganda</h4>
|
||||
@php $mcGlobalNumber = 0; @endphp
|
||||
@foreach ($questionsByMaterial as $materialId => $materialQuestions)
|
||||
@php
|
||||
|
|
@ -438,7 +438,7 @@
|
|||
@endforeach
|
||||
|
||||
{{-- ===================== ISIAN / ESSAY ===================== --}}
|
||||
<h4 class="text-lg font-semibold mt-6 mb-2">Bagian 2: Isian</h4>
|
||||
<h4 class="text-lg font-semibold mt-6 mb-2">Soal Isian</h4>
|
||||
@php $essayGlobalNumber = 0; @endphp
|
||||
@foreach ($questionsByMaterial as $materialId => $materialQuestions)
|
||||
@php
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
@include('literacy.student.assessments.modals.loading')
|
||||
|
||||
<!-- Modal Konfirmasi -->
|
||||
<div class="modal fade" id="confirmSubmitModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
|
|
@ -21,57 +19,30 @@
|
|||
|
||||
<!-- Skrip JavaScript untuk Submit Assessment -->
|
||||
<script>
|
||||
function showLoadingFeedback() {
|
||||
$('#loadingIcon').show();
|
||||
$('#successIcon').hide();
|
||||
$('#errorIcon').hide();
|
||||
$('#loadingText').text("Mohon tunggu, sedang menyelesaikan asesmen...");
|
||||
$('#loadingModal').modal({backdrop: 'static', keyboard: false});
|
||||
$('#loadingModal').modal('show');
|
||||
}
|
||||
|
||||
function showSuccessFeedback() {
|
||||
$('#loadingIcon').hide();
|
||||
$('#successIcon').show();
|
||||
$('#errorIcon').hide();
|
||||
$('#loadingText').text("Berhasil menyelesaikan asesmen!");
|
||||
}
|
||||
|
||||
function showErrorFeedback(message) {
|
||||
$('#loadingIcon').hide();
|
||||
$('#successIcon').hide();
|
||||
$('#errorIcon').show();
|
||||
$('#loadingText').text(message || "Terjadi kesalahan saat menyelesaikan asesmen.");
|
||||
}
|
||||
|
||||
function submitAssessment() {
|
||||
var assessmentId = {{ $assessment->id ?? 'null' }};
|
||||
var assessmentId = {{ $assessment->id ?? 'null' }}; // Pastikan ID ada
|
||||
|
||||
if (!assessmentId) {
|
||||
alert("Terjadi kesalahan: ID asesmen tidak ditemukan.");
|
||||
return;
|
||||
}
|
||||
|
||||
$('#confirmSubmitModal').modal('hide');
|
||||
showLoadingFeedback();
|
||||
|
||||
$.ajax({
|
||||
url: "/literacy/student/assessment/submit/" + assessmentId,
|
||||
url: "/literacy/student/assessment/submit/" + assessmentId, // Perbaiki URL sesuai route
|
||||
type: "POST",
|
||||
headers: {
|
||||
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
|
||||
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') // Pastikan CSRF Token dikirim
|
||||
},
|
||||
success: function(response) {
|
||||
showSuccessFeedback();
|
||||
setTimeout(function() {
|
||||
$('#loadingModal').modal('hide');
|
||||
window.location.href = "/literacy/student/assessments";
|
||||
}, 2000);
|
||||
alert(response.message);
|
||||
window.location.href = "/literacy/student/assessments"; // Redirect ke halaman indeks asesmen
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.error(xhr);
|
||||
showErrorFeedback(xhr.responseJSON?.error || "Gagal menyelesaikan asesmen.");
|
||||
alert(xhr.responseJSON?.error || "Gagal menyelesaikan asesmen.");
|
||||
}
|
||||
});
|
||||
|
||||
$('#confirmSubmitModal').modal('hide'); // Tutup modal setelah klik tombol
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<!-- Modal Loading (Bootstrap 4 Compatible) -->
|
||||
<div class="modal fade" id="loadingModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-md" role="document">
|
||||
<div class="modal-content text-center py-5" style="background: rgba(255, 255, 255, 0.95); border: none;">
|
||||
<div class="d-flex flex-column align-items-center justify-content-center">
|
||||
<!-- Spinner -->
|
||||
<div id="loadingIcon" class="spinner-border text-primary mb-4" role="status" style="width: 4rem; height: 4rem; display: none;">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
<!-- Success -->
|
||||
<div id="successIcon" class="text-success mb-4" style="display: none;">
|
||||
<i class="fa fa-check-circle" style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<!-- Error -->
|
||||
<div id="errorIcon" class="text-danger mb-4" style="display: none;">
|
||||
<i class="fa fa-times-circle" style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<!-- Text -->
|
||||
<p id="loadingText" style="font-size: 1.25rem; font-weight: 500;">Mohon tunggu, sedang memproses...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<div class="modal fade" id="notificationModal" tabindex="-1" role="dialog" aria-labelledby="notificationModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="notificationModalLabel">Pemberitahuan</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="notificationMessage" class=" fw-bold">Placeholder untuk pesan notifikasi.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Tutup</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
margin-top: 250px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,586 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
|
||||
<title>iCLOP</title>
|
||||
<link rel="icon" href="./images/logo.png" type="image/png">
|
||||
<style>
|
||||
.text {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: #3F3F46;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
.text-list {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: #3F3F46;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #EAEAEA;
|
||||
color: #636363;
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* CSS untuk mengatur sidebar */
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
background-color: #ffffff;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
overflow-x: hidden;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
/* Gaya dropdown */
|
||||
.dropdown {
|
||||
padding: 6px 8px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Gaya dropdown content */
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #f9f9f9;
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dropdown:hover .dropdown-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* justify-content: space-between; */
|
||||
padding: 10px;
|
||||
border: 1px solid #E4E4E7;
|
||||
cursor: pointer;
|
||||
margin-bottom: 10px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.list-item:hover {
|
||||
background-color: #F5F5F8;
|
||||
}
|
||||
|
||||
.list-item-title {
|
||||
font-size: 18px;
|
||||
margin-left: 10px;
|
||||
font-weight: 600;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-size: 16px;
|
||||
color: #3F3F46;
|
||||
}
|
||||
|
||||
.list-item-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.expandable-content {
|
||||
margin-top: 0px;
|
||||
display: none;
|
||||
padding: 10px;
|
||||
border-top: 1px solid #E4E4E7;
|
||||
border: none;
|
||||
margin-left: 32px;
|
||||
}
|
||||
|
||||
.radio-label {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
width: 100%;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 40;
|
||||
height: 30px;
|
||||
background-color: #4caf50;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
margin-top: 10px;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text:hover {
|
||||
color: black;
|
||||
/* Change text color to blue on hover */
|
||||
text-decoration: underline;
|
||||
/* Add underline on hover */
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
#outputDiv {
|
||||
background-color: #f4f4f4;
|
||||
border: 1px solid #ddd;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#outputDiv p {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#outputDiv h3 {
|
||||
color: #0056b3;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
@media only screen and (max-width: 600px) {
|
||||
#sidebar {
|
||||
display: none;
|
||||
/* Hide sidebar on small screens */
|
||||
}
|
||||
|
||||
div[style*="max-width: 800px"] {
|
||||
max-width: 90%;
|
||||
/* Adjust max-width of container */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- <style>
|
||||
#progressbar {
|
||||
width: @php
|
||||
echo $progress.'%';
|
||||
@endphp;
|
||||
height: 20px;
|
||||
background-color: #4caf50;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
||||
</style> -->
|
||||
</head>
|
||||
<!-- This is body test -->
|
||||
|
||||
<body>
|
||||
<!-- Navbar -->
|
||||
<nav class="navbar navbar-light bg-light" style="padding: 15px 20px; border-bottom: 1px solid #E4E4E7; font-family: 'Poppins', sans-serif;">
|
||||
<a class="navbar-brand" href="{{ route('literacy_welcome') }}">
|
||||
<img src="{{ asset('images/left-arrow.png') }}" style="height: 24px; margin-right: 10px;">
|
||||
{{ $row->title }}
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<!-- Sidebar -->
|
||||
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div id="sidebar" class="sidebar" style="border-left: 1px solid #E4E4E7; padding: 20px; width: 100%; max-width: 400px;">
|
||||
<p class="text-list" style="font-size: 18px; font-weight: 600; font-size: 20px"><img src="{{ asset('images/right.png') }}" style="height: 24px; margin-right: 10px; border:1px solid; border-radius:50%"> Task List</p>
|
||||
|
||||
@if($role == 'student')
|
||||
<div class="progress-container">
|
||||
<div id="progressbar"></div>
|
||||
</div>
|
||||
<div id="progress"> @php
|
||||
echo $progress.'%';
|
||||
@endphp</div>
|
||||
|
||||
@endif
|
||||
<ul class="list" style="margin-top: 20px">
|
||||
@foreach($topics as $topic)
|
||||
@php
|
||||
/*$results = DB::select("select * from php_topics where id = ?", [$topic->id]);
|
||||
if (!empty($results)) {
|
||||
$result = $results[0];
|
||||
$result->id;
|
||||
} else {
|
||||
echo "No results found.";
|
||||
}*/
|
||||
if($topic->id == $_GET['phpid'] ){
|
||||
$display = "display:block !important";
|
||||
$transform = "transform: rotate(180deg); !important";
|
||||
}else{
|
||||
$display = "";
|
||||
$transform = "";
|
||||
}
|
||||
@endphp
|
||||
|
||||
@php
|
||||
|
||||
|
||||
$row = DB::table('react_topics')
|
||||
->leftJoin('react_topics_detail', 'react_topics.id', '=', 'react_topics_detail.id_topics')
|
||||
->select('*')
|
||||
->where('react_topics_detail.id_topics', '=', $topic->id )
|
||||
->get();
|
||||
$no = 1;
|
||||
@endphp
|
||||
@foreach($row as $r)
|
||||
@php
|
||||
$no++;
|
||||
$count_ = ($no/$detailCount)*10;
|
||||
$phpdid = isset($_GET['start']) ? $_GET['start'] : '';
|
||||
if($r->id == $phpdid and $r->id_topics == $_GET['phpid']){
|
||||
$active = 'color:#000; font-weight:bold; text-decoration: underline;';
|
||||
|
||||
}else{
|
||||
$active = '';
|
||||
}
|
||||
@endphp
|
||||
<li class="list-item">
|
||||
<img class="list-item-icon" src="{{ asset('images/book.png') }}" style="height: 24px; margin: 20px; @php echo $transform; @endphp">
|
||||
<a class="text" style="{{ $active }};" href="{{ route('literacy_material_detail') }}?phpid={{$r->id}}&start={{$topic->id}}" id="requirement" onclick="updateProgress(@php echo $count_ @endphp)">{{ $r->description }} </a>
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
|
||||
<div style="@php echo $display; @endphp">
|
||||
|
||||
<div style="display: flex; flex-direction: column; align-items: left;">
|
||||
|
||||
@php
|
||||
$top = $topic->id;
|
||||
$task = DB::table('php_task')->where('id_topics', $top)->first(); // Menggunakan first() untuk mengambil satu baris pertama
|
||||
|
||||
|
||||
@endphp
|
||||
|
||||
@if($task)
|
||||
@php
|
||||
$tsk = $task->id;
|
||||
$task_get = isset($_GET['task']) ? $_GET['task'] : '';
|
||||
if($tsk == $task_get){
|
||||
$active_task = 'color:#000; font-weight:bold; text-decoration: underline;';
|
||||
|
||||
}else{
|
||||
$active_task = '';
|
||||
}
|
||||
|
||||
@endphp
|
||||
<div class="row">
|
||||
<div class="col-sm-1">
|
||||
<label class="radio-label">
|
||||
<svg width="16" height="16" class="" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9993 2.6665C8.63555 2.6665 2.66602 8.63604 2.66602 15.9998C2.66602 23.3636 8.63555 29.3332 15.9993 29.3332C23.3631 29.3332 29.3327 23.3636 29.3327 15.9998C29.3327 8.63604 23.3631 2.6665 15.9993 2.6665ZM5.33268 15.9998C5.33268 10.1088 10.1083 5.33317 15.9993 5.33317C21.8904 5.33317 26.666 10.1088 26.666 15.9998C26.666 21.8909 21.8904 26.6665 15.9993 26.6665C10.1083 26.6665 5.33268 21.8909 5.33268 15.9998Z" fill="#71717A"></path>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col" style="padding-bottom: 1rem;">
|
||||
|
||||
<a class="text" onclick="updateProgress(@php echo $count_ @endphp)" style="{{ $active_task }}" href="{{ route('send_task') }}?phpid={{$topic->id}}&task={{$task->id}}" id="requirement">{{ $task->task_name }} </a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endforeach
|
||||
</ul>
|
||||
<br><br>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
|
||||
|
||||
</div>
|
||||
<div style="padding: 20px; max-width: 68%; margin-left:5px; ">
|
||||
<div style="border: 1px solid #ccc; padding: 20px 10px 10px 30px; border-radius: 5px;margin-bottom:40px">
|
||||
@php
|
||||
if($pdf_reader == 0):
|
||||
echo $html_start;
|
||||
@endphp
|
||||
|
||||
|
||||
@php
|
||||
else:
|
||||
@endphp
|
||||
|
||||
<iframe src="{{ asset('react/document/'. $html_start ) }}" style="width: 100%; height: 510px"></iframe>
|
||||
</iframe>
|
||||
@php
|
||||
endif;
|
||||
@endphp
|
||||
@php echo explode('.', $html_start)[0] @endphp
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($role == ' ')
|
||||
<div style="padding: 20px; max-width: 68%; margin-left:5px; ">
|
||||
<div style="border: 1px solid #ccc; padding: 20px 10px 10px 30px; border-radius: 5px;margin-bottom:40px">
|
||||
<!-- <a href="{{ asset('/storage/private/febri syawaldi/febri syawaldi_db_conn.php') }}" download>Download File</a>
|
||||
<a href="{{public_path('storage/private/febri syawaldi/febri syawaldi_db_conn.php')}}" download>Click me</a> -->
|
||||
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Download File</th>
|
||||
<!-- Add more table headers as needed -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($listTask as $item)
|
||||
<tr>
|
||||
<td>{{ $item->name }}</td>
|
||||
<td>{{ $item->flag }}</td>
|
||||
<td><a href="{{ asset( $item->path ) }}" download="" class="btn btn-primary">Download File/a>
|
||||
</td>
|
||||
<!-- Add more table cells as needed -->
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
|
||||
@endif
|
||||
|
||||
@if($flag == 1)
|
||||
|
||||
<div style="padding: 20px; max-width: 68%; margin-left:5px; ">
|
||||
<div style="border: 1px solid #ccc; padding: 20px 10px 10px 30px; border-radius: 5px;margin-bottom:40px">
|
||||
<div style="padding-top: 15px; padding-bottom: 15px">
|
||||
<p class='text-list' style='font-size: 24px; font-weight: 600;width: 400px !important;'> Form Upload File</p>
|
||||
<div class="texts" style=" color:red; position: relative;">
|
||||
|
||||
</div>
|
||||
<div class="texts" style=" position: relative;">
|
||||
<style>
|
||||
text:hover {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
</style>
|
||||
<form id="apiForm" method="POST" action="{{ route('upload_file') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<input type="hidden" name="phpid" id="phpid" value="{{ $_GET['phpid'] }}">
|
||||
<input type="hidden" name="start" id="start" value="{{ $_GET['start'] }}">
|
||||
|
||||
<div class=" d-flex align-items-center">
|
||||
<div class="col-md-8">
|
||||
<div class="form-group">
|
||||
<label class="mb-2" for="fileJs">Upload File</label>
|
||||
<input type="file" name="uploadFile" id="fileJs" class="form-control">
|
||||
<small>*Files must be in accordance with the material</small> <br>
|
||||
<small>*Hello.js, Form.js, Counter.js, FormStyle.js, dan Navbar.js</small> <br>
|
||||
<small>*Hello.test.js, Form.test.js, Counter.test.js, FormStyle.test.js, dan Navbar.test.js</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="invisible">Upload</label>
|
||||
<input type="button" value="Upload" class="btn btn-success mb-5" id="uploadBtn">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="notificationMessage" class="mt-3"></div>
|
||||
@include('literacy.student.material.components.modalNotification')
|
||||
</form>
|
||||
|
||||
<!-- <div id="outputDiv"></div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer">
|
||||
© 2023 Your Website. All rights reserved.
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/34.2.0/classic/ckeditor.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
|
||||
<script>
|
||||
function showNotification(message, type) {
|
||||
var notificationDiv = document.getElementById('notificationMessage');
|
||||
notificationDiv.textContent = message;
|
||||
notificationDiv.className = `alert ${type}`;
|
||||
notificationDiv.style.display = 'block';
|
||||
notificationDiv.innerHTML = message.replace(/\n/g, '<br/>');
|
||||
}
|
||||
|
||||
document.getElementById('uploadBtn').addEventListener('click', function(event) {
|
||||
var fileInput = document.getElementById('fileJs');
|
||||
var file = fileInput.files[0];
|
||||
|
||||
if (file) {
|
||||
var formData = new FormData();
|
||||
formData.append('uploadFile', file);
|
||||
|
||||
uploadFile(formData);
|
||||
} else {
|
||||
showNotification('Choose File First.', 'alert-danger');
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
function uploadFile(formData) {
|
||||
fetch('{{ route('upload_file') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.uploaded) {
|
||||
showNotification('Tunggu Ya Pengecekan Sedang Di Lakukan.', 'alert-info');
|
||||
if (data.comparisonResult === 'Selamat, jawaban kamu benar.') {
|
||||
showNotification(data.comparisonResult, 'alert-success');
|
||||
} else {
|
||||
showNotification(data.comparisonResult, 'alert-warning');
|
||||
}
|
||||
} else {
|
||||
showNotification('Penamaan File ' + data.message, 'alert-danger');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error uploading file:', error);
|
||||
showNotification('Error uploading file.', 'alert-danger');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function submitScoreToLaravel(score) {
|
||||
fetch('/literacy/baru/submit_score', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
score: score,
|
||||
topics_id: @php echo $_GET['phpid'] @endphp
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Success Simmpan:', data);
|
||||
})
|
||||
.catch(error => console.error('Error submitting score:', error));
|
||||
}
|
||||
|
||||
function toggleSidebar() {
|
||||
document.getElementById("sidebar").classList.toggle("active");
|
||||
}
|
||||
|
||||
function toggleItem(item) {
|
||||
const content = item.nextElementSibling;
|
||||
const icon = item.querySelector('.list-item-icon');
|
||||
content.style.display = content.style.display === 'block' ? 'none' : 'block';
|
||||
icon.style.transform = content.style.display === 'block' ? 'rotate(180deg)' : 'none';
|
||||
}
|
||||
|
||||
const radioButtons = document.querySelectorAll('input[name="itemSelection"]');
|
||||
const textElements = document.querySelectorAll('.text');
|
||||
|
||||
radioButtons.forEach((button, index) => {
|
||||
button.addEventListener('change', () => {
|
||||
textElements.forEach((textElement, i) => {
|
||||
if (i === index) {
|
||||
textElement.style.fontWeight = 'bold';
|
||||
} else {
|
||||
textElement.style.fontWeight = 'normal';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// function move() {
|
||||
// // fetch
|
||||
|
||||
|
||||
// var progressBar = document.getElementById("myProgressBar");
|
||||
// var progressText = document.getElementById("progressText");
|
||||
// var width = 0;
|
||||
// var interval = setInterval(frame, progress);
|
||||
|
||||
// function frame() {
|
||||
// if (width >= progress) {
|
||||
// clearInterval(interval);
|
||||
// } else {
|
||||
// width++;
|
||||
// progressBar.style.width = @php
|
||||
// echo $progress;
|
||||
// @endphp +"%";
|
||||
// progressText.innerHTML = @php
|
||||
// echo $progress;
|
||||
// @endphp +"%";
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
move();
|
||||
|
||||
function updateProgress(params) {
|
||||
// Get CSRF token from the meta tag
|
||||
var csrfToken = $('meta[name="csrf-token"]').attr('content');
|
||||
|
||||
$.ajaxSetup({
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': csrfToken
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "{{ Route('session_progress') }}",
|
||||
data: {
|
||||
params: params
|
||||
},
|
||||
success: function(response) {
|
||||
$('#progressbar').css('width', params + '%');
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// $('#progress').text"@php"
|
||||
// $width = session('params');
|
||||
// echo $width."%";
|
||||
// @endphp
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
|
||||
<div style="padding-top: 15px; padding-bottom: 15px">
|
||||
<p class='text-list' style='font-size: 24px; font-weight: 600;width: 400px !important;'> Upload File Practicum </p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="texts" style=" position: relative;">
|
||||
<style>
|
||||
text:hover{
|
||||
text-decoration: none !important;
|
||||
}
|
||||
</style>
|
||||
<form action="{{ Route("task_submission") }}" method="POST" enctype="multipart/form-data">
|
||||
{{ csrf_field() }}
|
||||
<div class="form-group" >
|
||||
<label for="">Evidence</label>
|
||||
<input type="file" name="file" class="form-control">
|
||||
<small>Enter the work results <code>.php | .html </code></small>
|
||||
</div>
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<label for="">Comment</label>
|
||||
<textarea class="form-control" name="comment" placeholder="..."></textarea>
|
||||
</div>
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Upload" class="btn btn-primary">
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
<a type="submit" style="margin-top:10px" class="btn btn-primary" href="{{ Route("unittesting") }}">Hasil Testing PHP Unit</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
echo $result_up;
|
||||
?>
|
||||
|
|
@ -169,44 +169,27 @@
|
|||
|
||||
<title>Tab Example</title>
|
||||
|
||||
<!-- ====== CSS ====== -->
|
||||
<!-- Bootstrap 5 -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
|
||||
crossorigin="anonymous">
|
||||
|
||||
<!-- Bootstrap Icons (untuk bi bi-check-circle-fill, dll.) -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
||||
|
||||
<!-- Font Awesome (jika masih dipakai di tempat lain) -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Google Font – Poppins -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Custom style -->
|
||||
<!-- CSS Bootstrap -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
|
||||
rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||
|
||||
<!-- DataTables CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">
|
||||
|
||||
|
||||
<!-- ====== JavaScript ====== -->
|
||||
<!-- jQuery (dibutuhkan DataTables) -->
|
||||
<!-- JavaScript Bootstrap -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
|
||||
<!-- Bootstrap 5 JS bundle (sudah termasuk Popper) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- DataTables core -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
<!-- Place these in the <head> section -->
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">
|
||||
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>
|
||||
|
||||
<!-- Ekstensi DataTables Buttons + export -->
|
||||
<script src="https://cdn.datatables.net/buttons/2.0.0/js/dataTables.buttons.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/buttons/2.0.0/js/buttons.html5.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.7/pdfmake.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/vfs_fonts.js"></script>
|
||||
<script>
|
||||
|
||||
|
|
@ -448,26 +431,6 @@
|
|||
class="mt-4">
|
||||
@csrf
|
||||
|
||||
<!-- Pilihan Materi -->
|
||||
<div class="mb-3">
|
||||
<label for="material_id" class="form-label">Materi</label>
|
||||
<select class="form-select" name="material_id" class="form-control" id="materialSelect" required>
|
||||
<option value="">-- Pilih Materi --</option>
|
||||
@foreach ($materials as $material)
|
||||
<option value="{{ $material->id }}">{{ $material->title }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Pilihan Teks Bacaan -->
|
||||
<div class="mb-3">
|
||||
<label for="story_text_display" class="form-label">Teks Bacaan</label>
|
||||
<textarea id="story_text_display" class="form-control" rows="6" readonly></textarea>
|
||||
<input type="hidden" name="story_text_id" id="story_text_id">
|
||||
<button type="button" class="btn btn-secondary mt-2" id="btnPilihTeks">Pilih Teks
|
||||
Bacaan</button>
|
||||
</div>
|
||||
|
||||
<!-- Teks Pertanyaan -->
|
||||
<div class="mb-3">
|
||||
<label for="question_text" class="form-label">Teks Pertanyaan</label>
|
||||
|
|
@ -512,25 +475,16 @@ class="mt-4">
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@include('literacy.teacher.generate_questions.modals.loading')
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Pilih Teks Bacaan -->
|
||||
<div class="modal fade" id="modalPilihTeks" tabindex="-1" aria-labelledby="modalPilihTeksLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalPilihTeksLabel">Pilih Teks Bacaan</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Tutup"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="listTeksBacaan">
|
||||
<p>Memuat teks bacaan...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('toggleSidebar').addEventListener('click', function () {
|
||||
var sidebar = document.getElementById('sidebarMenu');
|
||||
sidebar.classList.toggle('active');
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
|
@ -538,42 +492,11 @@ class="mt-4">
|
|||
const contentElement = document.getElementById('content');
|
||||
const resetButton = document.getElementById('resetButton');
|
||||
|
||||
const loadingModalEl = document.getElementById('loadingModal');
|
||||
const loadingModal = new bootstrap.Modal(loadingModalEl, {
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
const loadingIcon = document.getElementById('loadingIcon');
|
||||
const successIcon = document.getElementById('successIcon');
|
||||
const errorIcon = document.getElementById('errorIcon');
|
||||
const loadingText = document.getElementById('loadingText');
|
||||
|
||||
function showLoading() {
|
||||
loadingIcon.style.display = 'inline-block';
|
||||
successIcon.style.display = 'none';
|
||||
errorIcon.style.display = 'none';
|
||||
loadingText.innerText = "Mohon tunggu, sedang menghasilkan soal...";
|
||||
loadingModal.show();
|
||||
}
|
||||
|
||||
function showSuccess() {
|
||||
loadingIcon.style.display = 'none';
|
||||
successIcon.style.display = 'block';
|
||||
errorIcon.style.display = 'none';
|
||||
loadingText.innerText = "Berhasil menghasilkan soal!";
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
loadingIcon.style.display = 'none';
|
||||
successIcon.style.display = 'none';
|
||||
errorIcon.style.display = 'block';
|
||||
loadingText.innerText = message || "Terjadi kesalahan saat menghasilkan soal.";
|
||||
}
|
||||
|
||||
aiForm.addEventListener('submit', async function (event) {
|
||||
event.preventDefault();
|
||||
showLoading();
|
||||
|
||||
const formData = new FormData(aiForm);
|
||||
contentElement.innerText = "Menghasilkan pertanyaan...";
|
||||
|
||||
try {
|
||||
let payload = {
|
||||
|
|
@ -595,24 +518,19 @@ function showError(message) {
|
|||
let data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
// Mengubah output untuk menggunakan label yang lebih user-friendly
|
||||
let formattedText = data.generated_questions;
|
||||
if (formattedText) {
|
||||
formattedText = formattedText.replace(/Soal Pilihan Ganda:/g, '**Soal Pilihan Ganda:**');
|
||||
formattedText = formattedText.replace(/Soal Isian:/g, '**Soal Isian:**');
|
||||
}
|
||||
contentElement.innerText = formattedText || "Soal berhasil dibuat, tapi tidak ada data.";
|
||||
showSuccess();
|
||||
setTimeout(() => loadingModal.hide(), 2000);
|
||||
} else {
|
||||
contentElement.innerText = "Error: " + (data.message || "Terjadi kesalahan.");
|
||||
showError(data.message);
|
||||
setTimeout(() => loadingModal.hide(), 2500);
|
||||
}
|
||||
} catch (error) {
|
||||
contentElement.innerText = "Gagal menghubungi server!";
|
||||
console.error("Error:", error);
|
||||
showError("Gagal menghubungi server!");
|
||||
setTimeout(() => loadingModal.hide(), 2500);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -622,75 +540,72 @@ function showError(message) {
|
|||
});
|
||||
</script>
|
||||
|
||||
{{--
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const aiForm = document.getElementById('aiForm');
|
||||
const contentElement = document.getElementById('content');
|
||||
const resetButton = document.getElementById('resetButton');
|
||||
const generateButton = aiForm.querySelector('button[type="submit"]'); // Get the Generate button
|
||||
|
||||
// Function to send the content to the server and get generated questions
|
||||
aiForm.addEventListener('submit', async function (event) {
|
||||
event.preventDefault(); // Prevent the form from submitting normally
|
||||
|
||||
let content = contentElement.innerText.trim(); // Get the content entered by the user
|
||||
|
||||
if (content === "") {
|
||||
content = "Buatlah soal literasi berdasarkan teks anak-anak tentang lingkungan.";
|
||||
}
|
||||
|
||||
contentElement.innerText = "Menghasilkan pertanyaan..."; // Display "Menghasilkan pertanyaan..."
|
||||
|
||||
try {
|
||||
let response = await fetch("{{ route('literacy_teacher_generate_from_ai') }}", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRF-TOKEN": '{{ csrf_token() }}',
|
||||
},
|
||||
body: JSON.stringify({ content: content })
|
||||
});
|
||||
|
||||
let data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
contentElement.innerText = data.generated_questions || "Soal berhasil dibuat, tapi tidak ada data.";
|
||||
} else {
|
||||
contentElement.innerText = "Error: " + (data.message || "Terjadi kesalahan.");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
contentElement.innerText = "Gagal menghubungi server!";
|
||||
console.error("Error:", error);
|
||||
}
|
||||
});
|
||||
|
||||
// Reset the content editable area when reset button is clicked
|
||||
resetButton.addEventListener('click', function () {
|
||||
contentElement.innerText = ''; // Clear the content
|
||||
});
|
||||
});
|
||||
</script> --}}
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const materialSelect = document.getElementById("materialSelect");
|
||||
const storyTextDisplay = document.getElementById("story_text_display");
|
||||
const storyTextIdInput = document.getElementById("story_text_id");
|
||||
const btnPilihTeks = document.getElementById("btnPilihTeks");
|
||||
const listTeksBacaan = document.getElementById("listTeksBacaan");
|
||||
|
||||
const questionType = document.getElementById("questionType");
|
||||
const multipleChoiceOptions = document.getElementById("multipleChoiceOptions");
|
||||
const answerOptions = document.getElementById("answerOptions");
|
||||
const addOptionBtn = document.getElementById("addOption");
|
||||
const essayScore = document.getElementById("essay_score");
|
||||
const essayAnswer = document.getElementById("essay_answer");
|
||||
const saveButton = document.getElementById("saveButton");
|
||||
const multipleChoiceOptions = document.getElementById("multipleChoiceOptions");
|
||||
const essayScoreField = document.getElementById("essayScoreField");
|
||||
const essayReferenceAnswerField = document.getElementById("essayReferenceAnswerField");
|
||||
|
||||
btnPilihTeks.addEventListener("click", function () {
|
||||
const materialId = materialSelect.value;
|
||||
if (!materialId) {
|
||||
alert("Pilih materi terlebih dahulu.");
|
||||
return;
|
||||
}
|
||||
|
||||
listTeksBacaan.innerHTML = "<p>Memuat teks bacaan...</p>";
|
||||
$('#modalPilihTeks').modal('show');
|
||||
|
||||
fetch(`/literacy/teacher/materials/${materialId}/story-texts`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.length === 0) {
|
||||
listTeksBacaan.innerHTML = "<p>Tidak ada teks bacaan.</p>";
|
||||
return;
|
||||
}
|
||||
|
||||
listTeksBacaan.innerHTML = "";
|
||||
data.forEach(text => {
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("mb-3", "p-2", "border", "rounded");
|
||||
div.innerHTML = `
|
||||
<div style="max-height: 150px; overflow-y: auto; white-space: pre-wrap;">${text.story_text}</div>
|
||||
<button type="button" class="btn btn-sm btn-primary mt-2 pilih-teks"
|
||||
data-id="${text.id}"
|
||||
data-teks="${text.story_text.replace(/"/g, '"')}">
|
||||
Pilih Teks Ini
|
||||
</button>
|
||||
`;
|
||||
listTeksBacaan.appendChild(div);
|
||||
});
|
||||
|
||||
document.querySelectorAll(".pilih-teks").forEach(btn => {
|
||||
btn.addEventListener("click", function () {
|
||||
const id = this.getAttribute("data-id");
|
||||
const teks = this.getAttribute("data-teks");
|
||||
|
||||
storyTextDisplay.textContent = teks;
|
||||
storyTextIdInput.value = id;
|
||||
|
||||
$('#modalPilihTeks').modal('hide');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
const saveButton = document.getElementById("saveButton");
|
||||
const essayScore = document.getElementById("essay_score");
|
||||
const essayAnswer = document.getElementById("essay_answer");
|
||||
const questionForm = document.getElementById("questionForm");
|
||||
|
||||
function toggleFields() {
|
||||
const isMultipleChoice = questionType.value === "multiple_choice";
|
||||
|
||||
if (isMultipleChoice) {
|
||||
if (questionType.value === "multiple_choice") {
|
||||
multipleChoiceOptions.style.display = "block";
|
||||
essayScoreField.style.display = "none";
|
||||
essayReferenceAnswerField.style.display = "none";
|
||||
|
|
@ -702,7 +617,10 @@ function toggleFields() {
|
|||
input.setAttribute("required", "required");
|
||||
});
|
||||
|
||||
if (answerOptions.children.length === 0) addNewOption();
|
||||
// Tambahkan minimal 1 opsi jika belum ada
|
||||
if (answerOptions.children.length === 0) {
|
||||
addNewOption();
|
||||
}
|
||||
} else {
|
||||
multipleChoiceOptions.style.display = "none";
|
||||
essayScoreField.style.display = "block";
|
||||
|
|
@ -722,14 +640,12 @@ function toggleFields() {
|
|||
function addNewOption() {
|
||||
let optionsCount = document.querySelectorAll(".option-group").length;
|
||||
let div = document.createElement("div");
|
||||
div.classList.add("option-group", "mb-2", "d-flex", "align-items-center", "gap-2");
|
||||
div.classList.add("option-group", "mb-2", "d-flex", "align-items-center");
|
||||
div.innerHTML = `
|
||||
<input type="text" name="options[${optionsCount}][text]" class="form-control option-text" style="width: 40%;" placeholder="Opsi ${optionsCount + 1}" required>
|
||||
<input type="number" name="options[${optionsCount}][score]" class="form-control option-score" style="width: 20%;" placeholder="Skor" min="0" max="100" required>
|
||||
<label class="d-flex align-items-center ms-2">
|
||||
<input type="checkbox" name="options[${optionsCount}][is_correct]" value="1" class="correct-answer me-1">
|
||||
<span>Benar</span>
|
||||
</label>
|
||||
<input type="text" name="options[${optionsCount}][text]" class="form-control me-2 option-text" style="width: 40%;" placeholder="Opsi ${optionsCount + 1}" required>
|
||||
<input type="number" name="options[${optionsCount}][score]" class="form-control me-2 option-score" style="width: 40%;" placeholder="Skor" min="0" max="100" required>
|
||||
<input type="checkbox" name="options[${optionsCount}][is_correct]" value="1" class="ms-2 correct-answer">
|
||||
<span class="ms-1">Jawaban Benar</span>
|
||||
<button type="button" class="btn btn-danger btn-sm ms-2 remove-option">X</button>
|
||||
`;
|
||||
answerOptions.appendChild(div);
|
||||
|
|
@ -739,6 +655,9 @@ function addNewOption() {
|
|||
updateOptionIndexes();
|
||||
validateForm();
|
||||
});
|
||||
|
||||
updateOptionIndexes();
|
||||
validateForm();
|
||||
}
|
||||
|
||||
function updateOptionIndexes() {
|
||||
|
|
@ -764,6 +683,7 @@ function validateForm() {
|
|||
}
|
||||
|
||||
saveButton.disabled = !isValid;
|
||||
// questionForm.reportValidity();
|
||||
}
|
||||
|
||||
questionType.addEventListener("change", toggleFields);
|
||||
|
|
@ -776,13 +696,6 @@ function validateForm() {
|
|||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.getElementById('toggleSidebar').addEventListener('click', function () {
|
||||
var sidebar = document.getElementById('sidebarMenu');
|
||||
sidebar.classList.toggle('active');
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- JavaScript untuk mengubah konten tab -->
|
||||
<script>
|
||||
function materialModal(id, title, controller) {
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
<!-- resources/views/generate_questions/modals/loading.blade.php -->
|
||||
<div class="modal fade" id="loadingModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-md">
|
||||
<div class="modal-content text-center py-5" style="background: rgba(255, 255, 255, 0.95); border: none;">
|
||||
<div class="d-flex flex-column align-items-center justify-content-center">
|
||||
<!-- Spinner -->
|
||||
<div id="loadingIcon" class="spinner-border text-primary mb-4" role="status" style="width: 4rem; height: 4rem;">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<!-- Success -->
|
||||
<div id="successIcon" class="text-success mb-4" style="display: none;">
|
||||
<i class="bi bi-check-circle-fill" style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<!-- Error -->
|
||||
<div id="errorIcon" class="text-danger mb-4" style="display: none;">
|
||||
<i class="bi bi-x-circle-fill" style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<!-- Text -->
|
||||
<p id="loadingText" style="font-size: 1.25rem; font-weight: 500;">Mohon tunggu, sedang menghasilkan soal...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -6,9 +6,9 @@
|
|||
<h5 class="modal-title">Tambah Pertanyaan</h5>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<form action="{{ route('literacy_questions_store') }}" method="POST">
|
||||
<form action="{{ route('literacy_questions_store') }}" method="POST" id="createQuestionForm">
|
||||
@csrf
|
||||
<div class="modal-body" style="max-height: 70vh; overflow-y: auto;">
|
||||
<div class="modal-body">
|
||||
<!-- Pilihan Materi -->
|
||||
<div class="mb-3">
|
||||
<label for="material_id" class="form-label">Materi</label>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<h5 class="modal-title">Detail Pertanyaan</h5>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body" style="max-height: 70vh; overflow-y: auto;">
|
||||
<div class="modal-body">
|
||||
<!-- Materi -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Materi:</label>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user