This commit is contained in:
gitaysn 2025-06-17 20:19:21 +07:00
parent 45115b3367
commit 1365a65409
6 changed files with 145 additions and 31 deletions

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Http\Controllers\Controller;
class ForgotUsernameController extends Controller
{
// Tampilkan form lupa username
public function showForm()
{
return view('admin.pages.auth.forgot-username');
}
// Proses kirim username ke email
public function sendUsername(Request $request)
{
$request->validate([
'email' => 'required|email|exists:users,email',
]);
$user = User::where('email', $request->email)->first();
if (!$user) {
return back()->withErrors(['email' => 'Email tidak ditemukan.']);
}
Mail::send('emails.username-reminder', ['user' => $user], function ($message) use ($user) {
$message->to($user->email);
$message->subject('Pengingat Username Anda');
});
return back()->with('status', 'Username telah dikirim ke email Anda.');
}
}

View File

@ -17,10 +17,12 @@ class HomeController extends Controller
{
public function index()
{
// Mengambil semua subkriteria beserta relasi kriterianya, lalu mengelompokkan berdasarkan nama kriteria
$subKriteria = SubKriteria::with('kriteria')->get()->groupBy(function ($item) {
return $item->kriteria->nama_kriteria;
});
// Mengambil semua data pakaian beserta relasi ke subkriteria
$pakaians = Pakaian::with('subKriterias')->get();
return view('landingpage.master', [
@ -30,31 +32,33 @@ class HomeController extends Controller
}
public function prosesRekomendasi(Request $request)
{
// Input validation and processing
// Memproses input pengguna dari form
$userInput = $this->processUserInput($request->input('sub_kriteria', []));
// Jika tidak ada input yang valid, tampilkan pesan error
if (empty($userInput)) {
return $this->returnNoResults('Silakan pilih minimal satu kriteria.');
}
// \Log::debug('User Input:', $userInput);
// Get selected subcriteria data
// Mengambil data subkriteria berdasarkan input user
$selectedSubkriteria = SubKriteria::whereIn('id', collect($userInput)->flatten())
->get()
->keyBy('id');
// Extract criteria inputs
// Menyusun input berdasarkan jenis kriteria
$criteriaInputs = $this->extractCriteriaInputs($userInput, $selectedSubkriteria);
// Apply strict filtering
// Filter ketat berdasarkan input wajib
$filteredClothing = $this->applyStrictFiltering($criteriaInputs);
// Jika tidak ada hasil setelah filter, tampilkan pesan
if ($filteredClothing->isEmpty()) {
return $this->returnNoResults('Tidak ada pakaian yang sesuai dengan kriteria yang Anda pilih.');
}
// Calculate recommendations using weighted scoring
// Hitung rekomendasi menggunakan metode SAW
$recommendations = $this->calculateRecommendations($filteredClothing, $userInput);
return view('landingpage.hasil', compact('recommendations'));
@ -70,11 +74,12 @@ class HomeController extends Controller
foreach ($userInput as $kriteria_id => $subkriteria) {
$subkriteriaArray = (array) $subkriteria;
// Remove empty values and validate
// Buang nilai kosong dan pastikan hanya angka yang valid
$cleanedSubkriteria = array_filter($subkriteriaArray, function($value) {
return !empty($value) && is_numeric($value);
});
// Simpan hanya jika ada nilai yang valid
if (!empty($cleanedSubkriteria)) {
$processedInput[$kriteria_id] = array_map('intval', $cleanedSubkriteria);
}
@ -96,7 +101,7 @@ class HomeController extends Controller
'lokasi' => $userInput[5] ?? [] // Criteria 5: Location
];
// Calculate price range if price criteria is selected
// Jika kriteria harga dipilih, hitung rentang harga min dan max
if (!empty($inputs['harga'])) {
$hargaRanges = $selectedSubkriteria->only($inputs['harga']);
$inputs['harga_min'] = $hargaRanges->min('min_value');
@ -115,7 +120,7 @@ class HomeController extends Controller
{
$query = Pakaian::with(['penilaian.subkriteria.kriteria']);
// Mandatory filter: Clothing Type (highest weight criteria)
// Filter berdasarkan jenis pakaian (wajib)
if (!empty($criteriaInputs['jenis_pakaian'])) {
// \Log::debug('Applying clothing type filter:', $criteriaInputs['jenis_pakaian']);
@ -125,7 +130,7 @@ class HomeController extends Controller
});
}
// Mandatory filter: Price Range (if specified)
// Filter berdasarkan harga jika ada
if (isset($criteriaInputs['harga_min']) && isset($criteriaInputs['harga_max'])) {
// \Log::debug("Applying price filter: {$criteriaInputs['harga_min']} - {$criteriaInputs['harga_max']}");
@ -138,7 +143,7 @@ class HomeController extends Controller
$filteredClothing = $query->get();
// Validate filtering results
// Validasi hasil filter agar benar-benar sesuai
$this->validateFilterResults($filteredClothing, $criteriaInputs);
// \Log::debug('Filtered clothing count: ' . $filteredClothing->count());
@ -170,19 +175,19 @@ class HomeController extends Controller
*/
private function calculateRecommendations($clothing, array $userInput): \Illuminate\Support\Collection
{
// Get criteria with weights
// Ambil data kriteria dan bobotnya
$criteria = Kriteria::with('subkriteria')
->whereIn('id', [1, 2, 3, 4, 5])
->orderByDesc('bobot') // Order by weight (highest first)
->get();
// Build decision matrix
// Susun decision matrix
$decisionMatrix = $this->buildDecisionMatrix($clothing, $criteria, $userInput);
// Calculate min/max values for normalization
// Hitung nilai normalisasi (min & max)
$normalizationValues = $this->calculateNormalizationValues($decisionMatrix, $criteria);
// Calculate preference scores
// Hitung skor preferensi
$results = $this->calculatePreferenceScores(
$clothing,
$decisionMatrix,
@ -191,7 +196,7 @@ class HomeController extends Controller
$userInput
);
// Sort by score (descending)
// Urutkan hasil berdasarkan skor
$recommendations = collect($results)
->sortByDesc('score')
->values();
@ -218,13 +223,13 @@ class HomeController extends Controller
continue; // Skip criteria not selected by user
}
// Get matching assessments for this criterion
// Ambil penilaian yang sesuai
$matchingAssessments = $item->penilaian->filter(function ($assessment) use ($userSubIds) {
return in_array($assessment->sub_kriteria_id, $userSubIds);
});
if ($matchingAssessments->isNotEmpty()) {
// Use average if multiple matches, first match for clothing type
// Gunakan nilai rata-rata (kecuali jenis pakaian, cukup satu)
$value = ($criterion->id == 3)
? $matchingAssessments->first()->nilai
: $matchingAssessments->avg('nilai');
@ -290,7 +295,7 @@ class HomeController extends Controller
if ($value <= 0) continue;
// Normalize value based on criterion type
// Normalisasi nilai sesuai jenis kriteria (benefit atau cost)
$normalizedValue = $this->normalizeValue(
$value,
$normValues['max'][$criterion->id],
@ -298,7 +303,7 @@ class HomeController extends Controller
$criterion->jenis
);
// Calculate weighted contribution
// Hitung skor kontribusi berdasarkan bobot
$weight = $criterion->bobot;
$contribution = $normalizedValue * $weight;
$preferenceScore += $contribution;
@ -307,6 +312,7 @@ class HomeController extends Controller
// \Log::debug("Item {$item->id} - Criterion {$criterion->id}: Value={$value}, Normalized={$normalizedValue}, Weight={$weight}, Contribution={$contribution}");
}
// Ambil nama jenis pakaian untuk ditampilkan
$clothingType = $item->penilaian
->firstWhere('subkriteria.kriteria_id', 3)
?->subkriteria->nama ?? 'Lainnya';

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Lupa Username</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="flex justify-center items-center min-h-screen bg-white text-gray-800 font-sans">
<div class="bg-green-100 border border-green-200 p-6 rounded-xl shadow-md w-full max-w-md">
<h2 class="text-2xl font-bold mb-4">Lupa Username?</h2>
<p class="text-gray-600 mb-6">
Masukkan email yang terdaftar untuk mengetahui username Anda.
</p>
@if (session('status'))
<div class="bg-blue-100 text-blue-800 border border-blue-300 p-3 rounded mb-4">
{{ session('status') }}
</div>
@endif
<form method="POST" action="{{ route('forgot.username.send') }}">
@csrf
<div class="mb-4">
<label for="email" class="block text-sm mb-1">Email</label>
<input type="email" id="email" name="email" required class="w-full px-3 py-2 border rounded bg-white text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-400" />
@error('email')
<span class="text-red-600 text-sm">{{ $message }}</span>
@enderror
</div>
<button type="submit" class="w-full bg-green-700 hover:bg-green-800 text-white px-4 py-2 rounded font-semibold">
Kirim Username
</button>
</form>
<p class="text-center text-sm text-gray-700 mt-4">
<a href="{{ route('login') }}" class="text-black hover:underline font-medium">
Kembali ke halaman login
</a>
</p>
</div>
</body>
</html>

View File

@ -30,6 +30,9 @@
<a href="{{ route('forgot.password.form') }}" class="text-green-700 hover:underline font-medium">
Lupa Password?
</a>
<a href="{{ route('forgot.username.form') }}" class="text-green-700 hover:underline font-medium">
Lupa Username?
</a>
</div>
<button type="submit" class="w-full bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded font-semibold">
Login

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Pengingat Username</title>
</head>
<body style="font-family: Arial, sans-serif; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<h2 style="color: #2f855a;">Halo {{ $user->name ?? 'Pengguna' }},</h2>
<p>Berikut adalah username yang terdaftar dengan email ini:</p>
<p><strong>Username Anda:</strong> {{ $user->username }}</p>
<br>
<p>Jika Anda tidak meminta informasi ini, abaikan email ini.</p>
<p>Terima kasih.</p>
<hr>
<p style="font-size: 12px; color: #888;">Email ini dikirim secara otomatis oleh sistem kami.</p>
</div>
</body>
</html>

View File

@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
use App\Http\Controllers\PakaianController;
use App\Http\Controllers\RiwayatController;
use App\Http\Controllers\KriteriaController;
use App\Http\Controllers\PenilaianController;
use App\Http\Controllers\Admin\AuthController;
@ -12,7 +13,8 @@ use App\Http\Controllers\Admin\DashboardController;
use App\Http\Controllers\SubKriteriaShowController;
use App\Http\Controllers\Landingpage\HomeController;
use App\Http\Controllers\Admin\ResetPasswordController;
use App\Http\Controllers\RiwayatController;
use App\Http\Controllers\Admin\ForgotPasswordController;
use App\Http\Controllers\Admin\ForgotUsernameController;
/*
|--------------------------------------------------------------------------
@ -34,17 +36,21 @@ Route::post('/proses-rekomendasi', [HomeController::class, 'prosesRekomendasi'])
Route::get('/login', [AuthController::class, 'showLogin'])->name('login');
Route::post('/login', [AuthController::class, 'login']);
// Menampilkan form lupa password
Route::get('/forgot-password', [\App\Http\Controllers\Admin\ForgotPasswordController::class, 'showForm'])->name('forgot.password.form');
// === Forgot Username (tanpa prefix 'admin') ===
Route::get('/forgot-username', [ForgotUsernameController::class, 'showForm'])
->name('forgot.username.form');
Route::post('/forgot-username', [ForgotUsernameController::class, 'sendUsername'])
->name('forgot.username.send');
// Mengirim email reset link
Route::post('/forgot-password', [\App\Http\Controllers\Admin\ForgotPasswordController::class, 'sendResetLink'])->name('forgot.password.send');
// Menampilkan form reset password (dari email)
Route::get('/reset-password/{token}', [ResetPasswordController::class, 'showResetForm'])->name('password.reset');
// Menyimpan password baru
Route::post('/reset-password', [ResetPasswordController::class, 'reset'])->name('password.update');
// === Forgot Password (tanpa prefix 'admin') ===
Route::get('/forgot-password', [ForgotPasswordController::class, 'showForm'])
->name('forgot.password.form');
Route::post('/forgot-password', [ForgotPasswordController::class, 'sendResetLink'])
->name('forgot.password.send');
Route::get('/reset-password/{token}', [ResetPasswordController::class, 'showResetForm'])
->name('password.reset');
Route::post('/reset-password', [ResetPasswordController::class, 'reset'])
->name('password.update');
// Logout
Route::post('/logout', function () {
@ -62,7 +68,6 @@ Route::prefix('admin')->middleware(['auth'])->as('admin.')->group(function () {
Route::resource('penilaian', PenilaianController::class);
Route::resource('user', UserController::class);
Route::get('riwayat', [RiwayatController::class, 'index'])->name('riwayat.index');
});
// // Kriteria