Update
|
@ -1,2 +1,11 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
* text=auto eol=lf
|
||||
|
||||
*.blade.php diff=html
|
||||
*.css diff=css
|
||||
*.html diff=html
|
||||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
|
|
61
README.md
|
@ -1,2 +1,61 @@
|
|||
# RECAJE
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
||||
- **[Redberry](https://redberry.international/laravel-development/)**
|
||||
- **[Active Logic](https://activelogic.com)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
|
|
@ -1,89 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$categories = Category::with('subcategories')->get();
|
||||
return view('admin.categories.index', compact('categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('admin.categories.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$category = Category::create([
|
||||
'name' => $request->name,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.subcategories.create', ['category_id' => $category->id])
|
||||
->with('success', 'Kategori berhasil ditambahkan! Silakan tambahkan sub-kategori.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Category $category)
|
||||
{
|
||||
return view('admin.categories.show', compact('category'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Category $category)
|
||||
{
|
||||
return view('admin.categories.edit', compact('category'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Category $category)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$category->update([
|
||||
'name' => $request->name,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.categories.index')
|
||||
->with('success', 'Kategori berhasil diperbarui!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Category $category)
|
||||
{
|
||||
$category->delete();
|
||||
|
||||
return redirect()->route('admin.categories.index')
|
||||
->with('success', 'Kategori berhasil dihapus!');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$categories = Category::with('subcategories')->get();
|
||||
return view('admin.categories.index', compact('categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('admin.categories.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$category = Category::create([
|
||||
'name' => $request->name,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.subcategories.create', ['category_id' => $category->id])
|
||||
->with('success', 'Kategori berhasil ditambahkan! Silakan tambahkan sub-kategori.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Category $category)
|
||||
{
|
||||
return view('admin.categories.show', compact('category'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Category $category)
|
||||
{
|
||||
return view('admin.categories.edit', compact('category'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Category $category)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$category->update([
|
||||
'name' => $request->name,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.categories.index')
|
||||
->with('success', 'Kategori berhasil diperbarui!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Category $category)
|
||||
{
|
||||
$category->delete();
|
||||
|
||||
return redirect()->route('admin.categories.index')
|
||||
->with('success', 'Kategori berhasil dihapus!');
|
||||
}
|
||||
}
|
|
@ -1,123 +1,123 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SubcategoryController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$subcategories = Subcategory::paginate(10);
|
||||
return view('admin.subcategories.index', compact('subcategories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$categories = Category::all();
|
||||
$selectedCategoryId = request('category_id');
|
||||
return view('admin.subcategories.create', compact('categories', 'selectedCategoryId'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
\Illuminate\Support\Facades\Log::info('Request data:', $request->all());
|
||||
|
||||
$request->validate([
|
||||
'category_id' => 'required|exists:categories,id',
|
||||
'subcategories' => 'required|array|min:1',
|
||||
'subcategories.*.name' => 'required|string|max:255',
|
||||
'subcategories.*.value' => 'required|integer|min:1|max:5'
|
||||
]);
|
||||
|
||||
\Illuminate\Support\Facades\Log::info('Validation passed, processing subcategories');
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
foreach ($request->subcategories as $subcategory) {
|
||||
\Illuminate\Support\Facades\Log::info('Creating subcategory:', $subcategory);
|
||||
|
||||
Subcategory::create([
|
||||
'category_id' => $request->category_id,
|
||||
'name' => $subcategory['name'],
|
||||
'value' => $subcategory['value']
|
||||
]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
\Illuminate\Support\Facades\Log::info('All subcategories created successfully');
|
||||
|
||||
return redirect()->route('admin.subcategories.index')
|
||||
->with('success', 'Sub-kategori berhasil ditambahkan!');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
\Illuminate\Support\Facades\Log::error('Error creating subcategories: ' . $e->getMessage());
|
||||
|
||||
return back()->with('error', 'Terjadi kesalahan saat menambahkan sub-kategori: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Subcategory $subcategory)
|
||||
{
|
||||
return view('admin.subcategories.show', compact('subcategory'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Subcategory $subcategory)
|
||||
{
|
||||
$categories = Category::all();
|
||||
return view('admin.subcategories.edit', compact('subcategory', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Subcategory $subcategory)
|
||||
{
|
||||
$request->validate([
|
||||
'category_id' => 'required|exists:categories,id',
|
||||
'name' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$subcategory->update([
|
||||
'category_id' => $request->category_id,
|
||||
'name' => $request->name,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.subcategories.index')
|
||||
->with('success', 'Sub-kategori berhasil diperbarui!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Subcategory $subcategory)
|
||||
{
|
||||
$subcategory->delete();
|
||||
|
||||
return redirect()->route('admin.subcategories.index')
|
||||
->with('success', 'Sub-kategori berhasil dihapus!');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SubcategoryController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$subcategories = Subcategory::paginate(10);
|
||||
return view('admin.subcategories.index', compact('subcategories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$categories = Category::all();
|
||||
$selectedCategoryId = request('category_id');
|
||||
return view('admin.subcategories.create', compact('categories', 'selectedCategoryId'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
\Illuminate\Support\Facades\Log::info('Request data:', $request->all());
|
||||
|
||||
$request->validate([
|
||||
'category_id' => 'required|exists:categories,id',
|
||||
'subcategories' => 'required|array|min:1',
|
||||
'subcategories.*.name' => 'required|string|max:255',
|
||||
'subcategories.*.value' => 'required|integer|min:1|max:5'
|
||||
]);
|
||||
|
||||
\Illuminate\Support\Facades\Log::info('Validation passed, processing subcategories');
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
foreach ($request->subcategories as $subcategory) {
|
||||
\Illuminate\Support\Facades\Log::info('Creating subcategory:', $subcategory);
|
||||
|
||||
Subcategory::create([
|
||||
'category_id' => $request->category_id,
|
||||
'name' => $subcategory['name'],
|
||||
'value' => $subcategory['value']
|
||||
]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
\Illuminate\Support\Facades\Log::info('All subcategories created successfully');
|
||||
|
||||
return redirect()->route('admin.subcategories.index')
|
||||
->with('success', 'Sub-kategori berhasil ditambahkan!');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
\Illuminate\Support\Facades\Log::error('Error creating subcategories: ' . $e->getMessage());
|
||||
|
||||
return back()->with('error', 'Terjadi kesalahan saat menambahkan sub-kategori: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Subcategory $subcategory)
|
||||
{
|
||||
return view('admin.subcategories.show', compact('subcategory'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Subcategory $subcategory)
|
||||
{
|
||||
$categories = Category::all();
|
||||
return view('admin.subcategories.edit', compact('subcategory', 'categories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Subcategory $subcategory)
|
||||
{
|
||||
$request->validate([
|
||||
'category_id' => 'required|exists:categories,id',
|
||||
'name' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$subcategory->update([
|
||||
'category_id' => $request->category_id,
|
||||
'name' => $request->name,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.subcategories.index')
|
||||
->with('success', 'Sub-kategori berhasil diperbarui!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Subcategory $subcategory)
|
||||
{
|
||||
$subcategory->delete();
|
||||
|
||||
return redirect()->route('admin.subcategories.index')
|
||||
->with('success', 'Sub-kategori berhasil dihapus!');
|
||||
}
|
||||
}
|
|
@ -1,182 +1,182 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// Middleware auth diterapkan di routes/web.php
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan daftar pengguna.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$users = User::orderBy('id', 'asc')->paginate(10);
|
||||
|
||||
// Format nama setiap user
|
||||
$users->getCollection()->transform(function ($user) {
|
||||
$user->name = ucwords(strtolower($user->name));
|
||||
return $user;
|
||||
});
|
||||
|
||||
return view('admin.users.index', compact('users'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan detail pengguna.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function show(User $user)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$user->name = ucwords(strtolower($user->name));
|
||||
return view('admin.users.show', compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Menghapus pengguna.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
// Tidak bisa menghapus diri sendiri
|
||||
if (Auth::id() === $user->id) {
|
||||
return back()->with('error', 'Anda tidak dapat menghapus akun Anda sendiri.');
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
|
||||
return redirect()->route('admin.users.index')
|
||||
->with('success', 'Pengguna berhasil dihapus!');
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
return view('admin.users.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menyimpan user baru.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
'role' => 'required|in:admin,user',
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => ucwords(strtolower($request->name)),
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'role' => $request->role,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.users.index')
|
||||
->with('success', 'Pengguna berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan form edit user.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function edit(User $user)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$user->name = ucwords(strtolower($user->name));
|
||||
return view('admin.users.edit', compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Memperbarui data user.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Models\User $user
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(Request $request, User $user)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
|
||||
'password' => 'nullable|string|min:8|confirmed',
|
||||
'role' => 'required|in:admin,user',
|
||||
]);
|
||||
|
||||
$data = [
|
||||
'name' => ucwords(strtolower($request->name)),
|
||||
'email' => $request->email,
|
||||
'role' => $request->role,
|
||||
];
|
||||
|
||||
// Update password hanya jika diisi
|
||||
if ($request->filled('password')) {
|
||||
$data['password'] = Hash::make($request->password);
|
||||
}
|
||||
|
||||
$user->update($data);
|
||||
|
||||
return redirect()->route('admin.users.index')
|
||||
->with('success', 'Pengguna berhasil diperbarui!');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// Middleware auth diterapkan di routes/web.php
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan daftar pengguna.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$users = User::orderBy('id', 'asc')->paginate(10);
|
||||
|
||||
// Format nama setiap user
|
||||
$users->getCollection()->transform(function ($user) {
|
||||
$user->name = ucwords(strtolower($user->name));
|
||||
return $user;
|
||||
});
|
||||
|
||||
return view('admin.users.index', compact('users'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan detail pengguna.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function show(User $user)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$user->name = ucwords(strtolower($user->name));
|
||||
return view('admin.users.show', compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Menghapus pengguna.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
// Tidak bisa menghapus diri sendiri
|
||||
if (Auth::id() === $user->id) {
|
||||
return back()->with('error', 'Anda tidak dapat menghapus akun Anda sendiri.');
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
|
||||
return redirect()->route('admin.users.index')
|
||||
->with('success', 'Pengguna berhasil dihapus!');
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
return view('admin.users.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menyimpan user baru.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
'role' => 'required|in:admin,user',
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => ucwords(strtolower($request->name)),
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'role' => $request->role,
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.users.index')
|
||||
->with('success', 'Pengguna berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan form edit user.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function edit(User $user)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$user->name = ucwords(strtolower($user->name));
|
||||
return view('admin.users.edit', compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Memperbarui data user.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Models\User $user
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(Request $request, User $user)
|
||||
{
|
||||
// Cek apakah pengguna adalah admin
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
return redirect('/')->with('error', 'Anda tidak memiliki akses ke halaman tersebut.');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
|
||||
'password' => 'nullable|string|min:8|confirmed',
|
||||
'role' => 'required|in:admin,user',
|
||||
]);
|
||||
|
||||
$data = [
|
||||
'name' => ucwords(strtolower($request->name)),
|
||||
'email' => $request->email,
|
||||
'role' => $request->role,
|
||||
];
|
||||
|
||||
// Update password hanya jika diisi
|
||||
if ($request->filled('password')) {
|
||||
$data['password'] = Hash::make($request->password);
|
||||
}
|
||||
|
||||
$user->update($data);
|
||||
|
||||
return redirect()->route('admin.users.index')
|
||||
->with('success', 'Pengguna berhasil diperbarui!');
|
||||
}
|
||||
}
|
|
@ -1,41 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan formulir reset password
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showLinkRequestForm()
|
||||
{
|
||||
return view('auth.passwords.email');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mengirim link reset password
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'email' => ['required', 'email'],
|
||||
]);
|
||||
|
||||
$status = Password::sendResetLink(
|
||||
$request->only('email')
|
||||
);
|
||||
|
||||
return $status === Password::RESET_LINK_SENT
|
||||
? back()->with('status', 'Link reset password telah dikirim ke email Anda!')
|
||||
: back()->withErrors(['email' => __($status)]);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan formulir reset password
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showLinkRequestForm()
|
||||
{
|
||||
return view('auth.passwords.email');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mengirim link reset password
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'email' => ['required', 'email'],
|
||||
]);
|
||||
|
||||
$status = Password::sendResetLink(
|
||||
$request->only('email')
|
||||
);
|
||||
|
||||
return $status === Password::RESET_LINK_SENT
|
||||
? back()->with('status', 'Link reset password telah dikirim ke email Anda!')
|
||||
: back()->withErrors(['email' => __($status)]);
|
||||
}
|
||||
}
|
|
@ -1,151 +1,151 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan form login
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showLoginForm()
|
||||
{
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Where to redirect users after login.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected function redirectTo()
|
||||
{
|
||||
if (Auth::check() && Auth::user()->role === 'admin') {
|
||||
return route('admin.dashboard');
|
||||
}
|
||||
|
||||
return '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Memproses request login
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
try {
|
||||
$credentials = $request->validate([
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required'],
|
||||
], [
|
||||
'email.required' => 'Email harus diisi',
|
||||
'email.email' => 'Format email tidak valid',
|
||||
'password.required' => 'Password harus diisi',
|
||||
]);
|
||||
|
||||
if (Auth::attempt($credentials, $request->boolean('remember'))) {
|
||||
$request->session()->regenerate();
|
||||
|
||||
if (Auth::user()->role === 'admin') {
|
||||
return redirect()->route('admin.dashboard');
|
||||
}
|
||||
|
||||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
return back()
|
||||
->withInput($request->only('email'))
|
||||
->withErrors([
|
||||
'email' => 'Email atau password yang diberikan tidak cocok dengan data kami.',
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()
|
||||
->withInput($request->only('email'))
|
||||
->withErrors([
|
||||
'error' => 'Terjadi kesalahan saat mencoba login. Silakan coba lagi.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout pengguna
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
Auth::logout();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect ke halaman login Google.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function redirectToGoogle()
|
||||
{
|
||||
return Socialite::driver('google')->redirect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle callback dari Google.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function handleGoogleCallback()
|
||||
{
|
||||
try {
|
||||
$googleUser = Socialite::driver('google')->user();
|
||||
|
||||
// Cari user berdasarkan google id, jika tidak ada cari berdasarkan email
|
||||
$user = User::where('google_id', $googleUser->id)->first();
|
||||
|
||||
if (!$user) {
|
||||
// Cek apakah email sudah terdaftar
|
||||
$user = User::where('email', $googleUser->email)->first();
|
||||
|
||||
if (!$user) {
|
||||
// Jika user belum terdaftar, buat user baru
|
||||
$user = User::create([
|
||||
'name' => $googleUser->name,
|
||||
'email' => $googleUser->email,
|
||||
'google_id' => $googleUser->id,
|
||||
'password' => Hash::make(rand(1,10000)),
|
||||
'email_verified_at' => now(), // Email otomatis terverifikasi
|
||||
]);
|
||||
} else {
|
||||
// Update google_id untuk user yang sudah ada
|
||||
$user->update([
|
||||
'google_id' => $googleUser->id,
|
||||
'email_verified_at' => now(), // Email otomatis terverifikasi
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Login user
|
||||
Auth::login($user);
|
||||
|
||||
return redirect('/');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return redirect('/login')->with('error', 'Terjadi kesalahan saat login dengan Google: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan form login
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showLoginForm()
|
||||
{
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Where to redirect users after login.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected function redirectTo()
|
||||
{
|
||||
if (Auth::check() && Auth::user()->role === 'admin') {
|
||||
return route('admin.dashboard');
|
||||
}
|
||||
|
||||
return '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Memproses request login
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
try {
|
||||
$credentials = $request->validate([
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required'],
|
||||
], [
|
||||
'email.required' => 'Email harus diisi',
|
||||
'email.email' => 'Format email tidak valid',
|
||||
'password.required' => 'Password harus diisi',
|
||||
]);
|
||||
|
||||
if (Auth::attempt($credentials, $request->boolean('remember'))) {
|
||||
$request->session()->regenerate();
|
||||
|
||||
if (Auth::user()->role === 'admin') {
|
||||
return redirect()->route('admin.dashboard');
|
||||
}
|
||||
|
||||
return redirect()->intended('/');
|
||||
}
|
||||
|
||||
return back()
|
||||
->withInput($request->only('email'))
|
||||
->withErrors([
|
||||
'email' => 'Email atau password yang diberikan tidak cocok dengan data kami.',
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return back()
|
||||
->withInput($request->only('email'))
|
||||
->withErrors([
|
||||
'error' => 'Terjadi kesalahan saat mencoba login. Silakan coba lagi.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout pengguna
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
Auth::logout();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect ke halaman login Google.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function redirectToGoogle()
|
||||
{
|
||||
return Socialite::driver('google')->redirect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle callback dari Google.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function handleGoogleCallback()
|
||||
{
|
||||
try {
|
||||
$googleUser = Socialite::driver('google')->user();
|
||||
|
||||
// Cari user berdasarkan google id, jika tidak ada cari berdasarkan email
|
||||
$user = User::where('google_id', $googleUser->id)->first();
|
||||
|
||||
if (!$user) {
|
||||
// Cek apakah email sudah terdaftar
|
||||
$user = User::where('email', $googleUser->email)->first();
|
||||
|
||||
if (!$user) {
|
||||
// Jika user belum terdaftar, buat user baru
|
||||
$user = User::create([
|
||||
'name' => $googleUser->name,
|
||||
'email' => $googleUser->email,
|
||||
'google_id' => $googleUser->id,
|
||||
'password' => Hash::make(rand(1,10000)),
|
||||
'email_verified_at' => now(), // Email otomatis terverifikasi
|
||||
]);
|
||||
} else {
|
||||
// Update google_id untuk user yang sudah ada
|
||||
$user->update([
|
||||
'google_id' => $googleUser->id,
|
||||
'email_verified_at' => now(), // Email otomatis terverifikasi
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Login user
|
||||
Auth::login($user);
|
||||
|
||||
return redirect('/');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return redirect('/login')->with('error', 'Terjadi kesalahan saat login dengan Google: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan form registrasi
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showRegistrationForm()
|
||||
{
|
||||
return view('auth.register');
|
||||
}
|
||||
|
||||
/**
|
||||
* Memproses request registrasi
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function register(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||||
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||
'terms' => ['required'],
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return redirect()->route('verification.notice');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan form registrasi
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showRegistrationForm()
|
||||
{
|
||||
return view('auth.register');
|
||||
}
|
||||
|
||||
/**
|
||||
* Memproses request registrasi
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function register(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||||
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||
'terms' => ['required'],
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return redirect()->route('verification.notice');
|
||||
}
|
||||
}
|
|
@ -1,58 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan formulir reset password dengan token
|
||||
*
|
||||
* @param string $token
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
return view('auth.passwords.reset')->with(
|
||||
['token' => $token, 'email' => $request->email]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Memproses reset password
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function reset(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'token' => ['required'],
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required', 'confirmed', 'min:8'],
|
||||
]);
|
||||
|
||||
$status = Password::reset(
|
||||
$request->only('email', 'password', 'password_confirmation', 'token'),
|
||||
function ($user, $password) {
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($password)
|
||||
])->setRememberToken(Str::random(60));
|
||||
|
||||
$user->save();
|
||||
|
||||
event(new PasswordReset($user));
|
||||
}
|
||||
);
|
||||
|
||||
return $status === Password::PASSWORD_RESET
|
||||
? redirect()->route('login')->with('status', 'Password Anda telah direset!')
|
||||
: back()->withErrors(['email' => [__($status)]]);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan formulir reset password dengan token
|
||||
*
|
||||
* @param string $token
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
return view('auth.passwords.reset')->with(
|
||||
['token' => $token, 'email' => $request->email]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Memproses reset password
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function reset(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'token' => ['required'],
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required', 'confirmed', 'min:8'],
|
||||
]);
|
||||
|
||||
$status = Password::reset(
|
||||
$request->only('email', 'password', 'password_confirmation', 'token'),
|
||||
function ($user, $password) {
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($password)
|
||||
])->setRememberToken(Str::random(60));
|
||||
|
||||
$user->save();
|
||||
|
||||
event(new PasswordReset($user));
|
||||
}
|
||||
);
|
||||
|
||||
return $status === Password::PASSWORD_RESET
|
||||
? redirect()->route('login')->with('status', 'Password Anda telah direset!')
|
||||
: back()->withErrors(['email' => [__($status)]]);
|
||||
}
|
||||
}
|
|
@ -1,91 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Cafe;
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CafeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Tampilkan halaman pencarian cafe dengan stepper
|
||||
*/
|
||||
public function search(Request $request)
|
||||
{
|
||||
// Tentukan langkah saat ini berdasarkan parameter URL
|
||||
$currentStep = 1;
|
||||
if ($request->has('weight')) {
|
||||
$currentStep = 3; // Hasil
|
||||
} elseif ($request->has('criteria')) {
|
||||
$currentStep = 2; // Kriteria
|
||||
}
|
||||
|
||||
return view('cafe.search-fixed', compact('currentStep'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan halaman rekomendasi cafe
|
||||
*/
|
||||
public function recommend()
|
||||
{
|
||||
return view('cafe.recommend');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan semua cafe dengan fitur fuzzy search toleran terhadap typo
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Cafe::with(['ratings.subcategory', 'ratings.category']);
|
||||
|
||||
// Filter berdasarkan pencarian jika ada
|
||||
if ($request->has('search') && !empty($request->search)) {
|
||||
$searchTerm = $request->search;
|
||||
|
||||
// Memecah kata kunci pencarian menjadi beberapa kata
|
||||
$keywords = explode(' ', $searchTerm);
|
||||
|
||||
$query->where(function($q) use ($searchTerm, $keywords) {
|
||||
// Exact match dengan prioritas tertinggi
|
||||
$q->where('nama', 'LIKE', "%{$searchTerm}%");
|
||||
|
||||
// Pencarian fuzzy untuk masing-masing kata kunci
|
||||
foreach ($keywords as $keyword) {
|
||||
if (strlen($keyword) >= 3) {
|
||||
// Membuat variasi kata kunci untuk toleransi typo
|
||||
$q->orWhere('nama', 'LIKE', "%{$keyword}%");
|
||||
|
||||
// Tambahkan wildcard di tengah untuk toleransi kesalahan ketik 1 karakter
|
||||
for ($i = 0; $i < strlen($keyword) - 1; $i++) {
|
||||
$fuzzyWord = substr($keyword, 0, $i) . '_' . substr($keyword, $i + 1);
|
||||
$q->orWhere('nama', 'LIKE', "%{$fuzzyWord}%");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mencoba juga pencarian dengan operator SOUNDS LIKE jika tersedia
|
||||
try {
|
||||
$q->orWhereRaw("SOUNDEX(nama) = SOUNDEX(?)", [$searchTerm]);
|
||||
} catch (\Exception $e) {
|
||||
// SOUNDEX mungkin tidak tersedia, tidak perlu error handling
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$cafes = $query->latest()->paginate(12)->withQueryString();
|
||||
|
||||
return view('cafe.index', compact('cafes'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan detail cafe
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$cafe = Cafe::with(['ratings.subcategory', 'ratings.category'])->findOrFail($id);
|
||||
return view('cafe.show', compact('cafe'));
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Cafe;
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CafeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Tampilkan halaman pencarian cafe dengan stepper
|
||||
*/
|
||||
public function search(Request $request)
|
||||
{
|
||||
// Tentukan langkah saat ini berdasarkan parameter URL
|
||||
$currentStep = 1;
|
||||
if ($request->has('weight')) {
|
||||
$currentStep = 3; // Hasil
|
||||
} elseif ($request->has('criteria')) {
|
||||
$currentStep = 2; // Kriteria
|
||||
}
|
||||
|
||||
return view('cafe.search-fixed', compact('currentStep'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan halaman rekomendasi cafe
|
||||
*/
|
||||
public function recommend()
|
||||
{
|
||||
return view('cafe.recommend');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan semua cafe dengan fitur fuzzy search toleran terhadap typo
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = Cafe::with(['ratings.subcategory', 'ratings.category']);
|
||||
|
||||
// Filter berdasarkan pencarian jika ada
|
||||
if ($request->has('search') && !empty($request->search)) {
|
||||
$searchTerm = $request->search;
|
||||
|
||||
// Memecah kata kunci pencarian menjadi beberapa kata
|
||||
$keywords = explode(' ', $searchTerm);
|
||||
|
||||
$query->where(function($q) use ($searchTerm, $keywords) {
|
||||
// Exact match dengan prioritas tertinggi
|
||||
$q->where('nama', 'LIKE', "%{$searchTerm}%");
|
||||
|
||||
// Pencarian fuzzy untuk masing-masing kata kunci
|
||||
foreach ($keywords as $keyword) {
|
||||
if (strlen($keyword) >= 3) {
|
||||
// Membuat variasi kata kunci untuk toleransi typo
|
||||
$q->orWhere('nama', 'LIKE', "%{$keyword}%");
|
||||
|
||||
// Tambahkan wildcard di tengah untuk toleransi kesalahan ketik 1 karakter
|
||||
for ($i = 0; $i < strlen($keyword) - 1; $i++) {
|
||||
$fuzzyWord = substr($keyword, 0, $i) . '_' . substr($keyword, $i + 1);
|
||||
$q->orWhere('nama', 'LIKE', "%{$fuzzyWord}%");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mencoba juga pencarian dengan operator SOUNDS LIKE jika tersedia
|
||||
try {
|
||||
$q->orWhereRaw("SOUNDEX(nama) = SOUNDEX(?)", [$searchTerm]);
|
||||
} catch (\Exception $e) {
|
||||
// SOUNDEX mungkin tidak tersedia, tidak perlu error handling
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$cafes = $query->latest()->paginate(12)->withQueryString();
|
||||
|
||||
return view('cafe.index', compact('cafes'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tampilkan detail cafe
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$cafe = Cafe::with(['ratings.subcategory', 'ratings.category'])->findOrFail($id);
|
||||
return view('cafe.show', compact('cafe'));
|
||||
}
|
||||
}
|
|
@ -1,71 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\ContactMessage;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ContactController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a newly created contact message in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255',
|
||||
'message' => 'required|string',
|
||||
]);
|
||||
|
||||
ContactMessage::create($validated);
|
||||
|
||||
return redirect()->back()->with('success', 'Pesan Anda telah terkirim! Terima kasih telah menghubungi kami.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of contact messages for admin.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$messages = ContactMessage::orderBy('created_at', 'desc')->paginate(10);
|
||||
|
||||
return view('admin.contact-messages.index', compact('messages'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified contact message.
|
||||
*
|
||||
* @param \App\Models\ContactMessage $contactMessage
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(ContactMessage $contactMessage)
|
||||
{
|
||||
// Mark as read
|
||||
if (!$contactMessage->is_read) {
|
||||
$contactMessage->is_read = true;
|
||||
$contactMessage->save();
|
||||
}
|
||||
|
||||
return view('admin.contact-messages.show', compact('contactMessage'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified contact message from storage.
|
||||
*
|
||||
* @param \App\Models\ContactMessage $contactMessage
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(ContactMessage $contactMessage)
|
||||
{
|
||||
$contactMessage->delete();
|
||||
|
||||
return redirect()->route('admin.contact-messages.index')
|
||||
->with('success', 'Pesan kontak berhasil dihapus.');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\ContactMessage;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ContactController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a newly created contact message in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255',
|
||||
'message' => 'required|string',
|
||||
]);
|
||||
|
||||
ContactMessage::create($validated);
|
||||
|
||||
return redirect()->back()->with('success', 'Pesan Anda telah terkirim! Terima kasih telah menghubungi kami.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of contact messages for admin.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$messages = ContactMessage::orderBy('created_at', 'desc')->paginate(10);
|
||||
|
||||
return view('admin.contact-messages.index', compact('messages'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified contact message.
|
||||
*
|
||||
* @param \App\Models\ContactMessage $contactMessage
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(ContactMessage $contactMessage)
|
||||
{
|
||||
// Mark as read
|
||||
if (!$contactMessage->is_read) {
|
||||
$contactMessage->is_read = true;
|
||||
$contactMessage->save();
|
||||
}
|
||||
|
||||
return view('admin.contact-messages.show', compact('contactMessage'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified contact message from storage.
|
||||
*
|
||||
* @param \App\Models\ContactMessage $contactMessage
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(ContactMessage $contactMessage)
|
||||
{
|
||||
$contactMessage->delete();
|
||||
|
||||
return redirect()->route('admin.contact-messages.index')
|
||||
->with('success', 'Pesan kontak berhasil dihapus.');
|
||||
}
|
||||
}
|
|
@ -1,111 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Cafe;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Exception;
|
||||
|
||||
class FavoriteController extends Controller
|
||||
{
|
||||
// Konstruktor tidak diperlukan karena middleware auth sudah diterapkan di route
|
||||
|
||||
/**
|
||||
* Toggle status favorit cafe
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $cafeId
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function toggleFavorite(Request $request, $cafeId)
|
||||
{
|
||||
try {
|
||||
// Validasi input
|
||||
if (!$cafeId || !is_numeric($cafeId)) {
|
||||
Log::error('Invalid cafe ID provided: ' . $cafeId);
|
||||
return response()->json(['success' => false, 'message' => 'ID cafe tidak valid'], 400);
|
||||
}
|
||||
|
||||
// Cek apakah cafe dengan ID tersebut ada
|
||||
$cafe = Cafe::find($cafeId);
|
||||
if (!$cafe) {
|
||||
Log::error('Cafe not found with ID: ' . $cafeId);
|
||||
return response()->json(['success' => false, 'message' => 'Cafe tidak ditemukan'], 404);
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
if (!$user) {
|
||||
Log::error('User not authenticated for favorite toggle');
|
||||
return response()->json(['success' => false, 'message' => 'Pengguna tidak terautentikasi'], 401);
|
||||
}
|
||||
|
||||
Log::info('Toggling favorite for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
|
||||
|
||||
// Pemeriksaan apakah sudah favorit secara langsung menggunakan query
|
||||
$existingFavorite = DB::table('favorites')
|
||||
->where('user_id', $user->id)
|
||||
->where('cafe_id', $cafeId)
|
||||
->first();
|
||||
|
||||
if ($existingFavorite) {
|
||||
// Hapus favorit
|
||||
DB::table('favorites')
|
||||
->where('user_id', $user->id)
|
||||
->where('cafe_id', $cafeId)
|
||||
->delete();
|
||||
|
||||
$isFavorited = false;
|
||||
Log::info('Favorite removed for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
|
||||
} else {
|
||||
// Tambahkan favorit
|
||||
DB::table('favorites')->insert([
|
||||
'user_id' => $user->id,
|
||||
'cafe_id' => $cafeId,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
]);
|
||||
|
||||
$isFavorited = true;
|
||||
Log::info('Favorite added for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
|
||||
}
|
||||
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'isFavorited' => $isFavorited
|
||||
]);
|
||||
}
|
||||
|
||||
return back()->with('success', $isFavorited ? 'Cafe ditambahkan ke favorit' : 'Cafe dihapus dari favorit');
|
||||
} catch (Exception $e) {
|
||||
Log::error('Error toggling favorite: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
|
||||
return back()->with('error', 'Terjadi kesalahan saat mengubah status favorit.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan daftar cafe favorit user
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
$favorites = $user->favorites()->with('cafe')->get();
|
||||
|
||||
return view('favorites.index', [
|
||||
'favorites' => $favorites
|
||||
]);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Cafe;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Exception;
|
||||
|
||||
class FavoriteController extends Controller
|
||||
{
|
||||
// Konstruktor tidak diperlukan karena middleware auth sudah diterapkan di route
|
||||
|
||||
/**
|
||||
* Toggle status favorit cafe
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $cafeId
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function toggleFavorite(Request $request, $cafeId)
|
||||
{
|
||||
try {
|
||||
// Validasi input
|
||||
if (!$cafeId || !is_numeric($cafeId)) {
|
||||
Log::error('Invalid cafe ID provided: ' . $cafeId);
|
||||
return response()->json(['success' => false, 'message' => 'ID cafe tidak valid'], 400);
|
||||
}
|
||||
|
||||
// Cek apakah cafe dengan ID tersebut ada
|
||||
$cafe = Cafe::find($cafeId);
|
||||
if (!$cafe) {
|
||||
Log::error('Cafe not found with ID: ' . $cafeId);
|
||||
return response()->json(['success' => false, 'message' => 'Cafe tidak ditemukan'], 404);
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
if (!$user) {
|
||||
Log::error('User not authenticated for favorite toggle');
|
||||
return response()->json(['success' => false, 'message' => 'Pengguna tidak terautentikasi'], 401);
|
||||
}
|
||||
|
||||
Log::info('Toggling favorite for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
|
||||
|
||||
// Pemeriksaan apakah sudah favorit secara langsung menggunakan query
|
||||
$existingFavorite = DB::table('favorites')
|
||||
->where('user_id', $user->id)
|
||||
->where('cafe_id', $cafeId)
|
||||
->first();
|
||||
|
||||
if ($existingFavorite) {
|
||||
// Hapus favorit
|
||||
DB::table('favorites')
|
||||
->where('user_id', $user->id)
|
||||
->where('cafe_id', $cafeId)
|
||||
->delete();
|
||||
|
||||
$isFavorited = false;
|
||||
Log::info('Favorite removed for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
|
||||
} else {
|
||||
// Tambahkan favorit
|
||||
DB::table('favorites')->insert([
|
||||
'user_id' => $user->id,
|
||||
'cafe_id' => $cafeId,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
]);
|
||||
|
||||
$isFavorited = true;
|
||||
Log::info('Favorite added for user ID: ' . $user->id . ' and cafe ID: ' . $cafeId);
|
||||
}
|
||||
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'isFavorited' => $isFavorited
|
||||
]);
|
||||
}
|
||||
|
||||
return back()->with('success', $isFavorited ? 'Cafe ditambahkan ke favorit' : 'Cafe dihapus dari favorit');
|
||||
} catch (Exception $e) {
|
||||
Log::error('Error toggling favorite: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
|
||||
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
|
||||
return back()->with('error', 'Terjadi kesalahan saat mengubah status favorit.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan daftar cafe favorit user
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
$favorites = $user->favorites()->with('cafe')->get();
|
||||
|
||||
return view('favorites.index', [
|
||||
'favorites' => $favorites
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,54 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan halaman profil
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
return view('profile.index', compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update informasi profil pengguna
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$validated = $request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,'.$user->id],
|
||||
]);
|
||||
|
||||
$user->update($validated);
|
||||
|
||||
return redirect()->route('profile.index')->with('success', 'Profil berhasil diperbarui!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update password pengguna
|
||||
*/
|
||||
public function updatePassword(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'current_password' => ['required', 'current_password'],
|
||||
'password' => ['required', 'confirmed', Password::defaults()],
|
||||
]);
|
||||
|
||||
$user = Auth::user();
|
||||
$user->password = Hash::make($validated['password']);
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('profile.index')->with('success', 'Password berhasil diperbarui!');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan halaman profil
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
return view('profile.index', compact('user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update informasi profil pengguna
|
||||
*/
|
||||
public function update(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$validated = $request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,'.$user->id],
|
||||
]);
|
||||
|
||||
$user->update($validated);
|
||||
|
||||
return redirect()->route('profile.index')->with('success', 'Profil berhasil diperbarui!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update password pengguna
|
||||
*/
|
||||
public function updatePassword(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'current_password' => ['required', 'current_password'],
|
||||
'password' => ['required', 'confirmed', Password::defaults()],
|
||||
]);
|
||||
|
||||
$user = Auth::user();
|
||||
$user->password = Hash::make($validated['password']);
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('profile.index')->with('success', 'Password berhasil diperbarui!');
|
||||
}
|
||||
}
|
|
@ -1,199 +1,199 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Cafe;
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use App\Models\SearchHistory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class SmartSearchController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the search form.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$currentStep = 1;
|
||||
return view('cafe.search-fixed', compact('currentStep'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the search form and display results.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function search(Request $request)
|
||||
{
|
||||
$currentStep = 3;
|
||||
$weights = $request->input('weight', []);
|
||||
$criterias = $request->input('criteria', []);
|
||||
$lokasi = $request->input('lokasi');
|
||||
|
||||
// Get cafes with ratings
|
||||
$cafes = Cafe::with(['ratings.subcategory', 'ratings.category'])->get();
|
||||
|
||||
// Filter berdasarkan lokasi jika ada
|
||||
if ($lokasi) {
|
||||
$cafes = $cafes->filter(function($cafe) use ($lokasi) {
|
||||
return stripos($cafe->lokasi, $lokasi) !== false;
|
||||
});
|
||||
}
|
||||
|
||||
// Hitung total semua bobot untuk normalisasi
|
||||
$totalWeight = array_sum($weights);
|
||||
if ($totalWeight == 0) {
|
||||
// Jika tidak ada bobot, buat bobot merata
|
||||
$categories = Category::all();
|
||||
foreach ($categories as $category) {
|
||||
$weights[$category->id] = 100 / $categories->count();
|
||||
}
|
||||
$totalWeight = 100;
|
||||
}
|
||||
|
||||
// Hitung skor SMART untuk setiap cafe
|
||||
$cafeScores = [];
|
||||
|
||||
foreach ($cafes as $cafe) {
|
||||
$score = 0;
|
||||
$debugInfo = [];
|
||||
|
||||
$ratings = \Illuminate\Support\Facades\DB::table('cafe_ratings')
|
||||
->join('subcategories', 'cafe_ratings.subcategory_id', '=', 'subcategories.id')
|
||||
->join('categories', 'cafe_ratings.category_id', '=', 'categories.id')
|
||||
->where('cafe_ratings.cafe_id', $cafe->id)
|
||||
->select(
|
||||
'cafe_ratings.category_id',
|
||||
'categories.name as category_name',
|
||||
'cafe_ratings.subcategory_id',
|
||||
'subcategories.name as subcategory_name',
|
||||
'subcategories.value'
|
||||
)
|
||||
->get();
|
||||
|
||||
foreach ($ratings as $rating) {
|
||||
// Jika ada bobot untuk kategori ini
|
||||
if (isset($weights[$rating->category_id])) {
|
||||
$weight = floatval($weights[$rating->category_id]);
|
||||
$normalizedWeight = $totalWeight > 0 ? $weight / $totalWeight : 0;
|
||||
|
||||
// Pastikan nilai valid
|
||||
$value = max(1, intval($rating->value));
|
||||
|
||||
// Bonus nilai jika kriteria dipilih
|
||||
if (isset($criterias[$rating->category_id]) &&
|
||||
$criterias[$rating->category_id] == $rating->subcategory_id) {
|
||||
$value = 5; // Nilai maksimal
|
||||
}
|
||||
|
||||
// Hitung utilitas - menerapkan konsep yang sama dengan view
|
||||
$utilityValue = ($value - 1) / 4; // Normalisasi ke rentang 0-1 (jika nilai antara 1-5)
|
||||
$ratingScore = $normalizedWeight * $utilityValue;
|
||||
$score += $ratingScore;
|
||||
|
||||
// Simpan untuk debug
|
||||
$debugInfo[$rating->category_name] = [
|
||||
'bobot' => $weight,
|
||||
'bobot_normal' => $normalizedWeight,
|
||||
'nilai' => $value,
|
||||
'nilai_asli' => $rating->value,
|
||||
'subcategory' => $rating->subcategory_name,
|
||||
'skor_kriteria' => $ratingScore
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$cafeScores[$cafe->id] = [
|
||||
'score' => $score,
|
||||
'debug' => $debugInfo
|
||||
];
|
||||
}
|
||||
|
||||
// Urutkan cafe berdasarkan skor tertinggi
|
||||
uasort($cafeScores, function($a, $b) {
|
||||
return $b['score'] <=> $a['score'];
|
||||
});
|
||||
|
||||
// Ambil id cafe yang sudah diurutkan
|
||||
$rankedCafeIds = array_keys($cafeScores);
|
||||
|
||||
// Urutkan koleksi cafe sesuai skor
|
||||
$rankedCafes = $cafes->sortBy(function($cafe) use ($rankedCafeIds) {
|
||||
return array_search($cafe->id, $rankedCafeIds);
|
||||
});
|
||||
|
||||
// Ambil hanya 10 cafe teratas
|
||||
$rankedCafes = $rankedCafes->take(10);
|
||||
|
||||
// Persiapkan data hasil untuk disimpan
|
||||
$resultsData = [];
|
||||
foreach ($rankedCafes as $cafe) {
|
||||
if (isset($cafeScores[$cafe->id])) {
|
||||
$resultsData[$cafe->id] = [
|
||||
'name' => $cafe->nama,
|
||||
'score' => $cafeScores[$cafe->id]['score'],
|
||||
'details' => $cafeScores[$cafe->id]['debug']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Simpan hasil pencarian ke database jika user sudah login
|
||||
if (Auth::check()) {
|
||||
SearchHistory::create([
|
||||
'user_id' => Auth::id(),
|
||||
'weights' => $weights,
|
||||
'criteria' => $criterias,
|
||||
'location' => $lokasi,
|
||||
'results' => $resultsData
|
||||
]);
|
||||
}
|
||||
|
||||
return view('cafe.search-fixed', compact('currentStep', 'rankedCafes', 'cafeScores'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display search history for the authenticated user.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function history()
|
||||
{
|
||||
$histories = SearchHistory::where('user_id', Auth::id())
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(10);
|
||||
|
||||
return view('cafe.search-history', compact('histories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display search history detail.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function detail($id)
|
||||
{
|
||||
$history = SearchHistory::where('user_id', Auth::id())
|
||||
->findOrFail($id);
|
||||
|
||||
// Retrieve cafes from the stored results
|
||||
$cafeIds = array_keys($history->results ?? []);
|
||||
$cafes = Cafe::whereIn('id', $cafeIds)->get();
|
||||
|
||||
// Map cafes to include their scores from history
|
||||
$rankedCafes = $cafes->map(function($cafe) use ($history) {
|
||||
$cafe->smart_score = $history->results[$cafe->id]['score'] ?? 0;
|
||||
$cafe->smart_details = $history->results[$cafe->id]['details'] ?? [];
|
||||
return $cafe;
|
||||
})->sortByDesc('smart_score');
|
||||
|
||||
$weights = $history->weights;
|
||||
|
||||
return view('cafe.search-detail', compact('history', 'rankedCafes', 'weights'));
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Cafe;
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use App\Models\SearchHistory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class SmartSearchController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the search form.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$currentStep = 1;
|
||||
return view('cafe.search-fixed', compact('currentStep'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the search form and display results.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function search(Request $request)
|
||||
{
|
||||
$currentStep = 3;
|
||||
$weights = $request->input('weight', []);
|
||||
$criterias = $request->input('criteria', []);
|
||||
$lokasi = $request->input('lokasi');
|
||||
|
||||
// Get cafes with ratings
|
||||
$cafes = Cafe::with(['ratings.subcategory', 'ratings.category'])->get();
|
||||
|
||||
// Filter berdasarkan lokasi jika ada
|
||||
if ($lokasi) {
|
||||
$cafes = $cafes->filter(function($cafe) use ($lokasi) {
|
||||
return stripos($cafe->lokasi, $lokasi) !== false;
|
||||
});
|
||||
}
|
||||
|
||||
// Hitung total semua bobot untuk normalisasi
|
||||
$totalWeight = array_sum($weights);
|
||||
if ($totalWeight == 0) {
|
||||
// Jika tidak ada bobot, buat bobot merata
|
||||
$categories = Category::all();
|
||||
foreach ($categories as $category) {
|
||||
$weights[$category->id] = 100 / $categories->count();
|
||||
}
|
||||
$totalWeight = 100;
|
||||
}
|
||||
|
||||
// Hitung skor SMART untuk setiap cafe
|
||||
$cafeScores = [];
|
||||
|
||||
foreach ($cafes as $cafe) {
|
||||
$score = 0;
|
||||
$debugInfo = [];
|
||||
|
||||
$ratings = \Illuminate\Support\Facades\DB::table('cafe_ratings')
|
||||
->join('subcategories', 'cafe_ratings.subcategory_id', '=', 'subcategories.id')
|
||||
->join('categories', 'cafe_ratings.category_id', '=', 'categories.id')
|
||||
->where('cafe_ratings.cafe_id', $cafe->id)
|
||||
->select(
|
||||
'cafe_ratings.category_id',
|
||||
'categories.name as category_name',
|
||||
'cafe_ratings.subcategory_id',
|
||||
'subcategories.name as subcategory_name',
|
||||
'subcategories.value'
|
||||
)
|
||||
->get();
|
||||
|
||||
foreach ($ratings as $rating) {
|
||||
// Jika ada bobot untuk kategori ini
|
||||
if (isset($weights[$rating->category_id])) {
|
||||
$weight = floatval($weights[$rating->category_id]);
|
||||
$normalizedWeight = $totalWeight > 0 ? $weight / $totalWeight : 0;
|
||||
|
||||
// Pastikan nilai valid
|
||||
$value = max(1, intval($rating->value));
|
||||
|
||||
// Bonus nilai jika kriteria dipilih
|
||||
if (isset($criterias[$rating->category_id]) &&
|
||||
$criterias[$rating->category_id] == $rating->subcategory_id) {
|
||||
$value = 5; // Nilai maksimal
|
||||
}
|
||||
|
||||
// Hitung utilitas - menerapkan konsep yang sama dengan view
|
||||
$utilityValue = ($value - 1) / 4; // Normalisasi ke rentang 0-1 (jika nilai antara 1-5)
|
||||
$ratingScore = $normalizedWeight * $utilityValue;
|
||||
$score += $ratingScore;
|
||||
|
||||
// Simpan untuk debug
|
||||
$debugInfo[$rating->category_name] = [
|
||||
'bobot' => $weight,
|
||||
'bobot_normal' => $normalizedWeight,
|
||||
'nilai' => $value,
|
||||
'nilai_asli' => $rating->value,
|
||||
'subcategory' => $rating->subcategory_name,
|
||||
'skor_kriteria' => $ratingScore
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$cafeScores[$cafe->id] = [
|
||||
'score' => $score,
|
||||
'debug' => $debugInfo
|
||||
];
|
||||
}
|
||||
|
||||
// Urutkan cafe berdasarkan skor tertinggi
|
||||
uasort($cafeScores, function($a, $b) {
|
||||
return $b['score'] <=> $a['score'];
|
||||
});
|
||||
|
||||
// Ambil id cafe yang sudah diurutkan
|
||||
$rankedCafeIds = array_keys($cafeScores);
|
||||
|
||||
// Urutkan koleksi cafe sesuai skor
|
||||
$rankedCafes = $cafes->sortBy(function($cafe) use ($rankedCafeIds) {
|
||||
return array_search($cafe->id, $rankedCafeIds);
|
||||
});
|
||||
|
||||
// Ambil hanya 10 cafe teratas
|
||||
$rankedCafes = $rankedCafes->take(10);
|
||||
|
||||
// Persiapkan data hasil untuk disimpan
|
||||
$resultsData = [];
|
||||
foreach ($rankedCafes as $cafe) {
|
||||
if (isset($cafeScores[$cafe->id])) {
|
||||
$resultsData[$cafe->id] = [
|
||||
'name' => $cafe->nama,
|
||||
'score' => $cafeScores[$cafe->id]['score'],
|
||||
'details' => $cafeScores[$cafe->id]['debug']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Simpan hasil pencarian ke database jika user sudah login
|
||||
if (Auth::check()) {
|
||||
SearchHistory::create([
|
||||
'user_id' => Auth::id(),
|
||||
'weights' => $weights,
|
||||
'criteria' => $criterias,
|
||||
'location' => $lokasi,
|
||||
'results' => $resultsData
|
||||
]);
|
||||
}
|
||||
|
||||
return view('cafe.search-fixed', compact('currentStep', 'rankedCafes', 'cafeScores'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display search history for the authenticated user.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function history()
|
||||
{
|
||||
$histories = SearchHistory::where('user_id', Auth::id())
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(10);
|
||||
|
||||
return view('cafe.search-history', compact('histories'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display search history detail.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function detail($id)
|
||||
{
|
||||
$history = SearchHistory::where('user_id', Auth::id())
|
||||
->findOrFail($id);
|
||||
|
||||
// Retrieve cafes from the stored results
|
||||
$cafeIds = array_keys($history->results ?? []);
|
||||
$cafes = Cafe::whereIn('id', $cafeIds)->get();
|
||||
|
||||
// Map cafes to include their scores from history
|
||||
$rankedCafes = $cafes->map(function($cafe) use ($history) {
|
||||
$cafe->smart_score = $history->results[$cafe->id]['score'] ?? 0;
|
||||
$cafe->smart_details = $history->results[$cafe->id]['details'] ?? [];
|
||||
return $cafe;
|
||||
})->sortByDesc('smart_score');
|
||||
|
||||
$weights = $history->weights;
|
||||
|
||||
return view('cafe.search-detail', compact('history', 'rankedCafes', 'weights'));
|
||||
}
|
||||
}
|
|
@ -1,67 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array<int, class-string|string>
|
||||
*/
|
||||
protected $middleware = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array<string, array<int, class-string|string>>
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
// Web middleware group
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// API middleware group
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware aliases.
|
||||
*
|
||||
* Aliases may be used instead of class names to assign middleware to routes and groups.
|
||||
*
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $middlewareAliases = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
|
||||
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'admin' => \App\Http\Middleware\AdminMiddleware::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the application's route middleware.
|
||||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
//
|
||||
];
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array<int, class-string|string>
|
||||
*/
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array<string, array<int, class-string|string>>
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
'throttle:api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware.
|
||||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'role' => \App\Http\Middleware\CheckRole::class,
|
||||
'is_admin' => \App\Http\Middleware\AdminMiddleware::class,
|
||||
];
|
||||
}
|
|
@ -1,26 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AdminMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (Auth::check() && Auth::user()->role === 'admin') {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return redirect()->route('home')->with('error', 'Anda tidak memiliki akses untuk halaman ini.');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class AdminMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (Auth::check() && Auth::user()->role === 'admin') {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return redirect()->route('home')->with('error', 'Anda tidak memiliki akses untuk halaman ini.');
|
||||
}
|
||||
}
|
|
@ -1,38 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null ...$guards
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ...$guards)
|
||||
{
|
||||
$guards = empty($guards) ? [null] : $guards;
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
if (Auth::guard($guard)->check()) {
|
||||
$user = Auth::guard($guard)->user();
|
||||
|
||||
if ($user && $user->role === 'admin') {
|
||||
return redirect()->route('admin.dashboard');
|
||||
}
|
||||
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null ...$guards
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ...$guards)
|
||||
{
|
||||
$guards = empty($guards) ? [null] : $guards;
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
if (Auth::guard($guard)->check()) {
|
||||
$user = Auth::guard($guard)->user();
|
||||
|
||||
if ($user && $user->role === 'admin') {
|
||||
return redirect()->route('admin.dashboard');
|
||||
}
|
||||
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class CafeImage extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'cafe_id',
|
||||
'image_path'
|
||||
];
|
||||
|
||||
public function cafe()
|
||||
{
|
||||
return $this->belongsTo(Cafe::class);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class CafeImage extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'cafe_id',
|
||||
'image_path'
|
||||
];
|
||||
|
||||
public function cafe()
|
||||
{
|
||||
return $this->belongsTo(Cafe::class);
|
||||
}
|
||||
}
|
|
@ -1,32 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CafeRating extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'cafe_id',
|
||||
'category_id',
|
||||
'subcategory_id',
|
||||
];
|
||||
|
||||
public function cafe()
|
||||
{
|
||||
return $this->belongsTo(Cafe::class);
|
||||
}
|
||||
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(Category::class);
|
||||
}
|
||||
|
||||
public function subcategory()
|
||||
{
|
||||
return $this->belongsTo(Subcategory::class);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CafeRating extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'cafe_id',
|
||||
'category_id',
|
||||
'subcategory_id',
|
||||
];
|
||||
|
||||
public function cafe()
|
||||
{
|
||||
return $this->belongsTo(Cafe::class);
|
||||
}
|
||||
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(Category::class);
|
||||
}
|
||||
|
||||
public function subcategory()
|
||||
{
|
||||
return $this->belongsTo(Subcategory::class);
|
||||
}
|
||||
}
|
|
@ -1,50 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Category extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'slug',
|
||||
'description'
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($category) {
|
||||
$category->slug = Str::slug($category->name);
|
||||
});
|
||||
|
||||
static::updating(function ($category) {
|
||||
$category->slug = Str::slug($category->name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subcategories for the category.
|
||||
*/
|
||||
public function subcategories()
|
||||
{
|
||||
return $this->hasMany(Subcategory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cafes for the category.
|
||||
*/
|
||||
public function cafes()
|
||||
{
|
||||
return $this->hasMany(Cafe::class);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Category extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'slug',
|
||||
'description'
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($category) {
|
||||
$category->slug = Str::slug($category->name);
|
||||
});
|
||||
|
||||
static::updating(function ($category) {
|
||||
$category->slug = Str::slug($category->name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subcategories for the category.
|
||||
*/
|
||||
public function subcategories()
|
||||
{
|
||||
return $this->hasMany(Subcategory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cafes for the category.
|
||||
*/
|
||||
public function cafes()
|
||||
{
|
||||
return $this->hasMany(Cafe::class);
|
||||
}
|
||||
}
|
|
@ -1,23 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ContactMessage extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'message',
|
||||
'is_read',
|
||||
];
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ContactMessage extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'message',
|
||||
'is_read',
|
||||
];
|
||||
}
|
|
@ -1,23 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Favorite extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'cafe_id'
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function cafe()
|
||||
{
|
||||
return $this->belongsTo(Cafe::class);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Favorite extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'cafe_id'
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function cafe()
|
||||
{
|
||||
return $this->belongsTo(Cafe::class);
|
||||
}
|
||||
}
|
|
@ -1,45 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SearchHistory extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'weights',
|
||||
'criteria',
|
||||
'location',
|
||||
'results'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'weights' => 'array',
|
||||
'criteria' => 'array',
|
||||
'results' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user that owns the search history.
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SearchHistory extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'weights',
|
||||
'criteria',
|
||||
'location',
|
||||
'results'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'weights' => 'array',
|
||||
'criteria' => 'array',
|
||||
'results' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user that owns the search history.
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
|
@ -1,43 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Subcategory extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'category_id',
|
||||
'name',
|
||||
'slug',
|
||||
'value',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($subcategory) {
|
||||
$subcategory->slug = Str::slug($subcategory->name);
|
||||
});
|
||||
|
||||
static::updating(function ($subcategory) {
|
||||
$subcategory->slug = Str::slug($subcategory->name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category that owns the subcategory.
|
||||
*/
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(Category::class);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Subcategory extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'category_id',
|
||||
'name',
|
||||
'slug',
|
||||
'value',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($subcategory) {
|
||||
$subcategory->slug = Str::slug($subcategory->name);
|
||||
});
|
||||
|
||||
static::updating(function ($subcategory) {
|
||||
$subcategory->slug = Str::slug($subcategory->name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category that owns the subcategory.
|
||||
*/
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(Category::class);
|
||||
}
|
||||
}
|
|
@ -1,116 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class CustomVerifyEmail extends Notification
|
||||
{
|
||||
/**
|
||||
* The callback that should be used to create the verify email URL.
|
||||
*
|
||||
* @var \Closure|null
|
||||
*/
|
||||
public static $createUrlCallback;
|
||||
|
||||
/**
|
||||
* The callback that should be used to build the mail message.
|
||||
*
|
||||
* @var \Closure|null
|
||||
*/
|
||||
public static $toMailCallback;
|
||||
|
||||
/**
|
||||
* Get the notification's channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array|string
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
$verificationUrl = $this->verificationUrl($notifiable);
|
||||
|
||||
if (static::$toMailCallback) {
|
||||
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
|
||||
}
|
||||
|
||||
return $this->buildMailMessage($verificationUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the verify email notification mail message for the given URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
protected function buildMailMessage($url)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject('Verifikasi Alamat Email Anda')
|
||||
->greeting('Halo!')
|
||||
->line('Terima kasih telah mendaftar di aplikasi Recaje.')
|
||||
->line('Silakan klik tombol di bawah ini untuk memverifikasi alamat email Anda.')
|
||||
->action('Verifikasi Email', $url)
|
||||
->line('Jika Anda tidak membuat akun, Anda dapat mengabaikan email ini.')
|
||||
->salutation('Salam, Tim Recaje');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the verification URL for the given notifiable.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return string
|
||||
*/
|
||||
protected function verificationUrl($notifiable)
|
||||
{
|
||||
if (static::$createUrlCallback) {
|
||||
return call_user_func(static::$createUrlCallback, $notifiable);
|
||||
}
|
||||
|
||||
return URL::temporarySignedRoute(
|
||||
'verification.verify',
|
||||
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
|
||||
[
|
||||
'id' => $notifiable->getKey(),
|
||||
'hash' => sha1($notifiable->getEmailForVerification()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when creating the email verification URL.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function createUrlUsing($callback)
|
||||
{
|
||||
static::$createUrlCallback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when building the notification mail message.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function toMailUsing($callback)
|
||||
{
|
||||
static::$toMailCallback = $callback;
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class CustomVerifyEmail extends Notification
|
||||
{
|
||||
/**
|
||||
* The callback that should be used to create the verify email URL.
|
||||
*
|
||||
* @var \Closure|null
|
||||
*/
|
||||
public static $createUrlCallback;
|
||||
|
||||
/**
|
||||
* The callback that should be used to build the mail message.
|
||||
*
|
||||
* @var \Closure|null
|
||||
*/
|
||||
public static $toMailCallback;
|
||||
|
||||
/**
|
||||
* Get the notification's channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array|string
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
$verificationUrl = $this->verificationUrl($notifiable);
|
||||
|
||||
if (static::$toMailCallback) {
|
||||
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
|
||||
}
|
||||
|
||||
return $this->buildMailMessage($verificationUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the verify email notification mail message for the given URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
protected function buildMailMessage($url)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject('Verifikasi Alamat Email Anda')
|
||||
->greeting('Halo!')
|
||||
->line('Terima kasih telah mendaftar di aplikasi Recaje.')
|
||||
->line('Silakan klik tombol di bawah ini untuk memverifikasi alamat email Anda.')
|
||||
->action('Verifikasi Email', $url)
|
||||
->line('Jika Anda tidak membuat akun, Anda dapat mengabaikan email ini.')
|
||||
->salutation('Salam, Tim Recaje');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the verification URL for the given notifiable.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return string
|
||||
*/
|
||||
protected function verificationUrl($notifiable)
|
||||
{
|
||||
if (static::$createUrlCallback) {
|
||||
return call_user_func(static::$createUrlCallback, $notifiable);
|
||||
}
|
||||
|
||||
return URL::temporarySignedRoute(
|
||||
'verification.verify',
|
||||
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
|
||||
[
|
||||
'id' => $notifiable->getKey(),
|
||||
'hash' => sha1($notifiable->getEmailForVerification()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when creating the email verification URL.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function createUrlUsing($callback)
|
||||
{
|
||||
static::$createUrlCallback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback that should be used when building the notification mail message.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return void
|
||||
*/
|
||||
public static function toMailUsing($callback)
|
||||
{
|
||||
static::$toMailCallback = $callback;
|
||||
}
|
||||
}
|
|
@ -1,49 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The path to your application's "home" route.
|
||||
*
|
||||
* Typically, users are redirected here after authentication.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->configureRateLimiting();
|
||||
|
||||
$this->routes(function () {
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/api.php'));
|
||||
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate limiters for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
//
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The path to your application's "home" route.
|
||||
*
|
||||
* Typically, users are redirected here after authentication.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->configureRateLimiting();
|
||||
|
||||
$this->routes(function () {
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/api.php'));
|
||||
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate limiters for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
|
@ -47,9 +47,9 @@
|
|||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'oyiwebid_recaje'),
|
||||
'username' => env('DB_USERNAME', 'oyiwebid_faradina'),
|
||||
'password' => env('DB_PASSWORD', 'Faradina14*'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateContactMessagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('contact_messages', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email');
|
||||
$table->text('message');
|
||||
$table->boolean('is_read')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('contact_messages');
|
||||
}
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateContactMessagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('contact_messages', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email');
|
||||
$table->text('message');
|
||||
$table->boolean('is_read')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('contact_messages');
|
||||
}
|
||||
}
|
|
@ -1,30 +1,30 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('cafes', function (Blueprint $table) {
|
||||
// Menambahkan kolom area setelah kolom lokasi dan sebelum kolom gambar
|
||||
$table->string('area')->nullable()->after('lokasi');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('cafes', function (Blueprint $table) {
|
||||
// Menghapus kolom area jika migrasi di-rollback
|
||||
$table->dropColumn('area');
|
||||
});
|
||||
}
|
||||
<?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('cafes', function (Blueprint $table) {
|
||||
// Menambahkan kolom area setelah kolom lokasi dan sebelum kolom gambar
|
||||
$table->string('area')->nullable()->after('lokasi');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('cafes', function (Blueprint $table) {
|
||||
// Menghapus kolom area jika migrasi di-rollback
|
||||
$table->dropColumn('area');
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,36 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSearchHistoriesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('search_histories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->json('weights')->nullable(); // Menyimpan bobot kriteria
|
||||
$table->json('criteria')->nullable(); // Menyimpan kriteria yang dipilih
|
||||
$table->string('location')->nullable(); // Menyimpan lokasi jika ada
|
||||
$table->json('results')->nullable(); // Menyimpan hasil cafe dengan skor
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('search_histories');
|
||||
}
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSearchHistoriesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('search_histories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->json('weights')->nullable(); // Menyimpan bobot kriteria
|
||||
$table->json('criteria')->nullable(); // Menyimpan kriteria yang dipilih
|
||||
$table->string('location')->nullable(); // Menyimpan lokasi jika ada
|
||||
$table->json('results')->nullable(); // Menyimpan hasil cafe dengan skor
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('search_histories');
|
||||
}
|
||||
}
|
|
@ -1,32 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('subcategory1')->nullable();
|
||||
$table->string('subcategory2')->nullable();
|
||||
$table->string('subcategory3')->nullable();
|
||||
$table->string('subcategory4')->nullable();
|
||||
$table->string('subcategory5')->nullable();
|
||||
$table->integer('value1')->nullable();
|
||||
$table->integer('value2')->nullable();
|
||||
$table->integer('value3')->nullable();
|
||||
$table->integer('value4')->nullable();
|
||||
$table->integer('value5')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('categories');
|
||||
}
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('subcategory1')->nullable();
|
||||
$table->string('subcategory2')->nullable();
|
||||
$table->string('subcategory3')->nullable();
|
||||
$table->string('subcategory4')->nullable();
|
||||
$table->string('subcategory5')->nullable();
|
||||
$table->integer('value1')->nullable();
|
||||
$table->integer('value2')->nullable();
|
||||
$table->integer('value3')->nullable();
|
||||
$table->integer('value4')->nullable();
|
||||
$table->integer('value5')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('categories');
|
||||
}
|
||||
};
|
|
@ -1,30 +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('categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('categories');
|
||||
}
|
||||
<?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('categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('categories');
|
||||
}
|
||||
};
|
|
@ -1,30 +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('subcategories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('category_id')->constrained()->onDelete('cascade');
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('subcategories');
|
||||
}
|
||||
<?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('subcategories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('category_id')->constrained()->onDelete('cascade');
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('subcategories');
|
||||
}
|
||||
};
|
|
@ -1,56 +1,56 @@
|
|||
<?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
|
||||
{
|
||||
// Hapus tabel subcategories jika ada
|
||||
Schema::dropIfExists('subcategories');
|
||||
|
||||
// Modifikasi tabel categories
|
||||
Schema::table('categories', function (Blueprint $table) {
|
||||
// Tambahkan kolom untuk subkategori
|
||||
$table->string('subcategory1')->nullable();
|
||||
$table->string('subcategory2')->nullable();
|
||||
$table->string('subcategory3')->nullable();
|
||||
$table->string('subcategory4')->nullable();
|
||||
$table->string('subcategory5')->nullable();
|
||||
|
||||
// Tambahkan kolom untuk nilai subkategori
|
||||
$table->integer('value1')->default(1);
|
||||
$table->integer('value2')->default(2);
|
||||
$table->integer('value3')->default(3);
|
||||
$table->integer('value4')->default(4);
|
||||
$table->integer('value5')->default(5);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('categories', function (Blueprint $table) {
|
||||
// Hapus kolom subkategori
|
||||
$table->dropColumn([
|
||||
'subcategory1', 'subcategory2', 'subcategory3', 'subcategory4', 'subcategory5',
|
||||
'value1', 'value2', 'value3', 'value4', 'value5'
|
||||
]);
|
||||
});
|
||||
|
||||
// Buat ulang tabel subcategories
|
||||
Schema::create('subcategories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('category_id')->constrained()->onDelete('cascade');
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
<?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
|
||||
{
|
||||
// Hapus tabel subcategories jika ada
|
||||
Schema::dropIfExists('subcategories');
|
||||
|
||||
// Modifikasi tabel categories
|
||||
Schema::table('categories', function (Blueprint $table) {
|
||||
// Tambahkan kolom untuk subkategori
|
||||
$table->string('subcategory1')->nullable();
|
||||
$table->string('subcategory2')->nullable();
|
||||
$table->string('subcategory3')->nullable();
|
||||
$table->string('subcategory4')->nullable();
|
||||
$table->string('subcategory5')->nullable();
|
||||
|
||||
// Tambahkan kolom untuk nilai subkategori
|
||||
$table->integer('value1')->default(1);
|
||||
$table->integer('value2')->default(2);
|
||||
$table->integer('value3')->default(3);
|
||||
$table->integer('value4')->default(4);
|
||||
$table->integer('value5')->default(5);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('categories', function (Blueprint $table) {
|
||||
// Hapus kolom subkategori
|
||||
$table->dropColumn([
|
||||
'subcategory1', 'subcategory2', 'subcategory3', 'subcategory4', 'subcategory5',
|
||||
'value1', 'value2', 'value3', 'value4', 'value5'
|
||||
]);
|
||||
});
|
||||
|
||||
// Buat ulang tabel subcategories
|
||||
Schema::create('subcategories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('category_id')->constrained()->onDelete('cascade');
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,30 +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('cafe_ratings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('cafe_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('category_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('subcategory_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cafe_ratings');
|
||||
}
|
||||
<?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('cafe_ratings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('cafe_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('category_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('subcategory_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cafe_ratings');
|
||||
}
|
||||
};
|
|
@ -1,57 +1,57 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// Langkah 1: Drop tabel cafe_ratings dahulu karena memiliki foreign key ke cafes
|
||||
Schema::dropIfExists('cafe_ratings');
|
||||
|
||||
// Langkah 2: Buat tabel cafes_temp dengan struktur baru
|
||||
Schema::create('cafes_temp', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama');
|
||||
$table->string('lokasi');
|
||||
$table->string('gambar')->nullable();
|
||||
$table->decimal('latitude', 10, 7)->nullable();
|
||||
$table->decimal('longitude', 10, 7)->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Langkah 3: Pindahkan data dari cafes ke cafes_temp
|
||||
DB::statement('INSERT INTO cafes_temp (id, nama, lokasi, gambar, latitude, longitude, created_at, updated_at)
|
||||
SELECT id, nama, lokasi, gambar, latitude, longitude, created_at, updated_at FROM cafes');
|
||||
|
||||
// Langkah 4: Drop tabel cafes lama
|
||||
Schema::dropIfExists('cafes');
|
||||
|
||||
// Langkah 5: Rename cafes_temp menjadi cafes
|
||||
Schema::rename('cafes_temp', 'cafes');
|
||||
|
||||
// Langkah 6: Buat kembali tabel cafe_ratings dengan struktur baru
|
||||
Schema::create('cafe_ratings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('cafe_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('category_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('subcategory_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Tak perlu diimplementasikan karena ini adalah migrasi restrukturisasi
|
||||
// Jika ingin rollback, perlu restore manual dari backup
|
||||
}
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// Langkah 1: Drop tabel cafe_ratings dahulu karena memiliki foreign key ke cafes
|
||||
Schema::dropIfExists('cafe_ratings');
|
||||
|
||||
// Langkah 2: Buat tabel cafes_temp dengan struktur baru
|
||||
Schema::create('cafes_temp', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama');
|
||||
$table->string('lokasi');
|
||||
$table->string('gambar')->nullable();
|
||||
$table->decimal('latitude', 10, 7)->nullable();
|
||||
$table->decimal('longitude', 10, 7)->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Langkah 3: Pindahkan data dari cafes ke cafes_temp
|
||||
DB::statement('INSERT INTO cafes_temp (id, nama, lokasi, gambar, latitude, longitude, created_at, updated_at)
|
||||
SELECT id, nama, lokasi, gambar, latitude, longitude, created_at, updated_at FROM cafes');
|
||||
|
||||
// Langkah 4: Drop tabel cafes lama
|
||||
Schema::dropIfExists('cafes');
|
||||
|
||||
// Langkah 5: Rename cafes_temp menjadi cafes
|
||||
Schema::rename('cafes_temp', 'cafes');
|
||||
|
||||
// Langkah 6: Buat kembali tabel cafe_ratings dengan struktur baru
|
||||
Schema::create('cafe_ratings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('cafe_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('category_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('subcategory_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
// Tak perlu diimplementasikan karena ini adalah migrasi restrukturisasi
|
||||
// Jika ingin rollback, perlu restore manual dari backup
|
||||
}
|
||||
};
|
|
@ -1,32 +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::create('favorites', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('cafe_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
|
||||
// Pastikan kombinasi user_id dan cafe_id unik
|
||||
$table->unique(['user_id', 'cafe_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('favorites');
|
||||
}
|
||||
<?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('favorites', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('cafe_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
|
||||
// Pastikan kombinasi user_id dan cafe_id unik
|
||||
$table->unique(['user_id', 'cafe_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('favorites');
|
||||
}
|
||||
};
|
|
@ -1,23 +1,23 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('cafe_images', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('cafe_id')->constrained()->onDelete('cascade');
|
||||
$table->string('image_path');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('cafe_images');
|
||||
}
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('cafe_images', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('cafe_id')->constrained()->onDelete('cascade');
|
||||
$table->string('image_path');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('cafe_images');
|
||||
}
|
||||
};
|
|
@ -1,29 +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('cafes', function (Blueprint $table) {
|
||||
$table->decimal('harga_termurah', 10, 2)->nullable()->after('area');
|
||||
$table->decimal('harga_termahal', 10, 2)->nullable()->after('harga_termurah');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('cafes', function (Blueprint $table) {
|
||||
$table->dropColumn(['harga_termurah', 'harga_termahal']);
|
||||
});
|
||||
}
|
||||
};
|
||||
<?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('cafes', function (Blueprint $table) {
|
||||
$table->decimal('harga_termurah', 10, 2)->nullable()->after('area');
|
||||
$table->decimal('harga_termahal', 10, 2)->nullable()->after('harga_termurah');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('cafes', function (Blueprint $table) {
|
||||
$table->dropColumn(['harga_termurah', 'harga_termahal']);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,126 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Cafe;
|
||||
use App\Models\CafeRating;
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CafeAndRatingsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Hapus data cafe dan rating yang sudah ada
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=0');
|
||||
CafeRating::truncate();
|
||||
Cafe::truncate();
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1');
|
||||
|
||||
// Data cafe dengan rating spesifik untuk setiap kategori
|
||||
$cafes = [
|
||||
['nama' => 'Poppins', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => 'Teras JTI', 'lokasi' => null, 'harga' => 4, 'wifi' => 5, 'jam' => 3, 'fotogenik' => 3, 'jarak' => 5, 'fasilitas' => 4, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
|
||||
['nama' => 'Nugas Jember', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 1, 'jarak' => 4, 'fasilitas' => 1, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Kopi Kampus', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Eterno', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => 'Kattappa', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 4, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
|
||||
['nama' => 'Fifty-Fifty', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Discuss Space & Coffee', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 4, 'area' => 'Indoor'],
|
||||
['nama' => 'Subur', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
|
||||
['nama' => 'Nuansa', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Semi Outdoor'],
|
||||
['nama' => 'Nol Kilometer', 'lokasi' => null, 'harga' => 5, 'wifi' => 2, 'jam' => 3, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Tanaloka', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 2, 'fotogenik' => 5, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
|
||||
['nama' => 'Wafa', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 2, 'fasilitas' => 5, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => '888', 'lokasi' => null, 'harga' => 2, 'wifi' => 5, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor'],
|
||||
['nama' => 'Sorai', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => 'Tharuh', 'lokasi' => null, 'harga' => 3, 'wifi' => 2, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor'],
|
||||
['nama' => 'Contact', 'lokasi' => null, 'harga' => 3, 'wifi' => 2, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
|
||||
['nama' => 'Cus Cus', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => 'Grufi', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 5, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
|
||||
['nama' => 'Tomoro', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 4, 'fasilitas' => 4, 'area' => 'Indoor'],
|
||||
['nama' => 'Fore', 'lokasi' => null, 'harga' => 2, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
|
||||
['nama' => 'Fox', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 1, 'area' => 'Indoor'],
|
||||
['nama' => 'Perasa', 'lokasi' => null, 'harga' => 2, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor'],
|
||||
['nama' => 'Kopi Boss', 'lokasi' => null, 'harga' => 5, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Navas', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 5, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
|
||||
];
|
||||
|
||||
// Get all categories and their subcategories
|
||||
$categories = Category::all();
|
||||
|
||||
// Mendapatkan kategori berdasarkan namanya untuk memudahkan penugasan subcategory
|
||||
$hargaCategory = Category::where('name', 'Harga')->first();
|
||||
$wifiCategory = Category::where('name', 'Kecepatan WiFi')->first();
|
||||
$jamOperasionalCategory = Category::where('name', 'Jam Operasional')->first();
|
||||
$fotogenikCategory = Category::where('name', 'Fotogenik')->first();
|
||||
$areaCategory = Category::where('name', 'Area (Outdoor/Indoor)')->first();
|
||||
$jarakCategory = Category::where('name', 'Jarak dengan Kampus')->first();
|
||||
$fasilitasCategory = Category::where('name', 'Fasilitas')->first();
|
||||
|
||||
// Membuat cafe dan ratings
|
||||
foreach ($cafes as $cafeData) {
|
||||
// Buat cafe dengan data dasar
|
||||
$cafe = Cafe::create([
|
||||
'nama' => $cafeData['nama'],
|
||||
'lokasi' => $cafeData['lokasi'],
|
||||
'area' => $cafeData['area'],
|
||||
]);
|
||||
|
||||
// Assign ratings berdasarkan data yang sudah ditentukan
|
||||
// Harga
|
||||
if ($hargaCategory) {
|
||||
$this->createRating($cafe->id, $hargaCategory->id, $cafeData['harga']);
|
||||
}
|
||||
|
||||
// WiFi
|
||||
if ($wifiCategory) {
|
||||
$this->createRating($cafe->id, $wifiCategory->id, $cafeData['wifi']);
|
||||
}
|
||||
|
||||
// Jam Operasional
|
||||
if ($jamOperasionalCategory) {
|
||||
$this->createRating($cafe->id, $jamOperasionalCategory->id, $cafeData['jam']);
|
||||
}
|
||||
|
||||
// Fotogenik
|
||||
if ($fotogenikCategory) {
|
||||
$this->createRating($cafe->id, $fotogenikCategory->id, $cafeData['fotogenik']);
|
||||
}
|
||||
|
||||
// Jarak
|
||||
if ($jarakCategory) {
|
||||
$this->createRating($cafe->id, $jarakCategory->id, $cafeData['jarak']);
|
||||
}
|
||||
|
||||
// Fasilitas
|
||||
if ($fasilitasCategory) {
|
||||
$this->createRating($cafe->id, $fasilitasCategory->id, $cafeData['fasilitas']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Membuat rating berdasarkan category_id dan nilai
|
||||
*/
|
||||
private function createRating($cafeId, $categoryId, $value)
|
||||
{
|
||||
// Cari subcategory yang sesuai dengan category_id dan value
|
||||
$subcategory = Subcategory::where('category_id', $categoryId)
|
||||
->where('value', $value)
|
||||
->first();
|
||||
|
||||
if ($subcategory) {
|
||||
CafeRating::create([
|
||||
'cafe_id' => $cafeId,
|
||||
'category_id' => $categoryId,
|
||||
'subcategory_id' => $subcategory->id
|
||||
]);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Cafe;
|
||||
use App\Models\CafeRating;
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CafeAndRatingsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Hapus data cafe dan rating yang sudah ada
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=0');
|
||||
CafeRating::truncate();
|
||||
Cafe::truncate();
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1');
|
||||
|
||||
// Data cafe dengan rating spesifik untuk setiap kategori
|
||||
$cafes = [
|
||||
['nama' => 'Poppins', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => 'Teras JTI', 'lokasi' => null, 'harga' => 4, 'wifi' => 5, 'jam' => 3, 'fotogenik' => 3, 'jarak' => 5, 'fasilitas' => 4, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
|
||||
['nama' => 'Nugas Jember', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 1, 'jarak' => 4, 'fasilitas' => 1, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Kopi Kampus', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Eterno', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => 'Kattappa', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 4, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
|
||||
['nama' => 'Fifty-Fifty', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Discuss Space & Coffee', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 4, 'area' => 'Indoor'],
|
||||
['nama' => 'Subur', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
|
||||
['nama' => 'Nuansa', 'lokasi' => null, 'harga' => 4, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Semi Outdoor'],
|
||||
['nama' => 'Nol Kilometer', 'lokasi' => null, 'harga' => 5, 'wifi' => 2, 'jam' => 3, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Tanaloka', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 2, 'fotogenik' => 5, 'jarak' => 3, 'fasilitas' => 5, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
|
||||
['nama' => 'Wafa', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 2, 'fasilitas' => 5, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => '888', 'lokasi' => null, 'harga' => 2, 'wifi' => 5, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor'],
|
||||
['nama' => 'Sorai', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => 'Tharuh', 'lokasi' => null, 'harga' => 3, 'wifi' => 2, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor'],
|
||||
['nama' => 'Contact', 'lokasi' => null, 'harga' => 3, 'wifi' => 2, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Semi Outdoor, Outdoor'],
|
||||
['nama' => 'Cus Cus', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 4, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor, Outdoor'],
|
||||
['nama' => 'Grufi', 'lokasi' => null, 'harga' => 2, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 5, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
|
||||
['nama' => 'Tomoro', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 4, 'fasilitas' => 4, 'area' => 'Indoor'],
|
||||
['nama' => 'Fore', 'lokasi' => null, 'harga' => 2, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Indoor'],
|
||||
['nama' => 'Fox', 'lokasi' => null, 'harga' => 3, 'wifi' => 3, 'jam' => 4, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 1, 'area' => 'Indoor'],
|
||||
['nama' => 'Perasa', 'lokasi' => null, 'harga' => 2, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 3, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Indoor'],
|
||||
['nama' => 'Kopi Boss', 'lokasi' => null, 'harga' => 5, 'wifi' => 3, 'jam' => 5, 'fotogenik' => 1, 'jarak' => 3, 'fasilitas' => 3, 'area' => 'Semi Outdoor'],
|
||||
['nama' => 'Navas', 'lokasi' => null, 'harga' => 3, 'wifi' => 4, 'jam' => 5, 'fotogenik' => 2, 'jarak' => 3, 'fasilitas' => 2, 'area' => 'Semi Outdoor'],
|
||||
];
|
||||
|
||||
// Get all categories and their subcategories
|
||||
$categories = Category::all();
|
||||
|
||||
// Mendapatkan kategori berdasarkan namanya untuk memudahkan penugasan subcategory
|
||||
$hargaCategory = Category::where('name', 'Harga')->first();
|
||||
$wifiCategory = Category::where('name', 'Kecepatan WiFi')->first();
|
||||
$jamOperasionalCategory = Category::where('name', 'Jam Operasional')->first();
|
||||
$fotogenikCategory = Category::where('name', 'Fotogenik')->first();
|
||||
$areaCategory = Category::where('name', 'Area (Outdoor/Indoor)')->first();
|
||||
$jarakCategory = Category::where('name', 'Jarak dengan Kampus')->first();
|
||||
$fasilitasCategory = Category::where('name', 'Fasilitas')->first();
|
||||
|
||||
// Membuat cafe dan ratings
|
||||
foreach ($cafes as $cafeData) {
|
||||
// Buat cafe dengan data dasar
|
||||
$cafe = Cafe::create([
|
||||
'nama' => $cafeData['nama'],
|
||||
'lokasi' => $cafeData['lokasi'],
|
||||
'area' => $cafeData['area'],
|
||||
]);
|
||||
|
||||
// Assign ratings berdasarkan data yang sudah ditentukan
|
||||
// Harga
|
||||
if ($hargaCategory) {
|
||||
$this->createRating($cafe->id, $hargaCategory->id, $cafeData['harga']);
|
||||
}
|
||||
|
||||
// WiFi
|
||||
if ($wifiCategory) {
|
||||
$this->createRating($cafe->id, $wifiCategory->id, $cafeData['wifi']);
|
||||
}
|
||||
|
||||
// Jam Operasional
|
||||
if ($jamOperasionalCategory) {
|
||||
$this->createRating($cafe->id, $jamOperasionalCategory->id, $cafeData['jam']);
|
||||
}
|
||||
|
||||
// Fotogenik
|
||||
if ($fotogenikCategory) {
|
||||
$this->createRating($cafe->id, $fotogenikCategory->id, $cafeData['fotogenik']);
|
||||
}
|
||||
|
||||
// Jarak
|
||||
if ($jarakCategory) {
|
||||
$this->createRating($cafe->id, $jarakCategory->id, $cafeData['jarak']);
|
||||
}
|
||||
|
||||
// Fasilitas
|
||||
if ($fasilitasCategory) {
|
||||
$this->createRating($cafe->id, $fasilitasCategory->id, $cafeData['fasilitas']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Membuat rating berdasarkan category_id dan nilai
|
||||
*/
|
||||
private function createRating($cafeId, $categoryId, $value)
|
||||
{
|
||||
// Cari subcategory yang sesuai dengan category_id dan value
|
||||
$subcategory = Subcategory::where('category_id', $categoryId)
|
||||
->where('value', $value)
|
||||
->first();
|
||||
|
||||
if ($subcategory) {
|
||||
CafeRating::create([
|
||||
'cafe_id' => $cafeId,
|
||||
'category_id' => $categoryId,
|
||||
'subcategory_id' => $subcategory->id
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Cafe;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CafePriceSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Data harga berdasarkan ID cafe yang sudah ada
|
||||
$priceData = [
|
||||
1 => ['harga_termurah' => 18000, 'harga_termahal' => 25000], // Poppins
|
||||
2 => ['harga_termurah' => 10000, 'harga_termahal' => 18000], // Teras JTI
|
||||
3 => ['harga_termurah' => 10000, 'harga_termahal' => 20000], // Nugas Jember
|
||||
4 => ['harga_termurah' => 4000, 'harga_termahal' => 18000], // Kopi Kampus
|
||||
5 => ['harga_termurah' => 15000, 'harga_termahal' => 40000], // Eterno
|
||||
7 => ['harga_termurah' => 8000, 'harga_termahal' => 20000], // Fifty-Fifty
|
||||
8 => ['harga_termurah' => 10000, 'harga_termahal' => 35000], // Discuss Space & Coffee
|
||||
9 => ['harga_termurah' => 12000, 'harga_termahal' => 34000], // Subur
|
||||
10 => ['harga_termurah' => 6000, 'harga_termahal' => 15000], // Nuansa
|
||||
11 => ['harga_termurah' => 6000, 'harga_termahal' => 8000], // Nol Kilometer
|
||||
13 => ['harga_termurah' => 6000, 'harga_termahal' => 25000], // Wafa
|
||||
14 => ['harga_termurah' => 15000, 'harga_termahal' => 45000], // 888
|
||||
15 => ['harga_termurah' => 7000, 'harga_termahal' => 25000], // Sorai
|
||||
16 => ['harga_termurah' => 8000, 'harga_termahal' => 23000], // Tharuh
|
||||
17 => ['harga_termurah' => 12000, 'harga_termahal' => 38000], // Contact
|
||||
18 => ['harga_termurah' => 14000, 'harga_termahal' => 20000], // Cus Cus
|
||||
19 => ['harga_termurah' => 18000, 'harga_termahal' => 34000], // Grufi
|
||||
20 => ['harga_termurah' => 15000, 'harga_termahal' => 25000], // Tomoro
|
||||
21 => ['harga_termurah' => 22000, 'harga_termahal' => 40000], // Fore
|
||||
22 => ['harga_termurah' => 1500, 'harga_termahal' => 37000], // Fox
|
||||
23 => ['harga_termurah' => 12000, 'harga_termahal' => 45000], // Perasa
|
||||
24 => ['harga_termurah' => 5000, 'harga_termahal' => 15000], // Kopi Boss
|
||||
25 => ['harga_termurah' => 10000, 'harga_termahal' => 30000], // Navas
|
||||
26 => ['harga_termurah' => 15000, 'harga_termahal' => 35000], // Kattappa (ID 26)
|
||||
28 => ['harga_termurah' => 17000, 'harga_termahal' => 47000], // Tanaloka (ID 28)
|
||||
];
|
||||
|
||||
// Update harga untuk setiap cafe
|
||||
foreach ($priceData as $cafeId => $prices) {
|
||||
$cafe = Cafe::find($cafeId);
|
||||
|
||||
if ($cafe) {
|
||||
$cafe->update([
|
||||
'harga_termurah' => $prices['harga_termurah'],
|
||||
'harga_termahal' => $prices['harga_termahal']
|
||||
]);
|
||||
|
||||
echo "Updated cafe ID {$cafeId} ({$cafe->nama}): {$prices['harga_termurah']} - {$prices['harga_termahal']}\n";
|
||||
} else {
|
||||
echo "Cafe with ID {$cafeId} not found\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Cafe;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CafePriceSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Data harga berdasarkan ID cafe yang sudah ada
|
||||
$priceData = [
|
||||
1 => ['harga_termurah' => 18000, 'harga_termahal' => 25000], // Poppins
|
||||
2 => ['harga_termurah' => 10000, 'harga_termahal' => 18000], // Teras JTI
|
||||
3 => ['harga_termurah' => 10000, 'harga_termahal' => 20000], // Nugas Jember
|
||||
4 => ['harga_termurah' => 4000, 'harga_termahal' => 18000], // Kopi Kampus
|
||||
5 => ['harga_termurah' => 15000, 'harga_termahal' => 40000], // Eterno
|
||||
7 => ['harga_termurah' => 8000, 'harga_termahal' => 20000], // Fifty-Fifty
|
||||
8 => ['harga_termurah' => 10000, 'harga_termahal' => 35000], // Discuss Space & Coffee
|
||||
9 => ['harga_termurah' => 12000, 'harga_termahal' => 34000], // Subur
|
||||
10 => ['harga_termurah' => 6000, 'harga_termahal' => 15000], // Nuansa
|
||||
11 => ['harga_termurah' => 6000, 'harga_termahal' => 8000], // Nol Kilometer
|
||||
13 => ['harga_termurah' => 6000, 'harga_termahal' => 25000], // Wafa
|
||||
14 => ['harga_termurah' => 15000, 'harga_termahal' => 45000], // 888
|
||||
15 => ['harga_termurah' => 7000, 'harga_termahal' => 25000], // Sorai
|
||||
16 => ['harga_termurah' => 8000, 'harga_termahal' => 23000], // Tharuh
|
||||
17 => ['harga_termurah' => 12000, 'harga_termahal' => 38000], // Contact
|
||||
18 => ['harga_termurah' => 14000, 'harga_termahal' => 20000], // Cus Cus
|
||||
19 => ['harga_termurah' => 18000, 'harga_termahal' => 34000], // Grufi
|
||||
20 => ['harga_termurah' => 15000, 'harga_termahal' => 25000], // Tomoro
|
||||
21 => ['harga_termurah' => 22000, 'harga_termahal' => 40000], // Fore
|
||||
22 => ['harga_termurah' => 1500, 'harga_termahal' => 37000], // Fox
|
||||
23 => ['harga_termurah' => 12000, 'harga_termahal' => 45000], // Perasa
|
||||
24 => ['harga_termurah' => 5000, 'harga_termahal' => 15000], // Kopi Boss
|
||||
25 => ['harga_termurah' => 10000, 'harga_termahal' => 30000], // Navas
|
||||
26 => ['harga_termurah' => 15000, 'harga_termahal' => 35000], // Kattappa (ID 26)
|
||||
28 => ['harga_termurah' => 17000, 'harga_termahal' => 47000], // Tanaloka (ID 28)
|
||||
];
|
||||
|
||||
// Update harga untuk setiap cafe
|
||||
foreach ($priceData as $cafeId => $prices) {
|
||||
$cafe = Cafe::find($cafeId);
|
||||
|
||||
if ($cafe) {
|
||||
$cafe->update([
|
||||
'harga_termurah' => $prices['harga_termurah'],
|
||||
'harga_termahal' => $prices['harga_termahal']
|
||||
]);
|
||||
|
||||
echo "Updated cafe ID {$cafeId} ({$cafe->nama}): {$prices['harga_termurah']} - {$prices['harga_termahal']}\n";
|
||||
} else {
|
||||
echo "Cafe with ID {$cafeId} not found\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,236 +1,236 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CategoryAndSubcategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Hapus data yang sudah ada untuk menghindari duplikasi
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=0');
|
||||
Subcategory::truncate();
|
||||
Category::truncate();
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1');
|
||||
|
||||
// Kategori 1: Harga
|
||||
$harga = Category::create([
|
||||
'name' => 'Harga'
|
||||
]);
|
||||
|
||||
// Subkategori Harga
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Sangat Murah (<10k/porsi)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Murah (10k–15k)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Sedang (15k–25k)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Agak Mahal (25k–35k)',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Mahal (>35k)',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 2: Kecepatan WiFi
|
||||
$wifi = Category::create([
|
||||
'name' => 'Kecepatan WiFi'
|
||||
]);
|
||||
|
||||
// Subkategori Kecepatan WiFi
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Sangat Cepat (>50 Mbps)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Cepat (30–50 Mbps)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Sedang (15–30 Mbps)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Lambat (5–15 Mbps)',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Sangat Lambat (<5 Mbps)',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 3: Jam Operasional
|
||||
$jamOperasional = Category::create([
|
||||
'name' => 'Jam Operasional'
|
||||
]);
|
||||
|
||||
// Subkategori Jam Operasional
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => '24 Jam',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => '08.00–00.00 (16 jam)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => '10.00–22.00 (12 jam)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => '<10 jam operasional',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => 'Tidak konsisten / Tidak tetap',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 4: Fotogenik
|
||||
$fotogenik = Category::create([
|
||||
'name' => 'Fotogenik'
|
||||
]);
|
||||
|
||||
// Subkategori Fotogenik
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Sangat Fotogenik (Desain unik, banyak spot foto)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Fotogenik (Desain bagus, cukup spot foto)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Cukup Fotogenik (estetis biasa saja)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Kurang Fotogenik',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Tidak Fotogenik',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 5: Area (Luas Tempat)
|
||||
$area = Category::create([
|
||||
'name' => 'Area (Outdoor/Indoor)'
|
||||
]);
|
||||
|
||||
Subcategory::create([
|
||||
'category_id' => $area->id,
|
||||
'name' => 'Outdoor & Indoor',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $area->id,
|
||||
'name' => 'Indoor',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $area->id,
|
||||
'name' => 'Outdoor',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 6: Jarak dengan Kampus
|
||||
$jarak = Category::create([
|
||||
'name' => 'Jarak dengan Kampus'
|
||||
]);
|
||||
|
||||
// Subkategori Jarak dengan Kampus
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Sangat Dekat (≤1 Km)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Dekat (1–2 Km)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Sedang (2–5 Km)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Jauh (5–10 Km)',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Sangat Jauh (>10 Km)',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 7: Fasilitas
|
||||
$fasilitas = Category::create([
|
||||
'name' => 'Fasilitas'
|
||||
]);
|
||||
|
||||
// Subkategori Fasilitas
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Sangat Lengkap (5 fasilitas)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Lengkap (4 fasilitas)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Cukup (3 fasilitas)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Kurang (2 fasilitas)',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Minim / Tidak Lengkap (0–1 fasilitas)',
|
||||
'value' => 1
|
||||
]);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\Subcategory;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CategoryAndSubcategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Hapus data yang sudah ada untuk menghindari duplikasi
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=0');
|
||||
Subcategory::truncate();
|
||||
Category::truncate();
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1');
|
||||
|
||||
// Kategori 1: Harga
|
||||
$harga = Category::create([
|
||||
'name' => 'Harga'
|
||||
]);
|
||||
|
||||
// Subkategori Harga
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Sangat Murah (<10k/porsi)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Murah (10k–15k)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Sedang (15k–25k)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Agak Mahal (25k–35k)',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $harga->id,
|
||||
'name' => 'Mahal (>35k)',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 2: Kecepatan WiFi
|
||||
$wifi = Category::create([
|
||||
'name' => 'Kecepatan WiFi'
|
||||
]);
|
||||
|
||||
// Subkategori Kecepatan WiFi
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Sangat Cepat (>50 Mbps)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Cepat (30–50 Mbps)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Sedang (15–30 Mbps)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Lambat (5–15 Mbps)',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $wifi->id,
|
||||
'name' => 'Sangat Lambat (<5 Mbps)',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 3: Jam Operasional
|
||||
$jamOperasional = Category::create([
|
||||
'name' => 'Jam Operasional'
|
||||
]);
|
||||
|
||||
// Subkategori Jam Operasional
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => '24 Jam',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => '08.00–00.00 (16 jam)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => '10.00–22.00 (12 jam)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => '<10 jam operasional',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jamOperasional->id,
|
||||
'name' => 'Tidak konsisten / Tidak tetap',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 4: Fotogenik
|
||||
$fotogenik = Category::create([
|
||||
'name' => 'Fotogenik'
|
||||
]);
|
||||
|
||||
// Subkategori Fotogenik
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Sangat Fotogenik (Desain unik, banyak spot foto)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Fotogenik (Desain bagus, cukup spot foto)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Cukup Fotogenik (estetis biasa saja)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Kurang Fotogenik',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fotogenik->id,
|
||||
'name' => 'Tidak Fotogenik',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 5: Area (Luas Tempat)
|
||||
$area = Category::create([
|
||||
'name' => 'Area (Outdoor/Indoor)'
|
||||
]);
|
||||
|
||||
Subcategory::create([
|
||||
'category_id' => $area->id,
|
||||
'name' => 'Outdoor & Indoor',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $area->id,
|
||||
'name' => 'Indoor',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $area->id,
|
||||
'name' => 'Outdoor',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 6: Jarak dengan Kampus
|
||||
$jarak = Category::create([
|
||||
'name' => 'Jarak dengan Kampus'
|
||||
]);
|
||||
|
||||
// Subkategori Jarak dengan Kampus
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Sangat Dekat (≤1 Km)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Dekat (1–2 Km)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Sedang (2–5 Km)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Jauh (5–10 Km)',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $jarak->id,
|
||||
'name' => 'Sangat Jauh (>10 Km)',
|
||||
'value' => 1
|
||||
]);
|
||||
|
||||
// Kategori 7: Fasilitas
|
||||
$fasilitas = Category::create([
|
||||
'name' => 'Fasilitas'
|
||||
]);
|
||||
|
||||
// Subkategori Fasilitas
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Sangat Lengkap (5 fasilitas)',
|
||||
'value' => 5
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Lengkap (4 fasilitas)',
|
||||
'value' => 4
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Cukup (3 fasilitas)',
|
||||
'value' => 3
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Kurang (2 fasilitas)',
|
||||
'value' => 2
|
||||
]);
|
||||
Subcategory::create([
|
||||
'category_id' => $fasilitas->id,
|
||||
'name' => 'Minim / Tidak Lengkap (0–1 fasilitas)',
|
||||
'value' => 1
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,87 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Hapus data yang ada terlebih dahulu
|
||||
DB::table('categories')->truncate();
|
||||
|
||||
// Data kategori dengan subkategori
|
||||
$categories = [
|
||||
[
|
||||
'name' => 'Jarak Kampus',
|
||||
'subcategory1' => 'Sangat Dekat',
|
||||
'subcategory2' => 'Dekat',
|
||||
'subcategory3' => 'Sedang',
|
||||
'subcategory4' => 'Jauh',
|
||||
'subcategory5' => 'Sangat Jauh',
|
||||
'value1' => 5,
|
||||
'value2' => 4,
|
||||
'value3' => 3,
|
||||
'value4' => 2,
|
||||
'value5' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'name' => 'Kisaran Harga',
|
||||
'subcategory1' => 'Sangat Murah',
|
||||
'subcategory2' => 'Murah',
|
||||
'subcategory3' => 'Sedang',
|
||||
'subcategory4' => 'Mahal',
|
||||
'subcategory5' => 'Sangat Mahal',
|
||||
'value1' => 5,
|
||||
'value2' => 4,
|
||||
'value3' => 3,
|
||||
'value4' => 2,
|
||||
'value5' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'name' => 'Fasilitas',
|
||||
'subcategory1' => 'Sangat Lengkap',
|
||||
'subcategory2' => 'Lengkap',
|
||||
'subcategory3' => 'Cukup',
|
||||
'subcategory4' => 'Kurang',
|
||||
'subcategory5' => 'Sangat Kurang',
|
||||
'value1' => 5,
|
||||
'value2' => 4,
|
||||
'value3' => 3,
|
||||
'value4' => 2,
|
||||
'value5' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'name' => 'Kecepatan WiFi',
|
||||
'subcategory1' => 'Sangat Cepat',
|
||||
'subcategory2' => 'Cepat',
|
||||
'subcategory3' => 'Sedang',
|
||||
'subcategory4' => 'Lambat',
|
||||
'subcategory5' => 'Sangat Lambat',
|
||||
'value1' => 5,
|
||||
'value2' => 4,
|
||||
'value3' => 3,
|
||||
'value4' => 2,
|
||||
'value5' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
]
|
||||
];
|
||||
|
||||
// Insert data kategori
|
||||
DB::table('categories')->insert($categories);
|
||||
|
||||
$this->command->info('Categories seeded successfully!');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Hapus data yang ada terlebih dahulu
|
||||
DB::table('categories')->truncate();
|
||||
|
||||
// Data kategori dengan subkategori
|
||||
$categories = [
|
||||
[
|
||||
'name' => 'Jarak Kampus',
|
||||
'subcategory1' => 'Sangat Dekat',
|
||||
'subcategory2' => 'Dekat',
|
||||
'subcategory3' => 'Sedang',
|
||||
'subcategory4' => 'Jauh',
|
||||
'subcategory5' => 'Sangat Jauh',
|
||||
'value1' => 5,
|
||||
'value2' => 4,
|
||||
'value3' => 3,
|
||||
'value4' => 2,
|
||||
'value5' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'name' => 'Kisaran Harga',
|
||||
'subcategory1' => 'Sangat Murah',
|
||||
'subcategory2' => 'Murah',
|
||||
'subcategory3' => 'Sedang',
|
||||
'subcategory4' => 'Mahal',
|
||||
'subcategory5' => 'Sangat Mahal',
|
||||
'value1' => 5,
|
||||
'value2' => 4,
|
||||
'value3' => 3,
|
||||
'value4' => 2,
|
||||
'value5' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'name' => 'Fasilitas',
|
||||
'subcategory1' => 'Sangat Lengkap',
|
||||
'subcategory2' => 'Lengkap',
|
||||
'subcategory3' => 'Cukup',
|
||||
'subcategory4' => 'Kurang',
|
||||
'subcategory5' => 'Sangat Kurang',
|
||||
'value1' => 5,
|
||||
'value2' => 4,
|
||||
'value3' => 3,
|
||||
'value4' => 2,
|
||||
'value5' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
],
|
||||
[
|
||||
'name' => 'Kecepatan WiFi',
|
||||
'subcategory1' => 'Sangat Cepat',
|
||||
'subcategory2' => 'Cepat',
|
||||
'subcategory3' => 'Sedang',
|
||||
'subcategory4' => 'Lambat',
|
||||
'subcategory5' => 'Sangat Lambat',
|
||||
'value1' => 5,
|
||||
'value2' => 4,
|
||||
'value3' => 3,
|
||||
'value4' => 2,
|
||||
'value5' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now()
|
||||
]
|
||||
];
|
||||
|
||||
// Insert data kategori
|
||||
DB::table('categories')->insert($categories);
|
||||
|
||||
$this->command->info('Categories seeded successfully!');
|
||||
}
|
||||
}
|
|
@ -1,66 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Subcategory;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SubcategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Hapus data yang ada terlebih dahulu
|
||||
DB::table('subcategories')->truncate();
|
||||
|
||||
// Data subkategori berdasarkan kategori
|
||||
$subcategories = [
|
||||
'Jarak Kampus' => [
|
||||
'Sangat Dekat (< 1 km)',
|
||||
'Dekat (1-2 km)',
|
||||
'Sedang (2-3 km)',
|
||||
'Jauh (> 3 km)'
|
||||
],
|
||||
'Kisaran Harga' => [
|
||||
'Sangat Murah (< Rp 10.000)',
|
||||
'Murah (Rp 10.000 - Rp 20.000)',
|
||||
'Sedang (Rp 20.000 - Rp 30.000)',
|
||||
'Mahal (> Rp 30.000)'
|
||||
],
|
||||
'Fasilitas' => [
|
||||
'Sangat Lengkap',
|
||||
'Lengkap',
|
||||
'Cukup',
|
||||
'Minimal'
|
||||
],
|
||||
'Kecepatan WiFi' => [
|
||||
'Sangat Cepat (> 50 Mbps)',
|
||||
'Cepat (30-50 Mbps)',
|
||||
'Sedang (10-30 Mbps)',
|
||||
'Lambat (< 10 Mbps)'
|
||||
]
|
||||
];
|
||||
|
||||
// Insert data subkategori
|
||||
foreach ($subcategories as $categoryName => $items) {
|
||||
$category = Category::where('name', $categoryName)->first();
|
||||
|
||||
if ($category) {
|
||||
foreach ($items as $subcategoryName) {
|
||||
Subcategory::create([
|
||||
'category_id' => $category->id,
|
||||
'name' => $subcategoryName,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info('Subcategories seeded successfully!');
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Subcategory;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SubcategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Hapus data yang ada terlebih dahulu
|
||||
DB::table('subcategories')->truncate();
|
||||
|
||||
// Data subkategori berdasarkan kategori
|
||||
$subcategories = [
|
||||
'Jarak Kampus' => [
|
||||
'Sangat Dekat (< 1 km)',
|
||||
'Dekat (1-2 km)',
|
||||
'Sedang (2-3 km)',
|
||||
'Jauh (> 3 km)'
|
||||
],
|
||||
'Kisaran Harga' => [
|
||||
'Sangat Murah (< Rp 10.000)',
|
||||
'Murah (Rp 10.000 - Rp 20.000)',
|
||||
'Sedang (Rp 20.000 - Rp 30.000)',
|
||||
'Mahal (> Rp 30.000)'
|
||||
],
|
||||
'Fasilitas' => [
|
||||
'Sangat Lengkap',
|
||||
'Lengkap',
|
||||
'Cukup',
|
||||
'Minimal'
|
||||
],
|
||||
'Kecepatan WiFi' => [
|
||||
'Sangat Cepat (> 50 Mbps)',
|
||||
'Cepat (30-50 Mbps)',
|
||||
'Sedang (10-30 Mbps)',
|
||||
'Lambat (< 10 Mbps)'
|
||||
]
|
||||
];
|
||||
|
||||
// Insert data subkategori
|
||||
foreach ($subcategories as $categoryName => $items) {
|
||||
$category = Category::where('name', $categoryName)->first();
|
||||
|
||||
if ($category) {
|
||||
foreach ($items as $subcategoryName) {
|
||||
Subcategory::create([
|
||||
'category_id' => $category->id,
|
||||
'name' => $subcategoryName,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info('Subcategories seeded successfully!');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews -Indexes
|
||||
</IfModule>
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Handle Authorization Header
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
# Handle X-XSRF-Token Header
|
||||
RewriteCond %{HTTP:x-xsrf-token} .
|
||||
RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
|
||||
|
||||
# Redirect Trailing Slashes If Not A Folder...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [L,R=301]
|
||||
|
||||
# Send Requests To Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
</IfModule>
|
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 20 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" zoomAndPan="magnify" viewBox="0 0 750 749.999995" height="1000" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="b632305565"><path d="M 104.71875 26.34375 L 645.46875 26.34375 L 645.46875 723.84375 L 104.71875 723.84375 Z M 104.71875 26.34375 " clip-rule="nonzero"/></clipPath></defs><g clip-path="url(#b632305565)"><path fill="#000000" d="M 645.464844 671.160156 C 645.464844 485.035156 524.316406 334.242188 375.09375 334.242188 C 225.867188 334.242188 104.71875 485.035156 104.71875 671.160156 C 104.71875 700.183594 225.867188 723.84375 375.09375 723.84375 C 524.316406 723.84375 645.464844 700.183594 645.464844 671.160156 Z M 375.09375 26.34375 C 456.488281 26.34375 522.425781 92.277344 522.425781 173.667969 C 522.425781 255.058594 456.488281 320.992188 375.09375 320.992188 C 293.695312 320.992188 227.757812 255.058594 227.757812 173.667969 C 227.757812 92.277344 293.695312 26.34375 375.09375 26.34375 Z M 375.09375 26.34375 " fill-opacity="1" fill-rule="evenodd"/></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 MiB |
After Width: | Height: | Size: 674 KiB |
After Width: | Height: | Size: 469 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,9 @@
|
|||
/* Ini akan mencegah papan ketikan (keyboard) muncul pada elemen yang bukan input */
|
||||
body:not(:focus-within) {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 696 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 696 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 618 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 618 B |
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Determine if the application is in maintenance mode...
|
||||
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||
require $maintenance;
|
||||
}
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the request...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
$app->handleRequest(Request::capture());
|
|
@ -0,0 +1,252 @@
|
|||
// Inisialisasi variabel global
|
||||
let hasSearchResults = false;
|
||||
let currentStep = 1;
|
||||
|
||||
// Global data untuk menyimpan nilai penting setiap kriteria
|
||||
const importanceValues = {};
|
||||
|
||||
// Label untuk tingkat kepentingan
|
||||
const importanceLabels = {
|
||||
1: "Sangat tidak penting (1)",
|
||||
2: "Tidak penting (2)",
|
||||
3: "Kurang penting (3)",
|
||||
4: "Agak penting (4)",
|
||||
5: "Sedang (5)",
|
||||
6: "Cukup penting (6)",
|
||||
7: "Penting (7)",
|
||||
8: "Sangat penting (8)",
|
||||
9: "Sangat penting sekali (9)",
|
||||
10: "Paling penting (10)"
|
||||
};
|
||||
|
||||
// Fungsi untuk update nilai slider yang dipanggil langsung dari oninput HTML
|
||||
function updateSliderValue(id, value) {
|
||||
// Update nilai penting di object global
|
||||
importanceValues[id] = parseInt(value);
|
||||
|
||||
// Update tampilan label tingkat kepentingan
|
||||
const importanceDisplay = document.getElementById(`importance_value_${id}`);
|
||||
if (importanceDisplay) {
|
||||
importanceDisplay.textContent = importanceLabels[value];
|
||||
}
|
||||
|
||||
// Hitung ulang bobot dan perbarui tampilan
|
||||
calculateAndUpdateWeights();
|
||||
}
|
||||
|
||||
// Fungsi untuk menghitung bobot berdasarkan tingkat kepentingan
|
||||
function calculateAndUpdateWeights() {
|
||||
let totalImportance = 0;
|
||||
Object.values(importanceValues).forEach(value => {
|
||||
totalImportance += value;
|
||||
});
|
||||
|
||||
// Hitung bobot berdasarkan proporsi kepentingan
|
||||
const weights = {};
|
||||
let totalWeight = 0;
|
||||
|
||||
if (totalImportance > 0) {
|
||||
// Hitung berdasarkan proporsi
|
||||
Object.keys(importanceValues).forEach(id => {
|
||||
weights[id] = Math.floor((importanceValues[id] / totalImportance) * 100);
|
||||
totalWeight += weights[id];
|
||||
});
|
||||
|
||||
// Distribusikan sisa persen
|
||||
if (totalWeight < 100) {
|
||||
// Urutkan ID berdasarkan tingkat kepentingan (tertinggi dulu)
|
||||
const sortedIds = Object.keys(importanceValues).sort((a, b) =>
|
||||
importanceValues[b] - importanceValues[a]
|
||||
);
|
||||
|
||||
// Distribusikan sisa ke nilai tertinggi
|
||||
let remainder = 100 - totalWeight;
|
||||
for (let i = 0; i < remainder; i++) {
|
||||
weights[sortedIds[i % sortedIds.length]]++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Distribusi merata jika semua nilai penting adalah 0
|
||||
const equalWeight = Math.floor(100 / Object.keys(importanceValues).length);
|
||||
let remainder = 100 - (equalWeight * Object.keys(importanceValues).length);
|
||||
|
||||
Object.keys(importanceValues).forEach((id, index) => {
|
||||
weights[id] = equalWeight + (index < remainder ? 1 : 0);
|
||||
});
|
||||
}
|
||||
|
||||
// Update tampilan bobot dan input tersembunyi
|
||||
Object.keys(weights).forEach(id => {
|
||||
const weightDisplay = document.getElementById(`weight_display_${id}`);
|
||||
const hiddenInput = document.getElementById(`hidden_weight_${id}`);
|
||||
|
||||
if (weightDisplay) {
|
||||
weightDisplay.textContent = `${weights[id]}%`;
|
||||
|
||||
// Terapkan kode warna berdasarkan nilai bobot
|
||||
if (weights[id] >= 30) {
|
||||
weightDisplay.className = 'text-sm font-medium text-green-600 dark:text-green-400';
|
||||
} else if (weights[id] >= 15) {
|
||||
weightDisplay.className = 'text-sm font-medium text-blue-600 dark:text-blue-400';
|
||||
} else {
|
||||
weightDisplay.className = 'text-sm font-medium text-gray-700 dark:text-gray-300';
|
||||
}
|
||||
}
|
||||
|
||||
if (hiddenInput) {
|
||||
hiddenInput.value = weights[id];
|
||||
}
|
||||
});
|
||||
|
||||
// Update indikator total bobot
|
||||
const totalWeightIndicator = document.getElementById('total_weight_indicator');
|
||||
if (totalWeightIndicator) {
|
||||
const calculatedTotal = Object.values(weights).reduce((sum, w) => sum + w, 0);
|
||||
totalWeightIndicator.textContent = `Total: ${calculatedTotal}%`;
|
||||
|
||||
if (calculatedTotal === 100) {
|
||||
totalWeightIndicator.classList.remove('bg-red-100', 'text-red-800', 'dark:bg-red-900', 'dark:text-red-300');
|
||||
totalWeightIndicator.classList.add('bg-green-100', 'text-green-800', 'dark:bg-green-900', 'dark:text-green-300');
|
||||
} else {
|
||||
totalWeightIndicator.classList.remove('bg-green-100', 'text-green-800', 'dark:bg-green-900', 'dark:text-green-300');
|
||||
totalWeightIndicator.classList.add('bg-red-100', 'text-red-800', 'dark:bg-red-900', 'dark:text-red-300');
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(weights).reduce((sum, w) => sum + w, 0) === 100;
|
||||
}
|
||||
|
||||
// Fungsi untuk cek apakah halaman ini hasil dari submit form
|
||||
function checkIfSearchResults() {
|
||||
return hasSearchResults;
|
||||
}
|
||||
|
||||
// Update fungsi showSection untuk memanggil updateStepProgress
|
||||
function showSection1() {
|
||||
document.getElementById('section1').style.display = 'block';
|
||||
document.getElementById('section2').style.display = 'none';
|
||||
document.getElementById('section3').style.display = 'none';
|
||||
updateStepperVisually(1);
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
function showSection2() {
|
||||
document.getElementById('section1').style.display = 'none';
|
||||
document.getElementById('section2').style.display = 'block';
|
||||
document.getElementById('section3').style.display = 'none';
|
||||
updateStepperVisually(2);
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
function showSection3() {
|
||||
document.getElementById('section1').style.display = 'none';
|
||||
document.getElementById('section2').style.display = 'none';
|
||||
document.getElementById('section3').style.display = 'block';
|
||||
updateStepperVisually(3);
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
// Fungsi untuk memperbarui tampilan stepper
|
||||
function updateStepperVisually(step) {
|
||||
// Ambil semua step-item dari stepper
|
||||
const stepItems = document.querySelectorAll('.step-item');
|
||||
|
||||
stepItems.forEach((item, index) => {
|
||||
const stepNum = index + 1;
|
||||
const circleDiv = item.querySelector('.w-10');
|
||||
const textSpan = item.querySelector('.text-sm');
|
||||
|
||||
if (stepNum <= step) {
|
||||
// Langkah aktif atau sudah dilalui
|
||||
circleDiv.classList.remove('bg-gray-200', 'dark:bg-gray-700', 'text-gray-600', 'dark:text-gray-400');
|
||||
circleDiv.classList.add('bg-blue-600', 'text-white');
|
||||
|
||||
textSpan.classList.remove('text-gray-500', 'dark:text-gray-400');
|
||||
textSpan.classList.add('text-blue-600', 'dark:text-blue-400');
|
||||
} else {
|
||||
// Langkah yang belum aktif
|
||||
circleDiv.classList.remove('bg-blue-600', 'text-white');
|
||||
circleDiv.classList.add('bg-gray-200', 'dark:bg-gray-700', 'text-gray-600', 'dark:text-gray-400');
|
||||
|
||||
textSpan.classList.remove('text-blue-600', 'dark:text-blue-400');
|
||||
textSpan.classList.add('text-gray-500', 'dark:text-gray-400');
|
||||
}
|
||||
|
||||
// Update status aktif di class
|
||||
if (stepNum <= step) {
|
||||
item.classList.add('active');
|
||||
} else {
|
||||
item.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Fungsi inisialisasi yang akan dipanggil ketika DOM selesai dimuat
|
||||
function initializeApp() {
|
||||
try {
|
||||
console.log('DOM Content Loaded.');
|
||||
|
||||
// Ambil data dari atribut script
|
||||
const scriptTag = document.querySelector('script[src*="cafe-search.js"]');
|
||||
if (scriptTag) {
|
||||
hasSearchResults = scriptTag.getAttribute('data-has-search-results') === 'true';
|
||||
currentStep = parseInt(scriptTag.getAttribute('data-current-step') || '1');
|
||||
}
|
||||
|
||||
// Inisialisasi nilai penting dan bobot dari URL jika ada
|
||||
const sliders = document.querySelectorAll('.importance-slider');
|
||||
sliders.forEach(slider => {
|
||||
const id = slider.dataset.id;
|
||||
const requestWeight = slider.dataset.requestWeight;
|
||||
|
||||
// Jika ada bobot dari request
|
||||
if (requestWeight) {
|
||||
// Cari nilai importance yang sesuai dengan bobot
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
importanceValues[id] = i;
|
||||
calculateAndUpdateWeights();
|
||||
const weight = document.getElementById(`hidden_weight_${id}`).value;
|
||||
if (parseInt(weight) === parseInt(requestWeight)) {
|
||||
slider.value = i;
|
||||
updateSliderValue(id, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default nilai awal
|
||||
const defaultValue = slider.value;
|
||||
importanceValues[id] = parseInt(defaultValue);
|
||||
}
|
||||
});
|
||||
|
||||
// Update semua bobot
|
||||
calculateAndUpdateWeights();
|
||||
|
||||
// Tampilkan section sesuai langkah
|
||||
if (hasSearchResults) {
|
||||
showSection3();
|
||||
} else if (currentStep === 2) {
|
||||
showSection2();
|
||||
} else {
|
||||
showSection1();
|
||||
}
|
||||
|
||||
// Form submission validation - ensure weights sum to 100%
|
||||
const smartForm = document.getElementById('smartForm');
|
||||
if (smartForm) {
|
||||
smartForm.addEventListener('submit', function(event) {
|
||||
if (!calculateAndUpdateWeights()) {
|
||||
event.preventDefault();
|
||||
alert('Total bobot harus 100%. Silakan sesuaikan tingkat kepentingan.');
|
||||
showSection1();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Terjadi error saat inisialisasi halaman:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Inisialisasi saat DOM selesai dimuat
|
||||
document.addEventListener('DOMContentLoaded', initializeApp);
|
|
@ -0,0 +1,23 @@
|
|||
// Mencegah papan ketikan (keyboard) muncul saat pengguna menyentuh area yang bukan input/textarea
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('touchstart', function(e) {
|
||||
if (e.target.tagName !== 'INPUT' &&
|
||||
e.target.tagName !== 'TEXTAREA' &&
|
||||
!e.target.isContentEditable) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, { passive: false });
|
||||
|
||||
// Menambahkan atribut readonly ke elemen date input untuk mencegah keyboard muncul
|
||||
const dateInputs = document.querySelectorAll('input[type="date"]');
|
||||
dateInputs.forEach(input => {
|
||||
input.setAttribute('readonly', 'readonly');
|
||||
// Tambahkan event listener untuk menghapus readonly saat diklik
|
||||
input.addEventListener('mousedown', function() {
|
||||
this.removeAttribute('readonly');
|
||||
});
|
||||
input.addEventListener('blur', function() {
|
||||
this.setAttribute('readonly', 'readonly');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,80 @@
|
|||
// Inisialisasi Leaflet Map untuk halaman tambah kafe
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Pastikan elemen peta ada
|
||||
const mapElement = document.getElementById('map');
|
||||
if (!mapElement) {
|
||||
console.error('Elemen peta tidak ditemukan!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Menginisialisasi peta Leaflet...');
|
||||
|
||||
// Koordinat default (Alun-Alun Jember)
|
||||
const defaultLat = -8.1722;
|
||||
const defaultLng = 113.6982;
|
||||
|
||||
try {
|
||||
// Inisialisasi peta
|
||||
const mymap = L.map('map').setView([defaultLat, defaultLng], 13);
|
||||
|
||||
// Tambahkan layer peta
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(mymap);
|
||||
|
||||
// Inisialisasi marker
|
||||
let marker;
|
||||
|
||||
// Fungsi untuk menambahkan marker
|
||||
function addMarker(lat, lng) {
|
||||
// Hapus marker sebelumnya jika ada
|
||||
if (marker) {
|
||||
mymap.removeLayer(marker);
|
||||
}
|
||||
|
||||
// Tambahkan marker baru
|
||||
marker = L.marker([lat, lng]).addTo(mymap);
|
||||
|
||||
// Update formulir
|
||||
document.getElementById('latitude').value = lat;
|
||||
document.getElementById('longitude').value = lng;
|
||||
document.getElementById('koordinat').value = lat + ', ' + lng;
|
||||
}
|
||||
|
||||
// Event listener untuk klik pada peta
|
||||
mymap.on('click', function(e) {
|
||||
console.log('Peta diklik pada:', e.latlng);
|
||||
addMarker(e.latlng.lat, e.latlng.lng);
|
||||
});
|
||||
|
||||
// Event listener untuk input koordinat manual
|
||||
const koordinatInput = document.getElementById('koordinat');
|
||||
if (koordinatInput) {
|
||||
koordinatInput.addEventListener('change', function() {
|
||||
const koordinat = this.value.split(',');
|
||||
if (koordinat.length === 2) {
|
||||
const lat = parseFloat(koordinat[0].trim());
|
||||
const lng = parseFloat(koordinat[1].trim());
|
||||
if (!isNaN(lat) && !isNaN(lng)) {
|
||||
console.log('Mengubah view peta ke koordinat:', lat, lng);
|
||||
mymap.setView([lat, lng], 13);
|
||||
addMarker(lat, lng);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Perbaiki ukuran peta
|
||||
setTimeout(function() {
|
||||
mymap.invalidateSize();
|
||||
console.log('Ukuran peta diperbaiki');
|
||||
}, 100);
|
||||
|
||||
console.log('Peta berhasil diinisialisasi');
|
||||
} catch (error) {
|
||||
console.error('Terjadi kesalahan saat menginisialisasi peta:', error);
|
||||
mapElement.innerHTML = '<div style="text-align: center; padding: 20px; color: red;">Terjadi kesalahan saat memuat peta: ' + error.message + '</div>';
|
||||
}
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
.leaflet-control-geocoder{border-radius:4px;background:#fff;min-width:26px;min-height:26px}.leaflet-touch .leaflet-control-geocoder{min-width:30px;min-height:30px}.leaflet-control-geocoder a,.leaflet-control-geocoder .leaflet-control-geocoder-icon{border-bottom:none;display:inline-block}.leaflet-control-geocoder .leaflet-control-geocoder-alternatives a{width:inherit;height:inherit;line-height:inherit}.leaflet-control-geocoder a:hover,.leaflet-control-geocoder .leaflet-control-geocoder-icon:hover{border-bottom:none;display:inline-block}.leaflet-control-geocoder-form{display:none;vertical-align:middle}.leaflet-control-geocoder-expanded .leaflet-control-geocoder-form{display:inline-block}.leaflet-control-geocoder-form input{font-size:120%;border:0;background-color:transparent;width:246px}.leaflet-control-geocoder-icon{border-radius:4px;width:26px;height:26px;border:none;background-color:#fff;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12.2 13l3.4 6.6c.6 1.1 2.5-.4 2-1.2l-4-6.2z'/%3E%3Ccircle cx='10.8' cy='8.9' r='3.9' fill='none' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center;cursor:pointer}.leaflet-touch .leaflet-control-geocoder-icon{width:30px;height:30px}.leaflet-control-geocoder-throbber .leaflet-control-geocoder-icon{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' stroke='%23000' stroke-linecap='round' stroke-width='1.6' viewBox='0 0 24 24'%3E%3Cdefs/%3E%3Cg%3E%3Cpath stroke-opacity='.1' d='M14 8.4l3-5'/%3E%3Cpath stroke-opacity='.2' d='M15.6 10l5-3'/%3E%3Cpath stroke-opacity='.3' d='M16.2 12H22'/%3E%3Cpath stroke-opacity='.4' d='M15.6 14l5 3m-6.5-1.4l2.9 5'/%3E%3Cpath stroke-opacity='.5' d='M12 16.2V22m-2-6.4l-3 5'/%3E%3Cpath stroke-opacity='.6' d='M8.4 14l-5 3'/%3E%3Cpath stroke-opacity='.7' d='M7.8 12H2'/%3E%3Cpath stroke-opacity='.8' d='M8.4 10l-5-3'/%3E%3Cpath stroke-opacity='.9' d='M10 8.4l-3-5'/%3E%3Cpath d='M12 7.8V2'/%3E%3CanimateTransform attributeName='transform' calcMode='discrete' dur='1s' repeatCount='indefinite' type='rotate' values='0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12'/%3E%3C/g%3E%3C/svg%3E")}.leaflet-control-geocoder-form-no-error{display:none}.leaflet-control-geocoder-form input:focus{outline:none}.leaflet-control-geocoder-form button{display:none}.leaflet-control-geocoder-error{margin-top:8px;margin-left:8px;display:block;color:#444}.leaflet-control-geocoder-alternatives{display:block;width:272px;list-style:none;padding:0;margin:0}.leaflet-control-geocoder-alternatives-minimized{display:none;height:0}.leaflet-control-geocoder-alternatives li{white-space:nowrap;display:block;overflow:hidden;padding:5px 8px;text-overflow:ellipsis;border-bottom:1px solid #ccc;cursor:pointer}.leaflet-control-geocoder-alternatives li a,.leaflet-control-geocoder-alternatives li a:hover{width:inherit;height:inherit;line-height:inherit;background:inherit;border-radius:inherit;text-align:left}.leaflet-control-geocoder-alternatives li:last-child{border-bottom:none}.leaflet-control-geocoder-alternatives li:hover,.leaflet-control-geocoder-selected{background-color:#f5f5f5}.leaflet-control-geocoder-address-context{color:#666}
|
|
@ -0,0 +1,161 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocodingResult } from './geocoders/api';
|
||||
export interface GeocoderControlOptions extends L.ControlOptions {
|
||||
/**
|
||||
* Collapse control unless hovered/clicked
|
||||
*/
|
||||
collapsed: boolean;
|
||||
/**
|
||||
* How to expand a collapsed control: `touch` or `click` or `hover`
|
||||
*/
|
||||
expand: 'touch' | 'click' | 'hover';
|
||||
/**
|
||||
* Placeholder text for text input
|
||||
*/
|
||||
placeholder: string;
|
||||
/**
|
||||
* Message when no result found / geocoding error occurs
|
||||
*/
|
||||
errorMessage: string;
|
||||
/**
|
||||
* Accessibility label for the search icon used by screen readers
|
||||
*/
|
||||
iconLabel: string;
|
||||
/**
|
||||
* Object to perform the actual geocoding queries
|
||||
*/
|
||||
geocoder?: IGeocoder;
|
||||
/**
|
||||
* Immediately show the unique result without prompting for alternatives
|
||||
*/
|
||||
showUniqueResult: boolean;
|
||||
/**
|
||||
* Show icons for geocoding results (if available); supported by Nominatim
|
||||
*/
|
||||
showResultIcons: boolean;
|
||||
/**
|
||||
* Minimum number characters before suggest functionality is used (if available from geocoder)
|
||||
*/
|
||||
suggestMinLength: number;
|
||||
/**
|
||||
* Number of milliseconds after typing stopped before suggest functionality is used (if available from geocoder)
|
||||
*/
|
||||
suggestTimeout: number;
|
||||
/**
|
||||
* Initial query string for text input
|
||||
*/
|
||||
query: string;
|
||||
/**
|
||||
* Minimum number of characters in search text before performing a query
|
||||
*/
|
||||
queryMinLength: number;
|
||||
/**
|
||||
* Whether to mark a geocoding result on the map by default
|
||||
*/
|
||||
defaultMarkGeocode: boolean;
|
||||
}
|
||||
/**
|
||||
* Event is fired when selecting a geocode result.
|
||||
* By default, the control will center the map on it and place a marker at its location.
|
||||
* To remove the control's default handler for marking a result, set {@link GeocoderControlOptions.defaultMarkGeocode} to `false`.
|
||||
*/
|
||||
export type MarkGeocodeEvent = {
|
||||
geocode: GeocodingResult;
|
||||
};
|
||||
export type MarkGeocodeEventHandlerFn = (event: MarkGeocodeEvent) => void;
|
||||
/**
|
||||
* Event is fired before invoking {@link IGeocoder.geocode} (or {@link IGeocoder.suggest}).
|
||||
* The event data contains the query string as `input`.
|
||||
*/
|
||||
export type StartGeocodeEvent = {
|
||||
input: string;
|
||||
};
|
||||
export type StartGeocodeEventHandlerFn = (event: StartGeocodeEvent) => void;
|
||||
/**
|
||||
* Event is fired before after receiving results from {@link IGeocoder.geocode} (or {@link IGeocoder.suggest}).
|
||||
* The event data contains the query string as `input` and the geocoding `results`.
|
||||
*/
|
||||
export type FinishGeocodeEvent = {
|
||||
input: string;
|
||||
results: GeocodingResult[];
|
||||
};
|
||||
export type FinishGeocodeEventHandlerFn = (event: FinishGeocodeEvent) => void;
|
||||
declare module 'leaflet' {
|
||||
interface Evented {
|
||||
on(type: 'markgeocode', fn: MarkGeocodeEventHandlerFn, context?: any): this;
|
||||
on(type: 'startgeocode', fn: StartGeocodeEventHandlerFn, context?: any): this;
|
||||
on(type: 'startsuggest', fn: StartGeocodeEventHandlerFn, context?: any): this;
|
||||
on(type: 'finishsuggest', fn: FinishGeocodeEventHandlerFn, context?: any): this;
|
||||
on(type: 'finishgeocode', fn: FinishGeocodeEventHandlerFn, context?: any): this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Leaflet mixins https://leafletjs.com/reference-1.7.1.html#class-includes
|
||||
* for TypeScript https://www.typescriptlang.org/docs/handbook/mixins.html
|
||||
* @internal
|
||||
*/
|
||||
declare class EventedControl {
|
||||
constructor(...args: any[]);
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface EventedControl extends L.Control, L.Evented {
|
||||
}
|
||||
/**
|
||||
* This is the geocoder control. It works like any other [Leaflet control](https://leafletjs.com/reference.html#control), and is added to the map.
|
||||
*/
|
||||
export declare class GeocoderControl extends EventedControl {
|
||||
options: GeocoderControlOptions;
|
||||
private _alts;
|
||||
private _container;
|
||||
private _errorElement;
|
||||
private _geocodeMarker;
|
||||
private _input;
|
||||
private _lastGeocode;
|
||||
private _map;
|
||||
private _preventBlurCollapse;
|
||||
private _requestCount;
|
||||
private _results;
|
||||
private _selection;
|
||||
private _suggestTimeout;
|
||||
/**
|
||||
* Instantiates a geocoder control (to be invoked using `new`)
|
||||
* @param options the options
|
||||
*/
|
||||
constructor(options?: Partial<GeocoderControlOptions>);
|
||||
addThrobberClass(): void;
|
||||
removeThrobberClass(): void;
|
||||
/**
|
||||
* Returns the container DOM element for the control and add listeners on relevant map events.
|
||||
* @param map the map instance
|
||||
* @see https://leafletjs.com/reference.html#control-onadd
|
||||
*/
|
||||
onAdd(map: L.Map): HTMLDivElement;
|
||||
/**
|
||||
* Sets the query string on the text input
|
||||
* @param string the query string
|
||||
*/
|
||||
setQuery(string: string): this;
|
||||
private _geocodeResult;
|
||||
/**
|
||||
* Marks a geocoding result on the map
|
||||
* @param result the geocoding result
|
||||
*/
|
||||
markGeocode(event: MarkGeocodeEvent): this;
|
||||
private _geocode;
|
||||
private _geocodeResultSelected;
|
||||
private _toggle;
|
||||
private _expand;
|
||||
private _collapse;
|
||||
private _clearResults;
|
||||
private _createAlt;
|
||||
private _keydown;
|
||||
private _change;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link GeocoderControl}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function geocoder(options?: Partial<GeocoderControlOptions>): GeocoderControl;
|
||||
export {};
|
|
@ -0,0 +1,77 @@
|
|||
import * as L from 'leaflet';
|
||||
/**
|
||||
* An object that represents a result from a geocoding query
|
||||
*/
|
||||
export interface GeocodingResult {
|
||||
/**
|
||||
* Name of found location
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The bounds of the location
|
||||
*/
|
||||
bbox: L.LatLngBounds;
|
||||
/**
|
||||
* The center coordinate of the location
|
||||
*/
|
||||
center: L.LatLng;
|
||||
/**
|
||||
* URL for icon representing result; optional
|
||||
*/
|
||||
icon?: string;
|
||||
/**
|
||||
* HTML formatted representation of the name
|
||||
*/
|
||||
html?: string;
|
||||
/**
|
||||
* Additional properties returned by the geocoder
|
||||
*/
|
||||
properties?: any;
|
||||
}
|
||||
/**
|
||||
* An interface implemented to respond to geocoding queries
|
||||
*/
|
||||
export interface IGeocoder {
|
||||
/**
|
||||
* Performs a geocoding query and returns the results as promise
|
||||
* @param query the query
|
||||
*/
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
/**
|
||||
* Performs a geocoding query suggestion (this happens while typing) and returns the results as promise
|
||||
* @param query the query
|
||||
*/
|
||||
suggest?(query: string): Promise<GeocodingResult[]>;
|
||||
/**
|
||||
* Performs a reverse geocoding query and returns the results as promise
|
||||
* @param location the coordinate to reverse geocode
|
||||
* @param scale the map scale possibly used for reverse geocoding
|
||||
*/
|
||||
reverse?(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
}
|
||||
export interface GeocoderOptions {
|
||||
/**
|
||||
* URL of the service
|
||||
*/
|
||||
serviceUrl: string;
|
||||
/**
|
||||
* Additional URL parameters (strings) that will be added to geocoding requests
|
||||
*/
|
||||
geocodingQueryParams?: Record<string, unknown>;
|
||||
/**
|
||||
* Additional URL parameters (strings) that will be added to reverse geocoding requests
|
||||
*/
|
||||
reverseQueryParams?: Record<string, unknown>;
|
||||
/**
|
||||
* API key to use this service
|
||||
*/
|
||||
apiKey?: string;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export declare function geocodingParams(options: GeocoderOptions, params: Record<string, unknown>): Record<string, unknown>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export declare function reverseParams(options: GeocoderOptions, params: Record<string, unknown>): Record<string, unknown>;
|
|
@ -0,0 +1,47 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface ArcGisOptions extends GeocoderOptions {
|
||||
}
|
||||
/**
|
||||
* Implementation of the [ArcGIS geocoder](https://developers.arcgis.com/features/geocoding/)
|
||||
*/
|
||||
export declare class ArcGis implements IGeocoder {
|
||||
options: ArcGisOptions;
|
||||
constructor(options?: Partial<ArcGisOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
suggest(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link ArcGis}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function arcgis(options?: Partial<ArcGisOptions>): ArcGis;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface ArcGisResponse {
|
||||
spatialReference: {
|
||||
wkid: number;
|
||||
latestWkid: number;
|
||||
};
|
||||
candidates: Candidate[];
|
||||
}
|
||||
interface Candidate {
|
||||
address: string;
|
||||
location: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
score: number;
|
||||
attributes: {
|
||||
Addr_Type: string;
|
||||
};
|
||||
extent: {
|
||||
xmin: number;
|
||||
ymin: number;
|
||||
xmax: number;
|
||||
ymax: number;
|
||||
};
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,84 @@
|
|||
import * as L from 'leaflet';
|
||||
import { GeocodingResult, IGeocoder } from './api';
|
||||
export interface AzureMapsOptions {
|
||||
apiKey: string;
|
||||
serviceUrl: string;
|
||||
}
|
||||
/**
|
||||
* Implementation of [Azure Maps Geocoding](https://www.microsoft.com/en-us/maps/azure/location-services/geocoding)
|
||||
*
|
||||
* https://learn.microsoft.com/en-us/rest/api/maps/search?view=rest-maps-1.0
|
||||
*/
|
||||
export declare class AzureMaps implements IGeocoder {
|
||||
private options;
|
||||
constructor(options: Partial<AzureMapsOptions>);
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address?view=rest-maps-1.0&tabs=HTTP
|
||||
*/
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* https://learn.microsoft.com/en-us/rest/api/maps/search/get-search-address-reverse?view=rest-maps-1.0&tabs=HTTP
|
||||
*/
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Azure}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function azure(options: AzureMapsOptions): AzureMaps;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface AzureMapsResponse {
|
||||
summary: Summary;
|
||||
results: Result[];
|
||||
}
|
||||
interface Result {
|
||||
type: string;
|
||||
id: string;
|
||||
score: number;
|
||||
address: Address;
|
||||
position: Position;
|
||||
viewport: Viewport;
|
||||
entryPoints: EntryPoint[];
|
||||
}
|
||||
interface Address {
|
||||
streetNumber: string;
|
||||
streetName: string;
|
||||
municipalitySubdivision: string;
|
||||
municipality: string;
|
||||
countrySecondarySubdivision: string;
|
||||
countryTertiarySubdivision: string;
|
||||
countrySubdivisionCode: string;
|
||||
postalCode: string;
|
||||
extendedPostalCode: string;
|
||||
countryCode: string;
|
||||
country: string;
|
||||
countryCodeISO3: string;
|
||||
freeformAddress: string;
|
||||
countrySubdivisionName: string;
|
||||
}
|
||||
interface EntryPoint {
|
||||
type: string;
|
||||
position: Position;
|
||||
}
|
||||
interface Position {
|
||||
lat: number;
|
||||
lon: number;
|
||||
}
|
||||
interface Viewport {
|
||||
topLeftPoint: Position;
|
||||
btmRightPoint: Position;
|
||||
}
|
||||
interface Summary {
|
||||
query: string;
|
||||
queryType: string;
|
||||
queryTime: number;
|
||||
numResults: number;
|
||||
offset: number;
|
||||
totalResults: number;
|
||||
fuzzyLevel: number;
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,22 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface BingOptions extends GeocoderOptions {
|
||||
}
|
||||
/**
|
||||
* Implementation of the [Bing Locations API](https://docs.microsoft.com/en-us/bingmaps/rest-services/locations/)
|
||||
*
|
||||
* Bing Maps for Enterprise is deprecated and will be retired.
|
||||
* Free (Basic) account customers can continue to use Bing Maps for Enterprise services until June 30th, 2025.
|
||||
* Enterprise account customers can continue to use Bing Maps for Enterprise services until June 30th, 2028.
|
||||
*/
|
||||
export declare class Bing implements IGeocoder {
|
||||
options: BingOptions;
|
||||
constructor(options?: Partial<BingOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Bing}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function bing(options?: Partial<BingOptions>): Bing;
|
|
@ -0,0 +1,52 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
/**
|
||||
* Implementation of the [Google Geocoding API](https://developers.google.com/maps/documentation/geocoding/)
|
||||
*/
|
||||
export interface GoogleOptions extends GeocoderOptions {
|
||||
}
|
||||
export declare class Google implements IGeocoder {
|
||||
options: GoogleOptions;
|
||||
constructor(options?: Partial<GoogleOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Google}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function google(options?: Partial<GoogleOptions>): Google;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface GoogleResponse {
|
||||
results: Result[];
|
||||
status: string;
|
||||
}
|
||||
interface Result {
|
||||
address_components: AddressComponent[];
|
||||
formatted_address: string;
|
||||
geometry: Geometry;
|
||||
place_id: string;
|
||||
types: string[];
|
||||
}
|
||||
interface AddressComponent {
|
||||
long_name: string;
|
||||
short_name: string;
|
||||
types: string[];
|
||||
}
|
||||
interface Geometry {
|
||||
bounds: Bounds;
|
||||
location: Location;
|
||||
location_type: string;
|
||||
viewport: Bounds;
|
||||
}
|
||||
interface Bounds {
|
||||
northeast: Location;
|
||||
southwest: Location;
|
||||
}
|
||||
interface Location {
|
||||
lat: number;
|
||||
lng: number;
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,119 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface HereOptions extends GeocoderOptions {
|
||||
/**
|
||||
* Use `apiKey` and the new `HEREv2` geocoder
|
||||
* @deprecated
|
||||
*/
|
||||
app_id: string;
|
||||
/**
|
||||
* Use `apiKey` and the new `HEREv2` geocoder
|
||||
* @deprecated
|
||||
*/
|
||||
app_code: string;
|
||||
reverseGeocodeProxRadius?: any;
|
||||
apiKey: string;
|
||||
maxResults: number;
|
||||
}
|
||||
/**
|
||||
* Implementation of the [HERE Geocoder API](https://developer.here.com/documentation/geocoder/topics/introduction.html)
|
||||
*/
|
||||
export declare class HERE implements IGeocoder {
|
||||
options: HereOptions;
|
||||
constructor(options?: Partial<HereOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
getJSON(url: string, params: any): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* Implementation of the new [HERE Geocoder API](https://developer.here.com/documentation/geocoding-search-api/api-reference-swagger.html)
|
||||
*/
|
||||
export declare class HEREv2 implements IGeocoder {
|
||||
options: HereOptions;
|
||||
constructor(options?: Partial<HereOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
getJSON(url: string, params: any): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link HERE}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function here(options?: Partial<HereOptions>): HERE | HEREv2;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface HEREv2Response {
|
||||
items: Item[];
|
||||
}
|
||||
interface Item {
|
||||
title: string;
|
||||
id: string;
|
||||
ontologyId: string;
|
||||
resultType: string;
|
||||
address: Address;
|
||||
mapView?: MapView;
|
||||
position: Position;
|
||||
access: Position[];
|
||||
distance: number;
|
||||
categories: Category[];
|
||||
references: Reference[];
|
||||
foodTypes: Category[];
|
||||
contacts: Contact[];
|
||||
openingHours: OpeningHour[];
|
||||
}
|
||||
interface MapView {
|
||||
east: number;
|
||||
north: number;
|
||||
south: number;
|
||||
west: number;
|
||||
}
|
||||
interface Position {
|
||||
lat: number;
|
||||
lng: number;
|
||||
}
|
||||
interface Address {
|
||||
label: string;
|
||||
countryCode: string;
|
||||
countryName: string;
|
||||
stateCode: string;
|
||||
state: string;
|
||||
county: string;
|
||||
city: string;
|
||||
district: string;
|
||||
street: string;
|
||||
postalCode: string;
|
||||
houseNumber: string;
|
||||
}
|
||||
interface Category {
|
||||
id: string;
|
||||
name: string;
|
||||
primary?: boolean;
|
||||
}
|
||||
interface Contact {
|
||||
phone: Email[];
|
||||
fax: Email[];
|
||||
www: Email[];
|
||||
email: Email[];
|
||||
}
|
||||
interface Email {
|
||||
value: string;
|
||||
}
|
||||
interface OpeningHour {
|
||||
text: string[];
|
||||
isOpen: boolean;
|
||||
structured: Structured[];
|
||||
}
|
||||
interface Structured {
|
||||
start: string;
|
||||
duration: string;
|
||||
recurrence: string;
|
||||
}
|
||||
interface Reference {
|
||||
supplier: Supplier;
|
||||
id: string;
|
||||
}
|
||||
interface Supplier {
|
||||
id: string;
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,16 @@
|
|||
export * from './api';
|
||||
export * from './arcgis';
|
||||
export * from './bing';
|
||||
export * from './azure';
|
||||
export * from './google';
|
||||
export * from './here';
|
||||
export * from './latlng';
|
||||
export * from './mapbox';
|
||||
export * from './mapquest';
|
||||
export * from './neutrino';
|
||||
export * from './nominatim';
|
||||
export * from './open-location-code';
|
||||
export * from './opencage';
|
||||
export * from './pelias';
|
||||
export * from './photon';
|
||||
export * from './what3words';
|
|
@ -0,0 +1,31 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocodingResult } from './api';
|
||||
export interface LatLngOptions {
|
||||
/**
|
||||
* The next geocoder to use for non-supported queries
|
||||
*/
|
||||
next?: IGeocoder;
|
||||
/**
|
||||
* The size in meters used for passing to `LatLng.toBounds`
|
||||
*/
|
||||
sizeInMeters: number;
|
||||
}
|
||||
/**
|
||||
* Parses basic latitude/longitude strings such as `'50.06773 14.37742'`, `'N50.06773 W14.37742'`, `'S 50° 04.064 E 014° 22.645'`, or `'S 50° 4′ 03.828″, W 14° 22′ 38.712″'`
|
||||
* @param query the latitude/longitude string to parse
|
||||
* @returns the parsed latitude/longitude
|
||||
*/
|
||||
export declare function parseLatLng(query: string): L.LatLng | undefined;
|
||||
/**
|
||||
* Parses basic latitude/longitude strings such as `'50.06773 14.37742'`, `'N50.06773 W14.37742'`, `'S 50° 04.064 E 014° 22.645'`, or `'S 50° 4′ 03.828″, W 14° 22′ 38.712″'`
|
||||
*/
|
||||
export declare class LatLng implements IGeocoder {
|
||||
options: LatLngOptions;
|
||||
constructor(options?: Partial<LatLngOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link LatLng}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function latLng(options?: Partial<LatLngOptions>): LatLng;
|
|
@ -0,0 +1,64 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface MapboxOptions extends GeocoderOptions {
|
||||
}
|
||||
/**
|
||||
* Implementation of the [Mapbox Geocoding](https://www.mapbox.com/api-documentation/#geocoding)
|
||||
*/
|
||||
export declare class Mapbox implements IGeocoder {
|
||||
options: MapboxOptions;
|
||||
constructor(options?: Partial<MapboxOptions>);
|
||||
_getProperties(loc: any): {
|
||||
text: any;
|
||||
address: any;
|
||||
};
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
suggest(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
private _parseResults;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Mapbox}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function mapbox(options?: Partial<MapboxOptions>): Mapbox;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface MapboxResponse {
|
||||
type: string;
|
||||
query: string[];
|
||||
features: Feature[];
|
||||
attribution: string;
|
||||
}
|
||||
interface Feature {
|
||||
id: string;
|
||||
type: string;
|
||||
place_type: string[];
|
||||
relevance: number;
|
||||
properties: Properties;
|
||||
text: string;
|
||||
place_name: string;
|
||||
matching_text: string;
|
||||
matching_place_name: string;
|
||||
center: [number, number];
|
||||
bbox?: [number, number, number, number];
|
||||
geometry: Geometry;
|
||||
address: string;
|
||||
context: Context[];
|
||||
}
|
||||
interface Context {
|
||||
id: string;
|
||||
text: string;
|
||||
wikidata?: string;
|
||||
short_code?: string;
|
||||
}
|
||||
interface Geometry {
|
||||
type: string;
|
||||
coordinates: number[];
|
||||
interpolated: boolean;
|
||||
omitted: boolean;
|
||||
}
|
||||
interface Properties {
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,20 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface MapQuestOptions extends GeocoderOptions {
|
||||
}
|
||||
/**
|
||||
* Implementation of the [MapQuest Geocoding API](http://developer.mapquest.com/web/products/dev-services/geocoding-ws)
|
||||
*/
|
||||
export declare class MapQuest implements IGeocoder {
|
||||
options: MapQuestOptions;
|
||||
constructor(options?: Partial<MapQuestOptions>);
|
||||
_formatName(...parts: string[]): string;
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
private _parseResults;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link MapQuest}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function mapQuest(options?: Partial<MapQuestOptions>): MapQuest;
|
|
@ -0,0 +1,20 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface NeutrinoOptions extends GeocoderOptions {
|
||||
userId: string;
|
||||
}
|
||||
/**
|
||||
* Implementation of the [Neutrino API](https://www.neutrinoapi.com/api/geocode-address/)
|
||||
*/
|
||||
export declare class Neutrino implements IGeocoder {
|
||||
options: NeutrinoOptions;
|
||||
constructor(options?: Partial<NeutrinoOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
suggest(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Neutrino}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function neutrino(options?: Partial<NeutrinoOptions>): Neutrino;
|
|
@ -0,0 +1,63 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export type NominatimResponse = NominatimResult[];
|
||||
export interface NominatimResult {
|
||||
place_id: number;
|
||||
licence: string;
|
||||
osm_type: string;
|
||||
osm_id: number;
|
||||
boundingbox: string[];
|
||||
lat: string;
|
||||
lon: string;
|
||||
display_name: string;
|
||||
class?: string;
|
||||
type?: string;
|
||||
importance?: number;
|
||||
icon?: string;
|
||||
address: NominatimAddress;
|
||||
}
|
||||
export interface NominatimAddress {
|
||||
building?: string;
|
||||
city_district?: string;
|
||||
city?: string;
|
||||
country_code?: string;
|
||||
country?: string;
|
||||
county?: string;
|
||||
hamlet?: string;
|
||||
house_number?: string;
|
||||
neighbourhood?: string;
|
||||
postcode?: string;
|
||||
road?: string;
|
||||
state_district?: string;
|
||||
state?: string;
|
||||
suburb?: string;
|
||||
village?: string;
|
||||
}
|
||||
export interface NominatimOptions extends GeocoderOptions {
|
||||
/**
|
||||
* Additional URL parameters (strings) that will be added to geocoding requests; can be used to restrict results to a specific country for example, by providing the [`countrycodes`](https://wiki.openstreetmap.org/wiki/Nominatim#Parameters) parameter to Nominatim
|
||||
*/
|
||||
geocodingQueryParams?: Record<string, unknown>;
|
||||
/**
|
||||
* A function that takes an GeocodingResult as argument and returns an HTML formatted string that represents the result. Default function breaks up address in parts from most to least specific, in attempt to increase readability compared to Nominatim's naming
|
||||
*/
|
||||
htmlTemplate: (r: NominatimResult) => string;
|
||||
}
|
||||
/**
|
||||
* Implementation of the [Nominatim](https://wiki.openstreetmap.org/wiki/Nominatim) geocoder.
|
||||
*
|
||||
* This is the default geocoding service used by the control, unless otherwise specified in the options.
|
||||
*
|
||||
* Unless using your own Nominatim installation, please refer to the [Nominatim usage policy](https://operations.osmfoundation.org/policies/nominatim/).
|
||||
*/
|
||||
export declare class Nominatim implements IGeocoder {
|
||||
options: NominatimOptions;
|
||||
constructor(options?: Partial<NominatimOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Nominatim}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function nominatim(options?: Partial<NominatimOptions>): Nominatim;
|
|
@ -0,0 +1,37 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocodingResult } from './api';
|
||||
export interface OpenLocationCodeOptions {
|
||||
OpenLocationCode: OpenLocationCodeApi;
|
||||
codeLength?: number;
|
||||
}
|
||||
export interface OpenLocationCodeApi {
|
||||
encode(latitude: number, longitude: number, codeLength?: number): string;
|
||||
decode(code: string): CodeArea;
|
||||
}
|
||||
export interface CodeArea {
|
||||
latitudeLo: number;
|
||||
longitudeLo: number;
|
||||
latitudeHi: number;
|
||||
longitudeHi: number;
|
||||
latitudeCenter: number;
|
||||
longitudeCenter: number;
|
||||
codeLength: number;
|
||||
}
|
||||
/**
|
||||
* Implementation of the [Plus codes](https://plus.codes/) (formerly OpenLocationCode) (requires [open-location-code](https://www.npmjs.com/package/open-location-code))
|
||||
*/
|
||||
export declare class OpenLocationCode implements IGeocoder {
|
||||
options: OpenLocationCodeOptions;
|
||||
constructor(options?: Partial<OpenLocationCodeOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<{
|
||||
name: string;
|
||||
center: L.LatLng;
|
||||
bbox: L.LatLngBounds;
|
||||
}[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link OpenLocationCode}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function openLocationCode(options?: Partial<OpenLocationCodeOptions>): OpenLocationCode;
|
|
@ -0,0 +1,16 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface OpenCageOptions extends GeocoderOptions {
|
||||
}
|
||||
/**
|
||||
* Implementation of the [OpenCage Data API](https://opencagedata.com/)
|
||||
*/
|
||||
export declare class OpenCage implements IGeocoder {
|
||||
options: OpenCageOptions;
|
||||
constructor(options?: Partial<OpenCageOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
suggest(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
private _parseResults;
|
||||
}
|
||||
export declare function opencage(options?: Partial<OpenCageOptions>): OpenCage;
|
|
@ -0,0 +1,83 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface PeliasOptions extends GeocoderOptions {
|
||||
}
|
||||
/**
|
||||
* Implementation of the [Pelias](https://pelias.io/), [geocode.earth](https://geocode.earth/) geocoder (formerly Mapzen Search)
|
||||
*/
|
||||
export declare class Pelias implements IGeocoder {
|
||||
options: PeliasOptions;
|
||||
constructor(options?: Partial<PeliasOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
suggest(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
_parseResults(data: any, bboxname: any): GeocodingResult[];
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Pelias}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function pelias(options?: Partial<PeliasOptions>): Pelias;
|
||||
export declare const GeocodeEarth: typeof Pelias;
|
||||
export declare const geocodeEarth: typeof pelias;
|
||||
/**
|
||||
* r.i.p.
|
||||
* @deprecated
|
||||
*/
|
||||
export declare const Mapzen: typeof Pelias;
|
||||
/**
|
||||
* r.i.p.
|
||||
* @deprecated
|
||||
*/
|
||||
export declare const mapzen: typeof pelias;
|
||||
/**
|
||||
* Implementation of the [Openrouteservice](https://openrouteservice.org/dev/#/api-docs/geocode) geocoder
|
||||
*/
|
||||
export declare class Openrouteservice extends Pelias {
|
||||
constructor(options?: Partial<PeliasOptions>);
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Openrouteservice}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function openrouteservice(options?: Partial<PeliasOptions>): Openrouteservice;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type PeliasResponse = GeoJSON.FeatureCollection<GeoJSON.Geometry, Properties> & {
|
||||
geocoding: Geocoding;
|
||||
};
|
||||
interface Properties {
|
||||
id: string;
|
||||
layer: string;
|
||||
source_id: string;
|
||||
name: string;
|
||||
confidence: number;
|
||||
match_type: string;
|
||||
accuracy: string;
|
||||
country: string;
|
||||
country_a: string;
|
||||
region: string;
|
||||
region_a: string;
|
||||
county: string;
|
||||
county_a: string;
|
||||
localadmin: string;
|
||||
locality: string;
|
||||
continent: string;
|
||||
label: string;
|
||||
}
|
||||
interface Geocoding {
|
||||
version: string;
|
||||
attribution: string;
|
||||
query: Query;
|
||||
warnings: string[];
|
||||
engine: Engine;
|
||||
}
|
||||
interface Engine {
|
||||
name: string;
|
||||
author: string;
|
||||
version: string;
|
||||
}
|
||||
interface Query {
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,46 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface PhotonOptions extends GeocoderOptions {
|
||||
reverseUrl: string;
|
||||
nameProperties: string[];
|
||||
htmlTemplate?: (r: any) => string;
|
||||
}
|
||||
/**
|
||||
* Implementation of the [Photon](http://photon.komoot.de/) geocoder
|
||||
*/
|
||||
export declare class Photon implements IGeocoder {
|
||||
options: PhotonOptions;
|
||||
constructor(options?: Partial<PhotonOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
suggest(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(latLng: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
_parseResults(data: GeoJSON.FeatureCollection<GeoJSON.Point>): GeocodingResult[];
|
||||
_decodeFeatureName(f: GeoJSON.Feature): string;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Photon}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function photon(options?: Partial<PhotonOptions>): Photon;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type PhotonResponse = GeoJSON.FeatureCollection<GeoJSON.Geometry, PhotonProperties>;
|
||||
interface PhotonProperties {
|
||||
osm_id: number;
|
||||
osm_type: string;
|
||||
extent?: number[];
|
||||
country: string;
|
||||
osm_key: string;
|
||||
city: string;
|
||||
countrycode: string;
|
||||
osm_value: string;
|
||||
name: string;
|
||||
state: string;
|
||||
type: string;
|
||||
postcode?: string;
|
||||
housenumber?: string;
|
||||
street?: string;
|
||||
district?: string;
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,19 @@
|
|||
import * as L from 'leaflet';
|
||||
import { IGeocoder, GeocoderOptions, GeocodingResult } from './api';
|
||||
export interface What3WordsOptions extends GeocoderOptions {
|
||||
}
|
||||
/**
|
||||
* Implementation of the What3Words service
|
||||
*/
|
||||
export declare class What3Words implements IGeocoder {
|
||||
options: What3WordsOptions;
|
||||
constructor(options: Partial<What3WordsOptions>);
|
||||
geocode(query: string): Promise<GeocodingResult[]>;
|
||||
suggest(query: string): Promise<GeocodingResult[]>;
|
||||
reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]>;
|
||||
}
|
||||
/**
|
||||
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link What3Words}
|
||||
* @param options the options
|
||||
*/
|
||||
export declare function what3words(options: Partial<What3WordsOptions>): What3Words;
|
|
@ -0,0 +1,5 @@
|
|||
import { GeocoderControl as Geocoder, geocoder } from './control';
|
||||
import * as geocoders from './geocoders/index';
|
||||
import './style.css';
|
||||
export default Geocoder;
|
||||
export { Geocoder, geocoder, geocoders };
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* @internal
|
||||
*/
|
||||
export declare function htmlEscape(string?: string): string;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export declare function getJSON<T>(url: string, params: Record<string, unknown>): Promise<T>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export declare function template(str: string, data: Record<string, any>): string;
|
After Width: | Height: | Size: 696 B |