From 638ae703c0f8859bec84d2ec996370bc273410b7 Mon Sep 17 00:00:00 2001 From: MufridaFaraDiani27 Date: Tue, 12 May 2026 15:41:15 +0700 Subject: [PATCH] update sistem analisis sentimen --- .../Auth/RegisteredUserController.php | 40 +- app/Http/Controllers/UserController.php | 123 ++++ app/Http/Middleware/RoleMiddleware.php | 20 + app/Models/User.php | 31 +- bootstrap/app.php | 8 +- ...6_05_12_102259_add_role_to_users_table.php | 25 + package-lock.json | 8 +- .../views/auth/forgot-password.blade.php | 578 +++++++++++++++++- resources/views/kelolauser/index.blade.php | 432 +++++++++++++ resources/views/layouts/app.blade.php | 22 +- resources/views/layouts/sentara.blade.php | 177 ++++-- resources/views/profile/index.blade.php | 149 ++++- routes/web.php | 115 +++- scraper/scraping_pipeline.py | 18 +- 14 files changed, 1594 insertions(+), 152 deletions(-) create mode 100644 app/Http/Controllers/UserController.php create mode 100644 app/Http/Middleware/RoleMiddleware.php create mode 100644 database/migrations/2026_05_12_102259_add_role_to_users_table.php create mode 100644 resources/views/kelolauser/index.blade.php diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php index 0739e2e..c598208 100644 --- a/app/Http/Controllers/Auth/RegisteredUserController.php +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -9,7 +9,6 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; -use Illuminate\Validation\Rules; use Illuminate\View\View; class RegisteredUserController extends Controller @@ -30,9 +29,40 @@ public function create(): View public function store(Request $request): RedirectResponse { $request->validate([ - 'name' => ['required', 'string', 'max:255'], - 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], - 'password' => ['required', 'confirmed', Rules\Password::defaults()], + + 'name' => [ + 'required', + 'string', + 'max:255' + ], + + 'email' => [ + 'required', + 'string', + 'lowercase', + 'email', + 'max:255', + 'unique:users' + ], + + 'password' => [ + 'required', + 'confirmed', + 'string', + 'min:8', + 'regex:/[a-z]/', + 'regex:/[A-Z]/', + 'regex:/[0-9]/', + ], + + ], [ + + 'password.min' => 'Password wajib minimal 8 karakter.', + + 'password.regex' => 'Password harus mengandung huruf besar, huruf kecil, dan angka.', + + 'password.confirmed' => 'Konfirmasi password tidak sesuai.', + ]); $user = User::create([ @@ -47,4 +77,4 @@ public function store(Request $request): RedirectResponse return redirect(route('dashboard', absolute: false)); } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..f2420ea --- /dev/null +++ b/app/Http/Controllers/UserController.php @@ -0,0 +1,123 @@ +search; + + $users = User::when($search, function ($query) use ($search) { + + $query->where('name', 'like', "%{$search}%") + ->orWhere('email', 'like', "%{$search}%"); + + })->paginate(5); + + return view('kelolauser.index', compact('users')); + } + + /* + |-------------------------------------------------------------------------- + | UPDATE USER + |-------------------------------------------------------------------------- + */ + + public function update(Request $request, int $id) + { + $request->validate([ + 'name' => 'required', + 'email' => 'required|email', + 'role' => 'required', + ]); + + $user = User::findOrFail($id); + + $user->update([ + 'name' => $request->name, + 'email' => $request->email, + 'role' => $request->role, + ]); + + return redirect() + ->back() + ->with('success', 'Pengguna berhasil diperbarui.'); + } + + /* + |-------------------------------------------------------------------------- + | RESET PASSWORD + |-------------------------------------------------------------------------- + */ + + public function updatePassword(Request $request, int $id) + { + $request->validate([ + 'password' => [ + 'required', + 'confirmed', + 'min:8', + 'regex:/[a-z]/', + 'regex:/[A-Z]/', + 'regex:/[0-9]/', + ], + ], [ + + 'password.min' => + 'Password minimal 8 karakter.', + + 'password.regex' => + 'Password harus mengandung huruf besar, huruf kecil, dan angka.', + ]); + + $user = User::findOrFail($id); + + $user->update([ + 'password' => Hash::make($request->password) + ]); + + return redirect() + ->back() + ->with('success', 'Password berhasil direset.'); + } + + /* + |-------------------------------------------------------------------------- + | DELETE USER + |-------------------------------------------------------------------------- + */ + + public function destroy(int $id) + { + User::findOrFail($id)->delete(); + + return redirect() + ->back() + ->with('success', 'Pengguna berhasil dihapus.'); + } + + public function store(Request $request) +{ + $request->validate([ + 'name' => 'required|string|max:255', + 'email' => 'required|email|unique:users,email', + 'role' => 'required|in:admin,staff', + 'password' => 'required|min:8|confirmed', + ]); + + User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'role' => $request->role, + 'password' => bcrypt($request->password), + ]); + + return redirect()->route('kelola-pengguna.index') + ->with('success', 'Pengguna berhasil ditambahkan.'); +} +} \ No newline at end of file diff --git a/app/Http/Middleware/RoleMiddleware.php b/app/Http/Middleware/RoleMiddleware.php new file mode 100644 index 0000000..1973ab0 --- /dev/null +++ b/app/Http/Middleware/RoleMiddleware.php @@ -0,0 +1,20 @@ +role !== $role) // ← ganti auth() ke Auth:: + { + abort(403, 'Akses ditolak.'); + } + + return $next($request); + } +} \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php index 749c7b7..80d5096 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,42 +2,26 @@ namespace App\Models; -// use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { - /** @use HasFactory<\Database\Factories\UserFactory> */ use HasFactory, Notifiable; - /** - * The attributes that are mass assignable. - * - * @var list - */ protected $fillable = [ 'name', 'email', 'password', + 'role', // ← tambah ini ]; - /** - * The attributes that should be hidden for serialization. - * - * @var list - */ protected $hidden = [ 'password', 'remember_token', ]; - /** - * Get the attributes that should be cast. - * - * @return array - */ protected function casts(): array { return [ @@ -45,4 +29,15 @@ protected function casts(): array 'password' => 'hashed', ]; } -} + + // ← tambah 2 method ini di bawah casts() + public function isAdmin(): bool + { + return $this->role === 'admin'; + } + + public function isStaff(): bool + { + return $this->role === 'staff'; + } +} \ No newline at end of file diff --git a/bootstrap/app.php b/bootstrap/app.php index c183276..c06db24 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -10,9 +10,11 @@ commands: __DIR__.'/../routes/console.php', health: '/up', ) - ->withMiddleware(function (Middleware $middleware): void { - // + ->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'role' => \App\Http\Middleware\RoleMiddleware::class, + ]); }) ->withExceptions(function (Exceptions $exceptions): void { // - })->create(); + })->create(); \ No newline at end of file diff --git a/database/migrations/2026_05_12_102259_add_role_to_users_table.php b/database/migrations/2026_05_12_102259_add_role_to_users_table.php new file mode 100644 index 0000000..a974423 --- /dev/null +++ b/database/migrations/2026_05_12_102259_add_role_to_users_table.php @@ -0,0 +1,25 @@ +enum('role', ['admin', 'staff'])->default('staff')->after('email'); + }); +} + +public function down() +{ + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('role'); + }); +} +}; diff --git a/package-lock.json b/package-lock.json index c9312c4..7eb4217 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "TugasAkhirFarah-main", + "name": "SistemAnalisisSentimen", "lockfileVersion": 3, "requires": true, "packages": { @@ -1418,6 +1418,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2690,6 +2691,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -3114,6 +3116,7 @@ "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -3234,6 +3237,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -3322,6 +3326,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -3426,6 +3431,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/auth/forgot-password.blade.php index cb32e08..f215bfd 100644 --- a/resources/views/auth/forgot-password.blade.php +++ b/resources/views/auth/forgot-password.blade.php @@ -1,25 +1,559 @@ - -
- {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} + + + + + + Lupa Password - SENTARA + + @vite(['resources/css/app.css','resources/js/app.js']) + + + + + + +
+ +
+
+ +
+ + +
+ + +
+ + + + + + + + + +
+ + +

+ Lupa Password? +

+ +

+ Untuk keamanan akun, pengaturan ulang password hanya dapat dilakukan +
+ oleh Administrator. +

+ + +
+ + + + + + + + + +

+ Silakan hubungi Administrator sistem untuk memperbarui password akun Anda. +

+ +
+ + +
+ + + + + + + + + + +

Cara Memperbarui Password

+ +
+ + +
+ +
1
+ +
+

Hubungi Administrator

+

+ Sampaikan kepada Administrator bahwa Anda lupa password akun SENTARA. +

+
+ +
+ +
+ +
2
+ +
+

Verifikasi Identitas

+

+ Administrator akan memverifikasi identitas Anda sebagai pengguna sistem. +

+
+ +
+ +
+ +
3
+ +
+

Password Baru

+

+ Administrator akan membuat password baru dan memberikannya kepada Anda. +

+
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + +
+ +
+

Kontak Administrator

+

+ Silakan hubungi administrator melalui kontak resmi berikut: +

+
+ +
+ +
+ + +
+ + + + + + + + 0881-0267-1527 + +
+ + +
+ + + + + + + + + + e31231226@student.polije.ac.id + +
+ +
+ +
+ +
+ + + +
- - +
-
- @csrf - - -
- - - -
- -
- - {{ __('Email Password Reset Link') }} - -
-
- + + \ No newline at end of file diff --git a/resources/views/kelolauser/index.blade.php b/resources/views/kelolauser/index.blade.php new file mode 100644 index 0000000..4374bb9 --- /dev/null +++ b/resources/views/kelolauser/index.blade.php @@ -0,0 +1,432 @@ +@extends('layouts.sentara') + +@section('content') + +
+ + {{-- ALERT SUCCESS --}} + @if(session('success')) +
+ {{ session('success') }} +
+ @endif + + {{-- ALERT ERROR --}} + @if(session('error')) +
+ {{ session('error') }} +
+ @endif + + +
+ +
+

Kelola Pengguna

+

Kelola akun pengguna sistem SENTARA.

+
+ + + + +
+ + +
+ + +
+ + + + + + + + + + + + + + + + + + @forelse ($users as $user) + + + + + + + + + + + + + + + + + + + + @empty + + + + + + @endforelse + + + +
NoNamaEmailRoleStatusTerakhir AktifAksi
{{ $loop->iteration }} +
+
+ {{ strtoupper(substr($user->name, 0, 2)) }} +
+ {{ $user->name }} +
+
{{ $user->email }} + @if($user->role == 'admin') + Admin + @else + Staff + @endif + + Aktif + {{ $user->updated_at->format('d M Y, H:i') }} +
+ + + + + + + +
+
+ Data pengguna belum tersedia. +
+ +
+ + +
+

+ Menampilkan {{ $users->firstItem() ?? 0 }} - {{ $users->lastItem() ?? 0 }} + dari {{ $users->total() }} pengguna +

+ {{ $users->links() }} +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +@endsection \ No newline at end of file diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index bb8c38f..b492250 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -86,11 +86,27 @@ class="block hover:text-gray-300"> @endisset + -
- @yield('content') -
+
+ {{-- ALERT SUCCESS --}} + @if(session('success')) +
+ {{ session('success') }} +
+ @endif + + {{-- ALERT ERROR --}} + @if(session('error')) +
+ {{ session('error') }} +
+ @endif + + @yield('content') + +
diff --git a/resources/views/layouts/sentara.blade.php b/resources/views/layouts/sentara.blade.php index 71953a5..d0004cf 100644 --- a/resources/views/layouts/sentara.blade.php +++ b/resources/views/layouts/sentara.blade.php @@ -7,12 +7,14 @@ @vite(['resources/css/app.css','resources/js/app.js']) + + - + -
+
@@ -39,7 +42,9 @@ class="flex items-center gap-4 px-4 py-3 rounded-xl hover:bg-blue-800 transition
- Dashboard Sentimen + + Dashboard Sentimen + @@ -50,7 +55,9 @@ class="flex items-center gap-4 px-4 py-3 rounded-xl hover:bg-blue-800 transition - Data Ulasan + + Data Ulasan + @@ -61,54 +68,103 @@ class="flex items-center gap-4 px-4 py-3 rounded-xl hover:bg-blue-800 transition - Riwayat Analisis + + Riwayat Analisis + + + @if(auth()->user()->role == 'admin') + + + +
+ +
+ + + Kelola Pengguna + +
+ + @endif + - +
@@ -119,11 +175,14 @@ class="w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-100"> -
+
+ @yield('content') @@ -134,28 +193,44 @@ class="md:hidden mb-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow">
+class="hidden fixed inset-0 bg-black/40 flex items-center justify-center z-50 px-4"> -
+
-

+ +
+ + + +
+ + +

Konfirmasi Logout

-

- Apakah Anda ingin keluar? + +

+ Apakah Anda ingin keluar dari sistem?

+
@@ -166,28 +241,48 @@ class="px-4 py-2 bg-red-500 text-white rounded-lg"> - + \ No newline at end of file diff --git a/resources/views/profile/index.blade.php b/resources/views/profile/index.blade.php index 2d513e3..0aa0a67 100644 --- a/resources/views/profile/index.blade.php +++ b/resources/views/profile/index.blade.php @@ -6,14 +6,20 @@
-
-

Profile Saya

+

+ Profile Saya +

+

- Informasi akun pengguna SENTARA + Informasi akun pengguna + + SENTARA +

+
@@ -21,30 +27,53 @@
+
+ {{ strtoupper(substr(Auth::user()->name, 0, 1)) }} +
+
+ +

{{ Auth::user()->name }}

-

- Staff - Dinas Pariwisata Jember + +

+ {{ Auth::user()->role }} - Dinas Pariwisata Jember

- - STAFF + + + + {{ Auth::user()->role }} + +
- 📋 Informasi Akun + + 📋 + + Informasi Akun +
@@ -52,48 +81,101 @@
-
+ +
+ 👤 +
-

Nama Lengkap

+ +

+ Nama Lengkap +

+

{{ Auth::user()->name }}

+
-
+ +
+ ✉️ +
-

Email

+ +

+ Email +

+

{{ Auth::user()->email }}

+
- +
-
- - - + +
+ + + + + + +
-

Role

-

Staff

+ +

+ Role +

+ +

+ {{ Auth::user()->role }} +

+
-
+ +
+ ✔ +
-

Status Akun

- + +

+ Status Akun +

+ + + Aktif + +
@@ -102,22 +184,37 @@
+
- Informasi Sistem + + + + Informasi Sistem +

+ Akun ini digunakan untuk mengakses sistem SENTARA (Sistem Analisis Sentimen Ulasan Dinas Pariwisata Jember). +

+
- © 2025 SENTARA. All rights reserved. - Dinas Pariwisata Jember + + + © 2025 SENTARA. All rights reserved. + + + + Dinas Pariwisata Jember + +
diff --git a/routes/web.php b/routes/web.php index 9055ce0..065e0de 100644 --- a/routes/web.php +++ b/routes/web.php @@ -6,6 +6,8 @@ use App\Http\Controllers\UlasanController; use App\Http\Controllers\AnalisisController; use App\Http\Controllers\RekomendasiController; +use App\Http\Controllers\UserController; + /* |-------------------------------------------------------------------------- @@ -13,7 +15,6 @@ |-------------------------------------------------------------------------- */ -// hanya SATU route '/' Route::get('/', function () { return redirect('/login'); }); @@ -26,48 +27,114 @@ Route::middleware(['auth', 'verified'])->group(function () { - // ✅ DASHBOARD + /* + |-------------------------------------------------------------------------- + | DASHBOARD + |-------------------------------------------------------------------------- + */ + Route::get('/dashboard', [DashboardController::class, 'index']) ->name('dashboard'); - // ✅ PROFILE (ini yang kamu tanya) + /* + |-------------------------------------------------------------------------- + | PROFILE + |-------------------------------------------------------------------------- + */ + Route::get('/profile', function () { return view('profile.index'); })->name('profile'); - // kalau mau aktifkan update: - // Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); - // Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); + /* + |-------------------------------------------------------------------------- + | KELOLA PENGGUNA + |-------------------------------------------------------------------------- + */ + + Route::get('/kelola-pengguna', + [UserController::class, 'index'] + )->name('kelola-pengguna.index'); + + // ✅ TAMBAHAN BARU — route untuk simpan pengguna baru + Route::post('/kelola-pengguna', + [UserController::class, 'store'] + )->name('kelola-pengguna.store'); + + Route::get('/kelola-pengguna/{id}/edit', + [UserController::class, 'edit'] + )->name('kelola-pengguna.edit'); + + Route::put('/kelola-pengguna/{id}', + [UserController::class, 'update'] + )->name('kelola-pengguna.update'); + + Route::get('/kelola-pengguna/{id}/reset-password', + [UserController::class, 'resetPassword'] + )->name('kelola-pengguna.reset-password'); + + Route::put('/kelola-pengguna/{id}/reset-password', + [UserController::class, 'updatePassword'] + )->name('kelola-pengguna.update-password'); + + Route::delete('/kelola-pengguna/{id}', + [UserController::class, 'destroy'] + )->name('kelola-pengguna.destroy'); + + /* + |-------------------------------------------------------------------------- + | DATA ULASAN + |-------------------------------------------------------------------------- + */ - // ✅ DATA ULASAN Route::get('/ulasan', [UlasanController::class, 'index']) ->name('ulasan.index'); Route::get('/riwayat', [UlasanController::class, 'riwayat']) ->name('riwayat.index'); - Route::post('/riwayat/import', [UlasanController::class, 'importRiwayat']) - ->name('riwayat.import'); + Route::post('/riwayat/import', + [UlasanController::class, 'importRiwayat'] + )->name('riwayat.import'); - Route::post('/ulasan/ambil', [UlasanController::class, 'ambilData']) - ->name('ulasan.ambil'); + Route::post('/ulasan/ambil', + [UlasanController::class, 'ambilData'] + )->name('ulasan.ambil'); - Route::delete('/ulasan/periode/kosongkan', [UlasanController::class, 'kosongkanPeriode']) - ->name('ulasan.kosongkan-periode'); + Route::delete('/ulasan/periode/kosongkan', + [UlasanController::class, 'kosongkanPeriode'] + )->name('ulasan.kosongkan-periode'); - // ✅ PROSES ANALISIS - Route::post('/proses-analisis', [UlasanController::class, 'analisisData']) - ->name('proses.analisis'); + /* + |-------------------------------------------------------------------------- + | PROSES ANALISIS + |-------------------------------------------------------------------------- + */ - // ✅ HASIL ANALISIS - Route::get('/analisis', [AnalisisController::class, 'index']) - ->name('analisis.index'); + Route::post('/proses-analisis', + [UlasanController::class, 'analisisData'] + )->name('proses.analisis'); - // ✅ REKOMENDASI - Route::get('/rekomendasi', function() { - return redirect('/dashboard?tab=rekomendasi'); -})->name('rekomendasi.index'); + /* + |-------------------------------------------------------------------------- + | HASIL ANALISIS + |-------------------------------------------------------------------------- + */ + + Route::get('/analisis', + [AnalisisController::class, 'index'] + )->name('analisis.index'); + + /* + |-------------------------------------------------------------------------- + | REKOMENDASI + |-------------------------------------------------------------------------- + */ + + Route::get('/rekomendasi', function () { + return redirect('/dashboard?tab=rekomendasi'); + })->name('rekomendasi.index'); }); -require __DIR__.'/auth.php'; +require __DIR__.'/auth.php'; \ No newline at end of file diff --git a/scraper/scraping_pipeline.py b/scraper/scraping_pipeline.py index 2638b02..702e219 100644 --- a/scraper/scraping_pipeline.py +++ b/scraper/scraping_pipeline.py @@ -65,7 +65,8 @@ def read_laravel_env(): def env_value(env, key, default=""): - value = os.getenv(key, env.get(key, default)) + # .env file dibaca DULUAN, baru fallback ke os.getenv, lalu default + value = env.get(key) or os.getenv(key) or default if value in {None, "", "null", "None"}: return default return value @@ -734,13 +735,12 @@ def main(): scraper = scraper_config() destinations = selected_destinations(args.wisata) conn = pymysql.connect( - host=config["host"], - port=config["port"], - user=config["user"], - password=config["password"], - database=config["database"], - charset="utf8mb4", - ) + host='localhost', + port=3306, + user='root', + password='', # isi password kalau ada + database='sistem_analisis', +) cursor = conn.cursor() try: @@ -767,4 +767,4 @@ def main(): if __name__ == "__main__": - main() + main() \ No newline at end of file