TIF_E41212002/resources/views/home.blade.php

1161 lines
43 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('layouts.app')
@section('title', 'Landing Page - Murid Ceria')
@push('styles')
<script src="https://kit.fontawesome.com/95c0931704.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="{{asset("assets/css/custom.css")}}">
<meta name="csrf-token" content="{{ csrf_token() }}">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet">
@endpush
@section('header')
<!-- Start .header-section -->
<div id="home" class="header-section flex-box-middle section gradiant-background header-curbed-circle background-circles header-software">
<div id="particles-js" class="particles-container"></div>
<div id="navigation" class="navigation is-transparent" data-spy="affix" data-offset-top="5">
<nav class="navbar navbar-default">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#site-collapse-nav" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#home">
<img class="logo logo-light" src="images/logo-muridceria.png" alt="logo" />
<img class="logo logo-color" src="images/logo-muridceria.png" alt="logo" />
</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse font-secondary" id="site-collapse-nav">
<ul class="nav nav-list navbar-nav navbar-right">
<li><a class="nav-item" href="#home">Beranda</a></li>
<li><a class="nav-item" href="#huruf">Kenali Huruf</a></li>
<li><a class="nav-item" href="#about">Belajar</a></li>
<li><a class="nav-item" href="#features">Baca</a></li>
<li><a class="nav-item" href="#students">Riwayat</a></li>
<li><a class="nav-item" href="#another">Bantuan</a></li>
<li><a class="nav-item" href="#testimonial">Pesan</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container -->
</nav>
</div><!-- .navigation -->
<div class="header-content pt-90">
<div class="container">
<div class="row text-center">
<div class="col-md-8 col-md-offset-2">
<div class="header-texts">
<h1 class="cd-headline clip is-full-width wow fadeInUp" data-wow-duration="2s">
<span>Murid Ceria </span>
<span class="cd-words-wrapper">
<b class="is-visible"></b>
<b>Membaca Lancar Sekolah Nyaman</b>
<b>Yuk Rajin Membaca Bacaan Menarik!</b>
</span>
</h1>
@if (!Auth::user())
<ul class="buttons">
<li>
<a href="{{ route('login') }}" class="button button-border button-transparent wow fadeInUp" data-wow-duration=".9s" data-wow-delay=".9s">
L O G I N
</a>
</li>
</ul>
@else
<ul class="buttons">
@if(Auth::user()->role_id !== '9e3efb34-c5ef-416e-b31e-58ba13807301')
<li>
<a href="{{ route('dashboard') }}" class="button button-border button-transparent wow fadeInUp" data-wow-duration=".9s" data-wow-delay=".9s">
Selamat bergabung, {{ Auth::user()->name }}
</a>
</li>
@else
{{-- Jika siswa, tombol disable / nonaktif --}}
<li>
<button class="button button-border button-transparent wow fadeInUp" data-wow-duration=".9s" data-wow-delay=".9s" disabled>
Selamat bergabung, {{ Auth::user()->name }}
</button>
</li>
@endif
{{-- Tambahkan tombol Logout di sini --}}
<li>
<form action="{{ route('logout') }}" method="POST" style="display:inline;">
@csrf
<button type="submit" class="button button-border button-transparent text-danger wow fadeInUp" data-wow-duration=".9s" data-wow-delay=".9s">
Logout
</button>
</form>
</li>
</ul>
@endif
</div>
</div><!-- .col -->
</div><!-- .row -->
<div class="row text-center">
<div class="col-md-10 col-md-offset-1">
<div class="header-mockups">
<div class="header-laptop-mockup black wow fadeInUp" data-wow-duration="1s" data-wow-delay=".6s" >
<img src="images/home-dashboard.png" alt="software-screen" />
</div>
<div class="iphonex-flat-mockup wow fadeInUp" data-wow-duration="1s" data-wow-delay=".9s">
<img src="images/home-dashboard.png" alt="app screen">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('content')
<!-- FITUR PENGENALAN HURUF -->
<div id="huruf" class="about-section section white-bg">
<div class="container tab-fix">
<div class="section-head text-center">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2 class="heading">Kenali <span>Huruf dan Angka</span></h2>
</div>
</div>
</div>
<!-- Video Slider -->
<div class="slider-container">
<div class="video-wrapper">
<div class="video-slider" id="videoSlider">
<video class="video-slide active" controls muted>
<source src="video/belajar-membaca-huruf.mp4" type="video/mp4">
Browser Anda tidak mendukung video!
</video>
<video class="video-slide" controls muted>
<source src="video/belajar-membaca-angka.mp4" type="video/mp4">
Browser Anda tidak mendukung video!
</video>
</div>
<button id="prev" class="arrow left-arrow"></button>
<button id="next" class="arrow right-arrow"></button>
</div>
<div id="videoError" class="error-message"></div>
</div>
</div>
</div>
<div class="writing-container">
<h3 class="text-center">Coba Menulis Huruf, Angka, atau Kata</h3>
<p class="text-center">Tulis huruf, angka, atau kata dengan jelas di dalam kotak.</p>
<canvas id="writingCanvas" width="800" height="500"></canvas>
<div class="button-group">
<button id="clearCanvas">Hapus</button>
<button id="detectButton">Deteksi</button>
</div>
<div id="recognitionResult"></div>
</div>
{{-- FITUR PEMBELAJARAN VIDEO INTERAKTIF --}}
<div id="about" class="about-section section white-bg">
<div class="container tab-fix">
<div class="section-head text-center">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2 class="heading">Pembelajaran <span>Nyaman</span></h2>
</div>
</div>
</div>
<div class="container">
<!-- Swiper Slider -->
<div class="swiper mySwiper py-4">
<div class="swiper-wrapper">
@foreach ($pembelajarans as $pembelajaran)
<div class="swiper-slide">
<div class="row align-items-center g-4">
<!-- Video dan Audio -->
<div class="col-md-6">
<div class="media wow fadeInLeft" data-wow-duration=".5s">
<!-- Video -->
<div class="media-card mb-4">
@if ($pembelajaran->video)
<div class="video-wrapper">
<video width="100%" controls class="rounded">
<source src="{{ asset('video/' . $pembelajaran->video) }}" type="video/mp4">
Browser Anda tidak mendukung pemutaran video.
</video>
</div>
@else
<div class="placeholder-card text-center bg-light rounded p-4">
<i class="fas fa-video-slash fa-2x text-muted mb-2"></i>
<p class="mb-0 text-muted">Tidak ada video tersedia</p>
</div>
@endif
</div>
<!-- Audio -->
<div class="media-card">
@if ($pembelajaran->audio)
<div class="audio-wrapper wow fadeInUp" data-wow-duration=".5s" data-wow-delay=".2s">
<audio controls class="w-100 rounded">
<source src="{{ asset('audio/' . $pembelajaran->audio) }}" type="audio/mpeg">
Browser Anda tidak mendukung pemutaran audio.
</audio>
</div>
@else
<div class="placeholder-card text-center bg-light rounded p-4 wow fadeInUp" data-wow-duration=".5s" data-wow-delay=".2s">
<i class="fas fa-volume-mute fa-2x text-muted mb-2"></i>
<p class="mb-0 text-muted">Tidak ada audio tersedia</p>
</div>
@endif
</div>
</div>
</div>
<!-- Bacaan -->
<div class="col-md-6">
<div class="txt-entry bg-light p-4 rounded shadow-sm h-100 d-flex flex-column justify-content-between wow fadeInRight" data-wow-duration=".5s">
<p class="text-secondary mb-2">Silahkan Mencoba</p>
<h4 id="bacaan-{{ $pembelajaran->id }}" class="mb-3 text-primary">{{ $pembelajaran->bacaan ?? 'Tidak ada bacaan.' }}</h4>
<button class="button mt-auto" onclick="mulaiRekaman({{ $pembelajaran->id }})">
Mulai Membaca
</button>
<p class="mt-3"><strong>Hasil:</strong> <span id="hasil-{{ $pembelajaran->id }}">-</span></p>
</div>
</div>
</div>
</div>
@endforeach
</div>
<!-- Tombol Navigasi -->
<div class="swiper-button-next custom-swiper-button"></div>
<div class="swiper-button-prev custom-swiper-button"></div>
<!-- Pagination -->
<div class="swiper-pagination"></div>
</div>
<!-- SwiperJS Init -->
<script>
document.addEventListener("DOMContentLoaded", function() {
var swiper = new Swiper(".mySwiper", {
slidesPerView: 1,
spaceBetween: 10,
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
pagination: {
el: ".swiper-pagination",
clickable: true,
},
});
});
</script>
<!-- Swiper CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.css">
<!-- Swiper JS -->
<script src="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.js"></script>
<!-- Font Awesome untuk ikon placeholder -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<script>
function hitungAkurasi(teksAsli, teksDibaca) {
function levenshtein(a, b) {
let tmp;
if (a.length === 0) return b.length;
if (b.length === 0) return a.length;
if (a.length > b.length) tmp = a, a = b, b = tmp;
let row = Array(a.length + 1).fill(0).map((_, i) => i);
for (let i = 1; i <= b.length; i++) {
let prev = i;
for (let j = 1; j <= a.length; j++) {
let val = b[i - 1] === a[j - 1] ? row[j - 1] : Math.min(row[j - 1] + 1, prev + 1, row[j] + 1);
row[j - 1] = prev;
prev = val;
}
row[a.length] = prev;
}
return row[a.length];
}
let distance = levenshtein(teksAsli.toLowerCase(), teksDibaca.toLowerCase());
let maxLength = Math.max(teksAsli.length, teksDibaca.length);
let similarity = ((maxLength - distance) / maxLength) * 100;
return similarity;
}
function mulaiRekaman(id) {
const teksAsli = document.getElementById(`bacaan-${id}`).innerText;
const hasilEl = document.getElementById(`hasil-${id}`);
if (!('webkitSpeechRecognition' in window)) {
alert('Browser tidak mendukung speech recognition.');
return;
}
let recognition = new webkitSpeechRecognition();
recognition.lang = "id-ID";
recognition.continuous = false;
recognition.interimResults = false;
recognition.start();
recognition.onresult = function(event) {
let teksDibaca = event.results[0][0].transcript;
let akurasi = hitungAkurasi(teksAsli, teksDibaca);
if (akurasi >= 80) {
hasilEl.innerHTML = `Benar ✅ (Akurasi: ${akurasi.toFixed(2)}%)`;
hasilEl.style.color = "green";
} else {
hasilEl.innerHTML = `Salah ❌ (Akurasi: ${akurasi.toFixed(2)}%)`;
hasilEl.style.color = "red";
}
};
recognition.onerror = function(event) {
hasilEl.innerHTML = "Terjadi kesalahan, coba lagi.";
hasilEl.style.color = "red";
};
}
</script>
</div>
<div id="features" class="features-box-section section pb-90 white-bg">
<div class="container tab-fix">
<div class="section-head text-center">
<div class="row">
<div class="col-md-6 col-sm-8 col-md-offset-3 col-sm-offset-2">
<h2 class="heading" id="titleHeading">Membaca <span>Yuk!</span></h2>
</div>
</div>
<div class="row text-center">
<div class="col-md-12">
<p id="randomText" class="lead font-weight-bold">Sedang memuat...</p>
<div class="timer-box">
<img src="images/waktu-berlari.gif" alt="Timer Animasi" class="timer-gif">
<p id="countdown" class="countdown-text">120</p>
</div>
<button id="startRecognition" class="btn-start">
<i class="ti ti-microphone"></i> Mulai Membaca
</button>
<p class="mt-3" id="recognizedWrap" style="display: none;">
<strong>Hasil Rekaman:</strong> <span id="recognizedText">...</span>
</p>
<div id="result" style="display: none; margin-top: 20px;">
<h3>Hasil Penilaian</h3>
<p><strong>Biner Naive Bayes Classifier:</strong> <span id="finalScore"></span></p>
<p><strong>Tingkat Pemahaman:</strong> <span id="comprehensionLevel"></span></p>
<p><strong>Akurasi:</strong> <span id="accuracy"></span></p>
<p><strong>Waktu Pengerjaan:</strong> <span id="timeTaken"></span> detik</p>
<p><strong>Keterangan:</strong> <span id="evaluation"></span></p>
</div>
<button id="nextText" class="btn btn-success mt-3" style="visibility: hidden; position: absolute; right: 10px; top: 30px;">
Selanjutnya
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Start .team-section -->
<div class="team-section section grey-background pb-90" id="students">
<div class="container">
<div class="section-head text-center">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2 class="heading">Riwayat <span>Membaca</span></h2>
</div>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="table-responsive">
@if($riwayat->isEmpty())
<p class="text-center text-muted">Belum ada riwayat membaca.</p>
@else
<table class="table table-bordered table-striped">
<thead class="bg-primary text-white">
<tr>
<th>No</th>
<th>Nilai Biner</th>
<th>Tingkat Pemahaman</th>
<th>Akurasi</th>
<th>Waktu Mengerjakan</th>
<th>Keterangan</th>
<th>Tanggal</th>
</tr>
</thead>
<tbody>
@forelse ($riwayat as $index => $data)
<tr>
<td>{{ $index + 1 }}</td>
<td>{{ $data->nilai_akhir }}</td>
<td>{{ $data->tingkat_pemahaman }}%</td>
<td>{{ $data->akurasi }}%</td>
<td>{{ $data->waktu_mengerjakan }} detik</td>
<td>{{ $data->evaluasi }}</td>
<td>{{ optional($data->created_at)->format('d M Y') }}</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center text-muted">Belum ada riwayat membaca.</td>
</tr>
@endforelse
</tbody>
</table>
@endif
</div>
</div>
</div>
</div>
</div>
<!-- Start .faq-section -->
<div class="faq-section section white-bg pt-120 pb-100">
<div class="container">
<div class="faq-alt">
<div class="row tab-fix">
<div class="col-md-4 tab-center mobile-center col-md-offset-1">
<div class="side-heading">
<h2 class="heading">Panduan <span>Website</span></h2>
<p>Pelajari alur website berikut untuk memaksimalkan penggunaan.</p>
</div>
</div><!-- .col -->
<div class="col-md-6">
<!-- Accordion -->
<div class="panel-group accordion" id="another" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="accordion-i1">
<h6 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#another" href="#accordion-pane-i1" aria-expanded="false">
Apa yang bisa dilakukan guru pada website ini ?
<span class="plus-minus"><span></span></span>
</a>
</h6>
</div>
<div id="accordion-pane-i1" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="accordion-i1">
<div class="panel-body">
<p>1. Bertindak mengelola website admin</p>
<p>2. Guru dapat memasukkan video interaktif serta latihan bacaan siswa sesuai dengan pembelajaran yang diajarkan di sekolah.</p>
<p>3. Guru dapat melihat riwayat baca siswa dan mengajarkan kekurangan siswa sesuai evaluasi yang diberikan sistem.</p>
</div>
</div>
</div>
<!-- each panel for accordion -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="accordion-i2">
<h6 class="panel-title">
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#another" href="#accordion-pane-i2" aria-expanded="false">
Apa saja yang dilakukan siswa pada website ini?
<span class="plus-minus"><span></span></span>
</a>
</h6>
</div>
<div id="accordion-pane-i2" class="panel-collapse collapse" role="tabpanel" aria-labelledby="accordion-i2">
<div class="panel-body">
<p>1. Siswa dapat melihat fitur pengenalan huruf berupa video-video pembelajaran yang menarik serta dapat menulis.</p>
<p>2. Siswa dapat melihat fitur pembelajaran interaktif serta membaca bacaan latihan yang sesuai dengan materi video sebagai praktek.</p>
<p>3. Siswa dapat membaca fitur membaca dari sistem untuk mengetahui nilai dan evaluasi kekurangan.</p>
</div>
</div>
</div>
<!-- each panel for accordion -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="accordion-i3">
<h6 class="panel-title">
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#another" href="#accordion-pane-i3" aria-expanded="false">
Apakah website ini universal ?
<span class="plus-minus"><span></span></span>
</a>
</h6>
</div>
<div id="accordion-pane-i3" class="panel-collapse collapse" role="tabpanel" aria-labelledby="accordion-i3">
<div class="panel-body">
<p>Ya website ini universal bisa diakses siapapun, namun untuk siswa yang sudah dibuatkan akun akan semakin mudah karena riwayat membaca dapat dilihat untuk meningkatkan kemampuan membaca.</p>
</div>
</div>
</div>
<!-- each panel for accordion -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="accordion-i4">
<h6 class="panel-title">
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#another" href="#accordion-pane-i4" aria-expanded="false">
Apakah ada data guru pada website admin ?
<span class="plus-minus"><span></span></span>
</a>
</h6>
</div>
<div id="accordion-pane-i4" class="panel-collapse collapse" role="tabpanel" aria-labelledby="accordion-i4">
<div class="panel-body">
<p>Data guru dan data siswa terdapat pada website admin hal tersebut bertujuan dapat melakukan pemantauan pada masing-masing siswa, dan data guru digunakan sebagai penanggung jawab pembimbing pada setiap siswa.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Beri Masukan Kepada Guru Admin -->
<div id="testimonial" class="contact-section section gradiant-background pb-90">
<div class="container">
<div class="section-head heading-light text-center">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2 class="heading heading-light">Beri Masukan</h2>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="container d-flex justify-content-center">
<div class="fullscreen-container">
<div class="col-md-6 mx-auto">
<div class="contact-form white-bg text-center">
<h3>Pesan Bagi Guru Admin</h3>
<p>Masukan sangat dibutuhkan agar pengembangan website semakin baik</p>
<form id="contact-form" class="form-message" action="{{ route('contact.store') }}" method="post">
@csrf
<div class="form-results"></div>
<div class="form-group row fix-gutter-10">
<div class="form-field col-sm-6 gutter-10 form-m-bttm">
<input name="contact-name" type="text" placeholder="Nama *" class="form-control required">
</div>
<div class="form-field col-sm-6 gutter-10">
<input name="contact-email" type="email" placeholder="Email *" class="form-control required email">
</div>
</div>
<div class="form-group row fix-gutter-10">
<div class="form-field col-md-6 gutter-10 form-m-bttm">
<input name="contact-phone" type="text" placeholder="Nomor Telepon *" class="form-control required">
</div>
<div class="form-field col-md-6 gutter-10">
<input name="contact-subject" type="text" placeholder="Topik *" class="form-control required">
</div>
</div>
<div class="form-group row">
<div class="form-field col-md-12">
<textarea name="contact-message" placeholder="Masukan *" class="txtarea form-control required"></textarea>
</div>
</div>
<button type="submit" class="button solid-btn sb-h">Kirim</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const texts = [
"Anak bermain di taman", "Ibu sedang memasak", "Saya suka membaca",
"Dia sedang belajar", "Mobil merah impian", "membaca itu seru",
"Burung berkicau merdu", "Kucing lucu sekali", "Matematika tambah kurang",
"Sekolah perlu membaca", "Membaca buku pelajaran", "Olahraga sebelum belajar",
"Sapi makan rumput", "Sekolah setiap pagi", "Rajin pangkal pandai",
"Bunga mekar indah", "Menjaga kebersihan", "Menjadi pribadi yang baik",
"Teruslah belajar", "Cita-cita perlu digapai",
"Belajar sopan santun", "Membaca dengan rajin", "Belajar tatakrama"
];
function getRandomTexts(array, count) {
return array.sort(() => Math.random() - 0.5).slice(0, count);
}
const selectedTexts = getRandomTexts(texts, 12);
let index = 0, totalWords = 0, totalCorrectWords = 0;
let totalCorrectSentences = 0;
let startTime, totalTimeSpent = 0, timer;
let recognitionActive = false;
// Array untuk menyimpan akurasi per teks (untuk menghitung pemahaman)
let textAccuracies = [];
let predictions = [];
let actualLabels = [];
const randomTextElement = document.getElementById("randomText");
const recognizedTextElement = document.getElementById("recognizedText");
const recognizedWrap = document.getElementById("recognizedWrap");
const accuracyElement = document.getElementById("accuracy");
const comprehensionElement = document.getElementById("comprehensionLevel");
const timeTakenElement = document.getElementById("timeTaken");
const finalScoreElement = document.getElementById("finalScore");
const evaluationElement = document.getElementById("evaluation");
const resultSection = document.getElementById("result");
const startRecognitionButton = document.getElementById("startRecognition");
const countdownElement = document.getElementById("countdown");
const nextTextButton = document.getElementById("nextText");
let countdownStartTime;
const totalDuration = 120;
function startTimer() {
clearInterval(timer);
countdownStartTime = Date.now();
timer = setInterval(() => {
const elapsed = Math.floor((Date.now() - countdownStartTime) / 1000);
const remaining = Math.max(0, totalDuration - elapsed);
countdownElement.textContent = remaining;
if (remaining <= 0) {
stopRecognition();
clearInterval(timer);
calculateFinalScore();
}
}, 1000);
}
function loadNewText() {
if (index >= selectedTexts.length) {
stopRecognition();
clearInterval(timer);
calculateFinalScore();
return;
}
randomTextElement.textContent = selectedTexts[index];
index++;
startTime = Date.now();
recognizedTextElement.textContent = "...";
recognizedWrap.style.display = "none";
resultSection.style.display = "none";
nextTextButton.style.visibility = "hidden";
}
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
alert("Browser Anda tidak mendukung Web Speech API.");
return;
}
const recognition = new SpeechRecognition();
recognition.lang = "id-ID";
recognition.continuous = true;
recognition.interimResults = false;
startRecognitionButton.addEventListener("click", function () {
if (!recognitionActive) {
recognizedTextElement.textContent = "Mendengarkan...";
resultSection.style.display = "none";
startTimer();
recognition.start();
recognitionActive = true;
}
});
recognition.onresult = function (event) {
const transcript = event.results[event.results.length - 1][0].transcript.trim();
if (!transcript) return;
recognizedTextElement.textContent = transcript;
recognizedWrap.style.display = "block";
const originalText = randomTextElement.textContent;
const accuracy = calculateAccuracy(originalText, transcript);
accuracyElement.textContent = accuracy.toFixed(2);
const originalWords = originalText.split(" ");
totalWords += originalWords.length;
totalCorrectWords += (accuracy / 100) * originalWords.length;
// Simpan akurasi per teks untuk menghitung pemahaman
textAccuracies.push(accuracy);
const timeUsed = (Date.now() - startTime) / 1000;
totalTimeSpent += timeUsed;
// Untuk contoh, asumsikan label sebenarnya bervariasi
// Dalam implementasi nyata, ambil dari database atau input guru
// Misalnya, secara acak untuk simulasi: 70% Mahir (1), 30% Tidak Mahir (0)
actualLabels.push(Math.random() > 0.3 ? 1 : 0);
nextTextButton.style.visibility = 'visible';
};
recognition.onend = function () {
if (recognitionActive) {
recognition.start();
}
};
function stopRecognition() {
recognitionActive = false;
recognition.stop();
}
function calculateAccuracy(original, spoken) {
const originalWords = original.toLowerCase().split(" ");
const spokenWords = spoken.toLowerCase().split(" ");
let correctCount = 0;
originalWords.forEach((word, i) => {
if (spokenWords[i] && spokenWords[i] === word) {
correctCount++;
}
});
return (correctCount / originalWords.length) * 100;
}
function naiveBayesClassifier(accuracy, comprehension, timeTaken) {
const diskretisasi = (nilai, batas) => {
if (nilai >= batas[2]) return "tinggi";
if (nilai >= batas[1]) return "sedang";
return "rendah";
};
const accuracyLevel = diskretisasi(accuracy, [0, 50, 80]);
const comprehensionLevel = diskretisasi(comprehension, [0, 50, 80]);
const timeLevel = timeTaken <= 40 ? "cepat" : timeTaken <= 80 ? "sedang" : "lambat";
const model = {
mahir: {
accuracy: { tinggi: 0.7, sedang: 0.2, rendah: 0.1 },
comprehension: { tinggi: 0.6, sedang: 0.3, rendah: 0.1 },
time: { cepat: 0.6, sedang: 0.3, lambat: 0.1 },
prior: 0.5
},
tidak_mahir: {
accuracy: { tinggi: 0.2, sedang: 0.5, rendah: 0.3 },
comprehension: { tinggi: 0.2, sedang: 0.5, rendah: 0.3 },
time: { cepat: 0.2, sedang: 0.4, lambat: 0.4 },
prior: 0.5
}
};
const hitungProbabilitas = (kelas) => {
const data = model[kelas];
return (
data.accuracy[accuracyLevel] *
data.comprehension[comprehensionLevel] *
data.time[timeLevel] *
data.prior
);
};
const pMahir = hitungProbabilitas("mahir");
const pTidakMahir = hitungProbabilitas("tidak_mahir");
return pMahir > pTidakMahir ? "Mahir" : "Tidak Mahir";
}
function calculateConfusionMatrix(predictions, actualLabels) {
let TP = 0, TN = 0, FP = 0, FN = 0;
for (let i = 0; i < predictions.length; i++) {
if (predictions[i] === 1 && actualLabels[i] === 1) TP++; // True Positive
else if (predictions[i] === 0 && actualLabels[i] === 0) TN++; // True Negative
else if (predictions[i] === 1 && actualLabels[i] === 0) FP++; // False Positive
else if (predictions[i] === 0 && actualLabels[i] === 1) FN++; // False Negative
}
return { TP, TN, FP, FN };
}
function calculateMetrics(TP, TN, FP, FN) {
const accuracy = (TP + TN + FP + FN) > 0 ? (TP + TN) / (TP + TN + FP + FN) * 100 : 0;
const precision = (TP + FP) > 0 ? (TP / (TP + FP)) * 100 : 0;
const recall = (TP + FN) > 0 ? (TP / (TP + FN)) * 100 : 0;
return { accuracy, precision, recall };
}
function calculateFinalScore() {
// Hitung akurasi rata-rata
let avgAccuracy = totalWords > 0 ? (totalCorrectWords / totalWords) * 100 : 0;
// Hitung tingkat pemahaman berdasarkan rata-rata akurasi per teks
let comprehensionScore = textAccuracies.length > 0
? textAccuracies.reduce((sum, acc) => sum + acc, 0) / textAccuracies.length
: 0;
let totalTimeUsed = parseFloat(totalTimeSpent.toFixed(2));
// Hitung prediksi menggunakan nilai akhir
let evaluation = naiveBayesClassifier(avgAccuracy, comprehensionScore, totalTimeUsed);
let finalScore = evaluation === "Mahir" ? 1 : 0;
// Isi predictions berdasarkan evaluasi akhir
predictions = Array(textAccuracies.length).fill(finalScore);
accuracyElement.textContent = avgAccuracy.toFixed(2);
comprehensionElement.textContent = comprehensionScore.toFixed(2);
timeTakenElement.textContent = totalTimeUsed;
finalScoreElement.textContent = finalScore;
evaluationElement.textContent = evaluation;
resultSection.style.display = "block";
// Hitung confusion matrix
const { TP, TN, FP, FN } = calculateConfusionMatrix(predictions, actualLabels);
// Hitung metrik
const { accuracy, precision, recall } = calculateMetrics(TP, TN, FP, FN);
// Tampilkan metrik di HTML
const metricsDiv = document.createElement('div');
metricsDiv.innerHTML = `
<h3>Metrik Evaluasi Model</h3>
<p><strong>Akurasi:</strong> ${accuracy.toFixed(2)}%</p>
<p><strong>Presisi:</strong> ${precision.toFixed(2)}%</p>
<p><strong>Recall:</strong> ${recall.toFixed(2)}%</p>
`;
resultSection.appendChild(metricsDiv);
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
const userId = document.querySelector('meta[name="user-id"]')?.getAttribute('content');
if (csrfToken && userId) {
fetch('http://127.0.0.1:8000/admin/riwayat/store', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({
user_id: userId,
nilai_akhir: finalScore,
tingkat_pemahaman: comprehensionScore,
akurasi: avgAccuracy,
waktu_mengerjakan: totalTimeUsed,
evaluasi: evaluation
})
})
.then(response => response.json())
.then(data => {
Swal.fire({
title: 'Berhasil!',
text: 'Hasil membaca berhasil disimpan!',
icon: 'success',
confirmButtonText: 'OK'
});
})
.catch(error => console.error('Error:', error));
} else {
console.error("CSRF token atau User ID tidak ditemukan.");
}
}
nextTextButton.addEventListener("click", function () {
nextTextButton.style.visibility = 'hidden';
loadNewText();
});
loadNewText();
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function () {
let index = 0;
const videos = document.querySelectorAll(".video-slide");
const totalVideos = videos.length;
function showVideo(i) {
videos.forEach(video => {
video.style.display = "none";
video.style.opacity = "0";
});
videos[i].style.display = "block";
setTimeout(() => {
videos[i].style.opacity = "1";
}, 50);
videos[i].play();
}
document.getElementById("prev").addEventListener("click", function () {
index = (index - 1 + totalVideos) % totalVideos;
showVideo(index);
});
document.getElementById("next").addEventListener("click", function () {
index = (index + 1) % totalVideos;
showVideo(index);
});
showVideo(index);
});
</script>
<script>
function fetchMessages() {
fetch("{{ route('messages.index') }}")
.then(response => response.json())
.then(data => {
let messageDropdown = document.querySelector("#messageDropdown .dropdown-menu");
let badge = document.querySelector("#messageDropdown .badge");
messageDropdown.innerHTML = ""; // Kosongkan daftar sebelumnya
if (data.length > 0) {
badge.textContent = data.length; // Update badge count
data.forEach(message => {
let item = `<a class="dropdown-item">
<strong>${message.name}</strong><br>
${message.message.substring(0, 50)}...
<small class="text-muted d-block">${message.created_at}</small>
</a>
<div class="dropdown-divider"></div>`;
messageDropdown.innerHTML += item;
});
} else {
badge.style.display = "none";
messageDropdown.innerHTML = `<a class="dropdown-item text-center">Tidak ada pesan</a>`;
}
});
}
// Panggil setiap 10 detik
setInterval(fetchMessages, 10000);
</script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
$(document).ready(function() {
$('#contact-form').submit(function(e) {
e.preventDefault();
$.ajax({
url: "{{ route('contact.store') }}",
type: "POST",
data: $(this).serialize(),
dataType: "json",
success: function(response) {
if (response.success) {
// Tampilkan notifikasi sukses
Swal.fire({
icon: 'success',
title: 'Pesan Terkirim!',
text: response.message,
timer: 2000,
showConfirmButton: false
});
// Reset form setelah submit berhasil
$('#contact-form')[0].reset();
// Perbarui daftar pesan di navbar
loadMessages();
}
},
error: function(xhr) {
// Tampilkan notifikasi error jika ada masalah
Swal.fire({
icon: 'error',
title: 'Oops...',
text: 'Terjadi kesalahan! Coba lagi.'
});
}
});
});
function loadMessages() {
$.ajax({
url: "{{ route('messages.index') }}",
type: "GET",
success: function(response) {
$('#messageDropdown .dropdown-menu').html(response);
}
});
}
});
</script>
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4.1.1/dist/tesseract.min.js"></script>
<script>
// Video Slider
const slider = document.getElementById('videoSlider');
const slides = document.querySelectorAll('.video-slide');
const prev = document.getElementById('prev');
const next = document.getElementById('next');
const videoError = document.getElementById('videoError');
let currentSlide = 0;
// Cek apakah video dapat dimuat
slides.forEach((slide, index) => {
const source = slide.querySelector('source');
const videoPath = source.getAttribute('src');
const video = document.createElement('video');
video.src = videoPath;
video.onloadeddata = () => {
console.log(`Video ${videoPath} dimuat dengan sukses.`);
};
video.onerror = () => {
videoError.innerText = `Gagal memuat video: ${videoPath}. Pastikan file ada di folder yang benar.`;
};
});
function updateSlider() {
// Hentikan semua video dan reset ke awal
slides.forEach(slide => {
slide.pause();
slide.currentTime = 0;
slide.style.display = 'none'; // Pastikan slide non-aktif disembunyikan
slide.style.visibility = 'hidden';
});
// Tampilkan slide aktif
slides[currentSlide].style.display = 'block';
slides[currentSlide].style.visibility = 'visible';
slides[currentSlide].classList.add('active');
// Geser slider
slider.style.transform = `translateX(0)`; // Tidak perlu translateX karena hanya satu slide yang terlihat
// Mainkan video aktif
slides[currentSlide].play().catch(err => {
console.error('Error playing video:', err);
videoError.innerText = 'Gagal memutar video. Pastikan format didukung.';
});
console.log(`Current slide: ${currentSlide}, Video: ${slides[currentSlide].querySelector('source').getAttribute('src')}`);
}
next.addEventListener('click', () => {
slides[currentSlide].classList.remove('active');
currentSlide = (currentSlide + 1) % slides.length;
updateSlider();
});
prev.addEventListener('click', () => {
slides[currentSlide].classList.remove('active');
currentSlide = (currentSlide - 1 + slides.length) % slides.length;
updateSlider();
});
// Inisialisasi slider
updateSlider();
// Canvas Drawing
const canvas = document.getElementById('writingCanvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
function startDrawing(e) {
isDrawing = true;
ctx.beginPath();
ctx.moveTo(e.offsetX, e.offsetY);
}
function draw(e) {
if (!isDrawing) return;
ctx.lineWidth = 10;
ctx.lineCap = 'round';
ctx.strokeStyle = 'black';
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(e.offsetX, e.offsetY);
}
function stopDrawing() {
isDrawing = false;
ctx.beginPath();
}
// Clear Canvas
document.getElementById('clearCanvas').addEventListener('click', () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
document.getElementById('recognitionResult').innerText = '';
});
// Image Preprocessing
function preprocessImage(canvas) {
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
tempCtx.drawImage(canvas, 0, 0);
const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
const data = imageData.data;
// Konversi ke grayscale
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = data[i + 1] = data[i + 2] = avg;
}
// Thresholding untuk meningkatkan kontras
const threshold = 200;
for (let i = 0; i < data.length; i += 4) {
const value = data[i] < threshold ? 0 : 255;
data[i] = data[i + 1] = data[i + 2] = value;
}
tempCtx.putImageData(imageData, 0, 0);
return tempCanvas.toDataURL('image/png');
}
// Detect Writing
document.getElementById('detectButton').addEventListener('click', () => {
const imageUrl = preprocessImage(canvas);
Tesseract.recognize(
imageUrl,
'eng',
{
logger: m => console.log(m),
tessedit_char_whitelist: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ',
tessedit_pageseg_mode: 6,
tessedit_ocr_engine_mode: 1,
}
).then(({ data: { text } }) => {
const cleanedText = text.trim();
document.getElementById('recognitionResult').innerText = cleanedText
? `Hasil Deteksi: ${cleanedText}`
: 'Tidak dapat mendeteksi tulisan. Tulis dengan lebih jelas.';
}).catch(err => {
console.error('OCR Error:', err);
document.getElementById('recognitionResult').innerText = 'Gagal mendeteksi. Coba lagi.';
});
});
</script>
@endsection
@section('footer')
<!-- Start .footer-section -->
<div class="footer-section section">
<div class="container">
<div class="row text-center">
<div class="col-md-12">
<ul class="footer-navigation inline-list">
<li><a class="nav-item" href="#home">Beranda</a></li>
<li><a class="nav-item" href="#huruf">Kenali Huruf</a></li>
<li><a class="nav-item" href="#about">Belajar</a></li>
<li><a class="nav-item" href="#features">Baca</a></li>
<li><a class="nav-item" href="#students">Riwayat</a></li>
<li><a class="nav-item" href="#another">Bantuan</a></li>
<li><a class="nav-item" href="#testimonial">Pesan</a></li>
</ul>
<ul class="social-list inline-list">
<li><a href="#"><em class="fa fa-facebook"></em></a></li>
<li><a href="#"><em class="fa fa-twitter"></em></a></li>
<li><a href="#"><em class="fa fa-linkedin"></em></a></li>
<li><a href="#"><em class="fa fa-instagram"></em></a></li>
</ul>
</div>
</div>
</div>
</div>
@endsection