23/06/25 update api

This commit is contained in:
vionar3 2025-06-23 16:17:09 +07:00
parent 08de48498d
commit d592d6ea43
39 changed files with 2057 additions and 271 deletions

View File

@ -0,0 +1,288 @@
<?php
// app/Http/Controllers/Api/ProgressController.php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Progress;
use App\Models\Materi;
use App\Models\user;
use App\Models\Latihan;
use App\Models\SubMateri;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Helpers\ResponseFormatter;
class ProgressController extends Controller
{
public function __construct()
{
// Menambahkan middleware untuk otentikasi dengan token
$this->middleware('auth:sanctum');
}
// Method untuk mendapatkan progres materi
public function getMateriProgress($materiId)
{
// Ambil user yang terotentikasi
$user = Auth::user();
// Ambil materi berdasarkan ID
$materi = Materi::findOrFail($materiId);
// Menghitung total kategori dan submateri yang selesai
$totalSubmateri = 0;
$completedSubmateri = 0;
// Looping untuk setiap kategori dari materi
foreach ($materi->kategori as $kategori) {
$totalSubmateri += $kategori->subMateri()->count();
// Menghitung jumlah submateri yang selesai untuk kategori ini
$completedSubmateri += Progress::where('user_id', $user->id)
->where('status', 'selesai')
->whereIn('sub_materi_id', $kategori->subMateri->pluck('id'))
->count();
}
// Menghitung persentase progres
$progressPercentage = ($totalSubmateri > 0) ? ($completedSubmateri / $totalSubmateri) * 100 : 0;
return response()->json([
'materi' => $materi->title,
'total_submateri' => $totalSubmateri,
'completed_submateri' => $completedSubmateri,
'progress_percentage' => $progressPercentage
]);
}
// Method untuk update progress
public function updateProgress(Request $request, $subMateriId)
{
$user = Auth::user();
// Update atau buat data progres baru
$progress = Progress::updateOrCreate(
['user_id' => $user->id, 'sub_materi_id' => $subMateriId],
['status' => 'selesai']
);
return response()->json(['message' => 'Progress updated successfully']);
}
public function getProgressBySubMateri($subMateriId)
{
// Ambil user yang terotentikasi
$user = Auth::user();
// Cek apakah ada progres untuk user ini pada submateri yang dimaksud, ambil yang terbaru
$progress = Progress::where('user_id', $user->id)
->where('sub_materi_id', $subMateriId)
->latest() // Mengambil data yang paling baru berdasarkan 'updated_at'
->first();
// Jika progres ada, kembalikan statusnya
if ($progress) {
return response()->json([
'sub_materi_id' => $subMateriId,
'status' => $progress->status, // 'selesai', 'gagal', atau status lainnya
'message' => 'Progress found',
]);
}
// Jika tidak ada progres, kembalikan status belum selesai
return response()->json([
'sub_materi_id' => $subMateriId,
'status' => 'belum selesai', // Default status
'message' => 'No progress found',
]);
}
public function getProgressPercentage(Request $request)
{
// Ambil user yang terotentikasi
$user = Auth::user();
// Ambil semua submateri yang ada di sistem
$totalSubmateri = SubMateri::count(); // Menghitung semua submateri yang ada
$completedSubmateri = Progress::where('user_id', $user->id)
->where('status', 'selesai')
->count(); // Menghitung submateri yang statusnya selesai
// Menghitung persentase progres
$progressPercentage = ($totalSubmateri > 0) ? ($completedSubmateri / $totalSubmateri) * 100 : 0;
// Return hasil persentase progres
return response()->json([
'user_id' => $user->id,
'nama_lengkap' => $user->nama_lengkap,
'total_submateri' => $totalSubmateri,
'completed_submateri' => $completedSubmateri,
'progress_percentage' => $progressPercentage
]);
}
public function getProgressPercentageById(Request $request, $user_id)
{
// Validasi user_id jika diperlukan
if (!User::find($user_id)) {
return response()->json([
'message' => 'User not found',
], 404);
}
// Ambil user berdasarkan ID
$user = User::find($user_id);
// Ambil semua submateri yang ada di sistem
$totalSubmateri = SubMateri::count(); // Menghitung semua submateri yang ada
$completedSubmateri = Progress::where('user_id', $user->id)
->where('status', 'selesai')
->count(); // Menghitung submateri yang statusnya selesai
// Menghitung persentase progres
$progressPercentage = ($totalSubmateri > 0) ? ($completedSubmateri / $totalSubmateri) * 100 : 0;
// Return hasil persentase progres
return response()->json([
'user_id' => $user->id,
'nama_lengkap' => $user->nama_lengkap,
'total_submateri' => $totalSubmateri,
'completed_submateri' => $completedSubmateri,
'progress_percentage' => $progressPercentage
]);
}
public function saveRecordedAudioName(Request $request, $id_latihan)
{
// Validasi data yang masuk
$request->validate([
'recorded_audio' => 'required|string', // Nama file audio yang disimpan
]);
// Temukan latihan berdasarkan ID (tanpa memeriksa user_id)
$latihan = Latihan::find($id_latihan);
// Jika latihan ditemukan, simpan nama file rekaman ke kolom recorder_audio
if ($latihan) {
$latihan->recorder_audio = $request->recorded_audio;
$latihan->status = 'benar'; // Status latihan diubah menjadi selesai
$latihan->save();
return response()->json([
'message' => 'Rekaman berhasil disimpan!',
'latihan' => $latihan,
], 200);
} else {
return response()->json([
'message' => 'Latihan tidak ditemukan.',
], 404);
}
}
// API untuk menyimpan data progres latihan yang telah diselesaikan
public function saveProgress(Request $request, $submateri_id)
{
// Validasi data yang masuk
$request->validate([
'latihan_ids' => 'required|array', // Array dari ID latihan yang telah diselesaikan
'latihan_ids.*' => 'exists:latihan,id', // Pastikan ID latihan ada di tabel latihan
]);
// Ambil user_id dari token yang digunakan (auth)
$user_id = Auth::id(); // Mendapatkan ID pengguna yang terautentikasi
// Ambil data submateri untuk menampilkan title
$submateri = SubMateri::find($submateri_id);
if (!$submateri) {
return response()->json(['message' => 'SubMateri tidak ditemukan'], 404);
}
// Menyimpan progres untuk setiap latihan yang diselesaikan oleh pengguna
$savedProgress = [];
foreach ($request->latihan_ids as $latihanId) {
// Temukan latihan berdasarkan ID
$latihan = Latihan::find($latihanId);
// Jika latihan ditemukan, simpan ke tabel progres
if ($latihan) {
$progress = Progress::updateOrCreate(
[
'user_id' => $user_id, // Gunakan user_id dari token
'sub_materi_id' => $submateri_id,
'id_latihan' => $latihan->id,
],
[
'status' => 'menunggu', // Status latihan diatur sebagai 'menunggu' setelah selesai
'nilai' => $latihan->nilai, // Nilai latihan yang diberikan oleh pengajar
]
);
// Menambahkan data progres yang baru disimpan untuk ditampilkan
$savedProgress[] = [
'user_id' => $user_id,
'sub_materi_id' => $submateri_id,
'submateri_title' => $submateri->title, // Menambahkan title dari submateri
'status' => 'menunggu',
'id_latihan' => $latihan->id,
'potongan_ayat' => $latihan->potongan_ayat,
'latin_text' => $latihan->latin_text,
'recorder_audio' => $latihan->recorder_audio,
];
}
}
return response()->json([
'message' => 'Progres latihan berhasil disimpan.',
'progress' => $savedProgress,
], 200);
}
public function getHasilPenilaianByUserAndSubMateri($submateri_id)
{
// Ambil user_id dari token yang digunakan (auth)
$user_id = Auth::id(); // Mendapatkan ID pengguna yang terautentikasi
// Ambil data berdasarkan user_id dan submateri_id
$progressData = Progress::with([
'latihan' => function ($query) {
$query->select('id', 'potongan_ayat', 'latin_text', 'recorder_audio', 'feedback_pengajar', 'status');
}
])
->where('user_id', $user_id) // Mengambil progress berdasarkan user_id yang terautentikasi
->where('sub_materi_id', $submateri_id) // Mengambil progress berdasarkan submateri_id
->get(['user_id', 'sub_materi_id', 'id_latihan', 'nilai']); // Pilih data yang diperlukan dari tabel progress
// Cek apakah ada data progress
if ($progressData->isEmpty()) {
return response()->json(['message' => 'Tidak ada data progres untuk pengguna ini pada submateri ini'], 404);
}
// Menyiapkan hasil yang akan dikembalikan
$results = $progressData->map(function ($progress) {
return [
'user_id' => $progress->user_id,
'sub_materi_id' => $progress->sub_materi_id,
'id_latihan' => $progress->id_latihan,
'nilai' => $progress->nilai,
'potongan_ayat' => $progress->latihan->potongan_ayat,
'latin_text' => $progress->latihan->latin_text,
'recorder_audio' => $progress->latihan->recorder_audio,
'feedback_pengajar' => $progress->latihan->feedback_pengajar,
'status' => $progress->latihan->status,
];
});
return response()->json([
'message' => 'Data hasil penilaian berhasil diambil.',
'data' => $results
], 200);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UploadAudioController extends Controller
{
public function uploadAudio(Request $request)
{
print($request->file('recorded_audio'));
// Validasi file
$request->validate([
// 'recorded_audio' => 'required|mimes:m4a,mp3,wav|max:10240', // Batasi ukuran file (misalnya 10MB)
'recorded_audio' => 'required|mimes:m4a,mp3,wav', // Batasi ukuran file (misalnya 10MB)
]);
// Mendapatkan file dari request
$file = $request->file('recorded_audio');
// Menyimpan file di disk public
$path = $file->store('audio_files', 'public'); // Menyimpan di folder `storage/app/public/audio_files`
return response()->json([
'message' => 'File berhasil diupload!',
'file_path' => $path
], 200);
}
}

View File

@ -5,11 +5,13 @@
use App\Helpers\ResponseFormatter; use App\Helpers\ResponseFormatter;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\User; use App\Models\User;
use App\Models\Progress;
use Exception; use Exception;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
class UserControler extends Controller class UserControler extends Controller
{ {
@ -87,6 +89,51 @@ public function login(Request $request)
} }
} }
public function loginWithTelp(Request $request)
{
try {
$request->validate([
'no_telp_wali' => 'required|string', // Validate 'no_telp_wali'
'password' => 'required' // Validate password
]);
// Find the user by 'no_telp_wali'
$user = User::where('no_telp_wali', $request->no_telp_wali)->first();
// Check if the user exists
if (!$user) {
return ResponseFormatter::error([
'message' => 'No telepon salah'
], 'Authentication Failed', 401);
}
// Check if the password is correct
if (!Hash::check($request->password, $user->password)) {
return ResponseFormatter::error([
'message' => 'Password salah'
], 'Authentication Failed', 401);
}
// Create token for the user
$tokenResult = $user->createToken('authToken')->plainTextToken;
return ResponseFormatter::success([
'access_token' => $tokenResult,
'token_type' => 'Bearer',
'user' => [
'peran' => $user->peran // Ensure 'peran' is included here
]
], 'Authenticated');
} catch (Exception $error) {
return ResponseFormatter::error([
'message' => 'Something went wrong',
'error' => $error->getMessage()
], 'Authentication Failed', 500);
}
}
public function fetch(Request $request) public function fetch(Request $request)
{ {
return ResponseFormatter::success($request->user(), 'Data berhasil di ambil'); return ResponseFormatter::success($request->user(), 'Data berhasil di ambil');
@ -102,8 +149,10 @@ public function logout(Request $request)
public function getUsersByRole(Request $request) public function getUsersByRole(Request $request)
{ {
try { try {
// Ambil data user yang memiliki peran 'santri' // Ambil data user yang memiliki peran 'santri' dan urutkan berdasarkan created_at secara descending
$users = User::where('peran', 'santri')->get(); $users = User::where('peran', 'santri')
->orderBy('created_at', 'desc')
->get();
// Jika tidak ada user dengan peran 'santri' // Jika tidak ada user dengan peran 'santri'
if ($users->isEmpty()) { if ($users->isEmpty()) {
@ -128,6 +177,7 @@ public function getUsersByRole(Request $request)
} }
} }
public function getUserInfoById($id) public function getUserInfoById($id)
{ {
// Mencari user berdasarkan ID // Mencari user berdasarkan ID
@ -259,55 +309,234 @@ public function tambahSantri(Request $request)
); );
} }
public function changePassword(Request $request)
{
// Validasi inputan password baru
$validator = Validator::make($request->all(), [
'new_password' => 'required|string|min:8|confirmed', // Pastikan password baru minimal 8 karakter dan dikonfirmasi
]);
// public function importSantri(Request $request) // Jika validasi gagal
// { if ($validator->fails()) {
// // Validasi input return response()->json([
// $validator = Validator::make($request->all(), [ 'status' => 'error',
// 'santri' => 'required|array', 'message' => $validator->errors(),
// 'santri.*.nama_lengkap' => 'required|string|max:255', ], 400);
// 'santri.*.alamat' => 'required|string|max:255', }
// 'santri.*.usia' => 'required|string|max:255',
// 'santri.*.no_telp_wali' => 'required|string|max:20',
// 'santri.*.email' => 'required|email|max:255',
// 'santri.*.jenis_kelamin' => 'required|string|in:Laki-laki,Perempuan',
// 'santri.*.jenjang_pendidikan' => 'required|string|max:255',
// ]);
// // Jika validasi gagal // Ambil user yang terautentikasi
// if ($validator->fails()) { $user = Auth::user();
// return ResponseFormatter::error(
// null,
// 'Validation Error',
// 422
// );
// }
// // Ambil data santri dari request // Update password pengguna secara manual
// $santriData = $request->input('santri'); DB::table('users') // Gunakan query builder untuk update langsung
// $santriInserted = []; ->where('id', $user->id) // Menentukan user berdasarkan ID
->update(['password' => Hash::make($request->new_password)]); // Enkripsi password baru dan simpan
return response()->json([
'status' => 'success',
'message' => 'Password berhasil diubah',
], 200);
}
public function getUserInfoByToken(Request $request)
{
// Mendapatkan pengguna yang terautentikasi
$user = Auth::user();
// Jika pengguna tidak ditemukan (misalnya, token tidak valid)
if (!$user) {
return ResponseFormatter::error(
null,
'User not found',
404
);
}
// Mengembalikan data pengguna dalam format JSON menggunakan ResponseFormatter
return ResponseFormatter::success(
[
'id' => $user->id,
'nama_lengkap' => $user->nama_lengkap,
'alamat' => $user->alamat,
'usia' => $user->usia,
'no_telp_wali' => $user->no_telp_wali,
'email' => $user->email,
'jenis_kelamin' => $user->jenis_kelamin,
'jenjang_pendidikan' => $user->jenjang_pendidikan,
],
'User data retrieved successfully'
);
}
public function updateUserByToken(Request $request)
{
// Validasi data yang diterima
$validator = Validator::make($request->all(), [
'nama_lengkap' => 'required|string|max:255',
'alamat' => 'required|string|max:255',
'usia' => 'required|string|max:255',
'no_telp_wali' => 'required|string|max:20',
'email' => 'required|email|max:255',
'jenjang_pendidikan' => 'required|string|max:255',
'jenis_kelamin' => 'required|string|in:Laki-laki,Perempuan',
]);
// Jika validasi gagal, kembalikan respons error
if ($validator->fails()) {
return ResponseFormatter::error(
null,
'Validation Error',
422
);
}
// Mendapatkan pengguna yang terautentikasi
$user = Auth::user();
// Jika pengguna tidak ditemukan (misalnya, token tidak valid)
if (!$user) {
return ResponseFormatter::error(
null,
'User not found',
404
);
}
// Menggunakan DB::table untuk memperbarui data pengguna secara manual
DB::table('users')
->where('id', $user->id)
->update([
'nama_lengkap' => $request->nama_lengkap,
'alamat' => $request->alamat,
'usia' => $request->usia,
'no_telp_wali' => $request->no_telp_wali,
'email' => $request->email,
'jenjang_pendidikan' => $request->jenjang_pendidikan,
'jenis_kelamin' => $request->jenis_kelamin,
]);
// Mengembalikan respons sukses
return ResponseFormatter::success(
null,
'User updated successfully'
);
}
public function importSantri(Request $request)
{
// Validasi input
$validator = Validator::make($request->all(), [
'santri' => 'required|array',
'santri.*.nama_lengkap' => 'required|string|max:255',
'santri.*.alamat' => 'required|string|max:255',
'santri.*.usia' => 'required|string|max:255',
'santri.*.no_telp_wali' => 'required|string|max:20',
'santri.*.email' => 'required|email|max:255',
'santri.*.jenis_kelamin' => 'required|string|in:Laki-laki,Perempuan',
'santri.*.jenjang_pendidikan' => 'required|string|max:255',
]);
// Jika validasi gagal
if ($validator->fails()) {
return ResponseFormatter::error(
null,
'Validation Error',
422
);
}
// Ambil data santri dari request
$santriData = $request->input('santri');
$santriInserted = [];
// Simpan tiap santri ke database
foreach ($santriData as $santri) {
$santriInserted[] = User::create([
'nama_lengkap' => $santri['nama_lengkap'],
'alamat' => $santri['alamat'],
'usia' => $santri['usia'],
'no_telp_wali' => $santri['no_telp_wali'],
'email' => $santri['email'],
'jenis_kelamin' => $santri['jenis_kelamin'],
'jenjang_pendidikan' => $santri['jenjang_pendidikan'],
'peran' => 'santri',
'password' => bcrypt('almuhajirin'), // password = email (dihash)
]);
}
// Respon sukses
return ResponseFormatter::success(
$santriInserted,
'Santri data imported successfully'
);
}
public function getUserProgres(Request $request)
{
try {
// Ambil semua user dengan peran 'santri'
$santriUsers = User::where('peran', 'santri')->get(); // Ambil semua user dengan peran 'santri'
// Jika tidak ada santri
if ($santriUsers->isEmpty()) {
return response()->json([
'status' => 'error',
'message' => 'Tidak ada santri yang ditemukan.',
], 404); // Status code 404 (Not Found)
}
// Ambil progres untuk setiap santri dan filter yang sudah menyelesaikan submateri
$santriProgressData = $santriUsers->map(function ($santri) {
// Ambil progres latihan yang telah diselesaikan oleh santri berdasarkan user ID
$progresData = Progress::where('user_id', $santri->id)
->where('status', 'selesai') // Menghitung submateri yang sudah selesai
->with('submateri') // Load data submateri yang terkait
->get();
// Hitung jumlah submateri yang selesai
$completedSubmateriCount = $progresData->count(); // Menghitung jumlah data progres yang selesai
// Hanya tampilkan santri yang telah menyelesaikan submateri
if ($completedSubmateriCount > 0) {
return [
'user_id' => $santri->id,
'nama_lengkap' => $santri->nama_lengkap,
'no_telp_wali' => $santri->no_telp_wali,
'completed_submateri' => $completedSubmateriCount, // Jumlah submateri yang telah selesai
];
}
})->filter(function ($santri) {
return $santri !== null; // Filter out santri that haven't completed any submateri
});
// Jika tidak ada santri yang menyelesaikan submateri
if ($santriProgressData->isEmpty()) {
return response()->json([
'status' => 'error',
'message' => 'Tidak ada santri yang telah menyelesaikan submateri.',
], 404); // Status code 404 (Not Found)
}
// Kirim data progres untuk setiap santri yang sudah selesai latihan
return response()->json([
'status' => 'success',
'data' => $santriProgressData, // Mengembalikan data progres yang telah diselesaikan
], 200);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Something went wrong: ' . $e->getMessage(),
], 500);
}
}
// // Simpan tiap santri ke database
// foreach ($santriData as $santri) {
// $santriInserted[] = User::create([
// 'nama_lengkap' => $santri['nama_lengkap'],
// 'alamat' => $santri['alamat'],
// 'usia' => $santri['usia'],
// 'no_telp_wali' => $santri['no_telp_wali'],
// 'email' => $santri['email'],
// 'jenis_kelamin' => $santri['jenis_kelamin'],
// 'jenjang_pendidikan' => $santri['jenjang_pendidikan'],
// 'peran' => 'santri',
// 'password' => bcrypt($santri['email']), // password = email (dihash)
// ]);
// }
// // Respon sukses
// return ResponseFormatter::success(
// $santriInserted,
// 'Santri data imported successfully'
// );
// }
} }

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ConfirmsPasswords;
class ConfirmPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Confirm Password Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password confirmations and
| uses a simple trait to include the behavior. You're free to explore
| this trait and override any functions that require customization.
|
*/
use ConfirmsPasswords;
/**
* Where to redirect users when the intended url fails.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
$this->middleware('auth')->only('logout');
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\Models\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = '/home';
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\VerifiesEmails;
class VerificationController extends Controller
{
/*
|--------------------------------------------------------------------------
| Email Verification Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling email verification for any
| user that recently registered with the application. Emails may also
| be re-sent if the user didn't receive the original email message.
|
*/
use VerifiesEmails;
/**
* Where to redirect users after verification.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
}

View File

@ -39,7 +39,7 @@ class Kernel extends HttpKernel
], ],
'api' => [ 'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
], ],

View File

@ -20,6 +20,14 @@ class Latihan extends Model
'materi_description', 'materi_description',
'correct_audio', 'correct_audio',
'recorder_audio', 'recorder_audio',
'feedback_pengajar',
'nilai',
'status',
]; ];
public function progress()
{
return $this->hasMany(Progress::class, 'id_latihan'); // Relasi balik ke Progress
}
} }

31
app/Models/Progress.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Progress extends Model
{
use HasFactory;
protected $fillable = ['user_id', 'sub_materi_id', 'status', 'nilai','id_latihan',];
// Relasi ke User
public function user()
{
return $this->belongsTo(User::class);
}
// Relasi ke SubMateri
public function subMateri()
{
return $this->belongsTo(SubMateri::class);
}
// Relasi ke Latihan
public function Latihan()
{
return $this->belongsTo(Latihan::class, 'id_latihan');
}
}

View File

@ -9,7 +9,9 @@
"guzzlehttp/guzzle": "^7.2", "guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.0", "laravel/framework": "^10.0",
"laravel/sanctum": "^3.2", "laravel/sanctum": "^3.2",
"laravel/tinker": "^2.8" "laravel/tinker": "^2.8",
"laravel/ui": "^4.6",
"phpmailer/phpmailer": "^6.10"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.9.1", "fakerphp/faker": "^1.9.1",

146
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "bfe12996eeecb6fdc8713a9fd9d431f8", "content-hash": "1787b3b8dd905d7a2fb5758e07bf55a0",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@ -1512,6 +1512,69 @@
}, },
"time": "2025-01-27T14:24:01+00:00" "time": "2025-01-27T14:24:01+00:00"
}, },
{
"name": "laravel/ui",
"version": "v4.6.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/ui.git",
"reference": "7d6ffa38d79f19c9b3e70a751a9af845e8f41d88"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/ui/zipball/7d6ffa38d79f19c9b3e70a751a9af845e8f41d88",
"reference": "7d6ffa38d79f19c9b3e70a751a9af845e8f41d88",
"shasum": ""
},
"require": {
"illuminate/console": "^9.21|^10.0|^11.0|^12.0",
"illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0",
"illuminate/support": "^9.21|^10.0|^11.0|^12.0",
"illuminate/validation": "^9.21|^10.0|^11.0|^12.0",
"php": "^8.0",
"symfony/console": "^6.0|^7.0"
},
"require-dev": {
"orchestra/testbench": "^7.35|^8.15|^9.0|^10.0",
"phpunit/phpunit": "^9.3|^10.4|^11.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Laravel\\Ui\\UiServiceProvider"
]
},
"branch-alias": {
"dev-master": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"Laravel\\Ui\\": "src/",
"Illuminate\\Foundation\\Auth\\": "auth-backend/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel UI utilities and presets.",
"keywords": [
"laravel",
"ui"
],
"support": {
"source": "https://github.com/laravel/ui/tree/v4.6.1"
},
"time": "2025-01-28T15:15:29+00:00"
},
{ {
"name": "league/commonmark", "name": "league/commonmark",
"version": "2.6.1", "version": "2.6.1",
@ -2390,6 +2453,87 @@
], ],
"time": "2024-11-21T10:36:35+00:00" "time": "2024-11-21T10:36:35+00:00"
}, },
{
"name": "phpmailer/phpmailer",
"version": "v6.10.0",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144",
"reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-filter": "*",
"ext-hash": "*",
"php": ">=5.5.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
"doctrine/annotations": "^1.2.6 || ^1.13.3",
"php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.3.5",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.7.2",
"yoast/phpunit-polyfills": "^1.0.4"
},
"suggest": {
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
"psr/log": "For optional PSR-3 debug logging",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
},
"type": "library",
"autoload": {
"psr-4": {
"PHPMailer\\PHPMailer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-only"
],
"authors": [
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0"
},
"funding": [
{
"url": "https://github.com/Synchro",
"type": "github"
}
],
"time": "2025-04-24T15:19:31+00:00"
},
{ {
"name": "phpoption/phpoption", "name": "phpoption/phpoption",
"version": "1.9.3", "version": "1.9.3",

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
};

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::create('progress', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
$table->foreignId('sub_materi_id')->constrained('sub_materi')->onDelete('cascade');
$table->enum('status', ['selesai', 'belum selesai'])->default('belum selesai');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('progress');
}
};

View File

@ -0,0 +1,32 @@
<?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('latihan', function (Blueprint $table) {
$table->text('feedback_pengajar')->nullable()->after('recorder_audio');
$table->decimal('nilai', 5, 2)->nullable()->after('feedback_pengajar');
$table->enum('status', ['benar', 'salah'])->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('latihan', function (Blueprint $table) {
$table->dropColumn('feedback_pengajar');
$table->dropColumn('nilai');
$table->dropColumn('status');
});
}
};

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('progress', function (Blueprint $table) {
$table->decimal('nilai', 5, 2)->nullable()->after('status'); // Kolom nilai yang didapatkan oleh santri
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('progress', function (Blueprint $table) {
$table->dropColumn('nilai');
});
}
};

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('progress', function (Blueprint $table) {
$table->enum('status', ['selesai', 'menunggu', 'gagal', 'belum selesai'])->default('belum selesai')->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('progress', function (Blueprint $table) {
$table->enum('status', ['selesai', 'belum selesai'])->default('belum selesai')->change();
});
}
};

View File

@ -0,0 +1,29 @@
<?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('progress', function (Blueprint $table) {
$table->foreignId('id_latihan')->nullable()->constrained('latihan')->onDelete('cascade')->after('id_user');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('progress', function (Blueprint $table) {
$table->dropForeign(['id_latihan']); // Menghapus foreign key
$table->dropColumn('id_latihan'); // Menghapus kolom
});
}
};

View File

@ -15,226 +15,257 @@ class QuizTableSeeder extends Seeder
public function run() public function run()
{ {
$questions = [ $questions = [
// Soal 1
[ [
'id_materi' => 1, 'id_materi' => 2,
'question' => 'Makharijul Huruf artinya .....', 'question' => 'Ada berapa jumlah huruf yang memiliki sifat Jahr?',
'option_a' => 'Tempat-tempat keluarnya huruf hijaiah', 'option_a' => '19',
'option_b' => 'Tempat-tempat berubahnya huruf hijaiah', 'option_b' => '20',
'option_c' => 'Tempat-tempat hilangnya huruf hijaiah', 'option_c' => '21',
'option_d' => 'Tempat-tempat berkumpulnya huruf hijaiah', 'option_d' => '10',
'correct_option' => 'a', 'correct_option' => 'b',
'score' => 20, 'score' => 20,
], ],
// Soal 2 [
[ 'id_materi' => 2,
'id_materi' => 1, 'question' => 'Ada berapa jumlah huruf yang memiliki sifat Tawassuth?',
'question' => 'Jumlah makhraj-makhraj huruf menurut Ibnu Jazari adalah .....', 'option_a' => '4',
'option_a' => '15 makhraj', 'option_b' => '5',
'option_b' => '16 makhraj', 'option_c' => '10',
'option_c' => '17 makhraj', 'option_d' => '7',
'option_d' => '18 makhraj', 'correct_option' => 'b',
'correct_option' => 'c', 'score' => 20,
'score' => 20, ],
], [
// Soal 3 'id_materi' => 2,
[ 'question' => 'Ada berapa jumlah huruf yang memiliki sifat Tafasysyi?',
'id_materi' => 1, 'option_a' => '1',
'question' => 'Salah satu tempat keluarnya huruf adalah Al-Halqi. Halqi artinya .....', 'option_b' => '4',
'option_a' => 'Lidah', 'option_c' => '5',
'option_b' => 'Tenggorokan', 'option_d' => '7',
'option_c' => 'Rongga hidung', 'correct_option' => 'a',
'option_d' => 'Bibir', 'score' => 20,
'correct_option' => 'b', ],
'score' => 20, [
], 'id_materi' => 2,
// Soal 4 'question' => 'Ada berapa jumlah huruf yang memiliki sifat Qalqalah?',
[ 'option_a' => '1',
'id_materi' => 1, 'option_b' => '4',
'question' => 'Huruf ain (ع) merupakan huruf yang keluar dari .....', 'option_c' => '5',
'option_a' => 'Lidah bagian tengah', 'option_d' => '7',
'option_b' => 'Tenggorokan bagian tengah', 'correct_option' => 'b',
'option_c' => 'Lidah bagian ujung', 'score' => 20,
'option_d' => 'Tenggorokan bagian atas', ],
'correct_option' => 'b', [
'score' => 20, 'id_materi' => 2,
], 'question' => 'Ada berapa jumlah huruf yang memiliki sifat Shafir?',
// Soal 5 'option_a' => '1',
[ 'option_b' => '4',
'id_materi' => 1, 'option_c' => '5',
'question' => 'Jumlah huruf yang keluar lewat tenggorokan adalah .....', 'option_d' => '3',
'option_a' => '5 huruf', 'correct_option' => 'd',
'option_b' => '6 huruf', 'score' => 20,
'option_c' => '7 huruf', ],
'option_d' => '8 huruf', [
'correct_option' => 'b', 'id_materi' => 2,
'score' => 20, 'question' => 'Sifat apa yang berlawanan dari sifat Hams?',
], 'option_a' => 'Syiddah',
// Soal 6 'option_b' => 'Tawassuth',
[ 'option_c' => 'Istila',
'id_materi' => 1, 'option_d' => 'Jahr',
'question' => 'Berikut ini merupakan huruf yang keluar lewat tenggorokan, kecuali .....', 'correct_option' => 'd',
'option_a' => 'Huruf ghain (غ)', 'score' => 20,
'option_b' => 'Huruf hamzah (ء)', ],
'option_c' => 'Huruf kha (خ)', [
'option_d' => 'Huruf nun (ن)', 'id_materi' => 2,
'correct_option' => 'd', 'question' => 'Sifat apa yang berlawanan dari sifat Ishmat?',
'score' => 20, 'option_a' => 'Infitah',
], 'option_b' => 'Idzlaq',
// Soal 7 'option_c' => 'Istila',
[ 'option_d' => 'Tawassuth',
'id_materi' => 1, 'correct_option' => 'b',
'question' => 'Berikut ini merupakan huruf yang memiliki makhraj sama dengan huruf jim (ج) adalah .....', 'score' => 20,
'option_a' => 'Huruf syin (ش)', ],
'option_b' => 'Huruf fa (ف)', [
'option_c' => 'Huruf lam (ل)', 'id_materi' => 2,
'option_d' => 'Huruf nun (ن)', 'question' => 'Sifat apa yang berlawanan dari sifat Istila?',
'correct_option' => 'a', 'option_a' => 'Infitah',
'score' => 20, 'option_b' => 'Istifal',
], 'option_c' => 'Lin',
// Soal 8 'option_d' => 'Shafir',
[ 'correct_option' => 'b',
'id_materi' => 1, 'score' => 20,
'question' => 'Huruf qaf (ق) merupakan huruf yang keluar dari lidah bagian ....', ],
'option_a' => 'Pangkal lidah', [
'option_b' => 'Ujung lidah', 'id_materi' => 2,
'option_c' => 'Tengah lidah', 'question' => 'Sifat apa yang berlawanan dari sifat Shafir?',
'option_d' => 'Tepi lidah', 'option_a' => 'Lin',
'correct_option' => 'a', 'option_b' => 'Qalqalah',
'score' => 20, 'option_c' => 'Hams',
], 'option_d' => 'Tidak ada',
// Soal 9 'correct_option' => 'd',
[ 'score' => 20,
'id_materi' => 1, ],
'question' => 'Jumlah huruf yang keluar lewat lidah adalah .....', [
'option_a' => '15 huruf', 'id_materi' => 2,
'option_b' => '16 huruf', 'question' => 'Sifat apa yang berlawanan dari sifat Infitah?',
'option_c' => '17 huruf', 'option_a' => 'Ithbaq',
'option_d' => '18 huruf', 'option_b' => 'Idzlaq',
'correct_option' => 'd', 'option_c' => 'Jahr',
'score' => 20, 'option_d' => 'Hams',
], 'correct_option' => 'a',
// Soal 10 'score' => 20,
[ ],
'id_materi' => 1, [
'question' => 'Jumlah huruf yang keluar lewat bibir adalah .....', 'id_materi' => 2,
'option_a' => '2 huruf', 'question' => 'Berikut ini adalah sifat-sifat yang dimiliki huruf ba (ب) kecuali...',
'option_b' => '3 huruf', 'option_a' => 'Qalqalah',
'option_c' => '4 huruf', 'option_b' => 'Jahr',
'option_d' => '5 huruf', 'option_c' => 'Syiddah',
'correct_option' => 'c', 'option_d' => 'Istila',
'score' => 20, 'correct_option' => 'd',
], 'score' => 20,
// Soal 11 ],
[ [
'id_materi' => 1, 'id_materi' => 2,
'question' => 'Berikut ini merupakan kelompok huruf yang keluar dari makhraj yang sama, kecuali .....', 'question' => 'Berikut ini adalah sifat-sifat yang dimiliki huruf ro (ر) kecuali...',
'option_a' => 'Huruf ع dan ح', 'option_a' => 'Jahr',
'option_b' => 'Huruf غ dan خ', 'option_b' => 'Syiddah',
'option_c' => 'Huruf ل dan ط', 'option_c' => 'Takrir',
'option_d' => 'Huruf ث, ذ, dan ظ', 'option_d' => 'Infitah',
'correct_option' => 'a', 'correct_option' => 'd',
'score' => 20, 'score' => 20,
], ],
// Soal 12 [
[ 'id_materi' => 2,
'id_materi' => 1, 'question' => 'Berikut ini adalah sifat-sifat yang dimiliki huruf syin (ش) kecuali...',
'question' => 'Berikut ini merupakan huruf yang keluar dari pangkal lidah adalah .....', 'option_a' => 'Hams',
'option_a' => 'Huruf kaf (ك)', 'option_b' => 'Rakhawah',
'option_b' => 'Huruf nun (ن)', 'option_c' => 'Qalqalah',
'option_c' => 'Huruf jim (ج)', 'option_d' => 'Tafasysyi',
'option_d' => 'Huruf shad (ص)', 'correct_option' => 'c',
'correct_option' => 'a', 'score' => 20,
'score' => 20, ],
], [
// Soal 13 'id_materi' => 2,
[ 'question' => 'Berikut ini adalah sifat-sifat yang dimiliki huruf ta (ت) kecuali...',
'id_materi' => 1, 'option_a' => 'Hams',
'question' => 'Berikut ini merupakan huruf yang keluar dari tengah lidah adalah .....', 'option_b' => 'Syiddah',
'option_a' => 'Huruf kaf (ك)', 'option_c' => 'Istifal',
'option_b' => 'Huruf nun (ن)', 'option_d' => 'Ithbaq',
'option_c' => 'Huruf jim (ج)', 'correct_option' => 'd',
'option_d' => 'Huruf shad (ص)', 'score' => 20,
'correct_option' => 'c', ],
'score' => 20, [
], 'id_materi' => 2,
// Soal 14 'question' => 'Berikut ini adalah sifat-sifat yang dimiliki huruf kho (خ) kecuali...',
[ 'option_a' => 'Hams',
'id_materi' => 1, 'option_b' => 'Rakhawah',
'question' => 'Huruf dhad (ض) merupakan huruf yang keluar dari .....', 'option_c' => 'Idzlaq',
'option_a' => 'Pangkal lidah', 'option_d' => 'Istila',
'option_b' => 'Bibir dalam', 'correct_option' => 'b',
'option_c' => 'Tepi lidah', 'score' => 20,
'option_d' => 'Ujung lidah', ],
'correct_option' => 'c', [
'score' => 20, 'id_materi' => 2,
], 'question' => 'Manakah huruf berikut yang memiliki sifat Syiddah?',
// Soal 15 'option_a' => 'Syin',
[ 'option_b' => 'Qaf',
'id_materi' => 1, 'option_c' => 'Nun',
'question' => 'Dua huruf yang keluar dari makhraj yang sama disebut .....', 'option_d' => 'Lam',
'option_a' => 'Mutamatsilain', 'correct_option' => 'b',
'option_b' => 'Mutajanisain', 'score' => 20,
'option_c' => 'Mutaqaribain', ],
'option_d' => 'Mutabaidain', [
'correct_option' => 'c', 'id_materi' => 2,
'score' => 20, 'question' => 'Manakah huruf berikut yang memiliki sifat Inhiraf?',
], 'option_a' => 'Syin',
// Soal 16 'option_b' => 'Qaf',
[ 'option_c' => 'Nun',
'id_materi' => 1, 'option_d' => 'Lam',
'question' => 'Berikut ini adalah huruf-huruf yang keluar dari ujung lidah, kecuali .....', 'correct_option' => 'a',
'option_a' => 'Huruf dal (د)', 'score' => 20,
'option_b' => 'Huruf tsa (ث)', ],
'option_c' => 'Huruf ya (ي)', [
'option_d' => 'Huruf dha (ظ)', 'id_materi' => 2,
'correct_option' => 'c', 'question' => 'Manakah huruf berikut yang memiliki sifat Istithalah?',
'score' => 20, 'option_a' => 'Ro',
], 'option_b' => 'Dhad',
// Soal 17 'option_c' => 'Mim',
[ 'option_d' => 'Ain',
'id_materi' => 1, 'correct_option' => 'b',
'question' => 'Huruf yang keluar dari al-Jauf adalah .....', 'score' => 20,
'option_a' => 'Alif', ],
'option_b' => 'Ba', [
'option_c' => 'Ta', 'id_materi' => 2,
'option_d' => 'Tsa', 'question' => 'Manakah huruf berikut yang memiliki sifat Ghunnah?',
'correct_option' => 'a', 'option_a' => 'Ro',
'score' => 20, 'option_b' => 'Dhad',
], 'option_c' => 'Mim',
// Soal 18 'option_d' => 'Ain',
[ 'correct_option' => 'c',
'id_materi' => 1, 'score' => 20,
'question' => 'Berapa jumlah huruf yang keluar dari pangkal lidah? .....', ],
'option_a' => '1 huruf', [
'option_b' => '2 huruf', 'id_materi' => 2,
'option_c' => '3 huruf', 'question' => 'Manakah huruf berikut yang memiliki sifat Idzlaq?',
'option_d' => '4 huruf', 'option_a' => 'Ro',
'correct_option' => 'b', 'option_b' => 'Dhad',
'score' => 20, 'option_c' => 'Syin',
], 'option_d' => 'Ain',
// Soal 19 'correct_option' => 'c',
[ 'score' => 20,
'id_materi' => 1, ],
'question' => 'Berikut ini manakah pernyataan yang benar? .....', [
'option_a' => 'Makhraj sin (س) adalah ujung lidah dengan rongga antara gigi atas dan gigi bawah yang lebih dekat dengan gigi bawah', 'id_materi' => 2,
'option_b' => 'Makhraj ta (ت) adalah ujung lidah menempel dengan ujung gigi atas', 'question' => 'Pilih pernyataan benar berikut ini!',
'option_c' => 'Makhraj tha (ط) adalah ujung lidah menempel dengan ujung gigi atas', 'option_a' => 'Jahr adalah tertahannya aliran nafas ketika mengucapkan huruf',
'option_d' => 'Makhraj tsa (ث) adalah ujung lidah dengan rongga antara gigi atas dan gigi bawah yang lebih dekat dengan gigi bawah', 'option_b' => 'Syiddah adalah mengalirnya suara ketika mengucapkan huruf',
'correct_option' => 'b', 'option_c' => 'Istila adalah turunnya pangkal lidah ketika mengucapkan huruf',
'score' => 20, 'option_d' => 'Tawassuth adalah tertahannya suara ketika mengucapkan huruf',
], 'correct_option' => 'b',
// Soal 20 'score' => 20,
[ ],
'id_materi' => 1, [
'question' => 'Makhraj huruf zay (ز) adalah .....', 'id_materi' => 2,
'option_a' => 'Ujung lidah dengan rongga antara gigi atas dan gigi bawah yang lebih dekat dengan gigi bawah', 'question' => 'Huruf ro dan lam memiliki sifat yang hampir sama, namun ada satu sifat yang membedakan keduanya. Sifat itu adalah sifat....',
'option_b' => 'Ujung lidah menempel dengan pangkal gigi atas', 'option_a' => 'Istithalah',
'option_c' => 'Ujung lidah menempel dengan ujung gigi atas', 'option_b' => 'Tafasysyi',
'option_d' => 'Tenggorakan bagian tengah', 'option_c' => 'Takrir',
'correct_option' => 'c', 'option_d' => 'Lin',
'score' => 20, 'correct_option' => 'c',
], 'score' => 20,
],
[
'id_materi' => 2,
'question' => 'Pilih pernyataan benar berikut ini!',
'option_a' => 'Tawassuth merupakan sifat pertengahan antara Istila dan Istifal',
'option_b' => 'Sifat Qalqalah merupakan sifat yang tidak memiliki lawan',
'option_c' => 'Ada 10 huruf yang memiliki sifat Istila',
'option_d' => 'Jumlah sifat yang dimiliki huruf hamzah adalah 9 sifat.',
'correct_option' => 'a',
'score' => 20,
],
[
'id_materi' => 2,
'question' => 'Sifat Inhiraf hanya dimiliki oleh dua huruf, yaitu',
'option_a' => 'Lam dan Ro',
'option_b' => 'Nun dan Mim',
'option_c' => 'Hamzah dan Alif',
'option_d' => 'Sin dan Shad',
'correct_option' => 'a',
'score' => 20,
],
[
'id_materi' => 2,
'question' => 'Pilih pernyataan benar berikut ini!',
'option_a' => 'Sifat Takrir adalah bergetarnya bibir ketika mengucapkan huruf',
'option_b' => 'Sifat Ishmat adalah cepatnya suara ketika mengucapkan huruf',
'option_c' => 'Sifat Tafasysyi adalah lawan kata dari sifat Inhiraf',
'option_d' => 'Sifat Lin adalah mengucapkan huruf dengan lentur',
'correct_option' => 'd',
'score' => 20,
]
]; ];
// Insert data soal ke dalam tabel quiz // Insert data soal ke dalam tabel quiz

View File

@ -5,8 +5,13 @@
"build": "vite build" "build": "vite build"
}, },
"devDependencies": { "devDependencies": {
"@popperjs/core": "^2.11.6",
"@vitejs/plugin-vue": "^4.5.0",
"axios": "^1.1.2", "axios": "^1.1.2",
"bootstrap": "^5.2.3",
"laravel-vite-plugin": "^0.7.2", "laravel-vite-plugin": "^0.7.2",
"vite": "^4.0.0" "sass": "^1.56.1",
"vite": "^4.0.0",
"vue": "^3.2.37"
} }
} }

View File

@ -1 +1,39 @@
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
import './bootstrap'; import './bootstrap';
import { createApp } from 'vue';
/**
* Next, we will create a fresh Vue application instance. You may then begin
* registering components with the application instance so they are ready
* to use in your application's views. An example is included for you.
*/
const app = createApp({});
import ExampleComponent from './components/ExampleComponent.vue';
app.component('example-component', ExampleComponent);
/**
* The following block of code may be used to automatically register your
* Vue components. It will recursively scan this directory for the Vue
* components and automatically register them with their "basename".
*
* Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
*/
// Object.entries(import.meta.glob('./**/*.vue', { eager: true })).forEach(([path, definition]) => {
// app.component(path.split('/').pop().replace(/\.\w+$/, ''), definition.default);
// });
/**
* Finally, we will attach the application instance to a HTML element with
* an "id" attribute of "app". This element is included with the "auth"
* scaffolding. Otherwise, you will need to add an element yourself.
*/
app.mount('#app');

View File

@ -1,3 +1,5 @@
import 'bootstrap';
/** /**
* We'll load the axios HTTP library which allows us to easily issue requests * We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the * to our Laravel back-end. This library automatically handles sending the
@ -24,7 +26,7 @@ window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// broadcaster: 'pusher', // broadcaster: 'pusher',
// key: import.meta.env.VITE_PUSHER_APP_KEY, // key: import.meta.env.VITE_PUSHER_APP_KEY,
// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1', // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1',
// wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`, // wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, // wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, // wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', // forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',

View File

@ -0,0 +1,23 @@
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Example Component</div>
<div class="card-body">
I'm an example component.
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
}
}
</script>

View File

@ -0,0 +1,7 @@
// Body
$body-bg: #f8fafc;
// Typography
$font-family-sans-serif: 'Nunito', sans-serif;
$font-size-base: 0.9rem;
$line-height-base: 1.6;

8
resources/sass/app.scss Normal file
View File

@ -0,0 +1,8 @@
// Fonts
@import url('https://fonts.bunny.net/css?family=Nunito');
// Variables
@import 'variables';
// Bootstrap
@import 'bootstrap/scss/bootstrap';

View File

@ -0,0 +1,73 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Login') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
</div>
<div class="row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Login') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,49 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Confirm Password') }}</div>
<div class="card-body">
{{ __('Please confirm your password before continuing.') }}
<form method="POST" action="{{ route('password.confirm') }}">
@csrf
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Confirm Password') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,47 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,65 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Reset Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,77 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Register') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="row mb-3">
<label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,28 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Verify Your Email Address') }}</div>
<div class="card-body">
@if (session('resent'))
<div class="alert alert-success" role="alert">
{{ __('A fresh verification link has been sent to your email address.') }}
</div>
@endif
{{ __('Before proceeding, please check your email for a verification link.') }}
{{ __('If you did not receive the email') }},
<form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
@csrf
<button type="submit" class="btn btn-link p-0 m-0 align-baseline">{{ __('click here to request another') }}</button>.
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,23 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{ __('You are logged in!') }}
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,80 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">
<!-- Scripts -->
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="navbar-nav me-auto">
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ms-auto">
<!-- Authentication Links -->
@guest
@if (Route::has('login'))
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@endif
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }}
</a>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</div>
</div>
</nav>
<main class="py-4">
@yield('content')
</main>
</div>
</body>
</html>

View File

@ -1,13 +1,16 @@
<?php <?php
use App\Http\Controllers\API\UserControler; use App\Http\Controllers\API\UserControler;
use App\Http\Controllers\API\ChangePassController;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\MateriControler; use App\Http\Controllers\API\MateriControler;
use App\Http\Controllers\API\UploadAudioController;
use App\Http\Controllers\API\KategoryController; use App\Http\Controllers\API\KategoryController;
use App\Http\Controllers\API\SubMateriControler; use App\Http\Controllers\API\SubMateriControler;
use App\Http\Controllers\API\LatihanControler; use App\Http\Controllers\API\LatihanControler;
use App\Http\Controllers\QuizController; use App\Http\Controllers\QuizController;
use App\Http\Controllers\API\ProgressController;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -23,10 +26,12 @@
Route::post('register', [UserControler::class, 'register']); Route::post('register', [UserControler::class, 'register']);
Route::post('login', [UserControler::class, 'login']); Route::post('login', [UserControler::class, 'login']);
Route::post('loginWithTelp', [UserControler::class, 'loginWithTelp']);
Route::middleware('auth:sanctum')->group(function () { Route::middleware('auth:sanctum')->group(function () {
Route::get('user', [UserControler::class, 'fetch']); Route::get('user', [UserControler::class, 'fetch']);
Route::post('logout', [UserControler::class, 'logout']); Route::post('logout', [UserControler::class, 'logout']);
Route::post('/change_password', [UserControler::class, 'changePassword']);
}); });
Route::get('/materi', [MateriControler::class, 'getMateri']); Route::get('/materi', [MateriControler::class, 'getMateri']);
@ -53,5 +58,27 @@
Route::put('/updateUser/{id}', [UserControler::class, 'updateUserById']); Route::put('/updateUser/{id}', [UserControler::class, 'updateUserById']);
Route::post('/tambahSantri', [UserControler::class, 'tambahSantri']); Route::post('/tambahSantri', [UserControler::class, 'tambahSantri']);
Route::get('/userByToken', [UserControler::class, 'getUserInfoByToken']);
// Memperbarui data pengguna berdasarkan token
Route::post('/user/updateBytoken', [UserControler::class, 'updateUserByToken']);
Route::post('/importSantri', [UserControler::class, 'importSantri']);
}); });
Route::middleware('auth:sanctum')->group(function () {
Route::get('/materi/{materiId}/progress', [ProgressController::class, 'getMateriProgress']);
Route::post('/progress/{subMateriId}/update', [ProgressController::class, 'updateProgress']);
Route::get('/progress/{subMateriId}/status', [ProgressController::class, 'getProgressBySubMateri']);
Route::get('/progres/presentase', [ProgressController::class, 'getProgressPercentage']);
Route::get('/progres/{user_id}', [ProgressController::class, 'getProgressPercentageById']);
Route::put('latihan/{id_latihan}/saverecord', [ProgressController::class, 'saveRecordedAudioName']);
Route::post('progress/{submateri_id}/save', [ProgressController::class, 'saveProgress']);
Route::get('/hasil-penilaian/{submateri_id}', [ProgressController::class, 'getHasilPenilaianByUserAndSubMateri']);
Route::post('/upload_audio', [UploadAudioController::class, 'uploadAudio']);
});
Route::get('/users/progres', [UserControler::class, 'getUserProgres']);

View File

@ -16,3 +16,7 @@
Route::get('/', function () { Route::get('/', function () {
return view('welcome'); return view('welcome');
}); });
Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

View File

@ -1,11 +1,28 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin'; import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
laravel({ laravel({
input: ['resources/css/app.css', 'resources/js/app.js'], input: [
'resources/sass/app.scss',
'resources/js/app.js',
],
refresh: true, refresh: true,
}), }),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
], ],
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js',
},
},
}); });