update
This commit is contained in:
parent
45115b3367
commit
1365a65409
|
@ -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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,10 +17,12 @@ class HomeController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
// Mengambil semua subkriteria beserta relasi kriterianya, lalu mengelompokkan berdasarkan nama kriteria
|
||||||
$subKriteria = SubKriteria::with('kriteria')->get()->groupBy(function ($item) {
|
$subKriteria = SubKriteria::with('kriteria')->get()->groupBy(function ($item) {
|
||||||
return $item->kriteria->nama_kriteria;
|
return $item->kriteria->nama_kriteria;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Mengambil semua data pakaian beserta relasi ke subkriteria
|
||||||
$pakaians = Pakaian::with('subKriterias')->get();
|
$pakaians = Pakaian::with('subKriterias')->get();
|
||||||
|
|
||||||
return view('landingpage.master', [
|
return view('landingpage.master', [
|
||||||
|
@ -30,31 +32,33 @@ class HomeController extends Controller
|
||||||
}
|
}
|
||||||
public function prosesRekomendasi(Request $request)
|
public function prosesRekomendasi(Request $request)
|
||||||
{
|
{
|
||||||
// Input validation and processing
|
// Memproses input pengguna dari form
|
||||||
$userInput = $this->processUserInput($request->input('sub_kriteria', []));
|
$userInput = $this->processUserInput($request->input('sub_kriteria', []));
|
||||||
|
|
||||||
|
// Jika tidak ada input yang valid, tampilkan pesan error
|
||||||
if (empty($userInput)) {
|
if (empty($userInput)) {
|
||||||
return $this->returnNoResults('Silakan pilih minimal satu kriteria.');
|
return $this->returnNoResults('Silakan pilih minimal satu kriteria.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// \Log::debug('User Input:', $userInput);
|
// \Log::debug('User Input:', $userInput);
|
||||||
|
|
||||||
// Get selected subcriteria data
|
// Mengambil data subkriteria berdasarkan input user
|
||||||
$selectedSubkriteria = SubKriteria::whereIn('id', collect($userInput)->flatten())
|
$selectedSubkriteria = SubKriteria::whereIn('id', collect($userInput)->flatten())
|
||||||
->get()
|
->get()
|
||||||
->keyBy('id');
|
->keyBy('id');
|
||||||
|
|
||||||
// Extract criteria inputs
|
// Menyusun input berdasarkan jenis kriteria
|
||||||
$criteriaInputs = $this->extractCriteriaInputs($userInput, $selectedSubkriteria);
|
$criteriaInputs = $this->extractCriteriaInputs($userInput, $selectedSubkriteria);
|
||||||
|
|
||||||
// Apply strict filtering
|
// Filter ketat berdasarkan input wajib
|
||||||
$filteredClothing = $this->applyStrictFiltering($criteriaInputs);
|
$filteredClothing = $this->applyStrictFiltering($criteriaInputs);
|
||||||
|
|
||||||
|
// Jika tidak ada hasil setelah filter, tampilkan pesan
|
||||||
if ($filteredClothing->isEmpty()) {
|
if ($filteredClothing->isEmpty()) {
|
||||||
return $this->returnNoResults('Tidak ada pakaian yang sesuai dengan kriteria yang Anda pilih.');
|
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);
|
$recommendations = $this->calculateRecommendations($filteredClothing, $userInput);
|
||||||
|
|
||||||
return view('landingpage.hasil', compact('recommendations'));
|
return view('landingpage.hasil', compact('recommendations'));
|
||||||
|
@ -70,11 +74,12 @@ class HomeController extends Controller
|
||||||
foreach ($userInput as $kriteria_id => $subkriteria) {
|
foreach ($userInput as $kriteria_id => $subkriteria) {
|
||||||
$subkriteriaArray = (array) $subkriteria;
|
$subkriteriaArray = (array) $subkriteria;
|
||||||
|
|
||||||
// Remove empty values and validate
|
// Buang nilai kosong dan pastikan hanya angka yang valid
|
||||||
$cleanedSubkriteria = array_filter($subkriteriaArray, function($value) {
|
$cleanedSubkriteria = array_filter($subkriteriaArray, function($value) {
|
||||||
return !empty($value) && is_numeric($value);
|
return !empty($value) && is_numeric($value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Simpan hanya jika ada nilai yang valid
|
||||||
if (!empty($cleanedSubkriteria)) {
|
if (!empty($cleanedSubkriteria)) {
|
||||||
$processedInput[$kriteria_id] = array_map('intval', $cleanedSubkriteria);
|
$processedInput[$kriteria_id] = array_map('intval', $cleanedSubkriteria);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +101,7 @@ class HomeController extends Controller
|
||||||
'lokasi' => $userInput[5] ?? [] // Criteria 5: Location
|
'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'])) {
|
if (!empty($inputs['harga'])) {
|
||||||
$hargaRanges = $selectedSubkriteria->only($inputs['harga']);
|
$hargaRanges = $selectedSubkriteria->only($inputs['harga']);
|
||||||
$inputs['harga_min'] = $hargaRanges->min('min_value');
|
$inputs['harga_min'] = $hargaRanges->min('min_value');
|
||||||
|
@ -115,7 +120,7 @@ class HomeController extends Controller
|
||||||
{
|
{
|
||||||
$query = Pakaian::with(['penilaian.subkriteria.kriteria']);
|
$query = Pakaian::with(['penilaian.subkriteria.kriteria']);
|
||||||
|
|
||||||
// Mandatory filter: Clothing Type (highest weight criteria)
|
// Filter berdasarkan jenis pakaian (wajib)
|
||||||
if (!empty($criteriaInputs['jenis_pakaian'])) {
|
if (!empty($criteriaInputs['jenis_pakaian'])) {
|
||||||
// \Log::debug('Applying clothing type filter:', $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'])) {
|
if (isset($criteriaInputs['harga_min']) && isset($criteriaInputs['harga_max'])) {
|
||||||
// \Log::debug("Applying price filter: {$criteriaInputs['harga_min']} - {$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();
|
$filteredClothing = $query->get();
|
||||||
|
|
||||||
// Validate filtering results
|
// Validasi hasil filter agar benar-benar sesuai
|
||||||
$this->validateFilterResults($filteredClothing, $criteriaInputs);
|
$this->validateFilterResults($filteredClothing, $criteriaInputs);
|
||||||
|
|
||||||
// \Log::debug('Filtered clothing count: ' . $filteredClothing->count());
|
// \Log::debug('Filtered clothing count: ' . $filteredClothing->count());
|
||||||
|
@ -170,19 +175,19 @@ class HomeController extends Controller
|
||||||
*/
|
*/
|
||||||
private function calculateRecommendations($clothing, array $userInput): \Illuminate\Support\Collection
|
private function calculateRecommendations($clothing, array $userInput): \Illuminate\Support\Collection
|
||||||
{
|
{
|
||||||
// Get criteria with weights
|
// Ambil data kriteria dan bobotnya
|
||||||
$criteria = Kriteria::with('subkriteria')
|
$criteria = Kriteria::with('subkriteria')
|
||||||
->whereIn('id', [1, 2, 3, 4, 5])
|
->whereIn('id', [1, 2, 3, 4, 5])
|
||||||
->orderByDesc('bobot') // Order by weight (highest first)
|
->orderByDesc('bobot') // Order by weight (highest first)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
// Build decision matrix
|
// Susun decision matrix
|
||||||
$decisionMatrix = $this->buildDecisionMatrix($clothing, $criteria, $userInput);
|
$decisionMatrix = $this->buildDecisionMatrix($clothing, $criteria, $userInput);
|
||||||
|
|
||||||
// Calculate min/max values for normalization
|
// Hitung nilai normalisasi (min & max)
|
||||||
$normalizationValues = $this->calculateNormalizationValues($decisionMatrix, $criteria);
|
$normalizationValues = $this->calculateNormalizationValues($decisionMatrix, $criteria);
|
||||||
|
|
||||||
// Calculate preference scores
|
// Hitung skor preferensi
|
||||||
$results = $this->calculatePreferenceScores(
|
$results = $this->calculatePreferenceScores(
|
||||||
$clothing,
|
$clothing,
|
||||||
$decisionMatrix,
|
$decisionMatrix,
|
||||||
|
@ -191,7 +196,7 @@ class HomeController extends Controller
|
||||||
$userInput
|
$userInput
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sort by score (descending)
|
// Urutkan hasil berdasarkan skor
|
||||||
$recommendations = collect($results)
|
$recommendations = collect($results)
|
||||||
->sortByDesc('score')
|
->sortByDesc('score')
|
||||||
->values();
|
->values();
|
||||||
|
@ -218,13 +223,13 @@ class HomeController extends Controller
|
||||||
continue; // Skip criteria not selected by user
|
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) {
|
$matchingAssessments = $item->penilaian->filter(function ($assessment) use ($userSubIds) {
|
||||||
return in_array($assessment->sub_kriteria_id, $userSubIds);
|
return in_array($assessment->sub_kriteria_id, $userSubIds);
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($matchingAssessments->isNotEmpty()) {
|
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)
|
$value = ($criterion->id == 3)
|
||||||
? $matchingAssessments->first()->nilai
|
? $matchingAssessments->first()->nilai
|
||||||
: $matchingAssessments->avg('nilai');
|
: $matchingAssessments->avg('nilai');
|
||||||
|
@ -290,7 +295,7 @@ class HomeController extends Controller
|
||||||
|
|
||||||
if ($value <= 0) continue;
|
if ($value <= 0) continue;
|
||||||
|
|
||||||
// Normalize value based on criterion type
|
// Normalisasi nilai sesuai jenis kriteria (benefit atau cost)
|
||||||
$normalizedValue = $this->normalizeValue(
|
$normalizedValue = $this->normalizeValue(
|
||||||
$value,
|
$value,
|
||||||
$normValues['max'][$criterion->id],
|
$normValues['max'][$criterion->id],
|
||||||
|
@ -298,7 +303,7 @@ class HomeController extends Controller
|
||||||
$criterion->jenis
|
$criterion->jenis
|
||||||
);
|
);
|
||||||
|
|
||||||
// Calculate weighted contribution
|
// Hitung skor kontribusi berdasarkan bobot
|
||||||
$weight = $criterion->bobot;
|
$weight = $criterion->bobot;
|
||||||
$contribution = $normalizedValue * $weight;
|
$contribution = $normalizedValue * $weight;
|
||||||
$preferenceScore += $contribution;
|
$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}");
|
// \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
|
$clothingType = $item->penilaian
|
||||||
->firstWhere('subkriteria.kriteria_id', 3)
|
->firstWhere('subkriteria.kriteria_id', 3)
|
||||||
?->subkriteria->nama ?? 'Lainnya';
|
?->subkriteria->nama ?? 'Lainnya';
|
||||||
|
|
|
@ -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>
|
|
@ -30,6 +30,9 @@
|
||||||
<a href="{{ route('forgot.password.form') }}" class="text-green-700 hover:underline font-medium">
|
<a href="{{ route('forgot.password.form') }}" class="text-green-700 hover:underline font-medium">
|
||||||
Lupa Password?
|
Lupa Password?
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ route('forgot.username.form') }}" class="text-green-700 hover:underline font-medium">
|
||||||
|
Lupa Username?
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="w-full bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded font-semibold">
|
<button type="submit" class="w-full bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded font-semibold">
|
||||||
Login
|
Login
|
||||||
|
|
|
@ -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>
|
|
@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use App\Http\Controllers\UserController;
|
use App\Http\Controllers\UserController;
|
||||||
use App\Http\Controllers\PakaianController;
|
use App\Http\Controllers\PakaianController;
|
||||||
|
use App\Http\Controllers\RiwayatController;
|
||||||
use App\Http\Controllers\KriteriaController;
|
use App\Http\Controllers\KriteriaController;
|
||||||
use App\Http\Controllers\PenilaianController;
|
use App\Http\Controllers\PenilaianController;
|
||||||
use App\Http\Controllers\Admin\AuthController;
|
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\SubKriteriaShowController;
|
||||||
use App\Http\Controllers\Landingpage\HomeController;
|
use App\Http\Controllers\Landingpage\HomeController;
|
||||||
use App\Http\Controllers\Admin\ResetPasswordController;
|
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::get('/login', [AuthController::class, 'showLogin'])->name('login');
|
||||||
Route::post('/login', [AuthController::class, 'login']);
|
Route::post('/login', [AuthController::class, 'login']);
|
||||||
|
|
||||||
// Menampilkan form lupa password
|
// === Forgot Username (tanpa prefix 'admin') ===
|
||||||
Route::get('/forgot-password', [\App\Http\Controllers\Admin\ForgotPasswordController::class, 'showForm'])->name('forgot.password.form');
|
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
|
// === Forgot Password (tanpa prefix 'admin') ===
|
||||||
Route::post('/forgot-password', [\App\Http\Controllers\Admin\ForgotPasswordController::class, 'sendResetLink'])->name('forgot.password.send');
|
Route::get('/forgot-password', [ForgotPasswordController::class, 'showForm'])
|
||||||
|
->name('forgot.password.form');
|
||||||
// Menampilkan form reset password (dari email)
|
Route::post('/forgot-password', [ForgotPasswordController::class, 'sendResetLink'])
|
||||||
Route::get('/reset-password/{token}', [ResetPasswordController::class, 'showResetForm'])->name('password.reset');
|
->name('forgot.password.send');
|
||||||
|
Route::get('/reset-password/{token}', [ResetPasswordController::class, 'showResetForm'])
|
||||||
// Menyimpan password baru
|
->name('password.reset');
|
||||||
Route::post('/reset-password', [ResetPasswordController::class, 'reset'])->name('password.update');
|
Route::post('/reset-password', [ResetPasswordController::class, 'reset'])
|
||||||
|
->name('password.update');
|
||||||
|
|
||||||
// Logout
|
// Logout
|
||||||
Route::post('/logout', function () {
|
Route::post('/logout', function () {
|
||||||
|
@ -62,7 +68,6 @@ Route::prefix('admin')->middleware(['auth'])->as('admin.')->group(function () {
|
||||||
|
|
||||||
Route::resource('penilaian', PenilaianController::class);
|
Route::resource('penilaian', PenilaianController::class);
|
||||||
Route::resource('user', UserController::class);
|
Route::resource('user', UserController::class);
|
||||||
Route::get('riwayat', [RiwayatController::class, 'index'])->name('riwayat.index');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// // Kriteria
|
// // Kriteria
|
||||||
|
|
Loading…
Reference in New Issue