Compare commits

..

31 Commits

Author SHA1 Message Date
Mahayoga 8e032dd1fb Merge pull request 'halaman-psikolog' (#1) from halaman-psikolog into master
Reviewed-on: #1
2026-07-06 12:37:22 +07:00
Mahayoga 1d8ade06f1 WIP: Bagian data mata bisa di downloaf 2026-06-13 20:56:20 +07:00
Mahayoga 043e2a81c0 WIP: Perbaikan validasi booking (belum mari) 2026-06-13 20:56:00 +07:00
Mahayoga a475b0a740 WIP: penambahan data soal 2026-05-30 22:04:55 +07:00
Mahayoga 7f6cf7dcd6 WIP: nambah parameter normal di halaman hasil ujian 2026-05-27 21:08:36 +07:00
Mahayoga d0bcdd2949 WIP: ngubah total score doang 2026-05-26 17:35:56 +07:00
Mahayoga 22f532cdd6 WIP: Overlay 2026-05-26 04:32:07 +07:00
Mahayoga 1357d96c78 WIP: confidence model 2026-05-25 20:51:17 +07:00
Mahayoga aebcdbb844 WIP: perbaikan data soal 2026-05-23 21:10:58 +07:00
Mahayoga 48c822c558 WIP: dashboard 2026-05-23 18:17:44 +07:00
Mahayoga e2954898e9 WIP: Halaman data hasil kecemasan 2026-05-23 15:55:58 +07:00
Mahayoga cc783674b6 In Progress: halaman psikolog bagian data soal 2026-05-21 03:29:03 +07:00
Mahayoga f11c3a879d WIP: Auth page 2026-05-21 00:27:11 +07:00
Mahayoga d07e24e39b WIP: perbaikan reverse soal dan jawaban 2026-05-20 12:25:14 +07:00
Mahayoga 6c41ac4cf3 WIP: Halaman hasil ujian 2026-05-20 11:23:50 +07:00
Mahayoga 01258eb92d WIP: Statistik mata 2026-05-19 22:36:49 +07:00
Mahayoga 9a895c57ba In Progress: adding screen dimension to db 2026-05-19 19:21:01 +07:00
Mahayoga 3b6968c2ae WIP: Fix landing page 2026-05-19 04:36:03 +07:00
Mahayoga 057cdaffa9 WIP: email booking 2026-05-17 20:41:08 +07:00
Mahayoga feb7916fff WIP: perbaikan sistem booking 2026-05-17 16:32:25 +07:00
Mahayoga d88b961e4b Perbaikan websokect dan lainnya 2026-05-17 00:45:09 +07:00
Mahayoga 46040fa886 Done: bagian halaman ujian only 2026-05-11 14:53:56 +07:00
Mahayoga fbb03ba9fe FIX: titik titik kalibrasi 2026-05-09 10:14:07 +07:00
Mahayoga 2a9dbe5624 WIP: halam ujian ajax only 2026-05-08 05:42:48 +07:00
Mahayoga 731e95a864 WIP: validasi fullscreen 2026-05-07 03:37:04 +07:00
Mahayoga f896e54d34 Done: Appointment tanpa konfirmasi psikolog 2026-05-07 02:41:12 +07:00
Mahayoga b2dbb09951 WIP: grafik heatmap pake overlay 2026-05-07 01:59:35 +07:00
Mahayoga 2e56c45db9 WIP: fetch data dari FastAPI lewat WebSocket 2026-05-06 04:23:21 +07:00
Mahayoga 1821b6f59e WIP: halaman finish ujian 2026-05-04 21:18:37 +07:00
Mahayoga b1f5b14287 WIP: halaman finish ujian 2026-05-04 11:04:29 +07:00
Mahayoga f18c470f14 WIP: halaman ujian (mayan progress) 2026-04-29 02:37:26 +07:00
56 changed files with 13056 additions and 1149 deletions

View File

@ -28,7 +28,7 @@ public function create(Request $request): View
* *
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
*/ */
public function store(Request $request): RedirectResponse public function store(Request $request)
{ {
$request->validate([ $request->validate([
'token' => ['required'], 'token' => ['required'],
@ -51,12 +51,17 @@ function (User $user) use ($request) {
} }
); );
return response()->json([
'status' => $status == Password::PASSWORD_RESET,
'msg' => $status == Password::PASSWORD_RESET ? 'Password reset successful' : 'Password reset failed'
]);
// If the password was successfully reset, we will redirect the user back to // If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can // the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message. // redirect them back to where they came from with their error message.
return $status == Password::PASSWORD_RESET // return $status == Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status)) // ? redirect()->route('login')->with('status', __($status))
: back()->withInput($request->only('email')) // : back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]); // ->withErrors(['email' => __($status)]);
} }
} }

View File

@ -23,7 +23,7 @@ public function create(): View
* *
* @throws \Illuminate\Validation\ValidationException * @throws \Illuminate\Validation\ValidationException
*/ */
public function store(Request $request): RedirectResponse public function store(Request $request)
{ {
$request->validate([ $request->validate([
'email' => ['required', 'email'], 'email' => ['required', 'email'],
@ -36,9 +36,14 @@ public function store(Request $request): RedirectResponse
$request->only('email') $request->only('email')
); );
return $status == Password::RESET_LINK_SENT return response()->json([
? back()->with('status', __($status)) 'status' => $status == Password::RESET_LINK_SENT,
: back()->withInput($request->only('email')) 'msg' => __($status)
->withErrors(['email' => __($status)]); ]);
// return $status == Password::RESET_LINK_SENT
// ? back()->with('status', __($status))
// : back()->withInput($request->only('email'))
// ->withErrors(['email' => __($status)]);
} }
} }

View File

@ -2,9 +2,13 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Mail\SendUniqueCode;
use App\Models\BookingModel; use App\Models\BookingModel;
use App\Models\TestSessionsModel;
use Carbon\Carbon;
use Generator; use Generator;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class BookingController extends Controller class BookingController extends Controller
{ {
@ -13,6 +17,28 @@ class BookingController extends Controller
*/ */
public function getchData() { public function getchData() {
$data = BookingModel::get(); $data = BookingModel::get();
$data_ = $data->toArray();
foreach($data_ as $key => $value) {
if(time() > $value['unix_expired_time'] && $value['status_tes'] != '2') {
$data_session = TestSessionsModel::where('id_booking', $value['id_booking'])->first();
$data_booking = BookingModel::where('id_booking', $value['id_booking'])->first();
if($data_session != null) {
$data_session->update([
'status_session' => '2',
'is_active' => '0'
]);
$data_booking->update([
'status_tes' => '2'
]);
} else {
$data_booking->update([
'status_tes' => '0'
]);
}
}
}
$data = BookingModel::orderBy('updated_at', 'desc')->get();
return response()->json([ return response()->json([
'status' => true, 'status' => true,
@ -74,6 +100,7 @@ public function store(Request $request)
]); ]);
$kode_unik = $this->generateCode(); $kode_unik = $this->generateCode();
$tgl_request = Carbon::parse($request->tgl_booking)->timestamp + 86399;
BookingModel::create([ BookingModel::create([
'nama_lengkap' => $request->nama_lengkap, 'nama_lengkap' => $request->nama_lengkap,
@ -81,9 +108,18 @@ public function store(Request $request)
'no_telp' => $request->no_telp, 'no_telp' => $request->no_telp,
'tgl_booking' => $request->tgl_booking, 'tgl_booking' => $request->tgl_booking,
'deskripsi_tambahan' => $request->deskripsi_tambahan, 'deskripsi_tambahan' => $request->deskripsi_tambahan,
'kode_unik' => $kode_unik 'kode_unik' => $kode_unik,
'unix_expired_time' => $tgl_request
]); ]);
Mail::to($request->email)->send(new SendUniqueCode(
$request->nama_lengkap,
$kode_unik,
date('H:i:s', $tgl_request - 86399),
date('H:i:s', $tgl_request),
$request->tgl_booking,
));
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'msg' => 'Booking success!' 'msg' => 'Booking success!'
@ -130,8 +166,10 @@ public function update(Request $request, string $id)
'status_tes' => ['required'] 'status_tes' => ['required']
]); ]);
BookingModel::where('id_booking', $id)->update([ $data = BookingModel::where('id_booking', $id)->first();
'status_tes' => $request->status_tes
$data->update([
'status_tes' => str($request->status_tes)
]); ]);
return response()->json([ return response()->json([

View File

@ -38,6 +38,19 @@ public function show(string $id)
{ {
$data = BookingModel::where('kode_unik', $id)->first(); $data = BookingModel::where('kode_unik', $id)->first();
if($data != null) { if($data != null) {
if($data->status_tes == '1' && $data->unix_expired_time < time()) { // Lebih dari exp
$data->update([
'status_tes' => '0'
]);
} else if($data->status_tes == '1' && time() < ($data->unix_expired_time - 86399)) { // Kurang dari exp 00.00
$data->update([
'status_tes' => '0'
]);
} else if($data->status_tes == '0' && time() > ($data->unix_expired_time - 86399) && time() < ($data->unix_expired_time)) {
$data->update([
'status_tes' => '1'
]);
}
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'msg' => 'Data ditemukan (' . $id . ')', 'msg' => 'Data ditemukan (' . $id . ')',
@ -46,6 +59,8 @@ public function show(string $id)
'nama_lengkap' => $data->nama_lengkap, 'nama_lengkap' => $data->nama_lengkap,
'kode_unik' => $data->kode_unik, 'kode_unik' => $data->kode_unik,
'tgl_booking' => $data->tgl_booking, 'tgl_booking' => $data->tgl_booking,
'start_test' => date('H:i:s', $data->unix_expired_time - 86399),
'end_test' => date('H:i:s', $data->unix_expired_time),
'deskripsi_tambahan' => $data->deskripsi_tambahan, 'deskripsi_tambahan' => $data->deskripsi_tambahan,
]); ]);
} else { } else {

View File

@ -2,10 +2,90 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\BookingModel;
use App\Models\LogKecemasanModel;
use App\Models\TestSessionsModel;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class DashboardController extends Controller class DashboardController extends Controller
{ {
public function getDataForDashboardTable()
{
$data_booking = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m-d') . '%')->get();
$result = [];
foreach ($data_booking as $booking) {
$status_session = '0';
$data_sessions = TestSessionsModel::where('id_booking', $booking->id_booking)->first();
if($data_sessions != null) {
$status_session = $data_sessions->status_session;
}
$result[] = [
'nama_lengkap' => $booking->nama_lengkap,
'email' => $booking->email,
'no_telp' => $booking->no_telp,
'tanggal_booking' => $booking->created_at->format('Y-m-d'),
'status_session' => $status_session,
'status_tes' => $booking->status_tes,
];
}
return response()->json([
'status' => true,
'data' => $result
]);
}
public function getDataDashboard()
{
$data_booking_today = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m-d') . '%')->count();
$data_booking_yesterday = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m-d', strtotime('-1 day')) . '%')->count();
$data_booking_month = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m') . '%')->count();
$booking_diff = $data_booking_month - $data_booking_today;
$booking_percent = $data_booking_month > 0 ? ($booking_diff / $data_booking_month) * 100 : 0;
$data_tes_today = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m-d') . '%')->where('status_tes', '2')->count();
$data_tes_yesterday = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m-d', strtotime('-1 day')) . '%')->where('status_tes', '2')->count();
$data_tes_month = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m') . '%')->where('status_tes', '2')->count();
$tes_diff = $data_tes_month - $data_tes_today;
$tes_percent = $data_tes_month > 0 ? ($tes_diff / $data_tes_month) * 100 : 0;
$data_tes_berlangsung_today = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m-d') . '%')->where('status_tes', '1')->count();
$data_tes_berlangsung_yesterday = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m-d', strtotime('-1 day')) . '%')->where('status_tes', '1')->count();
$data_tes_berlangsung_month = BookingModel::where('created_at', 'LIKE', '%' . date('Y-m') . '%')->where('status_tes', '1')->count();
$tes_berlangsung_diff = $data_tes_berlangsung_month - $data_tes_berlangsung_today;
$tes_berlangsung_percent = $data_tes_berlangsung_month > 0 ? ($tes_berlangsung_diff / $data_tes_berlangsung_month) * 100 : 0;
$data_rata_rata_kecemasan_today = LogKecemasanModel::where('created_at', 'LIKE', '%' . date('Y-m-d') . '%')->avg('skor_kecemasan');
$data_rata_rata_kecemasan_yesterday = LogKecemasanModel::where('created_at', 'LIKE', '%' . date('Y-m-d', strtotime('-1 day')) . '%')->avg('skor_kecemasan');
$data_rata_rata_kecemasan_month = LogKecemasanModel::where('created_at', 'LIKE', '%' . date('Y-m') . '%')->avg('skor_kecemasan');
$rata_rata_kecemasan_diff = $data_rata_rata_kecemasan_month - $data_rata_rata_kecemasan_today;
$rata_rata_kecemasan_percent = $data_rata_rata_kecemasan_month > 0 ? ($rata_rata_kecemasan_diff / $data_rata_rata_kecemasan_month) * 100 : 0;
return response()->json([
'total_booking' => $data_booking_today,
'booking_percent' => number_format($booking_percent, 1),
'booking_diff' => $booking_diff,
'booking_trend' => $booking_diff >= 0 ? 'bertambah' : 'berkurang',
'from_month_booking_count' => $data_booking_month,
'total_tes' => $data_tes_today,
'tes_percent' => number_format($tes_percent, 1),
'tes_diff' => $tes_diff,
'tes_trend' => $tes_diff >= 0 ? 'bertambah' : 'berkurang',
'from_month_tes_count' => $data_tes_month,
'total_tes_berlangsung' => $data_tes_berlangsung_today,
'tes_berlangsung_percent' => number_format($tes_berlangsung_percent, 1),
'tes_berlangsung_diff' => $tes_berlangsung_diff,
'tes_berlangsung_trend' => $tes_berlangsung_diff >= 0 ? 'bertambah' : 'berkurang',
'from_month_tes_berlangsung_count' => $data_tes_berlangsung_month,
'rata_rata_kecemasan' => number_format($data_rata_rata_kecemasan_today, 1),
'rata_rata_kecemasan_percent' => number_format($rata_rata_kecemasan_percent, 1),
'rata_rata_kecemasan_diff' => number_format($rata_rata_kecemasan_diff, 1),
'rata_rata_kecemasan_trend' => $rata_rata_kecemasan_diff >= 0 ? 'bertambah' : 'berkurang',
'from_month_rata_rata_kecemasan' => number_format($data_rata_rata_kecemasan_month, 1),
]);
}
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */

View File

@ -14,7 +14,7 @@ class DataSoalController extends Controller
public function getData() { public function getData() {
$data = DataSoalModel::get(); $data = DataSoalModel::get();
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'data' => $data->toArray() 'data' => $data->toArray()
]); ]);
} }
@ -46,7 +46,7 @@ public function store(Request $request)
'jawaban_c' => $request->jawaban_c, 'jawaban_c' => $request->jawaban_c,
'jawaban_d' => $request->jawaban_d, 'jawaban_d' => $request->jawaban_d,
'jawaban_e' => $request->jawaban_e, 'jawaban_e' => $request->jawaban_e,
'jawaban_benar' => $request->jawaban_benar, 'is_reverse' => $request->is_reverse,
]); ]);
return response()->json([ return response()->json([
@ -91,7 +91,7 @@ public function update(Request $request, string $id)
'jawaban_c' => $request->jawaban_c, 'jawaban_c' => $request->jawaban_c,
'jawaban_d' => $request->jawaban_d, 'jawaban_d' => $request->jawaban_d,
'jawaban_e' => $request->jawaban_e, 'jawaban_e' => $request->jawaban_e,
'jawaban_benar' => $request->jawaban_benar, 'is_reverse' => $request->is_reverse,
]); ]);
return response()->json([ return response()->json([

View File

@ -0,0 +1,283 @@
<?php
namespace App\Http\Controllers;
use App\Models\BookingModel;
use App\Models\DataSoalModel;
use App\Models\DetailTestModel;
use App\Models\LogKecemasanModel;
use App\Models\TestSessionsModel;
use Illuminate\Http\Request;
class FinishUjianController extends Controller
{
public function getNotAnswered(Request $request) {
$data_session = TestSessionsModel::where('id_test_sessions', $request->id_test_sessions)->first();
if($data_session != null) {
if($data_session->status_session == '0') {
return response()->json([
'status' => false,
'message' => 'Data sesi tes dinonaktifkan. Silahkan cek status dengan kode untuk informasi lebih lanjut.'
]);
} else if($data_session->status_session == '2') {
$soal = DataSoalModel::all();
$data_ujian = [];
foreach($soal as $detail) {
$data_soal = DetailTestModel::where('id_soal', $detail->id_soal)->where('id_test_sessions', $request->id_test_sessions)->first();
if($data_soal == null) {
$data_ujian[] = [
'id_soal' => $detail->id_soal,
'soal' => $detail->pertanyaan,
'skor' => 0
];
}
}
return response()->json([
'status' => true,
'data' => $data_ujian
]);
}
} else {
return response()->json([
'status' => false,
'message' => 'Data sesi tes tidak ditemukan.'
]);
}
}
public function getAllAnswer(Request $request) {
$data_session = TestSessionsModel::where('id_test_sessions', $request->id_test_sessions)->first();
if($data_session != null) {
if($data_session->status_session == '0') {
return response()->json([
'status' => false,
'message' => 'Data sesi tes dinonaktifkan. Silahkan cek status dengan kode untuk informasi lebih lanjut.'
]);
} else if($data_session->status_session == '2') {
$detail_test = DetailTestModel::where('id_test_sessions', $request->id_test_sessions)->get();
$data_ujian = [];
foreach($detail_test as $detail) {
$data_soal = DataSoalModel::where('id_soal', $detail->id_soal)->first();
$data_jawaban = $detail->jawaban;
$skor = null;
if($data_jawaban == 'a') {
$data_jawaban = $data_soal->jawaban_a;
$skor = 1;
} else if($data_jawaban == 'b') {
$data_jawaban = $data_soal->jawaban_b;
$skor = 2;
} else if($data_jawaban == 'c') {
$data_jawaban = $data_soal->jawaban_c;
$skor = 3;
} else if($data_jawaban == 'd') {
$data_jawaban = $data_soal->jawaban_d;
$skor = 4;
} else if($data_jawaban == 'e') {
$data_jawaban = $data_soal->jawaban_e;
} else {
$data_jawaban = 'Tidak menjawab';
}
if($detail->jawaban == 'a' && $data_soal->is_reverse == 1) {
$skor = 4;
} else if($detail->jawaban == 'b' && $data_soal->is_reverse == 1) {
$skor = 3;
} else if($detail->jawaban == 'c' && $data_soal->is_reverse == 1) {
$skor = 2;
} else if($detail->jawaban == 'd' && $data_soal->is_reverse == 1) {
$skor = 1;
}
$data_ujian[] = [
'id_soal' => $detail->id_soal,
'jawaban' => $data_jawaban,
'is_reverse' => $data_soal->is_reverse,
'skor' => $skor,
'jawaban_key' => $detail->jawaban,
'soal' => $data_soal->pertanyaan
];
}
return response()->json([
'status' => true,
'data' => $data_ujian
]);
}
} else {
return response()->json([
'status' => false,
'message' => 'Data sesi tes tidak ditemukan.'
]);
}
}
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$id_session = null;
if(session('id_test_sessions') != null) {
$id_session = session('id_test_sessions');
} else if($request->id_test_sessions != null) {
$id_session = $request->id_test_sessions;
} else {
return redirect()->route('cek-status.index')->with('error', 'Data sesi tes tidak ditemukan. Silakan mulai tes terlebih dahulu.');
}
$data_session = TestSessionsModel::where('id_test_sessions', $id_session)->first();
if($data_session != null) {
$start = strtotime($data_session->created_at);
$end = strtotime($data_session->updated_at);
$difference = gmdate("i:s", $end - $start);
$data_booking = BookingModel::where('id_booking', $data_session->id_booking)->first();
if($data_booking != null) {
if($data_booking->status_tes == '0') {
return redirect()->route('cek-status.index')->with('error', 'Data sesi tes tidak ditemukan atau telah dinonaktifkan.');
}
}
$data_information = [
'kode' => $data_booking->kode_unik,
'nama' => $data_booking->nama_lengkap,
'email' => $data_booking->email,
'no_hp' => $data_booking->no_telp,
];
$screen_dim = [
'width' => $data_session->screen_w,
'height' => $data_session->screen_h
];
$answered = DetailTestModel::where('id_test_sessions', $id_session)->where('jawaban', '!=', null)->count();
$question_count = DataSoalModel::all()->count();
$soalTerjawab = $answered;
$soalTidakTerjawab = $question_count - $answered;
$totalScore = 0;
$detail_test = DetailTestModel::where('id_test_sessions', $request->id_test_sessions)->get();
foreach($detail_test as $detail) {
$data_soal = DataSoalModel::where('id_soal', $detail->id_soal)->first();
$data_jawaban = $detail->jawaban;
if($data_soal->is_reverse == 1) {
if($data_jawaban == 'a') {
$totalScore += 4;
} else if($data_jawaban == 'b') {
$totalScore += 3;
} else if($data_jawaban == 'c') {
$totalScore += 2;
} else if($data_jawaban == 'd') {
$totalScore += 1;
} else if($data_jawaban == 'e') {
$totalScore += 0;
}
} else {
if($data_jawaban == 'a') {
$totalScore += 1;
} else if($data_jawaban == 'b') {
$totalScore += 2;
} else if($data_jawaban == 'c') {
$totalScore += 3;
} else if($data_jawaban == 'd') {
$totalScore += 4;
} else if($data_jawaban == 'e') {
$totalScore += 5;
}
}
}
$data_aswered = [
'total_question' => $question_count,
'answered' => $answered,
'unanswered' => $soalTidakTerjawab,
'totalScore' => $totalScore
];
$log_kecemasan = LogKecemasanModel::where('id_test_sessions', $id_session)->first();
if($log_kecemasan == null) {
LogKecemasanModel::create([
'id_booking' => $data_session->id_booking,
'id_test_sessions' => $id_session,
'start_timestamp' => $start,
'end_timestamp' => $end,
'difference_timestamp' => $end - $start,
'screen_width' => $data_session->screen_w,
'screen_height' => $data_session->screen_h,
'answered_count' => $answered,
'not_answered_count' => $soalTidakTerjawab,
'total_question' => $question_count,
'skor_kecemasan' => $totalScore
]);
}
$data_booking = BookingModel::where('id_booking', $data_session->id_booking)->first();
if($data_session->status_session == '0') {
return redirect()->route('cek-status.index')->with('error', 'Data sesi tes dinonaktifkan. Silahkan cek status dengan kode untuk informasi lebih lanjut.');
} else if($data_session->status_session == '1') {
$data_session->update([
'status_session' => '2',
'is_active' => '0'
]);
$data_booking->update([
'status_tes' => '2'
]);
return view('user.pages.finish_ujian.index', compact('id_session', 'difference', 'data_information', 'screen_dim', 'data_aswered'));
} else if($data_session->status_session == '2') {
$data_booking->update([
'status_tes' => '2'
]);
return view('user.pages.finish_ujian.index', compact('id_session', 'difference', 'data_information', 'screen_dim', 'data_aswered'));
}
} else {
return redirect()->route('cek-status.index')->with('error', 'Data sesi tes tidak ditemukan.');
}
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
}
}

View File

@ -2,12 +2,176 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\BookingModel;
use App\Models\DataSoalModel; use App\Models\DataSoalModel;
use App\Models\DetailTestModel;
use App\Models\TestSessionsModel; use App\Models\TestSessionsModel;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class HalamanUjianController extends Controller class HalamanUjianController extends Controller
{ {
function fetchQuestion(Request $request) {
$data_question = DataSoalModel::all();
if($data_question != null) {
$number_question = $request->question_index;
if($number_question <= 0 || $number_question > count($data_question)) {
$number_question = 1;
}
return response()->json([
'status' => true,
'data' => $data_question[$number_question - 1]
]);
} else {
return response()->json([
'status' => false,
'message' => 'Data soal tidak ditemukan.'
]);
}
}
public function getCurrentAnswer(Request $request) {
$data_choice = DataSoalModel::where('id_soal', $request->id_question)->get();
$choices = [];
if($data_choice != null) {
foreach($data_choice as $choice) {
if($choice->jawaban_a != null) {
$choices[] = [
'key' => 'a',
'value' => $choice->jawaban_a
];
}
if($choice->jawaban_b != null) {
$choices[] = [
'key' => 'b',
'value' => $choice->jawaban_b
];
}
if($choice->jawaban_c != null) {
$choices[] = [
'key' => 'c',
'value' => $choice->jawaban_c
];
}
if($choice->jawaban_d != null) {
$choices[] = [
'key' => 'd',
'value' => $choice->jawaban_d
];
}
if($choice->jawaban_e != null) {
$choices[] = [
'key' => 'e',
'value' => $choice->jawaban_e
];
}
}
} else {
return response()->json([
'status' => false,
'jawaban' => null,
'choices' => null
]);
}
$data_answer = DetailTestModel::where('id_soal', $request->id_question)
->where('id_test_sessions', $request->session_id)
->first();
if($data_answer != null) {
return response()->json([
'status' => true,
'jawaban' => $data_answer->jawaban,
'choices' => $choices
]);
} else {
return response()->json([
'status' => true,
'jawaban' => null,
'choices' => $choices
]);
}
}
public function getNavigationField(Request $request) {
$id_session = null;
if(session('session_id') != null) {
$id_session = session('session_id');
} else if($request->session_id != null) {
$id_session = $request->session_id;
} else {
return response()->json([
'status' => false,
'msg' => 'Test session id tidak ditemukan'
]);
}
$data_soal = DataSoalModel::all();
$result = [];
$index = 1;
foreach($data_soal as $soal) {
$data_jawaban = DetailTestModel::where(['id_soal' => $soal->id_soal, 'id_test_sessions' => $id_session])->first();
if($data_jawaban != null) {
$result[] = [
'index' => $index,
'answered' => true,
'data' => $data_jawaban
];
} else {
$result[] = [
'index' => $index,
'answered' => false
];
}
$index += 1;
}
return response()->json([
'status' => true,
'data' => $result
]);
}
public function getTimeLeft(Request $request) {
$data_session = TestSessionsModel::where('id_test_sessions', $request->session_id)->first();
if($data_session != null) {
if($data_session->status_session != '1') {
return response()->json([
'status' => 'error',
'message' => 'Data sesi tes belum dimulai atau telah usai. Silahkan cek status dengan kode untuk informasi lebih lanjut.'
]);
} else if($data_session->status_session == '1') {
if($data_session->is_active == '1') {
$time_left = $data_session->unix_end_time - time();
if($time_left <= 0) {
$data_session->update([
'status_session' => '2',
'is_active' => '0'
]);
return response()->json([
'status' => 'error',
'message' => 'Waktu sesi tes telah habis. Silahkan cek status dengan kode untuk informasi lebih lanjut.'
]);
} else {
return response()->json([
'status' => 'success',
'time_left' => $data_session->unix_end_time
]);
}
} else {
return response()->json([
'status' => 'error',
'message' => 'Sesi tes belum aktif. Silahkan mulai tes terlebih dahulu.'
]);
}
}
} else {
return response()->json([
'status' => 'error',
'message' => 'Data sesi tes tidak ditemukan. Silakan mulai tes terlebih dahulu.'
]);
}
}
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
@ -27,34 +191,112 @@ public function index(Request $request)
if($data_session != null) { if($data_session != null) {
if($data_session->status_session != '1') { if($data_session->status_session != '1') {
return redirect()->route('cek-status.index')->with('error', 'Data sesi tes belum dimulai atau telah usai. Silahkan cek status dengan kode untuk informasi lebih lanjut.'); return redirect()->route('cek-status.index')->with('error', 'Data sesi tes belum dimulai atau telah usai. Silahkan cek status dengan kode untuk informasi lebih lanjut.');
} } else if($data_session->status_session == '1') {
if($data_session->is_active == '0') {
$data_session->update([
'is_active' => '1',
'unix_start_time' => time(),
'unix_end_time' => time() + (20 * 60)
]);
} else if($data_session->is_active == '1') {
if(time() > $data_session->unix_end_time) {
$data_booking = BookingModel::where('id_booking', $data_session->id_booking)->first();
$data_session->update([
'status_session' => '2',
'is_active' => '0'
]);
$data_booking->update([
'status_tes' => '2'
]);
return redirect()->route('cek-status.index')->with('error', 'Waktu sesi tes telah habis. Silahkan cek status dengan kode untuk informasi lebih lanjut.');
}
}
}
} else { } else {
return redirect()->route('cek-status.index')->with('error', 'Data sesi tes tidak ditemukan. Silakan mulai tes terlebih dahulu.'); return redirect()->route('cek-status.index')->with('error', 'Data sesi tes tidak ditemukan. Silakan mulai tes terlebih dahulu.');
} }
$question_number = null; // $question_number = null;
$data_question = null; // $data_question = null;
if(session('question') != null) { // if(session('question') != null) {
$question_number = session('question'); // $question_number = session('question');
} else if($request->question != null) { // } else if($request->question != null) {
$question_number = $request->question; // $question_number = $request->question;
} else { // } else {
$question_number = 1; // $question_number = 1;
} // }
$data_question = DataSoalModel::all(); // $data_question = DataSoalModel::all();
$total_question = null; // $answered_question = [];
$is_last_question = false; // $index_question = 1;
if($question_number <= 0 || $question_number > count($data_question)) { // foreach($data_question as $detail) {
$question_number = 1; // $detail_answer = DetailTestModel::where('id_soal', $detail->id_soal)
} // ->where('id_test_sessions', $id_session)
if(count($data_question) == $question_number) { // ->first();
$is_last_question = true; // if($detail_answer != null) {
} // $answered_question[] = [
$total_question = count($data_question); // 'id_soal' => $detail->id_soal,
$data_question = $data_question[$question_number - 1]; // 'nomor_soal' => $index_question,
// 'answered' => true,
// 'jawaban' => $detail_answer->jawaban
// ];
// } else {
// $answered_question[] = [
// 'id_soal' => $detail->id_soal,
// 'nomor_soal' => $index_question,
// 'answered' => false,
// 'jawaban' => null
// ];
// }
// $index_question++;
// }
return view("user.pages.ujian.index", compact('data_session', 'data_question', 'total_question', 'is_last_question', 'question_number')); // $total_question = null;
// $is_last_question = false;
// if($question_number <= 0 || $question_number > count($data_question)) {
// $question_number = 1;
// }
// if(count($data_question) == $question_number) {
// $is_last_question = true;
// }
// $total_question = count($data_question);
// $data_question = $data_question[$question_number - 1];
// $answer = [];
// if($data_question->jawaban_a != null) {
// $answer[] = [
// 'key' => 'a',
// 'value' => $data_question->jawaban_a
// ];
// }
// if($data_question->jawaban_b != null) {
// $answer[] = [
// 'key' => 'b',
// 'value' => $data_question->jawaban_b
// ];
// }
// if($data_question->jawaban_c != null) {
// $answer[] = [
// 'key' => 'c',
// 'value' => $data_question->jawaban_c
// ];
// }
// if($data_question->jawaban_d != null) {
// $answer[] = [
// 'key' => 'd',
// 'value' => $data_question->jawaban_d
// ];
// }
// if($data_question->jawaban_e != null) {
// $answer[] = [
// 'key' => 'e',
// 'value' => $data_question->jawaban_e
// ];
// }
// dd($answer);
return view("user.pages.ujian.temp_index", compact('data_session'));
} }
/** /**
@ -70,7 +312,30 @@ public function create()
*/ */
public function store(Request $request) public function store(Request $request)
{ {
// $data_session = TestSessionsModel::where('id_test_sessions', $request->session_id)->first();
if($data_session != null) {
if($data_session->status_session != '1') {
return response()->json([
'status' => 'error',
'message' => 'Data sesi tes belum dimulai atau telah usai. Silahkan cek status dengan kode untuk informasi lebih lanjut.'
]);
}
DetailTestModel::updateOrCreate(
[
'id_soal' => $request->id_question,
'id_test_sessions' => $request->session_id
],
[
'jawaban' => $request->jawaban_final
]
);
} else {
return response()->json([
'status' => 'error',
'message' => 'Data sesi tes tidak ditemukan. Silakan mulai tes terlebih dahulu.'
]);
}
} }
/** /**

View File

@ -2,10 +2,34 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\BookingModel;
use App\Models\TestSessionsModel;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class HasilTesController extends Controller class HasilTesController extends Controller
{ {
public function fetchData() {
$data_booking = BookingModel::orderBy('updated_at', 'desc')->get();
$result = [];
foreach($data_booking as $booking) {
$data_test = TestSessionsModel::where('id_booking', $booking->id_booking)->first();
if($data_test != null) {
$result[] = [
'id_test_session' => $data_test->id_test_sessions,
'nama_lengkap' => $booking->nama_lengkap,
'tgl_tes' => $data_test->created_at->format('d-m-Y'),
'jam_tes' => $data_test->created_at->format('H:i'),
'kode_booking' => $booking->kode_unik,
'status_tes' => $booking->status_tes
];
}
}
return response()->json([
'status' => true,
'data' => $result
]);
}
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
@ -35,7 +59,20 @@ public function store(Request $request)
*/ */
public function show(string $id) public function show(string $id)
{ {
// $data = TestSessionsModel::where('id_test_sessions', $id)->first();
if($data != null) {
$data_booking = BookingModel::where('id_booking', $data->id_booking)->first();
return response()->json([
'status' => true,
'nama' => $data_booking->nama_lengkap
]);
}
return response()->json([
'status' => false,
'msg' => 'Data tidak ditemukan!'
]);
} }
/** /**
@ -59,6 +96,21 @@ public function update(Request $request, string $id)
*/ */
public function destroy(string $id) public function destroy(string $id)
{ {
// $data = TestSessionsModel::where('id_test_sessions', $id)->first();
if($data != null) {
$data_booking = BookingModel::where('id_booking', $data->id_booking)->first();
$data_booking->update([
'status_tes' => '0'
]);
return response()->json([
'status' => true,
'msg' => 'Data berhasil dinonaktifkan'
]);
}
return response()->json([
'status' => false,
'msg' => 'Data tidak ditemukan!'
]);
} }
} }

View File

@ -7,6 +7,22 @@
class TestSessionsController extends Controller class TestSessionsController extends Controller
{ {
public function getScreen(Request $request) {
$data = TestSessionsModel::where('id_test_sessions', $request->id_test_sessions)->first();
if($data != null) {
return response()->json([
'status' => true,
'screen_w' => $data->screen_w,
'screen_h' => $data->screen_h
]);
} else {
return response()->json([
'status' => false,
'msg' => 'Data tidak ditemukan!'
]);
}
}
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
@ -36,6 +52,8 @@ public function store(Request $request)
'id_booking' => $request->id_booking, 'id_booking' => $request->id_booking,
'status_session' => '0', 'status_session' => '0',
'is_active' => '0', 'is_active' => '0',
'screen_w' => $request->screen_w,
'screen_h' => $request->screen_h
]); ]);
return redirect()->route('tutorial-tes.index')->with([ return redirect()->route('tutorial-tes.index')->with([

View File

@ -4,6 +4,7 @@
use App\Models\User; use App\Models\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class UsersController extends Controller class UsersController extends Controller
{ {
@ -11,7 +12,7 @@ class UsersController extends Controller
* Display data for javascript ajax * Display data for javascript ajax
*/ */
public function getData() { public function getData() {
$data = User::get(); $data = User::where('id', '!=', Auth::user()->id)->get();
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'data' => $data->toArray() 'data' => $data->toArray()
@ -39,7 +40,18 @@ public function create()
*/ */
public function store(Request $request) public function store(Request $request)
{ {
// $data = User::create([
'name' => $request->name,
'email' => $request->email,
'role' => $request->role,
'alamat' => $request->alamat,
'password' => bcrypt('password', ['rounds' => 12])
]);
return response()->json([
'status' => true,
'msg' => 'Data berhasil di tambahkan'
]);
} }
/** /**
@ -74,7 +86,8 @@ public function update(Request $request, string $id)
$data = User::where('id', $id)->update([ $data = User::where('id', $id)->update([
'name' => $request->name, 'name' => $request->name,
'email' => $request->email, 'email' => $request->email,
'role' => $request->role 'role' => $request->role,
'alamat' => $request->alamat
]); ]);
return response()->json([ return response()->json([

View File

@ -0,0 +1,62 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class SendUniqueCode extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(public string $name, public string $code, public string $startExpirationTime, public string $endExpirationTime, public string $tglExpired)
{
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Selamat datang di Smart Anxiety',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'layouts.mail-send-code',
with: [
'code' => $this->code,
'name' => $this->name,
'startExpirationTime' => $this->startExpirationTime,
'endExpirationTime' => $this->endExpirationTime,
'tglExpired' => $this->tglExpired
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -22,7 +22,8 @@ class BookingModel extends Model
'kode_unik', 'kode_unik',
'tgl_booking', 'tgl_booking',
'deskripsi_tambahan', 'deskripsi_tambahan',
'status_tes' 'status_tes',
'unix_expired_time'
]; ];
protected $casts = [ protected $casts = [
@ -38,7 +39,11 @@ protected static function boot()
} }
if(empty($model->status_tes)) { if(empty($model->status_tes)) {
$model->status_tes = '0'; $model->status_tes = '1';
}
if(empty($model->unix_expired_time)) {
$model->unix_expired_time = (string) (time() + 86399); // Set expiration time to 24 hour from now
} }
}); });
} }

View File

@ -0,0 +1,38 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class DetailTestModel extends Model
{
protected $table = 'detail_test';
protected $primaryKey = 'id_detail_test';
protected $keyType = 'string';
public $incrementing = false;
protected $fillable = [
'id_detail_test',
'id_soal',
'id_test_sessions',
'jawaban'
];
protected $casts = [
'id_detail_test' => 'string',
];
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
if (empty($model->id_detail_test)) {
$model->id_detail_test = Str::uuid();
}
});
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class LogKecemasanModel extends Model
{
protected $table = 'log_hasil_kecemasan';
protected $primaryKey = 'id_log_kecemasan';
protected $keyType = 'string';
public $incrementing = false;
protected $fillable = [
'id_log_kecemasan',
'id_booking',
'id_test_sessions',
'start_timestamp',
'end_timestamp',
'difference_timestamp',
'screen_width',
'screen_height',
'answered_count',
'not_answered_count',
'total_question',
'skor_kecemasan'
];
protected $casts = [
'id_log_kecemasan' => 'string',
'id_booking' => 'string',
'id_test_sessions' => 'string',
];
public static function boot()
{
parent::boot();
static::creating(function ($model) {
if (empty($model->id_log_kecemasan)) {
$model->id_log_kecemasan = (string) \Illuminate\Support\Str::uuid();
}
});
}
}

View File

@ -18,7 +18,11 @@ class TestSessionsModel extends Model
'status_session', 'status_session',
'token_session', 'token_session',
'id_booking', 'id_booking',
'unix_start_time',
'is_active', 'is_active',
'unix_end_time',
'screen_w',
'screen_h'
]; ];
protected $casts = [ protected $casts = [

View File

@ -21,7 +21,8 @@ class User extends Authenticatable
'name', 'name',
'email', 'email',
'password', 'password',
'role' 'role',
'alamat'
]; ];
/** /**
@ -46,4 +47,14 @@ protected function casts(): array
'password' => 'hashed', 'password' => 'hashed',
]; ];
} }
public static function boot()
{
parent::boot();
static::creating(function ($model) {
if (empty($model->id_user)) {
$model->id_user = (string) \Illuminate\Support\Str::uuid();
}
});
}
} }

676
composer.lock generated

File diff suppressed because it is too large Load Diff

8448
composer.lock_1 Normal file

File diff suppressed because it is too large Load Diff

View File

@ -65,7 +65,7 @@
| |
*/ */
'timezone' => 'UTC', 'timezone' => 'Asia/Jakarta',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('detail_test', function (Blueprint $table) {
$table->dropForeign(['id_test']);
$table->dropColumn('id_test');
$table->char('id_test_sessions', 36)->after('id_soal');
$table->foreign('id_test_sessions')->references('id_test_sessions')->on('test_sessions')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('detail_test', function (Blueprint $table) {
$table->dropForeign(['id_test_sessions']);
$table->dropColumn('id_test_sessions');
$table->unsignedBigInteger('id_test')->after('id_soal');
$table->foreign('id_test')->references('id_test')->on('test')->onDelete('cascade');
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('detail_test', function (Blueprint $table) {
$table->foreign('id_soal')->references('id_soal')->on('soal')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('detail_test', function (Blueprint $table) {
$table->dropForeign(['id_soal']);
});
}
};

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('test_sessions', function (Blueprint $table) {
$table->char('unix_start_time', 20)->default(0)->after('token_session');
$table->char('unix_end_time', 20)->default(0)->after('is_active');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('test_sessions', function (Blueprint $table) {
$table->dropColumn('unix_start_time');
$table->dropColumn('unix_end_time');
});
}
};

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('soal', function (Blueprint $table) {
$table->string('jawaban_a')->nullable()->change();
$table->string('jawaban_b')->nullable()->change();
$table->string('jawaban_c')->nullable()->change();
$table->string('jawaban_d')->nullable()->change();
$table->string('jawaban_e')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('soal', function (Blueprint $table) {
$table->string('jawaban_a')->change();
$table->string('jawaban_b')->change();
$table->string('jawaban_c')->change();
$table->string('jawaban_d')->change();
$table->string('jawaban_e')->change();
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('booking', function (Blueprint $table) {
$table->char('unix_expired_time', 20)->after('kode_unik');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('booking', function (Blueprint $table) {
$table->dropColumn('unix_expired_time');
});
}
};

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('test_sessions', function (Blueprint $table) {
$table->char('screen_w', 20)->after('unix_end_time');
$table->char('screen_h', 20)->after('screen_w');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('test_sessions', function (Blueprint $table) {
$table->dropColumn('screen_w');
$table->dropColumn('screen_h');
});
}
};

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('soal', function (Blueprint $table) {
$table->enum('jawaban_benar', [0, 1])->default(0)->change();
$table->renameColumn('jawaban_benar', 'is_reverse');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('soal', function (Blueprint $table) {
$table->renameColumn('is_reverse', 'jawaban_benar');
$table->enum('jawaban_benar', ['a', 'b', 'c', 'd', 'e'])->change();
});
}
};

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('log_hasil_kecemasan', function (Blueprint $table) {
$table->uuid('id_log_kecemasan')->primary();
$table->uuid('id_booking');
$table->uuid('id_test_sessions');
$table->char('start_timestamp', 20);
$table->char('end_timestamp', 20);
$table->char('difference_timestamp', 20);
$table->char('screen_width', 10);
$table->char('screen_height', 10);
$table->integer('answered_count');
$table->integer('not_answered_count');
$table->integer('total_question');
$table->integer('skor_kecemasan');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('log_hasil_kecemasan');
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,9 +1,9 @@
<nav class="pc-sidebar"> <nav class="pc-sidebar">
<div class="navbar-wrapper"> <div class="navbar-wrapper">
<div class="m-header"> <div class="m-header">
<a href="{{ route('dashboard.index') }}" class="b-brand text-primary"> <a href="{{ route('dashboard.index') }}" class="b-brand text-primary fs-3 fw-bold text-dark">
<!-- ======== Change your logo from here ============ --> <!-- ======== Change your logo from here ============ -->
<img src="{{ asset('assets/admin/assets/images/logo-dark.svg') }}" class="img-fluid logo-lg" alt="logo" id="logo-admin"> {{ env('APP_NAME') }}
</a> </a>
</div> </div>
<div class="navbar-content"> <div class="navbar-content">

View File

@ -24,10 +24,15 @@
<div class="col-md-6 col-xl-3"> <div class="col-md-6 col-xl-3">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h6 class="mb-2 f-w-400 text-muted">Total Page Views</h6> <h6 class="mb-2 f-w-400 text-muted">Total Booking hari ini</h6>
<h4 class="mb-3">4,42,236 <span class="badge bg-light-primary border border-primary"><i <h4 class="mb-3">
class="ti ti-trending-up"></i> 59.3%</span></h4> <span id="total_booking"></span>
<p class="mb-0 text-muted text-sm">You made an extra <span class="text-primary">35,000</span> this year <span class="badge bg-light-primary border border-primary">
<i class="ti" id="trending-icon-booking"></i>
<span id="booking_percent">%</span>
</span>
</h4>
<p class="mb-0 text-muted text-sm">Booking <span id="booking_trend">bertambah</span> <span class="text-primary" id="from_month_booking_count"></span> dari bulan lalu
</p> </p>
</div> </div>
</div> </div>
@ -35,271 +40,146 @@ class="ti ti-trending-up"></i> 59.3%</span></h4>
<div class="col-md-6 col-xl-3"> <div class="col-md-6 col-xl-3">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h6 class="mb-2 f-w-400 text-muted">Total Users</h6> <h6 class="mb-2 f-w-400 text-muted">Total tes selesai hari ini</h6>
<h4 class="mb-3">78,250 <span class="badge bg-light-success border border-success"><i <h4 class="mb-3">
class="ti ti-trending-up"></i> 70.5%</span></h4> <span id="total_tes"></span>
<p class="mb-0 text-muted text-sm">You made an extra <span class="text-success">8,900</span> this year</p> <span class="badge bg-light-primary border border-primary">
<i class="ti" id="trending-icon-tes"></i>
<span id="tes_percent">%</span>
</span>
</h4>
<p class="mb-0 text-muted text-sm">Tes <span id="tes_trend">bertambah</span> <span class="text-primary" id="from_month_tes_count"></span> dari bulan lalu
</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6 col-xl-3"> <div class="col-md-6 col-xl-3">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h6 class="mb-2 f-w-400 text-muted">Total Order</h6> <h6 class="mb-2 f-w-400 text-muted">Total tes masih berlangsung hari ini</h6>
<h4 class="mb-3">18,800 <span class="badge bg-light-warning border border-warning"><i <h4 class="mb-3">
class="ti ti-trending-down"></i> 27.4%</span></h4> <span id="total_tes_berlangsung"></span>
<p class="mb-0 text-muted text-sm">You made an extra <span class="text-warning">1,943</span> this year</p> <span class="badge bg-light-primary border border-primary">
<i class="ti" id="trending-icon-tes-berlangsung"></i>
<span id="tes_berlangsung_percent">%</span>
</span>
</h4>
<p class="mb-0 text-muted text-sm">Tes yang sedang berlangsung <span id="tes_berlangsung_trend">bertambah</span> <span class="text-primary" id="from_month_tes_berlangsung_count"></span> dari bulan lalu
</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6 col-xl-3"> <div class="col-md-6 col-xl-3">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h6 class="mb-2 f-w-400 text-muted">Total Sales</h6> <h6 class="mb-2 f-w-400 text-muted">Rata rata skor kecemasan hari ini</h6>
<h4 class="mb-3">$35,078 <span class="badge bg-light-danger border border-danger"><i <h4 class="mb-3">
class="ti ti-trending-down"></i> 27.4%</span></h4> <span id="rata_rata_kecemasan"></span>
<p class="mb-0 text-muted text-sm">You made an extra <span class="text-danger">$20,395</span> this year <span class="badge bg-light-primary border border-primary">
<i class="ti" id="trending-icon-rata-rata"></i>
<span id="rata_rata_kecemasan_percent">%</span>
</span>
</h4>
<p class="mb-0 text-muted text-sm">Rata rata skor kecemasan <span id="rata_rata_kecemasan_trend">bertambah</span> <span class="text-primary" id="from_month_rata_rata_kecemasan_count"></span> dari bulan lalu
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-12 col-xl-8"> <div class="col-md-12 col-xl-8">
<div class="d-flex align-items-center justify-content-between mb-3"> <h5 class="mb-3">Tabel Monitoring Tes</h5>
<h5 class="mb-0">Unique Visitor</h5> <table id="table-data-booking" class="table table-bordered">
<ul class="nav nav-pills justify-content-end mb-0" id="chart-tab-tab" role="tablist"> <thead>
<li class="nav-item" role="presentation"> <tr>
<button class="nav-link" id="chart-tab-home-tab" data-bs-toggle="pill" data-bs-target="#chart-tab-home" <th>No</th>
type="button" role="tab" aria-controls="chart-tab-home" aria-selected="true">Month</button> <th>Nama</th>
</li> <th>Email</th>
<li class="nav-item" role="presentation"> <th>Telepon</th>
<button class="nav-link active" id="chart-tab-profile-tab" data-bs-toggle="pill" <th>Tanggal Booking</th>
data-bs-target="#chart-tab-profile" type="button" role="tab" aria-controls="chart-tab-profile" <th>Status Tes</th>
aria-selected="false">Week</button> <th>Status Sesi</th>
</li> </tr>
</ul> </thead>
</div> </table>
<div class="card">
<div class="card-body">
<div class="tab-content" id="chart-tab-tabContent">
<div class="tab-pane" id="chart-tab-home" role="tabpanel" aria-labelledby="chart-tab-home-tab"
tabindex="0">
<div id="visitor-chart-1"></div>
</div>
<div class="tab-pane show active" id="chart-tab-profile" role="tabpanel"
aria-labelledby="chart-tab-profile-tab" tabindex="0">
<div id="visitor-chart"></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-12 col-xl-4">
<h5 class="mb-3">Income Overview</h5>
<div class="card">
<div class="card-body">
<h6 class="mb-2 f-w-400 text-muted">This Week Statistics</h6>
<h3 class="mb-3">$7,650</h3>
<div id="income-overview-chart"></div>
</div>
</div>
</div>
<div class="col-md-12 col-xl-8">
<h5 class="mb-3">Recent Orders</h5>
<div class="card tbl-card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover table-borderless mb-0">
<thead>
<tr>
<th>TRACKING NO.</th>
<th>PRODUCT NAME</th>
<th>TOTAL ORDER</th>
<th>STATUS</th>
<th class="text-end">TOTAL AMOUNT</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Camera Lens</td>
<td>40</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-danger f-10 m-r-5"></i>Rejected</span>
</td>
<td class="text-end">$40,570</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Laptop</td>
<td>300</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-warning f-10 m-r-5"></i>Pending</span>
</td>
<td class="text-end">$180,139</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Mobile</td>
<td>355</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-success f-10 m-r-5"></i>Approved</span></td>
<td class="text-end">$180,139</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Camera Lens</td>
<td>40</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-danger f-10 m-r-5"></i>Rejected</span>
</td>
<td class="text-end">$40,570</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Laptop</td>
<td>300</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-warning f-10 m-r-5"></i>Pending</span>
</td>
<td class="text-end">$180,139</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Mobile</td>
<td>355</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-success f-10 m-r-5"></i>Approved</span></td>
<td class="text-end">$180,139</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Camera Lens</td>
<td>40</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-danger f-10 m-r-5"></i>Rejected</span>
</td>
<td class="text-end">$40,570</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Laptop</td>
<td>300</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-warning f-10 m-r-5"></i>Pending</span>
</td>
<td class="text-end">$180,139</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Mobile</td>
<td>355</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-success f-10 m-r-5"></i>Approved</span></td>
<td class="text-end">$180,139</td>
</tr>
<tr>
<td><a href="#" class="text-muted">84564564</a></td>
<td>Mobile</td>
<td>355</td>
<td><span class="d-flex align-items-center gap-2"><i
class="fas fa-circle text-success f-10 m-r-5"></i>Approved</span></td>
<td class="text-end">$180,139</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-md-12 col-xl-4">
<h5 class="mb-3">Analytics Report</h5>
<div class="card">
<div class="list-group list-group-flush">
<a href="#"
class="list-group-item list-group-item-action d-flex align-items-center justify-content-between">Company
Finance Growth<span class="h5 mb-0">+45.14%</span></a>
<a href="#"
class="list-group-item list-group-item-action d-flex align-items-center justify-content-between">Company
Expenses Ratio<span class="h5 mb-0">0.58%</span></a>
<a href="#"
class="list-group-item list-group-item-action d-flex align-items-center justify-content-between">Business
Risk Cases<span class="h5 mb-0">Low</span></a>
</div>
<div class="card-body px-2">
<div id="analytics-report-chart"></div>
</div>
</div>
</div>
<div class="col-md-12 col-xl-8">
<h5 class="mb-3">Sales Report</h5>
<div class="card">
<div class="card-body">
<h6 class="mb-2 f-w-400 text-muted">This Week Statistics</h6>
<h3 class="mb-0">$7,650</h3>
<div id="sales-report-chart"></div>
</div>
</div>
</div>
<div class="col-md-12 col-xl-4">
<h5 class="mb-3">Transaction History</h5>
<div class="card">
<div class="list-group list-group-flush">
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex">
<div class="flex-shrink-0">
<div class="avtar avtar-s rounded-circle text-success bg-light-success">
<i class="ti ti-gift f-18"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-1">Order #002434</h6>
<p class="mb-0 text-muted">Today, 2:00 AM</P>
</div>
<div class="flex-shrink-0 text-end">
<h6 class="mb-1">+ $1,430</h6>
<p class="mb-0 text-muted">78%</P>
</div>
</div>
</a>
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex">
<div class="flex-shrink-0">
<div class="avtar avtar-s rounded-circle text-primary bg-light-primary">
<i class="ti ti-message-circle f-18"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-1">Order #984947</h6>
<p class="mb-0 text-muted">5 August, 1:45 PM</P>
</div>
<div class="flex-shrink-0 text-end">
<h6 class="mb-1">- $302</h6>
<p class="mb-0 text-muted">8%</P>
</div>
</div>
</a>
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex">
<div class="flex-shrink-0">
<div class="avtar avtar-s rounded-circle text-danger bg-light-danger">
<i class="ti ti-settings f-18"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-1">Order #988784</h6>
<p class="mb-0 text-muted">7 hours ago</P>
</div>
<div class="flex-shrink-0 text-end">
<h6 class="mb-1">- $682</h6>
<p class="mb-0 text-muted">16%</P>
</div>
</div>
</a>
</div>
</div>
</div> </div>
</div> </div>
@endsection
@section('script')
<script>
let table = null;
$(document).ready(function() {
$.get('{{ route("dashboard.fetchData") }}', function(data) {
$('#total_booking').text(data.total_booking);
$('#booking_percent').text(data.booking_percent + '%');
$('#from_month_booking_count').text(data.from_month_booking_count);
$('#booking_trend').text(data.booking_trend);
if (data.booking_diff >= 0) {
$('#trending-icon-booking').addClass('ti-trending-up');
$('#booking_percent').addClass('text-primary');
} else {
$('#trending-icon-booking').addClass('ti-trending-down');
$('#booking_percent').addClass('text-danger');
}
$('#total_tes').text(data.total_tes);
$('#tes_percent').text(data.tes_percent + '%');
$('#from_month_tes_count').text(data.from_month_tes_count);
$('#tes_trend').text(data.tes_trend);
if (data.tes_diff >= 0) {
$('#trending-icon-tes').addClass('ti-trending-up');
$('#tes_percent').addClass('text-primary');
} else {
$('#trending-icon-tes').addClass('ti-trending-down');
$('#tes_percent').addClass('text-danger');
}
$('#total_tes_berlangsung').text(data.total_tes_berlangsung);
$('#tes_berlangsung_percent').text(data.tes_berlangsung_percent + '%');
$('#from_month_tes_berlangsung_count').text(data.from_month_tes_berlangsung_count);
$('#tes_berlangsung_trend').text(data.tes_berlangsung_trend);
if (data.tes_berlangsung_diff >= 0) {
$('#trending-icon-tes-berlangsung').addClass('ti-trending-up');
$('#tes_berlangsung_percent').addClass('text-primary');
} else {
$('#trending-icon-tes-berlangsung').addClass('ti-trending-down');
$('#tes_berlangsung_percent').addClass('text-danger');
}
$('#rata_rata_kecemasan').text(data.rata_rata_kecemasan);
$('#rata_rata_kecemasan_percent').text(data.rata_rata_kecemasan_percent + '%');
$('#from_month_rata_rata_kecemasan_count').text(data.from_month_rata_rata_kecemasan);
$('#rata_rata_kecemasan_trend').text(data.rata_rata_kecemasan_trend);
if (data.rata_rata_kecemasan_diff >= 0) {
$('#trending-icon-rata-rata').addClass('ti-trending-up');
$('#rata_rata_kecemasan_percent').addClass('text-primary');
} else {
$('#trending-icon-rata-rata').addClass('ti-trending-down');
$('#rata_rata_kecemasan_percent').addClass('text-danger');
}
table = new DataTable('#table-data-booking', {
searching: false,
paging: false,
ajax: {
url: '{{ route("dashboard.fetchDataTable") }}',
},
columns: [
{
data: null,
render: function(data, type, row, meta) {
return meta.row + 1;
}
},
{ data: 'nama_lengkap' },
{ data: 'email', name: 'email' },
{ data: 'no_telp' },
{ data: 'tanggal_booking' },
{ data: 'status_tes' },
{ data: 'status_session' },
]
});
});
});
</script>
@endsection @endsection

View File

@ -139,7 +139,7 @@
<div class="col-md-12 my-1"> <div class="col-md-12 my-1">
<label for="edit_status_tes" class="form-label">Status</label> <label for="edit_status_tes" class="form-label">Status</label>
<select class="form-select" name="edit_status_tes" id="edit_status_tes"> <select class="form-select" name="edit_status_tes" id="edit_status_tes">
<option value="0">Menunggu Konfirmasi</option> <option value="0">Nonaktif</option>
<option value="1">Siap/Sedang Tes</option> <option value="1">Siap/Sedang Tes</option>
<option value="2">Selesai</option> <option value="2">Selesai</option>
</select> </select>
@ -183,8 +183,8 @@
let table = null; let table = null;
let idEdit = null; let idEdit = null;
let idHapus = null; let idHapus = null;
let status_dict = ['warning', 'info', 'success']; let status_dict = ['secondary', 'info', 'success'];
let status_dict_text = ['Menunggu Konfirmasi', 'Siap/Sedang Tes', 'Selesai']; let status_dict_text = ['Nonaktif', 'Siap/Sedang Tes', 'Selesai'];
$(document).ready(function() { $(document).ready(function() {
table = new DataTable("#table-data", { table = new DataTable("#table-data", {
ajax: { ajax: {

View File

@ -41,30 +41,13 @@
<thead> <thead>
<tr> <tr>
<th>No</th> <th>No</th>
<th>Dummy</th> <th>Nama</th>
<th>Dummy</th> <th>Kode Unik</th>
<th>Dummy</th> <th>Tgl dan Jam Ujian</th>
<th>Status Data</th>
<th>Aksi</th> <th>Aksi</th>
</tr> </tr>
</thead> </thead>
<tbody>
<tr>
<td>1</td>
<td>lorem</td>
<td>lorem</td>
<td>lorem</td>
<td>
<div class="btn-group">
<button type="button" class="btn btn-info btn-sm rounded-circle mx-1" data-bs-toggle="modal" data-bs-target="#editModal" onclick="getDataEdit(this)" data-id="${data.id}">
<i class="ti ti-edit"></i>
</button>
<button class="btn btn-sm btn-danger delete-btn rounded-circle mx-1" data-bs-toggle="modal" data-bs-target="#deleteModal" onclick="getDataHapus(this)" data-id="${data.id}">
<i class="ti ti-trash"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table> </table>
</div> </div>
</div> </div>
@ -72,10 +55,229 @@
<!-- [ sample-page ] end --> <!-- [ sample-page ] end -->
</div> </div>
<!-- [ Main Content ] end --> <!-- [ Main Content ] end -->
<!-- Modal Delete -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">Hapus Data</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Apakah anda akan menghapus data dengan nama "<span id="delete_test" class="fw-bold"></span>" ini? (data hanya akan dinonaktikan)
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="button" class="btn btn-danger" onclick="hapusData()">Hapus</button>
</div>
</div>
</div>
</div>
{{-- <!-- Modal Restore -->
<div class="modal fade" id="restoreModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">Restore Data</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Apakah anda akan memulihkan data dengan nama "<span id="restore_test" class="fw-bold"></span>" ini? (data akan diperiksa dengan status terakhirnya)
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="button" class="btn btn-danger" onclick="restoreData()">Hapus</button>
</div>
</div>
</div>
</div> --}}
@endsection @endsection
@section('script') @section('script')
<script> <script>
new DataTable("#table-data", {}); let idHapus = null;
let tableData = null;
let ws = null;
function fetchRowData() {
if(tableData != null) {
tableData.ajax.reload(null, false);
}
}
function getDataInfo(btn) {
let idShow = btn.getAttribute('data-id');
Swal.fire({
title: 'Alert',
icon: 'question',
text: 'Apakah Anda ingin melihat hasil dari data ini?',
confirmButtonText: 'Ya',
cancelButtonText: 'Tidak',
showCancelButton: true,
showCloseButton: true,
}).then((result) => {
if (result.isConfirmed) {
window.open("{{ route('hasil-tes.index') }}?id_test_sessions=" + idShow, "_blank");
}
});
}
function getDataDownload(btn) {
let idShow = btn.getAttribute('data-id');
Swal.fire({
title: 'Alert',
icon: 'question',
text: 'Apakah Anda ingin mendownload hasil dari data ini?',
confirmButtonText: 'Ya',
cancelButtonText: 'Tidak',
showCancelButton: true,
showCloseButton: true,
}).then((result) => {
if (result.isConfirmed) {
ws.send(JSON.stringify({
'type': 'download_hasil_tes',
'id_test_sessions': idShow
}));
}
});
}
function getDataHapus(btn) {
idHapus = btn.getAttribute('data-id');
let urlShow = '{{ route('hasil_tes.show', ['__ID__']) }}';
$.get(urlShow.replace('__ID__', idHapus), function(data, status) {
if(data.status) {
$('#delete_test').html(data.nama);
}
});
}
function hapusData() {
if(idHapus != null) {
let urlDestroy = '{{ route('hasil_tes.destroy', ['__ID__']) }}';
$.post(urlDestroy.replace('__ID__', idHapus), {
'_token': document.getElementById('_token').getAttribute('value'),
'_method': 'DELETE'
}, function(data, status) {
if(data.status) {
$('#deleteModal').modal('hide');
Swal.fire({
title: "Info",
text: "Data berhasil dinonakstifkan!",
icon: "success"
});
fetchRowData();
idHapus = null;
}
});
}
}
$(document).ready(function() {
tableData = new DataTable("#table-data", {
'ajax': {
'url': '{{ route('hasil-tes.fetchData') }}'
},
'columns': [
{
data: null,
render: function(data, type, row, meta) {
return meta.row + 1;
}
},
{
data: 'nama_lengkap'
},
{
data: 'kode_booking'
},
{
data: null,
render: function(data, type, row, meta) {
return `${data.tgl_tes} ${data.jam_tes}`;
}
},
{
data: null,
render: function(data, type, row, meta) {
let bgClass = ['secondary', 'warning', 'success'];
let text = ['Nonaktif', 'Dalam Ujian', 'Selesai'];
return `
<span class="badge rounded-pill text-bg-${bgClass[data.status_tes]}">${text[data.status_tes]}</span>
`;
}
},
{
data: null,
render: function(data, type, row, meta) {
if(data.status_tes == 0) {
return `
<div class="btn-group">
<a class="btn btn-info btn-sm rounded-circle mx-1 text-light" onclick="getDataInfo(this)" data-id="${data.id_test_session}">
<i class="ti ti-file-info"></i>
</a>
</div>
`;
}
return `
<div class="btn-group">
<a class="btn btn-info btn-sm rounded-circle mx-1 text-light" onclick="getDataInfo(this)" data-id="${data.id_test_session}">
<i class="ti ti-file-info"></i>
</a>
<button class="btn btn-sm btn-success rounded-circle mx-1" data-bs-toggle="modal" data-bs-target="#downloadModal" onclick="getDataDownload(this)" data-id="${data.id_test_session}">
<i class="ti ti-download"></i>
</button>
<button class="btn btn-sm btn-danger delete-btn rounded-circle mx-1" data-bs-toggle="modal" data-bs-target="#deleteModal" onclick="getDataHapus(this)" data-id="${data.id_test_session}">
<i class="ti ti-trash"></i>
</button>
</div>
`;
}
},
]
});
console.log("Connecting to WebSocket...");
ws = new WebSocket("{{ env('WEBSOCKET_URL') }}");
ws.onopen = function() {
console.log("WebSocket connection established.");
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
if(data.type === 'download_hasil_tes_response') {
if(data.data != null) {
console.log(data)
const blob = new Blob([JSON.stringify(data.data)], { type: 'text/plain;charset=utf-8' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.href = url;
link.download = `hasil_tes_${data.id_test_sessions}.json`;
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
Swal.fire({
title: 'Sukses',
text: 'Hasil tes berhasil didownload!',
icon: 'success'
});
} else {
Swal.fire({
title: 'Error',
text: 'Gagal mendownload hasil tes.',
icon: 'error'
});
}
}
};
});
</script> </script>
@endsection @endsection

View File

@ -93,13 +93,10 @@
<input disabled type="text" class="form-control" id="info_jawaban_e" name="info_jawaban_e"> <input disabled type="text" class="form-control" id="info_jawaban_e" name="info_jawaban_e">
</div> </div>
<div class="col-md-6 my-1"> <div class="col-md-6 my-1">
<label for="info_jawaban_benar" class="form-label">Jawaban Benar</label> <label for="info_is_reverse" class="form-label">Apakah jawaban positif?</label>
<select disabled class="form-select" name="info_jawaban_benar" id="info_jawaban_benar"> <select disabled class="form-select" name="info_is_reverse" id="info_is_reverse">
<option value="a">A</option> <option value="0">Tidak</option>
<option value="b">B</option> <option value="1">Ya</option>
<option value="c">C</option>
<option value="d">D</option>
<option value="e">E</option>
</select> </select>
</div> </div>
</div> </div>
@ -146,13 +143,10 @@
<input type="text" class="form-control" id="jawaban_e" name="jawaban_e"> <input type="text" class="form-control" id="jawaban_e" name="jawaban_e">
</div> </div>
<div class="col-md-6 my-1"> <div class="col-md-6 my-1">
<label for="jawaban_benar" class="form-label">Jawaban Benar</label> <label for="is_reverse" class="form-label">Apakah jawaban positif?</label>
<select class="form-select" name="jawaban_benar" id="jawaban_benar"> <select class="form-select" name="is_reverse" id="is_reverse">
<option value="a">A</option> <option value="0">Tidak</option>
<option value="b">B</option> <option value="1">Ya</option>
<option value="c">C</option>
<option value="d">D</option>
<option value="e">E</option>
</select> </select>
</div> </div>
</div> </div>
@ -200,13 +194,10 @@
<input type="text" class="form-control" id="edit_jawaban_e" name="edit_jawaban_e"> <input type="text" class="form-control" id="edit_jawaban_e" name="edit_jawaban_e">
</div> </div>
<div class="col-md-6 my-1"> <div class="col-md-6 my-1">
<label for="edit_jawaban_benar" class="form-label">Jawaban Benar</label> <label for="is_reverse" class="form-label">Apakah jawaban positif?</label>
<select class="form-select" name="edit_jawaban_benar" id="edit_jawaban_benar"> <select class="form-select" name="edit_is_reverse" id="edit_is_reverse">
<option value="a">A</option> <option value="0">Tidak</option>
<option value="b">B</option> <option value="1">Ya</option>
<option value="c">C</option>
<option value="d">D</option>
<option value="e">E</option>
</select> </select>
</div> </div>
</div> </div>
@ -296,7 +287,7 @@ function simpanAddData() {
'jawaban_c': $('#jawaban_c').val(), 'jawaban_c': $('#jawaban_c').val(),
'jawaban_d': $('#jawaban_d').val(), 'jawaban_d': $('#jawaban_d').val(),
'jawaban_e': $('#jawaban_e').val(), 'jawaban_e': $('#jawaban_e').val(),
'jawaban_benar': $('#jawaban_benar').val(), 'is_reverse': $('#is_reverse').val(),
}, function(data, status) { }, function(data, status) {
$('#addModal').modal('hide'); $('#addModal').modal('hide');
Swal.fire({ Swal.fire({
@ -319,7 +310,7 @@ function getDataInfo(btn) {
$('#info_jawaban_c').attr('disabled', ''); $('#info_jawaban_c').attr('disabled', '');
$('#info_jawaban_d').attr('disabled', ''); $('#info_jawaban_d').attr('disabled', '');
$('#info_jawaban_e').attr('disabled', ''); $('#info_jawaban_e').attr('disabled', '');
$('#info_jawaban_benar').attr('disabled', ''); $('#info_is_reverse').attr('disabled', '');
$('#info_pertanyaan').val(data.data.pertanyaan); $('#info_pertanyaan').val(data.data.pertanyaan);
$('#info_jawaban_a').val(data.data.jawaban_a); $('#info_jawaban_a').val(data.data.jawaban_a);
@ -327,7 +318,7 @@ function getDataInfo(btn) {
$('#info_jawaban_c').val(data.data.jawaban_c); $('#info_jawaban_c').val(data.data.jawaban_c);
$('#info_jawaban_d').val(data.data.jawaban_d); $('#info_jawaban_d').val(data.data.jawaban_d);
$('#info_jawaban_e').val(data.data.jawaban_e); $('#info_jawaban_e').val(data.data.jawaban_e);
$('#info_jawaban_benar').val(data.data.jawaban_benar); $('#info_is_reverse').val(data.data.is_reverse);
$('#info_pertanyaan').attr('disabled', 'disabled'); $('#info_pertanyaan').attr('disabled', 'disabled');
$('#info_jawaban_a').attr('disabled', 'disabled'); $('#info_jawaban_a').attr('disabled', 'disabled');
@ -335,7 +326,7 @@ function getDataInfo(btn) {
$('#info_jawaban_c').attr('disabled', 'disabled'); $('#info_jawaban_c').attr('disabled', 'disabled');
$('#info_jawaban_d').attr('disabled', 'disabled'); $('#info_jawaban_d').attr('disabled', 'disabled');
$('#info_jawaban_e').attr('disabled', 'disabled'); $('#info_jawaban_e').attr('disabled', 'disabled');
$('#info_jawaban_benar').attr('disabled', 'disabled'); $('#info_is_reverse').attr('disabled', 'disabled');
} }
}); });
} }
@ -352,7 +343,7 @@ function getDataEdit(btn) {
$('#edit_jawaban_c').val(data.data.jawaban_c); $('#edit_jawaban_c').val(data.data.jawaban_c);
$('#edit_jawaban_d').val(data.data.jawaban_d); $('#edit_jawaban_d').val(data.data.jawaban_d);
$('#edit_jawaban_e').val(data.data.jawaban_e); $('#edit_jawaban_e').val(data.data.jawaban_e);
$('#edit_jawaban_benar').val(data.data.jawaban_benar); $('#edit_is_reverse').val(data.data.is_reverse);
} }
}); });
} }
@ -370,7 +361,7 @@ function simpanEditData() {
'jawaban_c': $('#edit_jawaban_c').val(), 'jawaban_c': $('#edit_jawaban_c').val(),
'jawaban_d': $('#edit_jawaban_d').val(), 'jawaban_d': $('#edit_jawaban_d').val(),
'jawaban_e': $('#edit_jawaban_e').val(), 'jawaban_e': $('#edit_jawaban_e').val(),
'jawaban_benar': $('#edit_jawaban_benar').val(), 'is_reverse': $('#edit_is_reverse').val(),
}, function(data, status) { }, function(data, status) {
if(data.status) { if(data.status) {
$('#editModal').modal('hide'); $('#editModal').modal('hide');

View File

@ -25,7 +25,16 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h5>Tabel Data</h5> <div class="row">
<div class="col-md-6">
<h5>Data Users</h5>
</div>
<div class="col-md-6 d-flex justify-content-end">
<button class="btn btn-sm btn-info delete-btn rounded-circle mx-1" data-bs-toggle="modal" data-bs-target="#addModal">
<i class="ti ti-database-import"></i>
</button>
</div>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<table class="table" id="table-data"> <table class="table" id="table-data">
@ -48,6 +57,45 @@
</div> </div>
<!-- [ Main Content ] end --> <!-- [ Main Content ] end -->
<!-- Modal Add -->
<div class="modal fade" id="addModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">Add Data</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6 my-1">
<label for="add_nama_user" class="form-label">Nama User</label>
<input type="text" class="form-control" id="add_nama_user" name="add_nama_user">
</div>
<div class="col-md-6 my-1">
<label for="add_email" class="form-label">Email</label>
<input type="text" class="form-control" id="add_email" name="add_email">
</div>
<div class="col-md-6 my-1">
<label for="add_alamat" class="form-label">Alamat</label>
<input type="text" class="form-control" id="add_alamat" name="add_alamat">
</div>
<div class="col-md-6 my-1">
<label for="add_role" class="form-label">Role</label>
<select class="form-select" name="add_role" id="add_role">
<option value="admin">Admin</option>
<option value="psikolog">Psikolog</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary" onclick="simpanAddData()">Simpan</button>
</div>
</div>
</div>
</div>
<!-- Modal Edit --> <!-- Modal Edit -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
@ -66,12 +114,15 @@
<label for="edit_email" class="form-label">Email</label> <label for="edit_email" class="form-label">Email</label>
<input type="text" class="form-control" id="edit_email" name="edit_email"> <input type="text" class="form-control" id="edit_email" name="edit_email">
</div> </div>
<div class="col-md-6 my-1">
<label for="edit_alamat" class="form-label">Alamat</label>
<input type="text" class="form-control" id="edit_alamat" name="edit_alamat">
</div>
<div class="col-md-6 my-1"> <div class="col-md-6 my-1">
<label for="edit_role" class="form-label">Role</label> <label for="edit_role" class="form-label">Role</label>
<select class="form-select" name="edit_role" id="edit_role"> <select class="form-select" name="edit_role" id="edit_role">
<option value="admin">Admin</option> <option value="admin">Admin</option>
<option value="psikolog">Psikolog</option> <option value="psikolog">Psikolog</option>
<option value="user">User</option>
</select> </select>
</div> </div>
</div> </div>
@ -167,6 +218,7 @@ function getDataEdit(btn) {
$('#edit_nama_user').val(data.data.name); $('#edit_nama_user').val(data.data.name);
$('#edit_email').val(data.data.email); $('#edit_email').val(data.data.email);
$('#edit_role').val(data.data.role); $('#edit_role').val(data.data.role);
$('#edit_alamat').val(data.data.alamat);
} }
}); });
} }
@ -181,6 +233,30 @@ function getDataHapus(btn) {
}); });
} }
function simpanAddData() {
$.post('{{ route('users.store') }}', {
'_token': document.getElementById('_token').getAttribute('value'),
'name': $('#add_nama_user').val(),
'email': $('#add_email').val(),
'role': $('#add_role').val(),
'alamat': $('#add_alamat').val()
}, function(data, status) {
if(data.status) {
$('#addModal').modal('hide');
Swal.fire({
title: "Info",
text: "Data berhasil ditambahkan!",
icon: "success"
});
fetchRowData();
$('#add_nama_user').val('');
$('#add_email').val('');
$('#add_role').val('admin');
$('#add_alamat').val('');
}
});
}
function simpanEditData() { function simpanEditData() {
if(idEdit != null) { if(idEdit != null) {
let urlUpdate = '{{ route('users.update', ['__ID__']) }}'; let urlUpdate = '{{ route('users.update', ['__ID__']) }}';
@ -189,13 +265,14 @@ function simpanEditData() {
'_method': 'PUT', '_method': 'PUT',
'name': $('#edit_nama_user').val(), 'name': $('#edit_nama_user').val(),
'email': $('#edit_email').val(), 'email': $('#edit_email').val(),
'role': $('#edit_role').val() 'role': $('#edit_role').val(),
'alamat': $('#edit_alamat').val()
}, function(data, status) { }, function(data, status) {
if(data.status) { if(data.status) {
$('#editModal').modal('hide'); $('#editModal').modal('hide');
Swal.fire({ Swal.fire({
title: "Info", title: "Info",
text: "Data berhasil ditambahkan!", text: "Data berhasil diubah!",
icon: "success" icon: "success"
}); });
fetchRowData(); fetchRowData();

View File

@ -1,25 +1,169 @@
<x-guest-layout> <!DOCTYPE html>
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400"> <html lang="en">
{{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} <!-- [Head] start -->
<head>
<title>Lupa Password | {{ env('APP_NAME') }}</title>
<!-- [Meta] -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description"
content="Mantis is made using Bootstrap 5 design framework. Download the free admin template & use it for your project.">
<meta name="keywords"
content="Mantis, Dashboard UI Kit, Bootstrap 5, Admin Template, Admin Dashboard, CRM, CMS, Bootstrap Admin Template">
<meta name="author" content="CodedThemes">
<!-- [Favicon] icon -->
<link rel="icon" href="{{ asset('assets/admin/assets/images/favicon.svg') }}" type="image/x-icon"> <!-- [Google Font] Family -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Public+Sans:wght@300;400;500;600;700&display=swap"
id="main-font-link">
<!-- [Tabler Icons] https://tablericons.com -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/fonts/tabler-icons.min.css') }}">
<!-- [Feather Icons] https://feathericons.com -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/fonts/feather.css') }}">
<!-- [Font Awesome Icons] https://fontawesome.com/icons -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/fonts/fontawesome.css') }}">
<!-- [Material Icons] https://fonts.google.com/icons -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/fonts/material.css') }}">
<!-- [Template CSS Files] -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/css/style.css') }}" id="main-style-link">
<link rel="stylesheet" href="{{ asset('assets/admin/assets/css/style-preset.css') }}">
<meta id="_token" value="{{ csrf_token() }}">
</head>
<!-- [Head] end -->
<!-- [Body] Start -->
<body>
<!-- [ Pre-loader ] start -->
<div class="loader-bg">
<div class="loader-track">
<div class="loader-fill"></div>
</div> </div>
</div>
<!-- [ Pre-loader ] End -->
<!-- Session Status --> <div class="auth-main">
<x-auth-session-status class="mb-4" :status="session('status')" /> <div class="auth-wrapper v3">
<div class="auth-form">
<form method="POST" action="{{ route('password.email') }}"> <div class="auth-header">
@csrf <a href="{{ route('beranda.index') }}" class="fs-1 text-dark">{{ env('APP_NAME') }}</a>
<!-- Email Address -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div> </div>
<div class="card my-5">
<div class="flex items-center justify-end mt-4"> <div class="card-body">
<x-primary-button> <div class="d-flex justify-content-between align-items-end mb-4">
{{ __('Email Password Reset Link') }} <h3 class="mb-0"><b>Lupa Password</b></h3>
</x-primary-button> </div>
<div class="form-group mb-3">
<p class="text-muted">Lupa password Anda? Masukkan email Anda dan kami akan mengirimkan link untuk mereset password.</p>
</div>
<div class="form-group mb-3">
<label class="form-label">Email Address</label>
<input type="email" id="email" class="form-control" placeholder="Email Address">
</div>
<div class="d-grid mt-4">
<button type="button" class="btn btn-primary" onclick="doForgotPassword()">Kirim Link Reset Password</button>
</div>
</div>
</div> </div>
</form> <div class="auth-footer row">
</x-guest-layout> <!-- <div class=""> -->
<div class="col my-1">
<p class="m-0">Copyright {{ date('Y') }} © <a href="https://polije.ac.id" target="_blank"> Politeknik Negeri Jember</a></p>
</div>
<div class="col-auto my-1">
<ul class="list-inline footer-link mb-0">
<li class="list-inline-item"><a href="{{ route('beranda.index') }}">Beranda</a></li>
<li class="list-inline-item"><a href="{{ route('beranda.index') }}#footer">Contact us</a></li>
</ul>
</div>
<!-- </div> -->
</div>
</div>
</div>
</div>
<!-- [ Main Content ] end -->
<!-- Required Js -->
<script src="{{ asset('assets/admin/assets/js/plugins/popper.min.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/plugins/simplebar.min.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/plugins/bootstrap.min.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/fonts/custom-font.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/pcoded.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/plugins/feather.min.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>layout_change('light');</script>
<script>change_box_container('false');</script>
<script>layout_rtl_change('false');</script>
<script>preset_change("preset-1");</script>
<script>font_change("Public-Sans");</script>
<script>
function doForgotPassword() {
try {
Swal.fire({
title: "Loading",
text: "Sedang memproses, tunggu sebentar yaa!",
icon: "info",
showConfirmButton: false,
allowOutsideClick: false
});
let token = document.getElementById('_token').getAttribute('value');
let email = document.getElementById('email').value;
if(email == '' || email == null) {
Swal.fire({
title: "Info",
text: "Email tidak boleh kosong!",
icon: "warning"
});
return;
}
let xhttp = new XMLHttpRequest();
let formData = new FormData();
formData.append('_token', token);
formData.append('email', email);
xhttp.onreadystatechange = function() {
if(this.status == 200) {
let jsonData = JSON.parse(this.responseText);
if(jsonData.status) {
Swal.fire({
title: "Info",
text: "Link reset password telah dikirim ke email Anda!",
icon: "success"
});
} else {
Swal.fire({
title: "Info",
text: "Gagal mengirim link reset password, cek email Anda!",
icon: "error"
});
}
} else {
Swal.fire({
title: "Info",
text: "Gagal mengirim link reset password, koneksi ke server gagal, coba lagi nanti!",
icon: "error"
});
}
}
xhttp.open('POST', '{{ route("password.email") }}', false);
xhttp.send(formData);
} catch(error) {
Swal.fire({
title: "Info",
text: "Gagal mengirim link reset password, kesalahan pada server, coba lagi nanti!",
icon: "error"
});
}
}
</script>
</body>
<!-- [Body] end -->
</html>

View File

@ -3,7 +3,7 @@
<!-- [Head] start --> <!-- [Head] start -->
<head> <head>
<title>Login | Smart Anxiety</title> <title>Login | {{ env('APP_NAME') }}</title>
<!-- [Meta] --> <!-- [Meta] -->
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
@ -50,7 +50,7 @@
<div class="auth-wrapper v3"> <div class="auth-wrapper v3">
<div class="auth-form"> <div class="auth-form">
<div class="auth-header"> <div class="auth-header">
<a href="#"><img src="{{ asset('assets/admin/assets/images/logo-dark.svg') }}" alt="img"></a> <a href="{{ route('beranda.index') }}" class="fs-1 text-dark">{{ env('APP_NAME') }}</a>
</div> </div>
<div class="card my-5"> <div class="card my-5">
<div class="card-body"> <div class="card-body">
@ -67,7 +67,7 @@
<input type="password" id="password" class="form-control" placeholder="Password"> <input type="password" id="password" class="form-control" placeholder="Password">
</div> </div>
<div class="d-flex mt-1 justify-content-between"> <div class="d-flex mt-1 justify-content-between">
<h5 class="text-secondary f-w-400">Forgot Password?</h5> <h5 class="text-secondary f-w-400"><a href="{{ route('password.request') }}">Forgot Password?</a></h5>
</div> </div>
<div class="d-grid mt-4"> <div class="d-grid mt-4">
<button type="button" class="btn btn-primary" onclick="doLogin()">Login</button> <button type="button" class="btn btn-primary" onclick="doLogin()">Login</button>
@ -81,8 +81,8 @@
</div> </div>
<div class="col-auto my-1"> <div class="col-auto my-1">
<ul class="list-inline footer-link mb-0"> <ul class="list-inline footer-link mb-0">
<li class="list-inline-item"><a href="#">Beranda</a></li> <li class="list-inline-item"><a href="{{ route('beranda.index') }}">Beranda</a></li>
<li class="list-inline-item"><a href="#">Contact us</a></li> <li class="list-inline-item"><a href="{{ route('beranda.index') }}#footer">Contact us</a></li>
</ul> </ul>
</div> </div>
<!-- </div> --> <!-- </div> -->

View File

@ -1,39 +1,198 @@
<x-guest-layout> <!DOCTYPE html>
<form method="POST" action="{{ route('password.store') }}"> <html lang="en">
@csrf <!-- [Head] start -->
<!-- Password Reset Token --> <head>
<input type="hidden" name="token" value="{{ $request->route('token') }}"> <title>Reset Password | {{ env('APP_NAME') }}</title>
<!-- [Meta] -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description"
content="Mantis is made using Bootstrap 5 design framework. Download the free admin template & use it for your project.">
<meta name="keywords"
content="Mantis, Dashboard UI Kit, Bootstrap 5, Admin Template, Admin Dashboard, CRM, CMS, Bootstrap Admin Template">
<meta name="author" content="CodedThemes">
<!-- Email Address --> <!-- [Favicon] icon -->
<div> <link rel="icon" href="{{ asset('assets/admin/assets/images/favicon.svg') }}" type="image/x-icon"> <!-- [Google Font] Family -->
<x-input-label for="email" :value="__('Email')" /> <link rel="stylesheet"
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email', $request->email)" required autofocus autocomplete="username" /> href="https://fonts.googleapis.com/css2?family=Public+Sans:wght@300;400;500;600;700&display=swap"
<x-input-error :messages="$errors->get('email')" class="mt-2" /> id="main-font-link">
<!-- [Tabler Icons] https://tablericons.com -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/fonts/tabler-icons.min.css') }}">
<!-- [Feather Icons] https://feathericons.com -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/fonts/feather.css') }}">
<!-- [Font Awesome Icons] https://fontawesome.com/icons -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/fonts/fontawesome.css') }}">
<!-- [Material Icons] https://fonts.google.com/icons -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/fonts/material.css') }}">
<!-- [Template CSS Files] -->
<link rel="stylesheet" href="{{ asset('assets/admin/assets/css/style.css') }}" id="main-style-link">
<link rel="stylesheet" href="{{ asset('assets/admin/assets/css/style-preset.css') }}">
<meta id="_token" value="{{ csrf_token() }}">
</head>
<!-- [Head] end -->
<!-- [Body] Start -->
<body>
<!-- [ Pre-loader ] start -->
<div class="loader-bg">
<div class="loader-track">
<div class="loader-fill"></div>
</div>
</div>
<!-- [ Pre-loader ] End -->
<div class="auth-main">
<div class="auth-wrapper v3">
<div class="auth-form">
<div class="auth-header">
<a href="{{ route('beranda.index') }}" class="fs-1 text-dark">{{ env('APP_NAME') }}</a>
</div> </div>
<div class="card my-5">
<!-- Password --> <div class="card-body">
<div class="mt-4"> <div class="d-flex justify-content-between align-items-end mb-4">
<x-input-label for="password" :value="__('Password')" /> <h3 class="mb-0"><b>Reset Password</b></h3>
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" /> {{-- <a href="#" class="link-primary">Don't have an account?</a> --}}
<x-input-error :messages="$errors->get('password')" class="mt-2" /> </div>
<div class="form-group mb-3">
<label class="form-label">Email Address</label>
<input type="email" id="email" class="form-control" placeholder="Email Address" value="{{ request()->get('email') }}">
</div>
<div class="form-group mb-3">
<label class="form-label">Password</label>
<input type="password" id="password" class="form-control" placeholder="Password">
</div>
<div class="form-group mb-3">
<label class="form-label">Confirm Password</label>
<input type="password" id="password_confirmation" class="form-control" placeholder="Confirm Password">
</div>
<input type="hidden" id="reset_token" value="{{ request()->route('token') }}">
<div class="d-flex mt-1 justify-content-between">
<h5 class="text-secondary f-w-400"><a href="{{ route('password.request') }}">Forgot Password?</a></h5>
</div>
<div class="d-grid mt-4">
<button type="button" class="btn btn-primary" onclick="doResetPassword()">Reset Password</button>
</div>
</div>
</div> </div>
<div class="auth-footer row">
<!-- Confirm Password --> <!-- <div class=""> -->
<div class="mt-4"> <div class="col my-1">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" /> <p class="m-0">Copyright {{ date('Y') }} © <a href="https://polije.ac.id" target="_blank"> Politeknik Negeri Jember</a></p>
</div>
<x-text-input id="password_confirmation" class="block mt-1 w-full" <div class="col-auto my-1">
type="password" <ul class="list-inline footer-link mb-0">
name="password_confirmation" required autocomplete="new-password" /> <li class="list-inline-item"><a href="{{ route('beranda.index') }}">Beranda</a></li>
<li class="list-inline-item"><a href="{{ route('beranda.index') }}#footer">Contact us</a></li>
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" /> </ul>
</div>
<!-- </div> -->
</div> </div>
</div>
</div>
</div>
<!-- [ Main Content ] end -->
<!-- Required Js -->
<script src="{{ asset('assets/admin/assets/js/plugins/popper.min.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/plugins/simplebar.min.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/plugins/bootstrap.min.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/fonts/custom-font.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/pcoded.js') }}"></script>
<script src="{{ asset('assets/admin/assets/js/plugins/feather.min.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>layout_change('light');</script>
<script>change_box_container('false');</script>
<script>layout_rtl_change('false');</script>
<script>preset_change("preset-1");</script>
<script>font_change("Public-Sans");</script>
<div class="flex items-center justify-end mt-4"> <script>
<x-primary-button> function doResetPassword() {
{{ __('Reset Password') }} try {
</x-primary-button> let csrfToken = document.getElementById('_token').getAttribute('value');
</div> let resetToken = document.getElementById('reset_token').value;
</form> let email = document.getElementById('email').value;
</x-guest-layout> let password = document.getElementById('password').value;
let password_confirmation = document.getElementById('password_confirmation').value;
if(email == '' || email == null) {
Swal.fire({
title: "Info",
text: "Email tidak boleh kosong!",
icon: "warning"
});
return;
}
if(password == '' || password == null) {
Swal.fire({
title: "Info",
text: "Password tidak boleh kosong!",
icon: "warning"
});
return;
}
if(password !== password_confirmation) {
Swal.fire({
title: "Info",
text: "Password dan konfirmasi password tidak cocok!",
icon: "warning"
});
return;
}
let xhttp = new XMLHttpRequest();
let formData = new FormData();
formData.append('_token', csrfToken);
formData.append('token', resetToken);
formData.append('email', email);
formData.append('password', password);
formData.append('password_confirmation', password_confirmation);
xhttp.onreadystatechange = function() {
if(this.status == 200) {
let jsonData = JSON.parse(this.responseText);
if(jsonData.status) {
Swal.fire({
title: "Info",
text: "Password berhasil direset!",
icon: "success"
});
window.location.href = '{{ route('login') }}';
} else {
Swal.fire({
title: "Info",
text: "Terdapat kesalahan!",
icon: "error"
});
}
} else {
Swal.fire({
title: "Info",
text: "Reset password gagal, koneksi ke server gagal, coba lagi nanti!",
icon: "error"
});
}
}
xhttp.open('POST', '{{ route("password.store") }}', false);
xhttp.send(formData);
} catch(error) {
Swal.fire({
title: "Info",
text: "Reset password gagal, kesalahan pada server, coba lagi nanti!",
icon: "error"
});
}
}
</script>
</body>
<!-- [Body] end -->
</html>

View File

@ -0,0 +1,273 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kode Unik Anda</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
/* background: linear-gradient(135deg, #84cc16 0%, #65a30d 100%); */
padding: 40px 20px;
line-height: 1.6;
}
.email-container {
max-width: 600px;
margin: 0 auto;
background: #ffffff;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
.header {
background: linear-gradient(135deg, #1e3ce6 0%, #b0b8ff 100%);
padding: 40px 30px;
text-align: center;
}
.header h1 {
color: #ffffff;
font-size: 28px;
font-weight: 700;
margin-bottom: 10px;
}
.header p {
color: rgba(255, 255, 255, 0.9);
font-size: 16px;
}
.content {
padding: 50px 40px;
}
.greeting {
font-size: 24px;
color: #2d3748;
font-weight: 600;
margin-bottom: 20px;
}
.message {
color: #4a5568;
font-size: 16px;
margin-bottom: 30px;
line-height: 1.8;
}
.button-container {
text-align: center;
margin: 40px 0;
color: #ffffff;
}
.reset-button {
display: inline-block;
background: linear-gradient(135deg, #84cc16 0%, #65a30d 100%);
color: #ffffff !important;
padding: 16px 48px;
text-decoration: none;
border-radius: 12px;
font-weight: 600;
font-size: 16px;
box-shadow: 0 10px 25px rgba(132, 204, 22, 0.4);
transition: all 0.3s ease;
}
.reset-button:hover {
transform: translateY(-2px);
box-shadow: 0 15px 35px rgba(132, 204, 22, 0.5);
}
.info-box {
background: #f7fee7;
border-left: 4px solid #7065ff;
padding: 20px;
border-radius: 8px;
margin: 30px 0;
}
.info-box .code {
background: #f7fee7;
border-left: 4px solid #7065ff;
padding: 20px;
border-radius: 8px;
margin: 30px 0;
}
.info-box p {
color: #4a5568;
font-size: 14px;
margin: 0;
}
.info-box strong {
color: #2d3748;
}
.warning {
background: #fff5f5;
border-left: 4px solid #f56565;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
}
.warning p {
color: #742a2a;
font-size: 14px;
margin: 0;
}
.alternative-link {
background: #edf2f7;
padding: 20px;
border-radius: 8px;
margin: 30px 0;
}
.alternative-link p {
color: #4a5568;
font-size: 13px;
margin-bottom: 10px;
}
.alternative-link a {
color: #84cc16;
word-break: break-all;
font-size: 12px;
}
.footer {
background: #2d3748;
padding: 40px;
text-align: center;
}
.footer p {
color: #a0aec0;
font-size: 14px;
margin-bottom: 10px;
}
.footer .company-name {
color: #ffffff;
font-weight: 600;
margin-bottom: 20px;
}
.social-links {
margin: 20px 0;
}
.social-links a {
display: inline-block;
margin: 0 10px;
color: #a0aec0;
text-decoration: none;
font-size: 14px;
}
.social-links a:hover {
color: #ffffff;
}
@media only screen and (max-width: 600px) {
body {
padding: 20px 10px;
}
.content {
padding: 30px 20px;
}
.header {
padding: 30px 20px;
}
.header h1 {
font-size: 24px;
}
.greeting {
font-size: 20px;
}
.reset-button {
padding: 14px 36px;
font-size: 15px;
}
}
</style>
</head>
<body>
<div class="email-container">
<!-- Header -->
<div class="header">
<div class="logo">
</div>
<h1>Smart Anxiety</h1>
<p>Sistem Pendeteksi Kecemasan</p>
</div>
<!-- Content -->
<div class="content">
<h2 class="greeting">Halo! 👋</h2>
<p class="message">
🔑 Berikut adalah kode unik Anda untuk verifikasi booking:
</p>
<div class="info-box code">
<h1 style="text-align: center">{{ $code }}</h1>
</div>
<div class="info-box">
<p><strong> Penting:</strong> Kode unik ini akan berlaku pada tanggal <strong>{{ $tglExpired }}</strong>, dari jam <strong>{{ $startExpirationTime }}</strong> sampai <strong>{{ $endExpirationTime }}</strong></p>
</div>
{{-- <div class="warning">
<p><strong>⚠️ Tidak meminta reset password?</strong> Abaikan email ini dan tidak ada perubahan yang akan
dilakukan pada akun Anda.</p>
</div> --}}
{{-- <div class="alternative-link">
<p><strong>Mengalami masalah dengan tombol di atas?</strong></p>
<p>Salin dan tempel URL berikut ke browser Anda:</p>
<a href="{{ $url }}">{{ $url }}</a>
</div> --}}
<p class="message" style="margin-top: 40px; font-size: 14px; color: #718096;">
Salam hangat,<br>
<strong>Tim Smart Anxiety</strong>
</p>
</div>
<!-- Footer -->
<div class="footer">
<p class="company-name">Smart Anxiety</p>
<p>Email ini dikirim secara otomatis, mohon tidak membalas email ini.</p>
<p style="margin-top: 20px; font-size: 12px;">
© {{ date('Y') }} Smart Anxiety. All rights reserved.
</p>
<div class="social-links">
<a href="#">Bantuan</a>
<a href="#">Kebijakan Privasi</a>
<a href="#">Syarat & Ketentuan</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -8,23 +8,22 @@
<div class="col-lg-5"> <div class="col-lg-5">
<div class="brand-section"> <div class="brand-section">
<a href="index.html" class="logo d-flex align-items-center mb-4"> <a href="index.html" class="logo d-flex align-items-center mb-4">
<span class="sitename">Clinic</span> <span class="sitename">{{ env('APP_NAME') }}</span>
</a> </a>
<p class="brand-description">Crafting exceptional digital experiences through thoughtful design and <p class="brand-description">Sistem berbasis web yang membantu proses asesmen kecemasan melalui analisis pergerakan pupil mata menggunakan teknologi eye-tracking dan machine learning.</p>
innovative solutions that elevate your brand presence.</p>
<div class="contact-info mt-5"> <div class="contact-info mt-5">
<div class="contact-item"> <div class="contact-item">
<i class="bi bi-geo-alt"></i> <i class="bi bi-geo-alt"></i>
<span>123 Creative Boulevard, Design District, NY 10012</span> <span>Politeknik Negeri Jember, Jurusan Teknologi Informasi, Jl. Mastrip, Krajan Timur, Sumbersari, Kec. Sumbersari, Kabupaten Jember, Jawa Timur</span>
</div> </div>
<div class="contact-item"> <div class="contact-item">
<i class="bi bi-telephone"></i> <i class="bi bi-telephone"></i>
<span>+1 (555) 987-6543</span> <span>+62 8233 7341 446</span>
</div> </div>
<div class="contact-item"> <div class="contact-item">
<i class="bi bi-envelope"></i> <i class="bi bi-envelope"></i>
<span>hello@designstudio.com</span> <span>e31231392@student.polije.ac.id</span>
</div> </div>
</div> </div>
</div> </div>
@ -34,65 +33,32 @@
<div class="footer-nav-wrapper"> <div class="footer-nav-wrapper">
<div class="row"> <div class="row">
<div class="col-6 col-lg-3"> <div class="col-6 col-lg-5">
<div class="nav-column"> <div class="nav-column">
<h6>Studio</h6> <h6>Informasi</h6>
<nav class="footer-nav"> <nav class="footer-nav">
<a href="#!">Our Story</a> <a href="https://maps.app.goo.gl/mLViMQhvs2KUq6An9" target="_blank">Politeknik Negeri Jember</a>
<a href="#!">Design Process</a> <a href="https://maps.app.goo.gl/2FpPBrs67Akm4PMZ9" target="_blank">Jurusan Teknologi Informasi</a>
<a href="#!">Portfolio</a> <a href="https://jti.polije.ac.id/lab/ksi" target="_blank">Lab. Komputasi Sistem Informasi</a>
<a href="#!">Case Studies</a> <a href="https://maps.app.goo.gl/C3eKW5gSoKqCTRYq8" target="_blank">Poliklinik Politeknik Negeri Jember</a>
<a href="#!">Awards</a>
</nav> </nav>
</div> </div>
</div> </div>
<div class="col-6 col-lg-3"> <div class="col-6 col-lg-5">
<div class="nav-column"> <div class="nav-column">
<h6>Services</h6> <h6>Contact Person</h6>
<nav class="footer-nav"> <nav class="footer-nav">
<a href="#!">Brand Identity</a> <a href="mailto:khafid@polije.ac.id" target="_blank">Khafidurrohman Agustianto, S.Pd, M.Eng</a>
<a href="#!">Web Design</a> <a href="mailto:myoga.bahtiar@gmail.com" target="_blank">Mahayoga Ksatria Hanafi Bahtiar</a>
<a href="#!">Mobile Apps</a>
<a href="#!">Digital Strategy</a>
<a href="#!">Consultation</a>
</nav> </nav>
</div> </div>
</div> </div>
<div class="col-6 col-lg-3">
<div class="nav-column">
<h6>Resources</h6>
<nav class="footer-nav">
<a href="#!">Design Blog</a>
<a href="#!">Style Guide</a>
<a href="#!">Free Assets</a>
<a href="#!">Tutorials</a>
<a href="#!">Inspiration</a>
</nav>
</div>
</div>
<div class="col-6 col-lg-3">
<div class="nav-column">
<h6>Connect</h6>
<nav class="footer-nav">
<a href="#!">Start Project</a>
<a href="#!">Schedule Call</a>
<a href="#!">Join Newsletter</a>
<a href="#!">Follow Updates</a>
<a href="#!">Partnership</a>
</nav>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="footer-bottom"> <div class="footer-bottom">
@ -102,7 +68,7 @@
<div class="col-lg-6"> <div class="col-lg-6">
<div class="copyright"> <div class="copyright">
<p>© <span class="sitename">Clinic</span>. All rights reserved.</p> <p>© <span class="sitename">{{ env('APP_NAME') }}</span>. All rights reserved.</p>
</div> </div>
</div> </div>
@ -112,11 +78,7 @@
<a href="#!">Terms of Service</a> <a href="#!">Terms of Service</a>
<a href="#!">Cookie Policy</a> <a href="#!">Cookie Policy</a>
<div class="credits"> <div class="credits">
<!-- All the links in the footer should remain intact. --> Designed by <a href="https://github.com/Mahayoga" target="_blank">Mahayoga</a>. Distributed by <a href="https://jti.polije.ac.id/lab/ksi" target="_blank">Lab. Komputasi Sistem Informasi</a>
<!-- You can delete the links only if you've purchased the pro version. -->
<!-- Licensing information: https://bootstrapmade.com/license/ -->
<!-- Purchase the pro version with working PHP/AJAX contact form: [buy-url] -->
Designed by <a href="https://bootstrapmade.com/">BootstrapMade</a>. Distributed by <a href="https://themewagon.com" target="_blank">ThemeWagon</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -28,6 +28,9 @@
<link href="{{ asset('assets/users/assets/vendor/fontawesome-free/css/all.min.css') }}" rel="stylesheet"> <link href="{{ asset('assets/users/assets/vendor/fontawesome-free/css/all.min.css') }}" rel="stylesheet">
<link href="{{ asset('assets/users/assets/vendor/swiper/swiper-bundle.min.css') }}" rel="stylesheet"> <link href="{{ asset('assets/users/assets/vendor/swiper/swiper-bundle.min.css') }}" rel="stylesheet">
<link href="https://cdn.datatables.net/2.3.7/css/dataTables.dataTables.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<!-- Main CSS File --> <!-- Main CSS File -->
<link href="{{ asset('assets/users/assets/css/main.css') }}" rel="stylesheet"> <link href="{{ asset('assets/users/assets/css/main.css') }}" rel="stylesheet">
@ -60,6 +63,7 @@ class="bi bi-arrow-up-short"></i></a>
<div id="preloader"></div> <div id="preloader"></div>
<!-- Vendor JS Files --> <!-- Vendor JS Files -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="{{ asset('assets/users/assets/vendor/bootstrap/js/bootstrap.bundle.min.js') }}"></script> <script src="{{ asset('assets/users/assets/vendor/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
@ -69,6 +73,10 @@ class="bi bi-arrow-up-short"></i></a>
<script src="{{ asset('assets/users/assets/vendor/purecounter/purecounter_vanilla.js') }}"></script> <script src="{{ asset('assets/users/assets/vendor/purecounter/purecounter_vanilla.js') }}"></script>
<script src="{{ asset('assets/users/assets/vendor/swiper/swiper-bundle.min.js') }}"></script> <script src="{{ asset('assets/users/assets/vendor/swiper/swiper-bundle.min.js') }}"></script>
<script src="https://cdn.datatables.net/2.3.7/js/dataTables.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/heatmap.js/2.0.1/heatmap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<!-- Main JS File --> <!-- Main JS File -->
<script src="{{ asset('assets/users/assets/js/main.js') }}"></script> <script src="{{ asset('assets/users/assets/js/main.js') }}"></script>

View File

@ -5,12 +5,12 @@
<div class="contact-info d-flex align-items-center"> <div class="contact-info d-flex align-items-center">
<i class="bi bi-envelope d-flex align-items-center"><a <i class="bi bi-envelope d-flex align-items-center"><a
href="mailto:e31231392@student.polije.ac.id">e31231392@student.polije.ac.id</a></i> href="mailto:e31231392@student.polije.ac.id">e31231392@student.polije.ac.id</a></i>
<i class="bi bi-phone d-flex align-items-center ms-4"><span>+62 8233 7341 446</span></i> <a href="https://wa.me/6282337341446" target="_blank"><i class="bi bi-phone d-flex align-items-center ms-4"><span>+62 8233 7341 446</span></i></a>
</div> </div>
<div class="social-links d-none d-md-flex align-items-center"> <div class="social-links d-none d-md-flex align-items-center">
<a href="#!" class="twitter"><i class="bi bi-github"></i></a> <a href="https://github.com/Mahayoga" class="twitter"><i class="bi bi-github"></i></a>
<a href="#!" class="instagram"><i class="bi bi-instagram"></i></a> <a href="https://www.instagram.com/_myoga.exe.zip/" class="instagram"><i class="bi bi-instagram"></i></a>
<a href="#!" class="linkedin"><i class="bi bi-discord"></i></a> <a href="https://discord.com/users/681789849342050304" class="linkedin"><i class="bi bi-discord"></i></a>
</div> </div>
</div> </div>
</div><!-- End Top Bar --> </div><!-- End Top Bar -->
@ -21,16 +21,16 @@
<a href="{{ route('beranda.index') }}" class="logo d-flex align-items-center"> <a href="{{ route('beranda.index') }}" class="logo d-flex align-items-center">
<!-- Uncomment the line below if you also wish to use an image logo --> <!-- Uncomment the line below if you also wish to use an image logo -->
<!-- <img src="assets/img/logo.webp" alt=""> --> <!-- <img src="assets/img/logo.webp" alt=""> -->
<h1 class="sitename">Smart Anxiety</h1> <h1 class="sitename">{{ env('APP_NAME') }}</h1>
</a> </a>
<nav id="navmenu" class="navmenu"> <nav id="navmenu" class="navmenu">
<ul> <ul>
<li><a href="{{ route('beranda.index') }}" class="active">Home</a></li> <li><a href="{{ route('beranda.index') }}" class="{{ request()->routeIs('beranda.index') ? 'active' : '' }}">Home</a></li>
<li><a href="{{ route('beranda.about') }}">About</a></li> <li><a href="{{ route('beranda.about') }}" class="{{ request()->routeIs('beranda.about') ? 'active' : '' }}">About</a></li>
<li><a href="{{ route('beranda.about') }}#system-architecture-tabs">System Architecture</a></li> <li><a href="{{ route('beranda.about') }}#system-architecture-tabs" class="">System Architecture</a></li>
<li><a href="{{ route('cek-status.index') }}">Cek Status</a></li> <li><a href="{{ route('cek-status.index') }}" class="{{ request()->routeIs('cek-status.index') ? 'active' : '' }}">Cek Status</a></li>
<li><a href="contact.html">Contact</a></li> <li><a href="#footer">Contact</a></li>
@if (session('is_logged_in')) @if (session('is_logged_in'))
<li class="dropdown"><a href="#"><span>{{ Auth::user()->name }}</span> <i class="bi bi-chevron-down toggle-dropdown"></i></a> <li class="dropdown"><a href="#"><span>{{ Auth::user()->name }}</span> <i class="bi bi-chevron-down toggle-dropdown"></i></a>
<ul> <ul>

View File

@ -40,7 +40,7 @@
<div class="col-lg-6" data-aos="fade-left" data-aos-delay="200"> <div class="col-lg-6" data-aos="fade-left" data-aos-delay="200">
<div class="image-wrapper"> <div class="image-wrapper">
<img src="{{ asset('assets/users/assets/img/health/facilities-6.webp') }}" class="img-fluid main-image" alt="Healthcare facility"> <img src="{{ asset('assets/users/assets/img/tobii_eye_tracker_device.webp') }}" class="img-fluid main-image" alt="Healthcare facility">
</div><!-- End Image Wrapper --> </div><!-- End Image Wrapper -->
</div> </div>
</div> </div>
@ -48,7 +48,7 @@
<div class="row align-items-center my-5"> <div class="row align-items-center my-5">
<div class="col-lg-6" data-aos="fade-left" data-aos-delay="200"> <div class="col-lg-6" data-aos="fade-left" data-aos-delay="200">
<div class="image-wrapper"> <div class="image-wrapper">
<img src="{{ asset('assets/users/assets/img/health/facilities-6.webp') }}" class="img-fluid main-image" alt="Healthcare facility"> <img src="{{ asset('assets/users/assets/img/gangguan-cemas-menyeluruh.jpg') }}" class="img-fluid main-image" alt="Healthcare facility">
</div><!-- End Image Wrapper --> </div><!-- End Image Wrapper -->
</div> </div>
@ -72,40 +72,40 @@
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="100"> <div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="100">
<div class="value-item"> <div class="value-item">
<div class="value-icon"> <div class="value-icon">
<i class="bi bi-heart-pulse"></i> <i class="bi bi-eye"></i>
</div> </div>
<h4>Compassion</h4> <h4>Eye Movement Monitoring</h4>
<p>Membantu tenaga profesional dalam proses asesmen kecemasan</p> <p>Memantau pergerakan mata dan pola fokus pengguna selama proses asesmen berlangsung.</p>
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="200"> <div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="200">
<div class="value-item"> <div class="value-item">
<div class="value-icon"> <div class="value-icon">
<i class="bi bi-clipboard-data"></i> <i class="bi bi-activity"></i>
</div> </div>
<h4>Excellence</h4> <h4>Anxiety Estimation</h4>
<p>Menyediakan analisis berbasis data terhadap respon visual pasien</p> <p>Menganalisis tingkat kecemasan berdasarkan data visual menggunakan model machine learning.</p>
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="300"> <div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="300">
<div class="value-item"> <div class="value-item">
<div class="value-icon"> <div class="value-icon">
<i class="bi bi-flask"></i> <i class="bi bi-clipboard2-pulse"></i>
</div> </div>
<h4>Integrity</h4> <h4>Decision Support</h4>
<p>Menyajikan hasil estimasi tingkat kecemasan secara sistematis</p> <p>Membantu tenaga profesional dalam proses evaluasi kondisi psikologis pengguna.</p>
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="400"> <div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="400">
<div class="value-item"> <div class="value-item">
<div class="value-icon"> <div class="value-icon">
<i class="bi bi-regex"></i> <i class="bi bi-display"></i>
</div> </div>
<h4>Innovation</h4> <h4>Real-Time Visualization</h4>
<p>Mendukung proses pengambilan keputusan selama konsultasi</p> <p>Menampilkan hasil analisis dan aktivitas visual pengguna secara real-time pada dashboard.</p>
</div> </div>
</div> </div>
</div><!-- End Values Row --> </div><!-- End Values Row -->
@ -123,40 +123,40 @@
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="100"> <div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="100">
<div class="value-item"> <div class="value-item">
<div class="value-icon"> <div class="value-icon">
<i class="bi bi-eye"></i> <i class="bi bi-camera-video"></i>
</div> </div>
<h4>Compassion</h4> <h4>Eye Tracking Capture</h4>
<p>Kamera menangkap pergerakan pupil pengguna</p> <p>Kamera atau eye tracker menangkap koordinat dan pergerakan pupil pengguna.</p>
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="300"> <div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="300">
<div class="value-item">
<div class="value-icon">
<i class="bi bi-bar-chart-steps"></i>
</div>
<h4>Integrity</h4>
<p>Fitur pergerakan mata diekstraksi</p>
</div>
</div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="400">
<div class="value-item"> <div class="value-item">
<div class="value-icon"> <div class="value-icon">
<i class="bi bi-cpu"></i> <i class="bi bi-cpu"></i>
</div> </div>
<h4>Innovation</h4> <h4>Data Processing</h4>
<p>Model machine learning melakukan klasifikasi</p> <p>Data gaze dan pergerakan mata diproses menjadi fitur analisis.</p>
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="400"> <div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="400">
<div class="value-item"> <div class="value-item">
<div class="value-icon"> <div class="value-icon">
<i class="bi bi-easel"></i> <i class="bi bi-cpu-fill"></i>
</div> </div>
<h4>Innovation</h4> <h4>Machine Learning Classification</h4>
<p>Hasil estimasi kecemasan ditampilkan pada dashboard</p> <p>Model machine learning melakukan klasifikasi tingkat kecemasan berdasarkan pola visual.</p>
</div>
</div>
<div class="col-lg-3 col-md-6" data-aos="fade-up" data-aos-delay="400">
<div class="value-item">
<div class="value-icon">
<i class="bi bi-layout-text-window-reverse"></i>
</div>
<h4>Result Visualization</h4>
<p>Hasil estimasi kecemasan ditampilkan dalam bentuk dashboard dan visualisasi data.</p>
</div> </div>
</div> </div>
</div><!-- End Values Row --> </div><!-- End Values Row -->
@ -183,19 +183,19 @@
data-aos-delay="400"> data-aos-delay="400">
<a class="nav-link department-tab active" id="neurology-tab" data-bs-toggle="pill" <a class="nav-link department-tab active" id="neurology-tab" data-bs-toggle="pill"
href="#camera-tabs" role="tab" aria-controls="camera-tabs" href="#camera-tabs" role="tab" aria-controls="camera-tabs"
aria-selected="true" data-aos="fade-up" data-aos-delay="100">Camera</a> aria-selected="true" data-aos="fade-up" data-aos-delay="100">Facial Analysis</a>
<a class="nav-link department-tab" id="surgery-tab" data-bs-toggle="pill" <a class="nav-link department-tab" id="surgery-tab" data-bs-toggle="pill"
href="#eye-detection-tabs" role="tab" aria-controls="eye-detection-tabs" href="#eye-detection-tabs" role="tab" aria-controls="eye-detection-tabs"
aria-selected="false" data-aos="fade-up" data-aos-delay="150">Eye Detection</a> aria-selected="false" data-aos="fade-up" data-aos-delay="150">Gaze Tracking</a>
<a class="nav-link department-tab" id="dental-tab" data-bs-toggle="pill" <a class="nav-link department-tab" id="dental-tab" data-bs-toggle="pill"
href="#departments-tabs-dental" role="tab" aria-controls="departments-tabs-dental" href="#departments-tabs-dental" role="tab" aria-controls="departments-tabs-dental"
aria-selected="false" data-aos="fade-up" data-aos-delay="200">Feature Extraction</a> aria-selected="false" data-aos="fade-up" data-aos-delay="200">Analytics</a>
<a class="nav-link department-tab" id="ophthalmology-tab" data-bs-toggle="pill" <a class="nav-link department-tab" id="ophthalmology-tab" data-bs-toggle="pill"
href="#departments-tabs-ophthalmology" role="tab" aria-controls="departments-tabs-ophthalmology" href="#departments-tabs-ophthalmology" role="tab" aria-controls="departments-tabs-ophthalmology"
aria-selected="false" data-aos="fade-up" data-aos-delay="250">ML Prediction</a> aria-selected="false" data-aos="fade-up" data-aos-delay="250">AI Assessment</a>
<a class="nav-link department-tab" id="cardiology-tab" data-bs-toggle="pill" <a class="nav-link department-tab" id="cardiology-tab" data-bs-toggle="pill"
href="#departments-tabs-cardiology" role="tab" aria-controls="departments-tabs-cardiology" href="#departments-tabs-cardiology" role="tab" aria-controls="departments-tabs-cardiology"
aria-selected="false" data-aos="fade-up" data-aos-delay="300">Result</a> aria-selected="false" data-aos="fade-up" data-aos-delay="300">Technologies</a>
</div> </div>
</div> </div>
</div> </div>
@ -214,51 +214,53 @@
</div> </div>
<div class="col-lg-8 order-lg-1"> <div class="col-lg-8 order-lg-1">
<div class="department-info"> <div class="department-info">
<h2 class="department-title">Camera</h2> <h2 class="department-title">Facial Expression Analysis</h2>
<p class="department-description">Kamera digunakan pada sistem ini untuk menangkap dan merekam pergerakan pengguna sebagai referensi kedua dari 'eye tracking'. Data dari rekaman kamera ini akan digunakan untuk menjadikan ekspresi muka sebagai bobot kedua dari menentukan kecemasan.</p> <p class="department-description">
Kamera digunakan untuk menangkap ekspresi wajah dan pergerakan pengguna selama proses asesmen berlangsung.
Data visual ini digunakan sebagai sumber analisis tambahan untuk membantu model machine learning dalam mengestimasi tingkat kecemasan pengguna.
</p>
<div class="row mt-4"> <div class="row mt-4">
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-brain"></i> <i class="bi bi-person-bounding-box"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Brain Monitoring</h4> <h4>Face Detection</h4>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore.</p> <p>Sistem mendeteksi area wajah pengguna secara real-time selama asesmen berlangsung.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-wave-square"></i> <i class="bi bi-eye"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>EEG Testing</h4> <h4>Eye Movement Tracking</h4>
<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.</p> <p>Pergerakan mata dan titik fokus pengguna dianalisis untuk memperoleh pola visual.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-stethoscope"></i> <i class="bi bi-emoji-neutral"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Neurological Exam</h4> <h4>Emotion Observation</h4>
<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium.</p> <p>Ekspresi wajah digunakan sebagai indikator tambahan dalam analisis kecemasan.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-file-medical"></i> <i class="bi bi-cpu"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Treatment Plans</h4> <h4>AI Classification</h4>
<p>At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis.</p> <p>Model machine learning melakukan estimasi tingkat kecemasan berdasarkan data visual pengguna.</p>
</div> </div>
</div> </div>
</div> </div>
@ -277,53 +279,54 @@
</div> </div>
<div class="col-lg-8 order-lg-1"> <div class="col-lg-8 order-lg-1">
<div class="department-info"> <div class="department-info">
<h2 class="department-title">Eye Detection</h2> <h2 class="department-title">Eye Tracking Analysis</h2>
<p class="department-description">Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut <p class="department-description">
odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Sistem melakukan analisis pergerakan mata pengguna menggunakan teknologi eye tracking untuk memperoleh pola perhatian visual selama proses asesmen.
Data gaze yang diperoleh digunakan sebagai fitur utama dalam proses estimasi tingkat kecemasan berbasis machine learning.
</p> </p>
<div class="row mt-4"> <div class="row mt-4">
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-cut"></i> <i class="bi bi-eye"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Minimally Invasive</h4> <h4>Gaze Tracking</h4>
<p>Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet.</p> <p>Sistem melacak arah pandangan dan titik fokus mata pengguna secara real-time.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-tools"></i> <i class="bi bi-crosshair"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Advanced Procedures</h4> <h4>Fixation Analysis</h4>
<p>Consectetur, adipisci velit, sed quia non numquam eius modi tempora.</p> <p>Durasi fokus pengguna pada area tertentu dianalisis untuk mendeteksi pola perhatian visual.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-shield-alt"></i> <i class="bi bi-grid-3x3-gap"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Safe Operations</h4> <h4>Heatmap Visualization</h4>
<p>Incidunt ut labore et dolore magnam aliquam quaerat voluptatem.</p> <p>Pergerakan mata divisualisasikan dalam bentuk heatmap untuk melihat distribusi perhatian pengguna.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-clock"></i> <i class="bi bi-cpu"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Recovery Support</h4> <h4>AI-Based Classification</h4>
<p>Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis.</p> <p>Model machine learning mengklasifikasikan tingkat kecemasan berdasarkan pola gaze pengguna.</p>
</div> </div>
</div> </div>
</div> </div>
@ -342,53 +345,54 @@
</div> </div>
<div class="col-lg-8 order-lg-1"> <div class="col-lg-8 order-lg-1">
<div class="department-info"> <div class="department-info">
<h2 class="department-title">Dental Care Department</h2> <h2 class="department-title">Result Visualization</h2>
<p class="department-description">Suscipit laboriosam, nisi ut aliquid ex ea commodi <p class="department-description">
consequatur. Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil Sistem menampilkan hasil analisis eye tracking dan estimasi kecemasan dalam bentuk visualisasi data interaktif secara real-time.
molestiae.</p> Dashboard digunakan untuk membantu proses observasi, evaluasi, dan interpretasi hasil analisis pengguna.
</p>
<div class="row mt-4"> <div class="row mt-4">
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="far fa-smile"></i> <i class="bi bi-grid-3x3-gap"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Oral Health</h4> <h4>Heatmap Analytics</h4>
<p>Consequatur aut perferendis doloribus asperiores repellat tempore.</p> <p>Visualisasi heatmap digunakan untuk melihat distribusi perhatian visual pengguna pada layar.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-tooth"></i> <i class="bi bi-activity"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Teeth Cleaning</h4> <h4>Real-Time Monitoring</h4>
<p>Et harum quidem rerum facilis est et expedita distinctio nam libero.</p> <p>Sistem memantau aktivitas pengguna dan data eye tracking secara langsung selama asesmen berlangsung.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-star"></i> <i class="bi bi-graph-up-arrow"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Cosmetic Dentistry</h4> <h4>Anxiety Prediction</h4>
<p>Tempore cum soluta nobis est eligendi optio cumque nihil impedit quo minus.</p> <p>Model machine learning menghasilkan estimasi tingkat kecemasan berdasarkan pola visual pengguna.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-cog"></i> <i class="bi bi-display"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Orthodontics</h4> <h4>Interactive Dashboard</h4>
<p>Id quod maxime placeat facere possimus, omnis voluptas assumenda est.</p> <p>Seluruh hasil analisis divisualisasikan melalui dashboard interaktif yang mudah dipahami.</p>
</div> </div>
</div> </div>
</div> </div>
@ -408,52 +412,53 @@
</div> </div>
<div class="col-lg-8 order-lg-1"> <div class="col-lg-8 order-lg-1">
<div class="department-info"> <div class="department-info">
<h2 class="department-title">Ophthalmology Department</h2> <h2 class="department-title">Behavioral Analysis</h2>
<p class="department-description">Omnis dolor repellendus temporibus autem quibusdam et aut <p class="department-description">
officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae.</p> Sistem menganalisis pola perilaku visual pengguna berdasarkan pergerakan mata, fokus perhatian, dan ekspresi wajah untuk membantu proses estimasi tingkat kecemasan secara lebih akurat.
</p>
<div class="row mt-4"> <div class="row mt-4">
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-eye"></i> <i class="bi bi-bullseye"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Vision Testing</h4> <h4>Attention Analysis</h4>
<p>Sint et voluptatum sint quia dolor sit amet consectetur adipiscing elit.</p> <p>Sistem mengevaluasi fokus dan perhatian visual pengguna selama proses asesmen berlangsung.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-camera"></i> <i class="bi bi-emoji-neutral"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Retinal Imaging</h4> <h4>Facial Behavior</h4>
<p>Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua ut.</p> <p>Ekspresi wajah dianalisis untuk membantu mendeteksi perubahan perilaku emosional pengguna.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-bolt"></i> <i class="bi bi-diagram-3"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Laser Surgery</h4> <h4>Pattern Recognition</h4>
<p>Enim ad minim veniam quis nostrud exercitation ullamco laboris nisi.</p> <p>Model AI mengenali pola visual tertentu yang berkaitan dengan tingkat kecemasan pengguna.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-prescription-bottle"></i> <i class="bi bi-cpu"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Eye Care Plans</h4> <h4>AI Assessment</h4>
<p>Ut aliquip ex ea commodo consequat duis aute irure dolor in reprehenderit.</p> <p>Machine learning menghasilkan estimasi tingkat kecemasan berdasarkan kombinasi data visual pengguna.</p>
</div> </div>
</div> </div>
</div> </div>
@ -473,52 +478,53 @@
</div> </div>
<div class="col-lg-8 order-lg-1"> <div class="col-lg-8 order-lg-1">
<div class="department-info"> <div class="department-info">
<h2 class="department-title">Cardiology Department</h2> <h2 class="department-title">Core Technologies</h2>
<p class="department-description">In voluptate velit esse cillum dolore eu fugiat nulla pariatur <p class="department-description">
excepteur sint occaecat cupidatat non proident sunt in culpa qui officia deserunt.</p> Sistem dibangun menggunakan kombinasi teknologi eye tracking, machine learning, dan visualisasi data real-time untuk mendukung proses analisis kecemasan secara interaktif dan akurat.
</p>
<div class="row mt-4"> <div class="row mt-4">
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-heartbeat"></i> <i class="bi bi-eye"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Heart Monitoring</h4> <h4>Eye Tracking Device</h4>
<p>Mollit anim id est laborum sed ut perspiciatis unde omnis iste natus error.</p> <p>Perangkat eye tracker digunakan untuk menangkap koordinat dan pergerakan mata pengguna secara real-time.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-chart-line"></i> <i class="bi bi-cpu"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>ECG Analysis</h4> <h4>Machine Learning</h4>
<p>Sit voluptatem accusantium doloremque laudantium totam rem aperiam.</p> <p>Model AI menganalisis pola visual pengguna untuk menghasilkan estimasi tingkat kecemasan.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-tint"></i> <i class="bi bi-wifi"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Blood Tests</h4> <h4>Real-Time Communication</h4>
<p>Eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae.</p> <p>Teknologi WebSocket digunakan untuk mengirim data monitoring secara langsung dan responsif.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="service-item"> <div class="service-item">
<div class="service-icon"> <div class="service-icon">
<i class="fas fa-shield-heart"></i> <i class="bi bi-display"></i>
</div> </div>
<div class="service-content"> <div class="service-content">
<h4>Preventive Care</h4> <h4>Interactive Dashboard</h4>
<p>Vitae dicta sunt explicabo nemo enim ipsam voluptatem quia voluptas sit.</p> <p>Dashboard interaktif menampilkan visualisasi data, heatmap, dan hasil analisis AI secara real-time.</p>
</div> </div>
</div> </div>
</div> </div>
@ -565,7 +571,7 @@
</div> </div>
<div class="col-lg-2 col-md-3 col-sm-4 col-6" data-aos="zoom-in" data-aos-delay="400"> <div class="col-lg-2 col-md-3 col-sm-4 col-6" data-aos="zoom-in" data-aos-delay="400">
<div class="certification-item"> <div class="certification-item">
<img src="{{ asset('assets/users/assets/img/logo/tensorflow_logo.webp') }}" class="img-fluid" alt="Tensorflow Logo"> <img src="{{ asset('assets/users/assets/img/logo/node-js.png') }}" class="img-fluid" alt="Node.js Logo">
</div> </div>
</div> </div>
</div><!-- End Certifications Row --> </div><!-- End Certifications Row -->

View File

@ -58,7 +58,7 @@
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
<li class="list-group-item">Nama Lengkap: <span id="nama_lengkap"></span></li> <li class="list-group-item">Nama Lengkap: <span id="nama_lengkap"></span></li>
<li class="list-group-item">Kode Unik: <span id="kode_unik"></span></li> <li class="list-group-item">Kode Unik: <span id="kode_unik"></span></li>
<li class="list-group-item">Tgl Booking: <span id="tgl_booking"></span></li> <li class="list-group-item">Tgl dan Jam Ujian (sesuai booking): <span id="tgl_booking"></span><span id="jam_booking"></span></li>
<li class="list-group-item">Deskripsi Tambahan: <span id="deskripsi"></span></li> <li class="list-group-item">Deskripsi Tambahan: <span id="deskripsi"></span></li>
<li class="list-group-item">Status Tes: <span id="status"></span></li> <li class="list-group-item">Status Tes: <span id="status"></span></li>
</ul> </ul>
@ -85,6 +85,8 @@
<form action="" method="POST" id="result_test_form"> <form action="" method="POST" id="result_test_form">
@csrf @csrf
<input type="hidden" name="id_booking" id="id_booking_input"> <input type="hidden" name="id_booking" id="id_booking_input">
<input type="hidden" name="screen_w" id="screen_w">
<input type="hidden" name="screen_h" id="screen_h">
<div class="row justify-content-center"> <div class="row justify-content-center">
<button type="button" class="btn btn-primary text-white fw-bold" onclick="startSessionTest()">Mulai Tes</button> <button type="button" class="btn btn-primary text-white fw-bold" onclick="startSessionTest()">Mulai Tes</button>
</div> </div>
@ -137,28 +139,29 @@ function searchUserStatus() {
xhttp.onreadystatechange = function() { xhttp.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) { if(this.readyState == 4 && this.status == 200) {
let response = JSON.parse(this.responseText); let response = JSON.parse(this.responseText);
let status_dict = ['Belum dikonfirmasi (cek email anda)', 'Sudah dikonfirmasi, belum melakukan/menyelesaikan tes', 'Sudah melakukan tes']; let status_dict = ['Sesi anda dinonaktifkan karena telah kedaluwarsa atau belum sesuai jadwal yang telah di booking', 'Belum melakukan/menyelesaikan tes', 'Sudah melakukan tes'];
if(response.status) { if(response.status) {
$('#result_data').show(); $('#result_data').show();
$('#nama_lengkap').html(response.nama_lengkap); $('#nama_lengkap').html(response.nama_lengkap);
$('#kode_unik').html(response.kode_unik); $('#kode_unik').html(response.kode_unik);
$('#tgl_booking').html(response.tgl_booking); $('#tgl_booking').html(`<b>${response.tgl_booking}</b>`);
$('#jam_booking').html(", Valid dari jam <b>" + response.start_test + "</b>, sampai dengan jam <b>" + response.end_test + "</b>");
$('#deskripsi').html(response.deskripsi_tambahan); $('#deskripsi').html(response.deskripsi_tambahan);
$('#status').html(status_dict[response.status_data]); $('#status').html(status_dict[response.status_data]);
Swal.close(); Swal.close();
if(response.status_data == 0) { if(response.status_data == 0) {
$('#alert_status').removeClass(); $('#alert_status').removeClass();
$('#alert_status').addClass('alert alert-primary'); $('#alert_status').addClass('alert alert-warning');
$('#alert_status').html('Kamu belum dikonfirmasi dengan psikolog, cek waktu konsultasi pada email anda untuk melakukan aktivasi!'); $('#alert_status').html('Session kamu dinonaktifkan atau belum sesuai jam yang telah di booking!');
} else if(response.status_data == 1) { } else if(response.status_data == 1) {
$('#alert_status').removeClass(); $('#alert_status').removeClass();
$('#alert_status').addClass('alert alert-info'); $('#alert_status').addClass('alert alert-info');
$('#alert_status').html('Kamu sudah dikonfirmasi oleh psikolog!'); $('#alert_status').html('Kamu bisa memulai tes sekarang!');
let test_session = getTestSession(response.id_booking); let test_session = getTestSession(response.id_booking);
$('#result_test').show(); $('#result_test').show();
$('#result_test_title').html('Kamu sudah dikonfirmasi oleh psikolog! Kalau kamu sudah siap menjalankan tes, klik tombol di bawah untuk mulai tes kecemasan dari sistem kami!'); $('#result_test_title').html('Kalau kamu sudah siap menjalankan tes, klik tombol di bawah untuk mulai tes kecemasan dari sistem kami!');
if(test_session.status) { if(test_session.status) {
if(test_session.status_session == 0) { if(test_session.status_session == 0) {
$('#id_booking_input').val(response.id_booking); $('#id_booking_input').val(response.id_booking);
@ -179,11 +182,15 @@ function searchUserStatus() {
$('#result_test_status').html('Selesai'); $('#result_test_status').html('Selesai');
$('#result_test_status').removeClass(); $('#result_test_status').removeClass();
$('#result_test_status').addClass('fw-bold text-white bg-success p-2 rounded-pill'); $('#result_test_status').addClass('fw-bold text-white bg-success p-2 rounded-pill');
let test_url = '{{ route('hasil-tes.index', ['id_test_sessions' => '__ID_TEST_SESSIONS__']) }}';
$('#result_test_link').show();
$('#result_test_link').html('Tes Sudah Selesai, Klik untuk melihat hasil');
$('#result_test_link').attr('href', test_url.replace('__ID_TEST_SESSIONS__', test_session.id_test_sessions));
} }
} else if(!test_session.status) { } else if(!test_session.status) {
$('#id_booking_input').val(response.id_booking); $('#id_booking_input').val(response.id_booking);
$('#result_test').show(); $('#result_test').show();
$('#result_test_title').html('Kamu sudah dikonfirmasi oleh psikolog! Kalau kamu sudah siap menjalankan tes, klik tombol di bawah untuk mulai tes kecemasan dari sistem kami!'); $('#result_test_title').html('Kalau kamu sudah siap menjalankan tes, klik tombol di bawah untuk mulai tes kecemasan dari sistem kami!');
$('#result_test_status').html('Belum Dimulai'); $('#result_test_status').html('Belum Dimulai');
$('#result_test_status').removeClass(); $('#result_test_status').removeClass();
@ -191,9 +198,22 @@ function searchUserStatus() {
$('#result_test_form').show(); $('#result_test_form').show();
} }
} else if(response.status_data == 2) { } else if(response.status_data == 2) {
let test_session = getTestSession(response.id_booking);
$('#alert_status').removeClass(); $('#alert_status').removeClass();
$('#alert_status').addClass('alert alert-success'); $('#alert_status').addClass('alert alert-success');
$('#alert_status').html('Kamu sudah melakukan tes, dan bisa cek hasil dengan tombol dibawah'); $('#alert_status').html('Kamu sudah melakukan tes, dan bisa cek hasil dengan tombol dibawah');
$('#result_test_status').html('Selesai');
$('#result_test_status').removeClass();
$('#result_test_status').addClass('fw-bold text-white bg-success p-2 rounded-pill');
let test_url = '{{ route('hasil-tes.index', ['id_test_sessions' => '__ID_TEST_SESSIONS__']) }}';
$('#result_test_form').hide();
$('#result_test_link').show();
$('#result_test_link').html('Tes Sudah Selesai, Klik untuk melihat hasil');
$('#result_test_link').attr('href', test_url.replace('__ID_TEST_SESSIONS__', test_session.id_test_sessions));
$('#result_test').show();
} }
} else { } else {
Swal.fire({ Swal.fire({
@ -254,6 +274,8 @@ function startSessionTest() {
cancelButtonText: 'Nggak jadi deh' cancelButtonText: 'Nggak jadi deh'
}).then((result) => { }).then((result) => {
if(result.isConfirmed) { if(result.isConfirmed) {
$('#screen_w').val(window.screen.width);
$('#screen_h').val(window.screen.height);
form.attr('action', url); form.attr('action', url);
form.submit(); form.submit();
} }

View File

@ -0,0 +1,756 @@
@section('title', 'Hasil Tes')
@extends('user.layouts.index')
@section('content')
<style>
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.stat-card {
padding: 20px;
border-radius: 12px;
background: #f5f5f5;
}
.bg-custom {
background: #f5f5f5;
}
</style>
<!-- Page Title -->
<div class="page-title">
<div class="heading">
<div class="container">
<div class="row d-flex justify-content-center text-center">
<div class="col-lg-8">
<h1 class="heading-title">Hasil Tes Anda</h1>
<p class="mb-0">
Cek hasil tes kamu dari sini ya, jika kamu sudah mendapatkan kode dari email setelah melakukan booking, kamu bisa melihat hasil tes disini dengan kode tadi.
</p>
</div>
</div>
</div>
</div>
<nav class="breadcrumbs">
<div class="container">
<ol>
<li><a href="{{ route('beranda.index') }}">Home</a></li>
<li class="current">Hasil Tes</li>
</ol>
</div>
</nav>
</div><!-- End Page Title -->
<!-- About Section -->
<section id="about" class="about section">
<div class="container mt-5">
<h1 class="text-center mb-4">
Prediksi & Analisis Kecemasan
</h1>
<div class="row g-4">
<!-- STAI Score -->
<div class="col-md-4">
<div class="card h-100 shadow-sm">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div class="col-md-10">
<h4 class="card-title">
Skor STAI-T
</h4>
<h2 class="mt-4">
{{ $data_aswered['totalScore'] }}
</h2>
<p class="text-muted">
Hasil dari kuesioner kecemasan
</p>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-clipboard2-pulse fs-3"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Anxiety Level -->
<div class="col-md-4">
<div class="card h-100 shadow-sm">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div class="col-md-10">
<h4 class="card-title">
Tingkat Kecemasan
</h4>
@if ($data_aswered['totalScore'] <= 37)
<h2 class="mt-4 text-success">
Normal
</h2>
@elseif ($data_aswered['totalScore'] <= 44)
<h2 class="mt-4 text-warning">
Sedang
</h2>
@elseif ($data_aswered['totalScore'] <= 80)
<h2 class="mt-4 text-danger">
Tinggi
</h2>
@endif
<p class="text-muted">
Berdasarkan skor <a href="https://headway.co/resources/state-trait-anxiety-inventory-scoring" target="_blank"><i>STAI-T</i></a>
</p>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-emoji-neutral fs-3"></i>
</div>
</div>
</div>
</div>
</div>
<!-- AI Prediction -->
<div class="col-md-4">
<div class="card h-100 shadow-sm">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div class="col-md-10">
<h4 class="card-title">
Prediksi AI
</h4>
<h2 class="mt-4" id="result-model">
Loading...
</h2>
<p class="text-muted">
Model Confidence: <span id="confidence-model"></span>
</p>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-cpu fs-3"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
<h2 class="title text-center">Detail Tes/Ujian kamu</h2>
<div class="row">
<div class="col-md-4">
<div class="bg-custom p-4 my-3 border rounded">
<div class="row">
<div class="col-md-10">
<h5 class="text-body">Informasi Pribadi</h5>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-person-vcard fs-4"></i>
</div>
</div>
<ul class="list-unstyled">
<li><strong>Kode Booking:</strong> {{ $data_information['kode'] }}</li>
<li><strong>Nama:</strong> {{ $data_information['nama'] }}</li>
<li><strong>Email:</strong> {{ $data_information['email'] }}</li>
<li><strong>Nomor Handphone:</strong> {{ $data_information['no_hp'] }}</li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="bg-custom p-4 my-3 border rounded">
<div class="row">
<div class="col-md-10">
<h5 class="text-body">Informasi Ujian</h5>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-clipboard fs-4"></i>
</div>
</div>
<ul class="list-unstyled">
<li><strong>Durasi Ujian:</strong> {{ $difference }}</li>
<li><strong>Resolusi Layar (saat ujian):</strong> {{ $screen_dim['width'] }} x {{ $screen_dim['height'] }}</li>
<li><strong>Jumlah Pergerakan Mata:</strong> <span id="total_movement">0</span></li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="bg-custom p-4 my-3 border rounded">
<div class="row">
<div class="col-md-10">
<h5 class="text-body">Hasil Ujian (STAI-T)</h5>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-easel fs-4"></i>
</div>
</div>
<ul class="list-unstyled">
<li><strong>Jumlah Soal:</strong> {{ $data_aswered['total_question'] }}</li>
<li><strong>Soal terjawab:</strong> {{ $data_aswered['answered'] }}</li>
<li><strong>Soal tidak terjawab:</strong> {{ $data_aswered['unanswered'] }}</li>
<li><strong>Total Skor:</strong> {{ $data_aswered['totalScore'] }}</li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="bg-custom p-4 my-3 border rounded">
<div class="row">
<div class="col-md-10">
<h5 class="text-body">Detail Confidence Prediksi Model</h5>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-easel fs-4"></i>
</div>
</div>
<ul class="list-unstyled">
<li><strong>Normal:</strong> <span id="normal-confidence">0%</span></li>
<li><strong>Sedang:</strong> <span id="sedang-confidence"></span></li>
<li><strong>Tinggi:</strong> <span id="tinggi-confidence"></span></li>
</ul>
</div>
</div>
<div class="col-md-4">
<div class="bg-custom p-4 my-3 border rounded">
<div class="row">
<div class="col-md-10">
<h5 class="text-body">Avg Velocity</h5>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-speedometer2 fs-4"></i>
</div>
</div>
<h4 id="avgVelocity"></h4>
<div class="row mt-2">
<div class="col-md-12">
<div class="btn btn-link p-0" onclick="showInsight('velocity')"><i class="bi bi-info-circle"></i> Lihat Insight</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="bg-custom p-4 my-3 border rounded">
<div class="row">
<div class="col-md-10">
<h5 class="text-body">Fixation Ratio</h5>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-bullseye fs-4"></i>
</div>
</div>
<h4 id="fixationRatio"></h4>
<div class="row mt-2">
<div class="col-md-12">
<div class="btn btn-link p-0" onclick="showInsight('fixation')"><i class="bi bi-info-circle"></i> Lihat Insight</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="bg-custom p-4 my-3 border rounded">
<div class="row">
<div class="col-md-10">
<h5 class="text-body">Total Eye Distance</h5>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-arrows-fullscreen fs-4"></i>
</div>
</div>
<h4 id="totalDistance"></h4>
<div class="row mt-2">
<div class="col-md-12">
<div class="btn btn-link p-0" onclick="showInsight('distance')"><i class="bi bi-info-circle"></i> Lihat Insight</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="bg-custom p-4 my-3 border rounded">
<div class="row">
<div class="col-md-10">
<h5 class="text-body">Most Viewed Area</h5>
</div>
<div class="col-md-2 d-flex justify-content-center align-items-center bg-primary-subtle border border primary rounded-circle" style="aspect-ratio: 1/1;">
<i class="bi bi-geo-alt fs-4"></i>
</div>
</div>
<h4 id="mostViewedArea"></h4>
<div class="row mt-2">
<div class="col-md-12">
<div class="btn btn-link p-0" onclick="showInsight('area')"><i class="bi bi-info-circle"></i> Lihat Insight</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container mt-5" data-aos="fade-up" data-aos-delay="100">
<div class="row">
<div class="col-lg-12">
<h2 class="title text-center">Tabel soal yang telah dijawab</h2>
</div>
<div class="col-md-12 mt-2">
<table class="table" id="table-data-hasil-ujian">
<thead>
<tr>
<th>No</th>
<th>Pertanyaan</th>
<th>Jawaban</th>
<th>Skor</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="col-lg-12">
<h2 class="title text-center">Tabel soal yang tidak dijawab</h2>
</div>
<div class="col-md-12 mt-2">
<table class="table" id="table-data-soal-not-answer">
<thead>
<tr>
<th>No</th>
<th>Pertanyaan</th>
<th>Jawaban</th>
<th>Skor</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<div class="container mt-5">
<div class="row">
<div class="col-lg-12">
<h2 class="title text-center">Grafik Kecepatan Mata</h2>
<canvas id="velocityChart"></canvas>
</div>
<div class="col-lg-12 mt-5">
<h2 class="title text-center">Heatmap Pergerakan Mata</h2>
<div style="position: relative; aspect-ratio: 16/9;">
<img src="{{ asset('assets/users/assets/img/overlay/overlay.png') }}" style="width:100%; height:100%; object-fit: cover; position: absolute;">
<div id="heatmapChart" style="aspect-ratio: 16/9;"></div>
</div>
</div>
<div class="col-lg-12 mt-5">
<h2 class="title text-center">Heatmap Pergerakan Mata (Grid)</h2>
<div style="position: relative; aspect-ratio: 16/9;">
<img src="{{ asset('assets/users/assets/img/overlay/overlay_grid.png') }}" style="width:100%; height:100%; object-fit: cover; position: absolute;">
<div id="heatmapChartGrid" style="aspect-ratio: 16/9;"></div>
</div>
</div>
</div>
</div>
</section><!-- /About Section -->
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto"><i class="bi bi-exclamation-triangle"></i> Kesalahan</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
{{ session('error') }}
</div>
</div>
</div>
@endsection
@section('script')
<script>
const ws = new WebSocket('{{ env("WEBSOCKET_URL") }}');
let table = null;
let data_tobii = null;
let heatmapContainer = null;
let clientWidth = null;
let clientHeight = null;
let recordedScreenWidth = null
let recordedScreenHeight = null
let mappedHeatmapData = null;
let mappedHeatmapDataGrid = null;
let scaleX = null;
let scaleY = null;
let heatmapInstance = null;
let heatmapInstanceGrid = null;
let score = null;
let totalScore = null;
let soalTerjawab = 0;
function renderVelocityChart(timeseries) {
const base = timeseries.timestamp[0];
const labels = timeseries.timestamp.map(t => (t - base) / 1000);
const ctx = document.getElementById('velocityChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Velocity',
data: timeseries.velocity,
borderWidth: 2,
tension: 0.3
}]
},
options: {
responsive: true,
plugins: {
tooltip: {
callbacks: {
label: function(context) {
return "Velocity: " + context.raw.toFixed(4);
}
}
}
},
scales: {
x: {
display: false
}
}
}
});
}
async function loadData(data) {
const res = await fetch("{{ env('PYTHON_API_URL') }}/extract", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
const result = await res.json();
if(result != null) {
renderVelocityChart(result.timeseries);
mappedHeatmapData = result.heatmap.map(point => ({
x: point.x * scaleX,
y: point.y * scaleY,
value: point.value
}));
heatmapInstance.setData({
max: 10,
data: mappedHeatmapData
});
mappedHeatmapDataGrid = result.heatmap.map(point => ({
x: point.x * scaleX,
y: point.y * scaleY,
value: point.value
}));
heatmapInstanceGrid.setData({
max: 10,
data: mappedHeatmapDataGrid
});
heatmapContainer.style.removeProperty('position');
renderStats(result.statistics);
}
}
async function getPredict(data) {
const res = await fetch("{{ env('PYTHON_API_URL') }}/predict", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
const result = await res.json();
if(result != null) {
let classBg = ['text-success', 'text-warning', 'text-danger'];
let textResult = ['Normal', 'Sedang', 'Tinggi'];
$('#result-model').addClass(classBg[result.hasil_prediksi]);
$('#result-model').html(textResult[result.hasil_prediksi]);
$('#confidence-model').html(result.confidence + '%');
$('#normal-confidence').html(result.detail_probabilitas.normal + "%");
$('#sedang-confidence').html(result.detail_probabilitas.sedang + "%");
$('#tinggi-confidence').html(result.detail_probabilitas.tinggi + "%");
Swal.close();
}
}
function renderStats(stats) {
document.getElementById("avgVelocity")
.innerText = stats.avg_velocity.toFixed(4);
document.getElementById("fixationRatio")
.innerText = stats.fixation_ratio.toFixed(2) + "%";
document.getElementById("totalDistance")
.innerText = stats.total_distance.toFixed(2);
document.getElementById("mostViewedArea")
.innerText = stats.most_viewed_area;
}
function showInsight(type) {
let title = null;
let html = null;
if(type === "velocity") {
title = "Avg Velocity";
html = `
<p>
Rata-rata kecepatan gerakan mata selama ujian.
</p>
<hr>
<b>Nilai tinggi:</b>
<p>
Mata aktif berpindah area,
Aktivitas visual tinggi
</p>
<b>Nilai rendah:</b>
<p>
Fokus lebih stabil,
Gerakan mata lebih tenang
</p>
`;
} else if(type === "fixation") {
title = "Fixation Ratio";
html = `
<p>
Persentase waktu mata berada pada area tertentu.
</p>
<hr>
<b>Semakin tinggi:</b>
<p>
Fokus lebih lama pada suatu area,
Perhatian lebih stabil
</p>
`;
}
else if(type === "distance") {
title = "Total Eye Distance";
html = `
<p>
Total jarak perpindahan mata selama ujian.
</p>
<hr>
<b>Nilai besar:</b>
<p>
Mata banyak menjelajah layar,
Aktivitas visual tinggi
</p>
`;
}
else if(type === "area") {
title = "Most Viewed Area";
html = `
<p>
Area layar yang paling sering dilihat selama ujian.
</p>
<hr>
<b>Insight:</b>
<p>
Menunjukkan fokus utama pengguna,
Dapat membantu memahami pola perhatian
</p>
`;
} else {
title = "Huh?";
html = `<h1>Hello There :D</h1>`;
}
Swal.fire({
title: title,
html: html,
icon: 'info',
showConfirmButton: true,
});
}
$(document).ready(function() {
table = new DataTable("#table-data-hasil-ujian", {
searching: false,
paging: false,
ajax: {
url: '{{ route('hasil-tes.getAllAnswer', ['id_test_sessions' => $id_session]) }}',
dataSrc: 'data' // Specify the key containing the data array
},
columns: [
{
data: null,
render: function(data, type, row, meta) {
return meta.row + 1;
}
},
{
data: 'soal'
},
{
data: null,
render: function(data, type, row) {
let renderedJawaban = null;
if(row.jawaban_key == 'a') {
if(row.is_reverse == 1) {
renderedJawaban = `<span class="badge text-bg-danger">${row.jawaban}</span>`;
} else {
renderedJawaban = `<span class="badge text-bg-primary">${row.jawaban}</span>`;
}
} else if(row.jawaban_key == 'b') {
if(row.is_reverse == 1) {
renderedJawaban = `<span class="badge text-bg-warning">${row.jawaban}</span>`;
} else {
renderedJawaban = `<span class="badge" style="background-color: #fc7938; color: white;">${row.jawaban}</span>`;
}
} else if(row.jawaban_key == 'c') {
if(row.is_reverse == 1) {
renderedJawaban = `<span class="badge" style="background-color: #fc7938; color: white;">${row.jawaban}</span>`;
} else {
renderedJawaban = `<span class="badge text-bg-warning">${row.jawaban}</span>`;
}
} else if(row.jawaban_key == 'd') {
if(row.is_reverse == 1) {
renderedJawaban = `<span class="badge text-bg-primary">${row.jawaban}</span>`;
} else {
renderedJawaban = `<span class="badge text-bg-danger">${row.jawaban}</span>`;
}
} else if(row.jawaban_key == 'e') {
renderedJawaban = `<span class="badge text-bg-success">${row.jawaban}</span>`;
score = 5;
} else {
renderedJawaban = `<span class="badge text-bg-secondary">${row.jawaban}</span>`;
}
return renderedJawaban;
}
},
{
data: 'skor',
}
]
});
table = new DataTable("#table-data-soal-not-answer", {
searching: false,
paging: false,
ajax: {
url: '{{ route('hasil-tes.getNotAnswered', ['id_test_sessions' => $id_session]) }}',
dataSrc: 'data' // Specify the key containing the data array
},
columns: [
{
data: null,
render: function(data, type, row, meta) {
return meta.row + 1;
}
},
{
data: 'soal'
},
{
data: null,
render: function(data, type, row, meta) {
return `<span class="badge text-bg-secondary">Tidak dijawab</span>`;
}
},
{
data: 'skor'
}
]
});
ws.onopen = () => {
console.log("Connected to WebSocket");
Swal.fire({
title: 'Status',
text: 'Memuat data...',
icon: 'info',
showConfirmButton: false,
allowOutsideClick: false,
allowEscapeKey: false
});
ws.send(JSON.stringify({
type: 'get_json_file',
session_id: '{{ $id_session }}'
}));
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// console.log(data);
if(data.type === 'json_file_data') {
if(data.data != null) {
data_tobii = null;
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
let data_ = JSON.parse(this.responseText);
$('#total_movement').html(data.data.length + " (1 detik = 3 pergerakan)");
if(data_.status) {
heatmapContainer = document.getElementById('heatmapChart');
clientWidth = heatmapContainer.clientWidth;
clientHeight = heatmapContainer.clientHeight;
recordedScreenWidth = data_.screen_w;
recordedScreenHeight = data_.screen_h;
scaleX = clientWidth / recordedScreenWidth;
scaleY = clientHeight / recordedScreenHeight;
data_tobii = {
data: data.data,
screen_w: recordedScreenWidth,
screen_h: recordedScreenHeight
};
heatmapInstance = h337.create({
container: heatmapContainer,
radius: 40,
maxOpacity: 0.6,
blur: 0.85
});
heatmapInstanceGrid = h337.create({
container: document.getElementById('heatmapChartGrid'),
radius: 40,
maxOpacity: 0.6,
blur: 0.85
});
loadData(data_tobii);
getPredict(data_tobii.data);
} else {
Swal.fire({
title: 'Error',
text: 'Gagal mencari data, silahkan coba lagi nanti',
icon: 'error',
showConfirmButton: true,
allowOutsideClick: false,
allowEscapeKey: false
});
}
}
};
xhttp.open('GET', '{{ route('test-sessions.getScreen', ['id_test_sessions' => $id_session]) }}', true);
xhttp.send();
} else {
Swal.fire({
title: 'Error',
text: 'Gagal memuat data mata, silahkan coba lagi nanti',
icon: 'error',
showConfirmButton: true,
allowOutsideClick: false,
allowEscapeKey: false
});
}
}
};
};
});
</script>
@if (session('error'))
<script>
$(document).ready(function() {
var toast = new bootstrap.Toast(document.getElementById('liveToast'));
toast.show();
});
</script>
@endif
@endsection

View File

@ -19,7 +19,7 @@
</p> </p>
<div class="hero-actions" data-aos="fade-right" data-aos-delay="600"> <div class="hero-actions" data-aos="fade-right" data-aos-delay="600">
<a href="{{ route('beranda.about') }}" class="btn btn-primary">Learn About The System</a> <a href="#appointment" class="btn btn-primary">Coba Sekarang</a>
<a href="https://www.youtube.com/watch?v=Y7f98aduVJ8" class="btn btn-outline glightbox"> <a href="https://www.youtube.com/watch?v=Y7f98aduVJ8" class="btn btn-outline glightbox">
<i class="bi bi-archive me-2"></i> <i class="bi bi-archive me-2"></i>
View Research View Research
@ -41,30 +41,7 @@
<div class="col-lg-6"> <div class="col-lg-6">
<div class="hero-visual" data-aos="fade-left" data-aos-delay="400"> <div class="hero-visual" data-aos="fade-left" data-aos-delay="400">
<div class="main-image"> <div class="main-image">
<img src="{{ asset('assets/users/assets/img/health/staff-10.webp') }}" alt="Modern Healthcare Facility" class="img-fluid"> <img src="{{ asset('assets/users/assets/img/health/depresi_crop.jpg') }}" alt="Modern Healthcare Facility" class="img-fluid">
<div class="floating-card appointment-card">
<div class="card-icon">
<i class="bi bi-calendar-check"></i>
</div>
<div class="card-content">
<h6>Next Available</h6>
<p>Today 2:30 PM</p>
<small>Dr. Sarah Johnson</small>
</div>
</div>
<div class="floating-card rating-card">
<div class="card-content">
<div class="rating-stars">
<i class="bi bi-star-fill"></i>
<i class="bi bi-star-fill"></i>
<i class="bi bi-star-fill"></i>
<i class="bi bi-star-fill"></i>
<i class="bi bi-star-fill"></i>
</div>
<h6>4.9/5</h6>
<small>1,234 Reviews</small>
</div>
</div>
</div> </div>
<div class="background-elements"> <div class="background-elements">
<div class="element element-1"></div> <div class="element element-1"></div>
@ -96,7 +73,7 @@
</p> </p>
<div class="cta-section"> <div class="cta-section">
<a href="about.html" class="btn-primary">Learn More About Us</a> <a href="{{ route('beranda.about') }}" class="btn-primary">Learn More About Us</a>
</div> </div>
</div> </div>
</div> </div>
@ -104,7 +81,7 @@
<div class="col-lg-6" data-aos="fade-left" data-aos-delay="300"> <div class="col-lg-6" data-aos="fade-left" data-aos-delay="300">
<div class="about-visual"> <div class="about-visual">
<div class="main-image"> <div class="main-image">
<img src="{{ asset('assets/users/assets/img/health/facilities-9.webp') }}" alt="Modern medical facility" class="img-fluid"> <img src="{{ asset('assets/users/assets/img/health/about.jpg') }}" alt="Modern medical facility" class="img-fluid">
</div> </div>
{{-- <div class="floating-card"> {{-- <div class="floating-card">
<div class="card-content"> <div class="card-content">
@ -146,7 +123,7 @@
<div class="service-image"> <div class="service-image">
<img src="{{ asset('assets/users/assets/img/health/cardiology-2.webp') }}" alt="Cardiology Services" class="img-fluid"> <img src="{{ asset('assets/users/assets/img/health/cardiology-2.webp') }}" alt="Cardiology Services" class="img-fluid">
<div class="service-overlay"> <div class="service-overlay">
<i class="fas fa-heartbeat"></i> <i class="fas fa-message"></i>
</div> </div>
</div> </div>
<div class="service-content"> <div class="service-content">
@ -169,7 +146,7 @@
<div class="service-image"> <div class="service-image">
<img src="{{ asset('assets/users/assets/img/health/neurology-3.webp') }}" alt="Neurology Services" class="img-fluid"> <img src="{{ asset('assets/users/assets/img/health/neurology-3.webp') }}" alt="Neurology Services" class="img-fluid">
<div class="service-overlay"> <div class="service-overlay">
<i class="fas fa-brain"></i> <i class="fas fa-face-rolling-eyes"></i>
</div> </div>
</div> </div>
<div class="service-content"> <div class="service-content">
@ -192,7 +169,7 @@
<div class="service-image"> <div class="service-image">
<img src="{{ asset('assets/users/assets/img/health/orthopedics-1.webp') }}" alt="Orthopedics Services" class="img-fluid"> <img src="{{ asset('assets/users/assets/img/health/orthopedics-1.webp') }}" alt="Orthopedics Services" class="img-fluid">
<div class="service-overlay"> <div class="service-overlay">
<i class="fas fa-bone"></i> <i class="fas fa-chart-gantt"></i>
</div> </div>
</div> </div>
<div class="service-content"> <div class="service-content">
@ -216,7 +193,7 @@
<div class="service-image"> <div class="service-image">
<img src="{{ asset('assets/users/assets/img/health/pediatrics-4.webp') }}" alt="Pediatrics Services" class="img-fluid"> <img src="{{ asset('assets/users/assets/img/health/pediatrics-4.webp') }}" alt="Pediatrics Services" class="img-fluid">
<div class="service-overlay"> <div class="service-overlay">
<i class="fas fa-child"></i> <i class="fas fa-computer"></i>
</div> </div>
</div> </div>
<div class="service-content"> <div class="service-content">
@ -324,7 +301,7 @@
<h1>Make an Appointment</h1> <h1>Make an Appointment</h1>
</div><!-- End Section Title --> </div><!-- End Section Title -->
<div class="container" data-aos="fade-up" data-aos-delay="100"> <div class="container" data-aos="fade-up" data-aos-delay="100" id="appointment">
<div class="row"> <div class="row">
<div class="col-lg-10 mx-auto"> <div class="col-lg-10 mx-auto">
@ -340,8 +317,8 @@
<i class="bi bi-calendar-check"></i> <i class="bi bi-calendar-check"></i>
</div> </div>
<div class="step-content"> <div class="step-content">
<h4>Select Service</h4> <h4>Isi Data Diri</h4>
<p>Choose the medical service you need</p> <p>Mohon isi data diri Anda dengan lengkap</p>
</div> </div>
</div> </div>
<div class="step"> <div class="step">
@ -349,8 +326,8 @@
<i class="bi bi-clock"></i> <i class="bi bi-clock"></i>
</div> </div>
<div class="step-content"> <div class="step-content">
<h4>Pick Date &amp; Time</h4> <h4>Ambil Tanggal &amp; Waktu</h4>
<p>Select your preferred appointment slot</p> <p>Pilih tanggal dan waktu yang paling cocok untuk Anda</p>
</div> </div>
</div> </div>
<div class="step"> <div class="step">
@ -358,8 +335,8 @@
<i class="bi bi-person-check"></i> <i class="bi bi-person-check"></i>
</div> </div>
<div class="step-content"> <div class="step-content">
<h4>Confirm Details</h4> <h4>Konfirmasi Detail</h4>
<p>Provide your information and confirm</p> <p>Periksa kembali semua detail yang telah Anda masukkan</p>
</div> </div>
</div> </div>
</div> </div>
@ -377,7 +354,7 @@
<input type="tel" name="phone" id="no_telp" class="form-control" placeholder="Nomor Telepon" required=""> <input type="tel" name="phone" id="no_telp" class="form-control" placeholder="Nomor Telepon" required="">
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<input type="date" name="date" id="tgl_book" class="form-control" required="" aria-describedby="dateHelp"> <input type="text" name="date" id="tgl_book" class="form-control" required="" aria-describedby="dateHelp" placeholder="Pilih tanggal...">
<div id="dateHelp" class="form-text">Untuk detail jam, akan dikirim melalui email mu yaa.</div> <div id="dateHelp" class="form-text">Untuk detail jam, akan dikirim melalui email mu yaa.</div>
</div> </div>
<div class="col-12"> <div class="col-12">
@ -412,7 +389,14 @@
<script> <script>
function sendAppointment() { function sendAppointment() {
$('#error_msg').hide(); $('#error_msg').hide();
Swal.fire({
title: "Loading",
text: "Sedang memproses booking kamu, tunggu sebentar yaa!",
icon: "info",
showConfirmButton: false,
allowOutsideClick: false
});
let name = $('#nama_lengkap').val(); let name = $('#nama_lengkap').val();
let email = $('#email').val(); let email = $('#email').val();
let no_telp = $('#no_telp').val(); let no_telp = $('#no_telp').val();
@ -484,5 +468,14 @@ function sendAppointment() {
xhttp.send(formData); xhttp.send(formData);
} }
$(document).ready(function() {
flatpickr("#tgl_book", {
enableTime: false,
dateFormat: "Y-m-d",
minDate: 'today',
maxDate: new Date().fp_incr(7)
});
});
</script> </script>
@endsection @endsection

View File

@ -30,37 +30,10 @@
<ol> <ol>
<li><strong>Persiapan:</strong> Pastikan Tobii sudah terhubung dan berfungsi dengan baik.</li> <li><strong>Persiapan:</strong> Pastikan Tobii sudah terhubung dan berfungsi dengan baik.</li>
<li><strong>Penempatan:</strong> Tempatkan Tobii di posisi yang sesuai untuk mendeteksi gerakan mata dengan akurat.</li> <li><strong>Penempatan:</strong> Tempatkan Tobii di posisi yang sesuai untuk mendeteksi gerakan mata dengan akurat.</li>
<li><strong>Hubungkan Tobii:</strong> Hubungkan Tobii ke dalam web dengan menggunakan link API dibawah ini.</li> <li><strong>Hubungkan Tobii:</strong> Hubungkan Tobii ke dalam web dengan menggunakan aplikasi API, jika tidak terhubung, hubungi administrator.</li>
<li><strong>Hasil:</strong> Setelah menghubungkan ke web berhasil, akan muncul notifikasi bahwa Tobii telah terhubung.</li> <li><strong>Hasil:</strong> Setelah menghubungkan ke web berhasil, akan muncul notifikasi bahwa Tobii telah terhubung.</li>
<li><strong>Selanjutnya:</strong> Melakukan kalibrasi mata dengan Tobii.</li> <li><strong>Selanjutnya:</strong> Melakukan kalibrasi mata dengan Tobii.</li>
</ol> </ol>
<h5 class="">Link API Tobii:</h5>
<div class="row px-3">
<div class="col-md-11">
<input type="text" disabled id="link_api_tobii" class="input form-control mb-3" value="">
</div>
<div class="col-md-1 p-0">
<button class="btn btn-secondary" onclick="copyLinkApiTobii()">Copy</button>
</div>
</div>
<h5 class="">Link ID Tobii:</h5>
<div class="row px-3">
<div class="col-md-11">
<input type="text" disabled id="link_id_tobii" class="input form-control mb-3" value="">
</div>
<div class="col-md-1 p-0">
<button class="btn btn-secondary" onclick="copyLinkIdTobii()">Copy</button>
</div>
</div>
<h5 class="">Link Token Tobii:</h5>
<div class="row px-3">
<div class="col-md-11">
<input type="text" disabled id="link_token_tobii" class="input form-control mb-3" value="">
</div>
<div class="col-md-1 p-0">
<button class="btn btn-secondary" onclick="copyLinkTokenTobii()">Copy</button>
</div>
</div>
<p class="card-text mt-3" id="status_tobii">Memuat...</p> <p class="card-text mt-3" id="status_tobii">Memuat...</p>
<button class="btn btn-primary" id="back_btn" onclick="showTutorial(1, 'tutorial_2')">Back</button> <button class="btn btn-primary" id="back_btn" onclick="showTutorial(1, 'tutorial_2')">Back</button>
<button class="btn btn-primary" id="next_btn_connect_tobii" onclick="showTutorial(3, 'tutorial_2')">Next</button> <button class="btn btn-primary" id="next_btn_connect_tobii" onclick="showTutorial(3, 'tutorial_2')">Next</button>
@ -84,55 +57,42 @@
<!-- btn show --> <!-- btn show -->
<button type="button" class="btn btn-secondary position-absolute" onclick="showCalibrationPoints()" id="show_calibration" style="z-index: 999">Show</button> <button type="button" class="btn btn-secondary position-absolute" onclick="showCalibrationPoints()" id="show_calibration" style="z-index: 999">Show</button>
<div class="position-absolute bg-white" style="width: 100vw; height: 100vh; z-index: -1;" id="calibration_point"> <div class="position-absolute bg-white" style="width: 100vw; height: 100vh; z-index: -1;" id="calibration_point">
<!-- row 1 --> <table class="w-100 h-100">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 2%; left: 2%"></div> <tr>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 2%; left: 19.2%"></div> <td align="center">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 2%; left: 38.4%"></div> <div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 2%; left: 57.6%"></div> </td>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 2%; left: 76.8%"></div> <td align="center">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 2%; left: 96%"></div> <div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
</td>
<!-- row 2 --> <td align="center">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 19.2%; left: 2%"></div> <div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 19.2%; left: 19.2%"></div> </td>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 19.2%; left: 38.4%"></div> </tr>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 19.2%; left: 57.6%"></div> <tr>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 19.2%; left: 76.8%"></div> <td align="center">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 19.2%; left: 96%"></div> <div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
</td>
<!-- row 3 --> <td align="center">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 38.4%; left: 2%"></div> <div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 38.4%; left: 19.2%"></div> </td>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 38.4%; left: 38.4%"></div> <td align="center">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 38.4%; left: 57.6%"></div> <div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 38.4%; left: 76.8%"></div> </td>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 38.4%; left: 96%"></div> </tr>
<tr>
<!-- row 4 --> <td align="center">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 57.6%; left: 2%"></div> <div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 57.6%; left: 19.2%"></div> </td>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 57.6%; left: 38.4%"></div> <td align="center">
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 57.6%; left: 57.6%"></div> <div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 57.6%; left: 76.8%"></div> </td>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 57.6%; left: 96%"></div> <td align="center">
<div class="rounded-circle bg-primary" style="width: 3%; aspect-ratio: 1/1;"></div>
<!-- row 5 --> </td>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 76.8%; left: 2%"></div> </tr>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 76.8%; left: 19.2%"></div> </table>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 76.8%; left: 38.4%"></div> <div id="the_eye" class="rounded-circle bg-danger position-absolute" style="width: 1%; aspect-ratio: 1/1;"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 76.8%; left: 57.6%"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 76.8%; left: 76.8%"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 76.8%; left: 96%"></div>
<!-- row 6 -->
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 96%; left: 2%"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 96%; left: 19.2%"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 96%; left: 38.4%"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 96%; left: 57.6%"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 96%; left: 76.8%"></div>
<div class="rounded-circle bg-primary position-absolute" style="width: 0.5%; aspect-ratio: 1/1; top: 96%; left: 96%"></div>
<div id="the_eye" class="rounded-circle bg-danger position-absolute" style="width: 0.5%; aspect-ratio: 1/1;"></div>
</div> </div>
@endsection @endsection
@ -140,20 +100,90 @@
<script> <script>
let getDataInterval = false; let getDataInterval = false;
let checkTobiiInterval = null; let checkTobiiInterval = null;
let tobiiConnectedStatus = true; let tobiiConnectedStatus = false;
const ws = new WebSocket('{{ env("WEBSOCKET_URL") }}'); let ws = null;
let reconnectDelay = 1000;
let maxReconnectDelay = 10000;
let SESSION_ID = '{{ $data_session->id_test_sessions }}'; let SESSION_ID = '{{ $data_session->id_test_sessions }}';
let TOKEN_SESSION = '{{ $data_session->token_session }}'; let TOKEN_SESSION = '{{ $data_session->token_session }}';
function startWebsocket() {
console.log("Connecting to WebSocket...");
ws = new WebSocket('{{ env("WEBSOCKET_URL") }}');
ws.onopen = () => {
console.log("Connected to WebSocket");
reconnectDelay = 1000;
ws.send(JSON.stringify({
type: "register",
session_id: SESSION_ID,
token_session: TOKEN_SESSION,
role: "web"
}));
searchingTobiiDevice();
ws.send(JSON.stringify({
type: "update_status",
status_sessions: false,
id_session: SESSION_ID
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received data from WebSocket:', data);
if(data.type === 'tobii_data') {
try {
moveTheEye(data.gaze_x, data.gaze_y);
} catch(error) {
console.log(error);
}
}
if(data.type === 'status_tobii_device') {
if(data.status) {
console.log('Tobii device is connected');
tobiiConnectedStatus = true;
} else {
console.log('Tobii device is not connected');
tobiiConnectedStatus = false;
searchingTobiiDevice();
}
checkTobiiConnection();
}
if(data.type === 'reset_start_tobii') {
startTobii();
}
};
ws.onerror = function (error) {
console.log("WebSocket error");
};
ws.onclose = function () {
setTimeout(() => {
reconnectDelay = Math.min(
reconnectDelay * 2,
maxReconnectDelay
);
console.log("Disconnected from WebSocket. Retrying in " + reconnectDelay + " ms...");
startWebsocket();
}, reconnectDelay);
};
}
function showTutorial(step, tutorialId) { function showTutorial(step, tutorialId) {
$(`#${tutorialId}`).hide(); $(`#${tutorialId}`).hide();
$(`#tutorial_${step}`).show(); $(`#tutorial_${step}`).show();
if(step === 2 || step === 3) { if(step === 2 || step === 3) {
startTobii(); startTobii();
checkTobiiInterval = setInterval(() => { checkTobiiConnection();
checkTobiiConnection(); // checkTobiiInterval = setInterval(() => {
}, 2000); // checkTobiiConnection();
// }, 2000);
} else { } else {
if(checkTobiiInterval) { if(checkTobiiInterval) {
clearInterval(checkTobiiInterval); clearInterval(checkTobiiInterval);
@ -282,10 +312,10 @@ function copyLinkTokenTobii() {
} }
function checkTobiiConnection() { function checkTobiiConnection() {
ws.send(JSON.stringify({ // ws.send(JSON.stringify({
type: "check_tobii_connection", // type: "check_tobii_connection",
session_id: SESSION_ID, // session_id: SESSION_ID,
})); // }));
if(tobiiConnectedStatus) { if(tobiiConnectedStatus) {
$('#status_tobii').text('Status Tobii: Terhubung').removeClass('text-danger').addClass('text-success'); $('#status_tobii').text('Status Tobii: Terhubung').removeClass('text-danger').addClass('text-success');
@ -309,6 +339,39 @@ function checkTobiiConnection() {
} }
} }
async function searchingTobiiDevice() {
try {
console.log('Searching for tobii...');
const response = await fetch('{{ env('TOBII_API_URL') }}', {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
session_id: SESSION_ID,
token: TOKEN_SESSION,
url: "{{ env('WEBSOCKET_URL') }}"
})
}
);
if (!response.ok) {
console.error('Failed to get tobii device, retrying...');
setTimeout(5000);
searchingTobiiDevice();
return;
}
const data = await response.json();
if(data.session_id == SESSION_ID && data.token == TOKEN_SESSION) {
tobiiConnectedStatus = true;
console.log('Done searching...');
}
} catch (error) {
console.error('Error getting Tobii device:', error);
setTimeout(5000);
searchingTobiiDevice();
}
}
function startTobii() { function startTobii() {
ws.send(JSON.stringify({ ws.send(JSON.stringify({
type: "command", type: "command",
@ -332,56 +395,8 @@ function stopTobii() {
$('#calibration_point').hide(); $('#calibration_point').hide();
$('#show_calibration').hide(); $('#show_calibration').hide();
generateLinkApiTobii(); generateLinkApiTobii();
startWebsocket();
ws.onopen = () => { console.log("hehehehehehehehehhehehe");
console.log("Connected to WebSocket");
ws.send(JSON.stringify({
type: "register",
session_id: SESSION_ID,
token_session: TOKEN_SESSION,
role: "web"
}));
ws.send(JSON.stringify({
type: "update_status",
status_sessions: false,
id_session: SESSION_ID
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received data from WebSocket:', data);
// if(data.type === 'temp_data') {
// console.log('Received temp_data:', data);
// if((Date.now() - data.data[0].timestamp) < 5000) {
// tobiiConnectedStatus = true;
// } else {
// tobiiConnectedStatus = false;
// }
// }
if(data.type === 'tobii_data') {
try {
moveTheEye(data.gaze_x, data.gaze_y);
} catch(error) {
console.log(error);
}
}
// if(data.type === 'status_tobii') {
// if(data.status) {
// tobiiConnectedStatus = true;
// } else if(!data.status) {
// tobiiConnectedStatus = false;
// }
// }
if(data.type === 'reset_start_tobii') {
startTobii();
}
};
}); });
</script> </script>

View File

@ -7,9 +7,19 @@
<nav class="navbar navbar-expand-lg bg-body-tertiary px-2"> <nav class="navbar navbar-expand-lg bg-body-tertiary px-2">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="#">Smart Anxiety</a> <a class="navbar-brand" href="#">Smart Anxiety</a>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse justify-content-between" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"></ul> <ul class="navbar-nav me-auto mb-2 mb-lg-0"></ul>
<div class="d-flex fw-bold">Time Left: <span id="time_left"></span></div> <div class="d-flex col-md-4">
<div class="col-md-4 d-flex justify-content-center align-items-center">
Time Left: <span id="time_left"></span>
</div>
<div class="col-md-4 d-flex justify-content-center align-items-center">
<span id="bg_status_tobii" class="rounded bg-warning d-block mx-2" style="width: 10px; aspect-ratio: 1/1;"></span>Status Tobii
</div>
<div class="col-md-4 d-flex justify-content-center align-items-center">
<button id="fullscreen_btn" data-status="fullscreen" class="btn btn-sm btn-outline-secondary" onclick="openFullscreen()">Fullscreen</button>
</div>
</div>
</div> </div>
</div> </div>
</nav> </nav>
@ -19,38 +29,25 @@
<div class="col-md-9 border-end"> <div class="col-md-9 border-end">
<div class="row justify-content-center flex-column p-4"> <div class="row justify-content-center flex-column p-4">
<div class="col-md-12 border-bottom fs-4 fw-bold p-2">Soal</div> <div class="col-md-12 border-bottom fs-4 fw-bold p-2">Soal</div>
<div class="col-md-12 d-none" id="id_question" value="{{ $data_question->id_soal }}"></div>
<div class="col-md-12 p-2" style="text-align: justify">{{ $data_question->pertanyaan }}</div> <div class="col-md-12 p-2" style="text-align: justify">{{ $data_question->pertanyaan }}</div>
<div class="col-md-12"> <div class="col-md-12">
<div class="fs-4 fw-bold p-2">Jawaban:</div> <div class="fs-4 fw-bold p-2">Jawaban:</div>
<div class="form-check m-2"> @foreach ($answer as $item)
<input class="form-check-input" type="radio" name="jawaban_final" id="jawaban_a"> <div class="form-check m-2">
<label class="form-check-label" for="jawaban_a">{{ $data_question->jawaban_a }}</label> <input class="form-check-input" type="radio" name="jawaban_final" id="jawaban_{{ $item['key'] }}">
</div> <label class="form-check-label" for="jawaban_{{ $item['key'] }}">{{ $item['value'] }}</label>
<div class="form-check m-2"> </div>
<input class="form-check-input" type="radio" name="jawaban_final" id="jawaban_b"> @endforeach
<label class="form-check-label" for="jawaban_b">{{ $data_question->jawaban_b }}</label>
</div>
<div class="form-check m-2">
<input class="form-check-input" type="radio" name="jawaban_final" id="jawaban_c">
<label class="form-check-label" for="jawaban_c">{{ $data_question->jawaban_c }}</label>
</div>
<div class="form-check m-2">
<input class="form-check-input" type="radio" name="jawaban_final" id="jawaban_d">
<label class="form-check-label" for="jawaban_d">{{ $data_question->jawaban_d }}</label>
</div>
<div class="form-check m-2">
<input class="form-check-input" type="radio" name="jawaban_final" id="jawaban_e">
<label class="form-check-label" for="jawaban_e">{{ $data_question->jawaban_e }}</label>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="row p-3"> <div class="row p-3">
<div class="col-md-12 border-bottom fs-4 fw-bold p-2 mb-3">Navigasi Soal</div> <div class="col-md-12 border-bottom fs-4 fw-bold p-2 mb-3">Navigasi Soal</div>
@for ($i = 0; $i < $total_question; $i++) @foreach ($answered_question as $question)
<div onclick="changeQuestion({{ $i + 1 }})" class="col-md-1 btn d-flex justify-content-center align-items-center bg-light-subtle border rounded p-0 m-1" style="aspect-ratio: 1/1.5;">{{ $i + 1 }}</div> <div onclick="changeQuestion({{ $question['nomor_soal'] }})" id="nomor_{{ $question['nomor_soal'] }}" class="col-md-1 btn d-flex justify-content-center align-items-center {{ $question['answered'] ? 'bg-secondary-subtle' : 'bg-light-subtle' }} border rounded p-0 m-1" style="aspect-ratio: 1/1.5;">{{ $question['nomor_soal'] }}</div>
@endfor @endforeach
<div class="col-md-12 p-0 mt-3"> <div class="col-md-12 p-0 mt-3">
@if ($question_number > 1) @if ($question_number > 1)
@ -64,6 +61,28 @@
@endif @endif
</div> </div>
</div> </div>
<div class="row p-3">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">Instruksi</h5>
<p class="card-text">Silahkan baca soal dengan teliti dan pilih jawaban yang paling tepat.</p>
<div class="card-text d-flex align-items-center">
<span class="col-md-1 bg-light-subtle border rounded p-0 m-1" style="aspect-ratio: 1/1.5;"></span>
<span>adalah tanda bahwa jawaban BELUM diisi.</span>
</div>
<div class="card-text d-flex align-items-center">
<span class="col-md-1 bg-secondary-subtle border rounded p-0 m-1" style="aspect-ratio: 1/1.5;"></span>
<span>adalah tanda bahwa jawaban SUDAH diisi.</span>
</div>
<div class="card-text d-flex align-items-center">
<span class="col-md-1 bg-info border rounded p-0 m-1 text-white" style="aspect-ratio: 1/1.5;"></span>
<span>adalah tanda bahwa anda sedang membaca/menjawab soal ini.</span>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -75,10 +94,41 @@
const ws = new WebSocket('{{ env("WEBSOCKET_URL") }}'); const ws = new WebSocket('{{ env("WEBSOCKET_URL") }}');
let SESSION_ID = '{{ $data_session->id_test_sessions }}'; let SESSION_ID = '{{ $data_session->id_test_sessions }}';
let TOKEN_SESSION = '{{ $data_session->token_session }}'; let TOKEN_SESSION = '{{ $data_session->token_session }}';
let UNIX_END_TIME = null;
function changeQuestion(num) { function changeQuestion(num) {
let url_temp = window.location.href; let url_temp = window.location.href;
let current_num = new URLSearchParams(window.location.search).get('question'); let current_num = new URLSearchParams(window.location.search).get('question');
let xhttp = new XMLHttpRequest();
let the_answer = null;
let token = document.getElementById('_token').getAttribute('value');
let formData = new FormData();
if(!current_num) {
current_num = '{{ $question_number }}';
}
if(current_num == num) {
console.log('same question');
return;
}
document.getElementsByName('jawaban_final').forEach(element => {
if (element.checked) {
the_answer = {
key: element.id.split('_')[1],
};
}
});
if(the_answer) {
formData.append('jawaban_final', the_answer.key);
formData.append('_token', token);
formData.append('question', num);
formData.append('id_question', $('#id_question').attr('value'));
formData.append('session_id', SESSION_ID);
xhttp.open("POST", "{{ route('ujian.store') }}", false);
xhttp.send(formData);
}
stopTobii(); stopTobii();
if(!url_temp.includes('question')) { if(!url_temp.includes('question')) {
window.location.href = url_temp + '&question=' + num; window.location.href = url_temp + '&question=' + num;
@ -87,24 +137,117 @@ function changeQuestion(num) {
} }
} }
function getCurrentAnswer() {
let id_question = $('#id_question').attr('value');
let xhttp = new XMLHttpRequest();
let current_num = new URLSearchParams(window.location.search).get('question');
if(!current_num) {
current_num = '{{ $question_number }}';
}
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
let response = JSON.parse(this.responseText);
document.getElementById('nomor_' + current_num).classList.remove('bg-light-subtle');
document.getElementById('nomor_' + current_num).classList.remove('bg-secondary-subtle');
document.getElementById('nomor_' + current_num).classList.add('bg-info');
document.getElementById('nomor_' + current_num).classList.add('text-white');
if(response.status == 'success') {
document.getElementsByName('jawaban_final').forEach(element => {
if (element.id.split('_')[1] == response.jawaban) {
element.checked = true;
}
});
}
}
};
xhttp.open("GET", "{{ route('ujian.getCurrentAnswer') }}?id_question=" + id_question + "&session_id=" + SESSION_ID, false);
xhttp.send();
}
function getTimeLeft() {
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
let response = JSON.parse(this.responseText);
UNIX_END_TIME = response.time_left;
}
};
xhttp.open("GET", "{{ route('ujian.getTimeLeft') }}?session_id=" + SESSION_ID, false);
xhttp.send();
}
function updateTimeLeft() {
let time_left = UNIX_END_TIME - Math.floor(Date.now() / 1000);
if(time_left < 0) {
time_left = 0;
finishExam();
}
let minutes = Math.floor(time_left / 60);
let seconds = time_left % 60;
document.getElementById('time_left').innerText = ' ' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
}
function finishExam() { function finishExam() {
ws.send(JSON.stringify({ Swal.fire({
type: "command", title: 'Alert',
action: "stop", icon: 'question',
session_id: SESSION_ID text: 'Apakah Anda yakin ingin mengakhiri ujian? (aksi tidak dapat dikembalikan)',
})); confirmButtonText: 'Ya',
cancelButtonText: 'Tidak',
showCancelButton: true,
showCloseButton: true,
//send_data_to_db }).then((result) => {
ws.send(JSON.stringify({ if (result.isConfirmed) {
type: "send_data_to_db", event.preventDefault();
session_id: SESSION_ID let current_num = new URLSearchParams(window.location.search).get('question');
})); let xhttp = new XMLHttpRequest();
let the_answer = null;
let token = document.getElementById('_token').getAttribute('value');
let formData = new FormData();
ws.send(JSON.stringify({ document.getElementsByName('jawaban_final').forEach(element => {
type: "update_status", if (element.checked) {
status_sessions: false, the_answer = {
session_id: SESSION_ID key: element.id.split('_')[1],
})); };
}
});
if(the_answer) {
formData.append('jawaban_final', the_answer.key);
formData.append('_token', token);
formData.append('question', current_num);
formData.append('id_question', $('#id_question').attr('value'));
formData.append('session_id', SESSION_ID);
xhttp.open("POST", "{{ route('ujian.store') }}", false);
xhttp.send(formData);
console.log("sending answer (finish)");
}
ws.send(JSON.stringify({
type: "command",
action: "stop",
session_id: SESSION_ID
}));
//send_data_to_db
ws.send(JSON.stringify({
type: "send_data_to_db",
session_id: SESSION_ID
}));
ws.send(JSON.stringify({
type: "update_status",
status_sessions: false,
session_id: SESSION_ID
}));
window.location.href = "{{ route('hasil-tes.index') }}?id_test_sessions=" + SESSION_ID;
}
});
} }
function startTobii() { function startTobii() {
@ -123,6 +266,63 @@ function stopTobii() {
})); }));
} }
function openFullscreen() {
const status = $('#fullscreen_btn').attr('data-status');
const elem = document.documentElement;
if (status === 'fullscreen') {
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen(); // Safari
} else if (elem.msRequestFullscreen) {
elem.msRequestFullscreen(); // IE
}
$('#fullscreen_btn').attr('data-status', 'exit-fullscreen');
$('#fullscreen_btn').text('Exit Fullscreen');
s.send(JSON.stringify({
type: "command",
action: "start",
session_id: SESSION_ID
}));
} else if (status === 'exit-fullscreen') {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen(); // Safari
} else if (document.msExitFullscreen) {
document.msExitFullscreen(); // IE
}
$('#fullscreen_btn').attr('data-status', 'fullscreen');
$('#fullscreen_btn').text('Enter Fullscreen');
}
}
function checkFullscreen() {
if (!document.fullscreenElement) {
ws.send(JSON.stringify({
type: "command",
action: "stop",
session_id: SESSION_ID
}));
Swal.fire({
title: 'Alert',
icon: 'warning',
text: 'Anda keluar dari mode fullscreen, harap masuk ke mode fullscreen untuk melanjutkan ujian.',
confirmButtonText: 'Ya',
showCancelButton: false,
showCloseButton: false,
allowOutsideClick: false,
allowEscapeKey: false,
backdrop: `rgba(0, 0, 0, 0.4)`
}).then((result) => {
if (result.isConfirmed) {
openFullscreen();
}
});
}
}
$(document).ready(function() { $(document).ready(function() {
ws.onopen = () => { ws.onopen = () => {
console.log("Connected to WebSocket"); console.log("Connected to WebSocket");
@ -133,13 +333,43 @@ function stopTobii() {
role: "web" role: "web"
})); }));
document.addEventListener('fullscreenchange', checkFullscreen);
checkFullscreen();
ws.send(JSON.stringify({ ws.send(JSON.stringify({
type: "update_status", type: "update_status",
status_sessions: true, status_sessions: true,
session_id: SESSION_ID session_id: SESSION_ID
})); }));
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if(data.type === 'reset_start_tobii') {
startTobii();
}
if(data.type === 'status_tobii_device') {
if(data.status) {
console.log('Tobii device is connected');
// document.getElementById('status_tobii_device').innerText = 'Connected';
document.getElementById('bg_status_tobii').classList.remove('bg-warning');
document.getElementById('bg_status_tobii').classList.remove('bg-danger');
document.getElementById('bg_status_tobii').classList.add('bg-success');
} else {
console.log('Tobii device is not connected');
// document.getElementById('status_tobii_device').innerText = 'Not Connected';
document.getElementById('bg_status_tobii').classList.remove('bg-success');
document.getElementById('bg_status_tobii').classList.remove('bg-warning');
document.getElementById('bg_status_tobii').classList.add('bg-danger');
}
}
};
startTobii(); startTobii();
getCurrentAnswer();
getTimeLeft();
setInterval(updateTimeLeft, 1000);
}; };
}); });
</script> </script>

View File

@ -0,0 +1,508 @@
@section('title', 'Ujian Kecemasan')
{{-- @section('body_status_overflow', 'overflow: hidden;') --}}
@section('main_style', 'min-height: 100vh;')
@extends('user.layouts.test_layout.index')
@section('content')
<div class="container-fluid p-0 d-flex flex-column" style="min-height: 100vh">
<nav class="navbar navbar-expand-lg bg-body-tertiary px-2">
<div class="container-fluid">
<a class="navbar-brand" href="#">Smart Anxiety</a>
<div class="collapse navbar-collapse justify-content-between" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"></ul>
<div class="d-flex col-md-4">
<div class="col-md-4 d-flex justify-content-center align-items-center">
Time Left: <span id="time_left"></span>
</div>
<div class="col-md-4 d-flex justify-content-center align-items-center">
<span id="bg_status_tobii" class="rounded bg-warning d-block mx-2" style="width: 10px; aspect-ratio: 1/1;"></span>Status Tobii
</div>
<div class="col-md-4 d-flex justify-content-center align-items-center">
<button id="fullscreen_btn" data-status="fullscreen" class="btn btn-sm btn-outline-secondary" onclick="openFullscreen()">Fullscreen</button>
</div>
</div>
</div>
</div>
</nav>
<div class="d-flex flex-grow-1 container-fluid">
<div class="row flex-grow-1">
<div class="col-md-9 border-end">
<div class="row justify-content-center flex-column p-4">
<div class="col-md-12 border-bottom fs-4 fw-bold p-2">Soal</div>
<div class="col-md-12 p-2 d-none" id="id_question" style="text-align: justify"></div>
<div class="col-md-12 p-2 fs-1 text-muted" id="text_question" style="text-align: justify"></div>
<div class="col-md-12 fs-1 text-muted" id="answer_field">
<div class="fs-4 fw-bold p-2">Jawaban:</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="row p-3" id="navigation_field">
<div class="col-md-12 border-bottom fs-4 fw-bold p-2 mb-3">Navigasi Soal</div>
</div>
<div class="row p-3">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">Instruksi</h5>
<p class="card-text">Silahkan baca soal dengan teliti dan pilih jawaban yang paling tepat.</p>
<div class="card-text d-flex align-items-center">
<span class="col-md-1 bg-light-subtle border rounded p-0 m-1" style="aspect-ratio: 1/1.5;"></span>
<span>adalah tanda bahwa jawaban BELUM diisi.</span>
</div>
<div class="card-text d-flex align-items-center">
<span class="col-md-1 bg-secondary-subtle border rounded p-0 m-1" style="aspect-ratio: 1/1.5;"></span>
<span>adalah tanda bahwa jawaban SUDAH diisi.</span>
</div>
<div class="card-text d-flex align-items-center">
<span class="col-md-1 bg-info border rounded p-0 m-1 text-white" style="aspect-ratio: 1/1.5;"></span>
<span>adalah tanda bahwa anda sedang membaca/menjawab soal ini.</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('script')
<script>
let ws = null;
let reconnectDelay = 1000;
let maxReconnectDelay = 10000;
const SESSION_ID = '{{ $data_session->id_test_sessions }}';
const TOKEN_SESSION = '{{ $data_session->token_session }}';
let UNIX_END_TIME = null;
let QUESTION_INDEX = 1;
let QUESTION_ID = null;
let CURRENT_BTN = null;
let CAPTION_BTN = null;
function startWebsocket() {
console.log("Connecting to WebSocket...");
ws = new WebSocket('{{ env("WEBSOCKET_URL") }}');
ws.onopen = () => {
console.log("Connected to WebSocket");
ws.send(JSON.stringify({
type: "register",
session_id: SESSION_ID,
token_session: TOKEN_SESSION,
role: "web"
}));
document.addEventListener('fullscreenchange', checkFullscreen);
ws.send(JSON.stringify({
type: "update_status",
status_sessions: true,
session_id: SESSION_ID
}));
searchingTobiiDevice()
checkFullscreen();
getTimeLeft();
setInterval(updateTimeLeft, 1000);
getNavigationField();
setCurrentNavigation();
fetchQuestion(QUESTION_INDEX);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if(data.type === 'reset_start_tobii') {
startTobii();
}
if(data.type === 'status_tobii_device') {
if(data.status) {
// console.log('Tobii device is connected');
// document.getElementById('status_tobii_device').innerText = 'Connected';
document.getElementById('bg_status_tobii').classList.remove('bg-warning');
document.getElementById('bg_status_tobii').classList.remove('bg-danger');
document.getElementById('bg_status_tobii').classList.add('bg-success');
} else {
console.log('Tobii device is not connected');
searchingTobiiDevice();
// document.getElementById('status_tobii_device').innerText = 'Not Connected';
document.getElementById('bg_status_tobii').classList.remove('bg-success');
document.getElementById('bg_status_tobii').classList.remove('bg-warning');
document.getElementById('bg_status_tobii').classList.add('bg-danger');
}
}
};
ws.onerror = function (error) {
console.log("WebSocket error");
};
ws.onclose = function () {
setTimeout(() => {
reconnectDelay = Math.min(
reconnectDelay * 2,
maxReconnectDelay
);
console.log("Disconnected from WebSocket. Retrying in " + reconnectDelay + " ms...");
document.getElementById('bg_status_tobii').classList.remove('bg-success');
document.getElementById('bg_status_tobii').classList.remove('bg-warning');
document.getElementById('bg_status_tobii').classList.add('bg-danger');
startWebsocket();
}, reconnectDelay);
};
}
async function searchingTobiiDevice() {
try {
console.log('Searching for tobii...');
const response = await fetch('{{ env('TOBII_API_URL') }}', {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
session_id: SESSION_ID,
token: TOKEN_SESSION,
url: "{{ env('WEBSOCKET_URL') }}"
})
}
);
if (!response.ok) {
console.error('Failed to get tobii device, retrying...');
setTimeout(5000);
searchingTobiiDevice();
return;
}
const data = await response.json();
if(data.session_id == SESSION_ID && data.token == TOKEN_SESSION) {
// tobiiConnectedStatus = true;
startTobii();
document.getElementById('bg_status_tobii').classList.remove('bg-success');
document.getElementById('bg_status_tobii').classList.remove('bg-warning');
document.getElementById('bg_status_tobii').classList.add('bg-danger');
console.log('Done searching...');
}
} catch (error) {
console.error('Error getting Tobii device:', error);
document.getElementById('bg_status_tobii').classList.remove('bg-success');
document.getElementById('bg_status_tobii').classList.remove('bg-warning');
document.getElementById('bg_status_tobii').classList.add('bg-danger');
setTimeout(5000);
searchingTobiiDevice();
}
}
function changeQuestion(num) {
Swal.showLoading();
let xhttp = new XMLHttpRequest();
let the_answer = null;
let token = document.getElementById('_token').getAttribute('value');
let formData = new FormData();
if(QUESTION_INDEX == num) {
console.log('same question');
Swal.close();
return;
}
document.getElementsByName('jawaban_final').forEach(element => {
if (element.checked) {
the_answer = {
key: element.id.split('_')[1],
};
}
});
if(the_answer) {
formData.append('jawaban_final', the_answer.key);
formData.append('_token', token);
formData.append('question', num);
formData.append('id_question', QUESTION_ID);
formData.append('session_id', SESSION_ID);
xhttp.open("POST", "{{ route('ujian.store') }}", true);
xhttp.send(formData);
}
// stopTobii();
QUESTION_INDEX = num;
getNavigationField();
fetchQuestion(QUESTION_INDEX);
}
function setCurrentNavigation() {
$('#nomor_' + QUESTION_INDEX).removeClass('bg-light-subtle');
$('#nomor_' + QUESTION_INDEX).removeClass('bg-secondary-subtle');
$('#nomor_' + QUESTION_INDEX).addClass('bg-info');
}
function fetchQuestion(question_index) {
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
let response = JSON.parse(this.responseText);
if(response.status) {
$('#text_question').html(response.data.pertanyaan);
QUESTION_ID = response.data.id_soal;
getCurrentAnswer(response.data.id_soal);
} else {
Swal.fire({
title: 'Error',
icon: 'error',
text: 'Terjadi kesalahan saat mengambil soal. Silahkan refresh halaman ini.',
confirmButtonText: 'OK'
});
}
}
}
xhttp.open("GET", "{{ route('ujian.fetchQuestion') }}?session_id=" + SESSION_ID + "&question_index=" + question_index, true);
xhttp.send();
}
function finishExam() {
Swal.fire({
title: 'Alert',
icon: 'question',
text: 'Apakah Anda yakin ingin mengakhiri ujian? (aksi tidak dapat dikembalikan)',
confirmButtonText: 'Ya',
cancelButtonText: 'Tidak',
showCancelButton: true,
showCloseButton: true,
}).then((result) => {
if (result.isConfirmed) {
event.preventDefault();
let xhttp = new XMLHttpRequest();
let the_answer = null;
let token = document.getElementById('_token').getAttribute('value');
let formData = new FormData();
document.getElementsByName('jawaban_final').forEach(element => {
if (element.checked) {
the_answer = {
key: element.id.split('_')[1],
};
}
});
if(the_answer) {
formData.append('jawaban_final', the_answer.key);
formData.append('_token', token);
formData.append('question', QUESTION_INDEX);
formData.append('id_question', QUESTION_ID);
formData.append('session_id', SESSION_ID);
xhttp.open("POST", "{{ route('ujian.store') }}", false);
xhttp.send(formData);
console.log("sending answer (finish)");
}
ws.send(JSON.stringify({
type: "command",
action: "stop",
finish: true,
session_id: SESSION_ID
}));
//send_data_to_db
ws.send(JSON.stringify({
type: "send_data_to_db",
session_id: SESSION_ID
}));
ws.send(JSON.stringify({
type: "update_status",
status_sessions: false,
session_id: SESSION_ID
}));
window.location.href = "{{ route('hasil-tes.index') }}?id_test_sessions=" + SESSION_ID;
}
});
}
function getCurrentAnswer(id_question) {
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
let response = JSON.parse(this.responseText);
if (response.status) {
let choices_html = '';
$('#answer_field').html('<div class="fs-4 fw-bold p-2">Jawaban:</div>');
response.choices.forEach((choice) => {
choices_html += `
<div class="form-check m-2">
<input class="form-check-input" type="radio" name="jawaban_final" id="jawaban_${choice.key}">
<label class="form-check-label" for="jawaban_${choice.key}">${choice.value}</label>
</div>
`;
});
$('#answer_field').append(choices_html);
if(response.jawaban != null) {
document.getElementsByName('jawaban_final').forEach(element => {
if (element.id.split('_')[1] == response.jawaban) {
element.checked = true;
}
});
}
Swal.close();
checkFullscreen();
}
}
};
xhttp.open("GET", "{{ route('ujian.getCurrentAnswer') }}?session_id=" + SESSION_ID + "&id_question=" + id_question, false);
xhttp.send();
}
function getNavigationField() {
let xhttp = new XMLHttpRequest();
let total_question = 0;
xhttp.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
let response = JSON.parse(this.responseText);
if(response.status) {
let navigation_field = '<div class="col-md-12 border-bottom fs-4 fw-bold p-2 mb-3">Navigasi Soal</div>';
$('#navigation_field').html(navigation_field);
response.data.forEach((q) => {
let answered = 'bg-light-subtle';
if(q.answered) {
answered = 'bg-secondary-subtle'
}
navigation_field += `
<div onclick="changeQuestion(${q.index})" id="nomor_${q.index}" class="col-md-1 btn d-flex justify-content-center align-items-center ${answered} border rounded p-0 m-1" style="aspect-ratio: 1/1.5;">${q.index}</div>
`;
total_question += 1;
});
if(QUESTION_INDEX <= total_question) {
let next_num = 0;
next_num = QUESTION_INDEX + 1;
if(next_num > total_question) {
// tombol finish dan back
navigation_field += `
<div class="btn btn-secondary mb-2" onclick="changeQuestion(${QUESTION_INDEX - 1})">Back</div>
`;
navigation_field += `
<div class="btn btn-success" onclick="finishExam()">Finish</div>
`;
} else {
if((QUESTION_INDEX - 1) != 0) {
navigation_field += `
<div class="btn btn-secondary mb-2" onclick="changeQuestion(${QUESTION_INDEX - 1})">Back</div>
`;
}
navigation_field += `
<div class="btn btn-primary" status="false" onclick="changeQuestion(${QUESTION_INDEX + 1})">Next</div>
`;
}
}
$('#navigation_field').html(navigation_field);
setCurrentNavigation();
}
}
};
xhttp.open('GET', '{{ route('ujian.navigationField') }}?session_id=' + SESSION_ID, true);
xhttp.send();
}
function getTimeLeft() {
let xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
let response = JSON.parse(this.responseText);
console.log('Time left:', response.time_left);
UNIX_END_TIME = response.time_left;
}
};
xhttp.open("GET", "{{ route('ujian.getTimeLeft') }}?session_id=" + SESSION_ID, false);
xhttp.send();
}
function updateTimeLeft() {
let time_left = UNIX_END_TIME - Math.floor(Date.now() / 1000);
if(time_left < 0) {
time_left = 0;
finishExam();
}
let minutes = Math.floor(time_left / 60);
let seconds = time_left % 60;
document.getElementById('time_left').innerText = ' ' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
}
function startTobii() {
ws.send(JSON.stringify({
type: "command",
action: "start",
session_id: SESSION_ID
}));
}
function stopTobii() {
ws.send(JSON.stringify({
type: "command",
action: "stop",
session_id: SESSION_ID
}));
}
function openFullscreen() {
const status = $('#fullscreen_btn').attr('data-status');
const elem = document.documentElement;
if (status === 'fullscreen') {
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen(); // Safari
} else if (elem.msRequestFullscreen) {
elem.msRequestFullscreen(); // IE
}
$('#fullscreen_btn').attr('data-status', 'exit-fullscreen');
$('#fullscreen_btn').text('Exit Fullscreen');
startTobii();
} else if (status === 'exit-fullscreen') {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen(); // Safari
} else if (document.msExitFullscreen) {
document.msExitFullscreen(); // IE
}
$('#fullscreen_btn').attr('data-status', 'fullscreen');
$('#fullscreen_btn').text('Enter Fullscreen');
}
}
function checkFullscreen() {
if (!document.fullscreenElement) {
ws.send(JSON.stringify({
type: "command",
action: "stop",
session_id: SESSION_ID
}));
Swal.fire({
title: 'Alert',
icon: 'warning',
text: 'Anda keluar dari mode fullscreen, harap masuk ke mode fullscreen untuk melanjutkan ujian.',
confirmButtonText: 'Ya',
showCancelButton: false,
showCloseButton: false,
allowOutsideClick: false,
allowEscapeKey: false,
backdrop: `rgba(0, 0, 0, 0.4)`
}).then((result) => {
if (result.isConfirmed) {
openFullscreen();
}
});
}
}
$(document).ready(function() {
startWebsocket();
});
</script>
@endsection

View File

@ -4,6 +4,7 @@
use App\Http\Controllers\CekStatusController; use App\Http\Controllers\CekStatusController;
use App\Http\Controllers\DashboardController; use App\Http\Controllers\DashboardController;
use App\Http\Controllers\DataSoalController; use App\Http\Controllers\DataSoalController;
use App\Http\Controllers\FinishUjianController;
use App\Http\Controllers\HalamanUjianController; use App\Http\Controllers\HalamanUjianController;
use App\Http\Controllers\HasilTesController; use App\Http\Controllers\HasilTesController;
use App\Http\Controllers\LandingController; use App\Http\Controllers\LandingController;
@ -35,11 +36,21 @@
Route::resource('cek-status', CekStatusController::class)->only(['index', 'show']); Route::resource('cek-status', CekStatusController::class)->only(['index', 'show']);
Route::resource('tutorial-tes', TutorialTesController::class)->only(['index']); Route::resource('tutorial-tes', TutorialTesController::class)->only(['index']);
Route::resource('test-sessions', TestSessionsController::class)->only(['store', 'show']); Route::resource('test-sessions', TestSessionsController::class)->only(['store', 'show']);
Route::get('test-sessions/get/screen', [TestSessionsController::class, 'getScreen'])->name('test-sessions.getScreen');
Route::resource('tobii-receiver', TobiiReceiverController::class)->only(['store', 'show']); Route::resource('tobii-receiver', TobiiReceiverController::class)->only(['store', 'show']);
Route::get('tobii-receiver/fetch/data/{id_test_sessions}', [TobiiReceiverController::class, 'fetchDataTobii'])->name('tobii-receiver.fetchData'); Route::get('tobii-receiver/fetch/data/{id_test_sessions}', [TobiiReceiverController::class, 'fetchDataTobii'])->name('tobii-receiver.fetchData');
Route::resource('ujian', HalamanUjianController::class); Route::resource('ujian', HalamanUjianController::class);
Route::get('ujian/get/current-answer', [HalamanUjianController::class, 'getCurrentAnswer'])->name('ujian.getCurrentAnswer');
Route::get('ujian/get/time-left', [HalamanUjianController::class, 'getTimeLeft'])->name('ujian.getTimeLeft');
Route::get('ujian/fetch/question', [HalamanUjianController::class, 'fetchQuestion'])->name('ujian.fetchQuestion');
Route::get('ujian/fetch/navigation-field', [HalamanUjianController::class, 'getNavigationField'])->name('ujian.navigationField');
Route::resource('hasil-tes', FinishUjianController::class)->only(['index', 'show']);
Route::get('hasil-tes/get/all-answer', [FinishUjianController::class, 'getAllAnswer'])->name('hasil-tes.getAllAnswer');
Route::get('hasil-tes/get/all-not-answer', [FinishUjianController::class, 'getNotAnswered'])->name('hasil-tes.getNotAnswered');
Route::middleware(['auth', 'role.auth'])->group(function() { Route::middleware(['auth', 'role.auth'])->group(function() {
Route::resource('dashboard', DashboardController::class); Route::resource('dashboard', DashboardController::class);
Route::get('dashboard/fetch/data', [DashboardController::class, 'getDataDashboard'])->name('dashboard.fetchData');
Route::get('dashboard/fetch/data-table', [DashboardController::class, 'getDataForDashboardTable'])->name('dashboard.fetchDataTable');
Route::resource('users', UsersController::class); Route::resource('users', UsersController::class);
Route::get('users/fetch/data', [UsersController::class, 'getData'])->name('users.fetchData'); Route::get('users/fetch/data', [UsersController::class, 'getData'])->name('users.fetchData');
Route::resource('booking', BookingController::class)->except(['store']); Route::resource('booking', BookingController::class)->except(['store']);
@ -47,10 +58,17 @@
Route::resource('soal', DataSoalController::class); Route::resource('soal', DataSoalController::class);
Route::get('soal/fetch/data', [DataSoalController::class, 'getData'])->name('soal.fetchData'); Route::get('soal/fetch/data', [DataSoalController::class, 'getData'])->name('soal.fetchData');
Route::resource('hasil_tes', HasilTesController::class); Route::resource('hasil_tes', HasilTesController::class);
Route::get('hasil_tes/fetch/data', [HasilTesController::class, 'fetchData'])->name('hasil-tes.fetchData');
}); });
Route::get('api/eye/test', function(Request $req) { Route::get('email/test', function(Request $req) {
return $req->token; return view('layouts.mail-send-code', [
'code' => 'ABC123',
'name' => 'Mahayoga',
'startExpirationTime' => '09:00:00',
'endExpirationTime' => '09:05:00',
'tglExpired' => '2023-10-01'
]);
}); });
require __DIR__.'/auth.php'; require __DIR__.'/auth.php';