update sistem analisis sentimen
This commit is contained in:
parent
67156e700a
commit
638ae703c0
|
|
@ -9,7 +9,6 @@
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Validation\Rules;
|
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
class RegisteredUserController extends Controller
|
class RegisteredUserController extends Controller
|
||||||
|
|
@ -30,9 +29,40 @@ public function create(): View
|
||||||
public function store(Request $request): RedirectResponse
|
public function store(Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'name' => ['required', 'string', 'max:255'],
|
|
||||||
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
|
'name' => [
|
||||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
'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([
|
$user = User::create([
|
||||||
|
|
@ -47,4 +77,4 @@ public function store(Request $request): RedirectResponse
|
||||||
|
|
||||||
return redirect(route('dashboard', absolute: false));
|
return redirect(route('dashboard', absolute: false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
class UserController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$search = $request->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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth; // ← tambah ini
|
||||||
|
|
||||||
|
class RoleMiddleware
|
||||||
|
{
|
||||||
|
public function handle(Request $request, Closure $next, string $role)
|
||||||
|
{
|
||||||
|
if (!Auth::check() || Auth::user()->role !== $role) // ← ganti auth() ke Auth::
|
||||||
|
{
|
||||||
|
abort(403, 'Akses ditolak.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,42 +2,26 @@
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
|
||||||
use HasFactory, Notifiable;
|
use HasFactory, Notifiable;
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var list<string>
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
|
'role', // ← tambah ini
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* The attributes that should be hidden for serialization.
|
|
||||||
*
|
|
||||||
* @var list<string>
|
|
||||||
*/
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
'password',
|
'password',
|
||||||
'remember_token',
|
'remember_token',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the attributes that should be cast.
|
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|
@ -45,4 +29,15 @@ protected function casts(): array
|
||||||
'password' => 'hashed',
|
'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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,9 +10,11 @@
|
||||||
commands: __DIR__.'/../routes/console.php',
|
commands: __DIR__.'/../routes/console.php',
|
||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
->withMiddleware(function (Middleware $middleware): void {
|
->withMiddleware(function (Middleware $middleware) {
|
||||||
//
|
$middleware->alias([
|
||||||
|
'role' => \App\Http\Middleware\RoleMiddleware::class,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions): void {
|
->withExceptions(function (Exceptions $exceptions): void {
|
||||||
//
|
//
|
||||||
})->create();
|
})->create();
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?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()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->enum('role', ['admin', 'staff'])->default('staff')->after('email');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('role');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "TugasAkhirFarah-main",
|
"name": "SistemAnalisisSentimen",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|
@ -1418,6 +1418,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
|
|
@ -2690,6 +2691,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
|
|
@ -3114,6 +3116,7 @@
|
||||||
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
|
|
@ -3234,6 +3237,7 @@
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -3322,6 +3326,7 @@
|
||||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
|
|
@ -3426,6 +3431,7 @@
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,559 @@
|
||||||
<x-guest-layout>
|
<!DOCTYPE html>
|
||||||
<div class="mb-4 text-sm text-gray-600">
|
<html lang="id">
|
||||||
{{ __('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.') }}
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Lupa Password - SENTARA</title>
|
||||||
|
|
||||||
|
@vite(['resources/css/app.css','resources/js/app.js'])
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body{
|
||||||
|
background:#f5f7fb;
|
||||||
|
font-family:'Poppins', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrapper{
|
||||||
|
min-height:100vh;
|
||||||
|
padding:40px 20px;
|
||||||
|
position:relative;
|
||||||
|
overflow:hidden;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BACKGROUND */
|
||||||
|
.circle-top{
|
||||||
|
position:absolute;
|
||||||
|
top:-120px;
|
||||||
|
right:-120px;
|
||||||
|
width:350px;
|
||||||
|
height:350px;
|
||||||
|
background:#edf3ff;
|
||||||
|
border-radius:50%;
|
||||||
|
z-index:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-bottom{
|
||||||
|
position:absolute;
|
||||||
|
bottom:-150px;
|
||||||
|
left:-150px;
|
||||||
|
width:320px;
|
||||||
|
height:320px;
|
||||||
|
background:#edf3ff;
|
||||||
|
border-radius:50%;
|
||||||
|
z-index:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content{
|
||||||
|
position:relative;
|
||||||
|
z-index:2;
|
||||||
|
width:100%;
|
||||||
|
max-width:950px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CARD */
|
||||||
|
.card-box{
|
||||||
|
background:#fff;
|
||||||
|
border-radius:26px;
|
||||||
|
padding:50px;
|
||||||
|
box-shadow:0 10px 35px rgba(0,0,0,0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ICON */
|
||||||
|
.icon-wrapper{
|
||||||
|
width:110px;
|
||||||
|
height:110px;
|
||||||
|
background:#edf3ff;
|
||||||
|
border-radius:50%;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
margin:0 auto 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TITLE */
|
||||||
|
.title{
|
||||||
|
text-align:center;
|
||||||
|
font-size:54px;
|
||||||
|
font-weight:700;
|
||||||
|
color:#0d1b52;
|
||||||
|
line-height:1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle{
|
||||||
|
text-align:center;
|
||||||
|
font-size:22px;
|
||||||
|
color:#475569;
|
||||||
|
margin-top:18px;
|
||||||
|
line-height:1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle span{
|
||||||
|
color:#2563eb;
|
||||||
|
font-weight:700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ALERT */
|
||||||
|
.info-box{
|
||||||
|
margin-top:35px;
|
||||||
|
background:#f4f8ff;
|
||||||
|
border:1px solid #dbeafe;
|
||||||
|
border-radius:18px;
|
||||||
|
padding:22px 24px;
|
||||||
|
display:flex;
|
||||||
|
align-items:flex-start;
|
||||||
|
gap:16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box p{
|
||||||
|
color:#334155;
|
||||||
|
font-size:18px;
|
||||||
|
line-height:1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECTION */
|
||||||
|
.section-title{
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:12px;
|
||||||
|
margin-top:45px;
|
||||||
|
margin-bottom:30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title h2{
|
||||||
|
font-size:34px;
|
||||||
|
font-weight:700;
|
||||||
|
color:#0d1b52;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STEP */
|
||||||
|
.step{
|
||||||
|
display:flex;
|
||||||
|
gap:20px;
|
||||||
|
margin-bottom:28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-number{
|
||||||
|
min-width:52px;
|
||||||
|
height:52px;
|
||||||
|
background:#2563eb;
|
||||||
|
color:#fff;
|
||||||
|
border-radius:50%;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
font-weight:700;
|
||||||
|
font-size:22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content h3{
|
||||||
|
font-size:28px;
|
||||||
|
font-weight:700;
|
||||||
|
color:#0f172a;
|
||||||
|
margin-bottom:6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content p{
|
||||||
|
color:#475569;
|
||||||
|
font-size:20px;
|
||||||
|
line-height:1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CONTACT */
|
||||||
|
.contact-box{
|
||||||
|
margin-top:40px;
|
||||||
|
background:#f8fbff;
|
||||||
|
border:1px solid #e2e8f0;
|
||||||
|
border-radius:22px;
|
||||||
|
padding:32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-header{
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:18px;
|
||||||
|
margin-bottom:18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-icon{
|
||||||
|
width:70px;
|
||||||
|
height:70px;
|
||||||
|
background:#edf3ff;
|
||||||
|
border-radius:50%;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-header h3{
|
||||||
|
font-size:30px;
|
||||||
|
font-weight:700;
|
||||||
|
color:#2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-header p{
|
||||||
|
font-size:18px;
|
||||||
|
color:#475569;
|
||||||
|
margin-top:4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-list{
|
||||||
|
display:flex;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
gap:24px;
|
||||||
|
align-items:center;
|
||||||
|
margin-top:18px;
|
||||||
|
padding-left:88px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-item{
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
gap:12px;
|
||||||
|
font-size:22px;
|
||||||
|
font-weight:600;
|
||||||
|
color:#0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FOOTER */
|
||||||
|
.footer{
|
||||||
|
display:flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
margin-top:28px;
|
||||||
|
color:#64748b;
|
||||||
|
font-size:18px;
|
||||||
|
padding:0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MOBILE */
|
||||||
|
@media(max-width:768px){
|
||||||
|
|
||||||
|
.main-wrapper{
|
||||||
|
padding:20px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-box{
|
||||||
|
padding:28px 22px;
|
||||||
|
border-radius:22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wrapper{
|
||||||
|
width:90px;
|
||||||
|
height:90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title{
|
||||||
|
font-size:36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle{
|
||||||
|
font-size:17px;
|
||||||
|
line-height:1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box{
|
||||||
|
padding:18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box p{
|
||||||
|
font-size:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title h2{
|
||||||
|
font-size:25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step{
|
||||||
|
gap:14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-number{
|
||||||
|
min-width:42px;
|
||||||
|
height:42px;
|
||||||
|
font-size:18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content h3{
|
||||||
|
font-size:21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content p{
|
||||||
|
font-size:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-box{
|
||||||
|
padding:22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-header{
|
||||||
|
align-items:flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-header h3{
|
||||||
|
font-size:24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-header p{
|
||||||
|
font-size:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-list{
|
||||||
|
padding-left:0;
|
||||||
|
flex-direction:column;
|
||||||
|
align-items:flex-start;
|
||||||
|
gap:18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-item{
|
||||||
|
font-size:16px;
|
||||||
|
word-break:break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer{
|
||||||
|
flex-direction:column;
|
||||||
|
gap:10px;
|
||||||
|
text-align:center;
|
||||||
|
font-size:14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="main-wrapper">
|
||||||
|
|
||||||
|
<div class="circle-top"></div>
|
||||||
|
<div class="circle-bottom"></div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
|
||||||
|
<!-- CARD -->
|
||||||
|
<div class="card-box">
|
||||||
|
|
||||||
|
<!-- ICON -->
|
||||||
|
<div class="icon-wrapper">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="55"
|
||||||
|
height="55"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="#2563eb"
|
||||||
|
stroke-width="2">
|
||||||
|
|
||||||
|
<path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M16 11V7a4 4 0 10-8 0v4m-2 0h12a2 2 0 012 2v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5a2 2 0 012-2z"/>
|
||||||
|
|
||||||
|
<circle cx="18.5" cy="18.5" r="3.5" stroke="#2563eb"/>
|
||||||
|
<path d="M18.5 17v3M17 18.5h3" stroke="#2563eb"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TITLE -->
|
||||||
|
<h1 class="title">
|
||||||
|
Lupa Password?
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="subtitle">
|
||||||
|
Untuk keamanan akun, pengaturan ulang password hanya dapat dilakukan
|
||||||
|
<br>
|
||||||
|
<span>oleh Administrator.</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- INFO -->
|
||||||
|
<div class="info-box">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="34"
|
||||||
|
height="34"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="#2563eb"
|
||||||
|
stroke-width="2">
|
||||||
|
|
||||||
|
<circle cx="12" cy="12" r="10"/>
|
||||||
|
<path d="M12 16v-4"/>
|
||||||
|
<path d="M12 8h.01"/>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Silakan hubungi Administrator sistem untuk memperbarui password akun Anda.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- SECTION TITLE -->
|
||||||
|
<div class="section-title">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="40"
|
||||||
|
height="40"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="#2563eb"
|
||||||
|
stroke-width="2">
|
||||||
|
|
||||||
|
<path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M17 20h5V4H2v16h5"/>
|
||||||
|
<path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M9 20h6"/>
|
||||||
|
<circle cx="12" cy="8" r="3"/>
|
||||||
|
<path d="M7 16c1.333-2 3-3 5-3s3.667 1 5 3"/>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<h2>Cara Memperbarui Password</h2>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- STEP -->
|
||||||
|
<div class="step">
|
||||||
|
|
||||||
|
<div class="step-number">1</div>
|
||||||
|
|
||||||
|
<div class="step-content">
|
||||||
|
<h3>Hubungi Administrator</h3>
|
||||||
|
<p>
|
||||||
|
Sampaikan kepada Administrator bahwa Anda lupa password akun SENTARA.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
|
||||||
|
<div class="step-number">2</div>
|
||||||
|
|
||||||
|
<div class="step-content">
|
||||||
|
<h3>Verifikasi Identitas</h3>
|
||||||
|
<p>
|
||||||
|
Administrator akan memverifikasi identitas Anda sebagai pengguna sistem.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
|
||||||
|
<div class="step-number">3</div>
|
||||||
|
|
||||||
|
<div class="step-content">
|
||||||
|
<h3>Password Baru</h3>
|
||||||
|
<p>
|
||||||
|
Administrator akan membuat password baru dan memberikannya kepada Anda.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CONTACT -->
|
||||||
|
<div class="contact-box">
|
||||||
|
|
||||||
|
<div class="contact-header">
|
||||||
|
|
||||||
|
<div class="contact-icon">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="38"
|
||||||
|
height="38"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="#2563eb"
|
||||||
|
stroke-width="2">
|
||||||
|
|
||||||
|
<path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4-.917L2 17l1.083-3.25A6.963 6.963 0 012 10c0-3.866 3.582-7 8-7s8 3.134 8 7z"/>
|
||||||
|
|
||||||
|
<path d="M15 8h.01"/>
|
||||||
|
<path d="M9 8h.01"/>
|
||||||
|
<path d="M8 12c1 1 2 1.5 4 1.5s3-.5 4-1.5"/>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Kontak Administrator</h3>
|
||||||
|
<p>
|
||||||
|
Silakan hubungi administrator melalui kontak resmi berikut:
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="contact-list">
|
||||||
|
|
||||||
|
<!-- PHONE -->
|
||||||
|
<div class="contact-item">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="26"
|
||||||
|
height="26"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="#2563eb"
|
||||||
|
stroke-width="2">
|
||||||
|
|
||||||
|
<path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M3 5a2 2 0 012-2h2.28a2 2 0 011.94 1.515l.57 2.28a2 2 0 01-.45 1.91l-1.27 1.27a16 16 0 006.59 6.59l1.27-1.27a2 2 0 011.91-.45l2.28.57A2 2 0 0121 16.72V19a2 2 0 01-2 2h-1C9.163 21 3 14.837 3 7V5z"/>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
0881-0267-1527
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- EMAIL -->
|
||||||
|
<div class="contact-item">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="26"
|
||||||
|
height="26"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="#2563eb"
|
||||||
|
stroke-width="2">
|
||||||
|
|
||||||
|
<path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M4 4h16v16H4z"/>
|
||||||
|
|
||||||
|
<path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M22 6l-10 7L2 6"/>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
e31231226@student.polije.ac.id
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<div class="footer">
|
||||||
|
<div>© 2025 SENTARA. All rights reserved.</div>
|
||||||
|
<div>Dinas Pariwisata Jember</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Session Status -->
|
</div>
|
||||||
<x-auth-session-status class="mb-4" :status="session('status')" />
|
|
||||||
|
|
||||||
<form method="POST" action="{{ route('password.email') }}">
|
</body>
|
||||||
@csrf
|
</html>
|
||||||
|
|
||||||
<!-- Email Address -->
|
|
||||||
<div>
|
|
||||||
<x-input-label for="email" :value="__('Email')" />
|
|
||||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
|
|
||||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-end mt-4">
|
|
||||||
<x-primary-button>
|
|
||||||
{{ __('Email Password Reset Link') }}
|
|
||||||
</x-primary-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</x-guest-layout>
|
|
||||||
|
|
@ -0,0 +1,432 @@
|
||||||
|
@extends('layouts.sentara')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<div class="space-y-6">
|
||||||
|
|
||||||
|
{{-- ALERT SUCCESS --}}
|
||||||
|
@if(session('success'))
|
||||||
|
<div class="bg-green-50 border border-green-200 text-green-700 px-5 py-4 rounded-2xl">
|
||||||
|
{{ session('success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- ALERT ERROR --}}
|
||||||
|
@if(session('error'))
|
||||||
|
<div class="bg-red-50 border border-red-200 text-red-700 px-5 py-4 rounded-2xl">
|
||||||
|
{{ session('error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- HEADER -->
|
||||||
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1 class="text-3xl font-bold text-slate-800">Kelola Pengguna</h1>
|
||||||
|
<p class="text-slate-500 mt-1">Kelola akun pengguna sistem SENTARA.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- BUTTON TAMBAH -->
|
||||||
|
<button
|
||||||
|
onclick="openTambahModal()"
|
||||||
|
class="bg-blue-600 hover:bg-blue-700 text-white px-5 py-3 rounded-2xl text-sm font-semibold shadow transition">
|
||||||
|
+ Tambah Pengguna
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CARD -->
|
||||||
|
<div class="bg-white rounded-3xl shadow-sm border border-slate-100 p-5">
|
||||||
|
|
||||||
|
<!-- TABLE -->
|
||||||
|
<div class="overflow-x-auto mt-6">
|
||||||
|
|
||||||
|
<table class="w-full text-sm min-w-[900px]">
|
||||||
|
|
||||||
|
<thead>
|
||||||
|
<tr class="text-slate-500 border-b">
|
||||||
|
<th class="py-4 text-left">No</th>
|
||||||
|
<th class="py-4 text-left">Nama</th>
|
||||||
|
<th class="py-4 text-left">Email</th>
|
||||||
|
<th class="py-4 text-left">Role</th>
|
||||||
|
<th class="py-4 text-left">Status</th>
|
||||||
|
<th class="py-4 text-left">Terakhir Aktif</th>
|
||||||
|
<th class="py-4 text-center">Aksi</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody class="text-slate-700">
|
||||||
|
|
||||||
|
@forelse ($users as $user)
|
||||||
|
|
||||||
|
<tr class="border-b hover:bg-slate-50 transition">
|
||||||
|
|
||||||
|
<td class="py-4">{{ $loop->iteration }}</td>
|
||||||
|
|
||||||
|
<td class="py-4">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="w-10 h-10 rounded-full bg-slate-200 flex items-center justify-center text-xs font-bold text-slate-600">
|
||||||
|
{{ strtoupper(substr($user->name, 0, 2)) }}
|
||||||
|
</div>
|
||||||
|
<span class="font-medium">{{ $user->name }}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="py-4">{{ $user->email }}</td>
|
||||||
|
|
||||||
|
<td class="py-4">
|
||||||
|
@if($user->role == 'admin')
|
||||||
|
<span class="bg-purple-100 text-purple-700 px-3 py-1 rounded-full text-xs font-semibold">Admin</span>
|
||||||
|
@else
|
||||||
|
<span class="bg-blue-100 text-blue-700 px-3 py-1 rounded-full text-xs font-semibold">Staff</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="py-4">
|
||||||
|
<span class="bg-green-100 text-green-700 px-3 py-1 rounded-full text-xs font-semibold">Aktif</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="py-4">{{ $user->updated_at->format('d M Y, H:i') }}</td>
|
||||||
|
|
||||||
|
<td class="py-4">
|
||||||
|
<div class="flex justify-center gap-2">
|
||||||
|
|
||||||
|
<button
|
||||||
|
onclick="openEditModal('{{ $user->id }}', '{{ addslashes($user->name) }}', '{{ $user->email }}', '{{ $user->role }}')"
|
||||||
|
class="w-11 h-11 rounded-2xl border border-blue-200 text-blue-600 hover:bg-blue-50 flex items-center justify-center transition"
|
||||||
|
title="Edit Pengguna">✏️</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onclick="openPasswordModal('{{ $user->id }}')"
|
||||||
|
class="w-11 h-11 rounded-2xl border border-amber-200 text-amber-500 hover:bg-amber-50 flex items-center justify-center transition"
|
||||||
|
title="Reset Password">🔒</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onclick="openDeleteModal('{{ $user->id }}', '{{ $user->role }}')"
|
||||||
|
class="w-11 h-11 rounded-2xl border border-red-200 text-red-500 hover:bg-red-50 flex items-center justify-center transition"
|
||||||
|
title="Hapus Pengguna">🗑️</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@empty
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="7" class="py-10 text-center text-slate-400">
|
||||||
|
Data pengguna belum tersedia.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@endforelse
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PAGINATION -->
|
||||||
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mt-6">
|
||||||
|
<p class="text-sm text-slate-500">
|
||||||
|
Menampilkan {{ $users->firstItem() ?? 0 }} - {{ $users->lastItem() ?? 0 }}
|
||||||
|
dari {{ $users->total() }} pengguna
|
||||||
|
</p>
|
||||||
|
{{ $users->links() }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================= MODAL TAMBAH PENGGUNA ================= -->
|
||||||
|
<div id="tambahModal"
|
||||||
|
class="hidden fixed inset-0 bg-black/40 z-50 flex items-center justify-center px-4">
|
||||||
|
|
||||||
|
<div class="bg-white rounded-3xl p-6 shadow-2xl w-full" style="max-width: 480px;">
|
||||||
|
|
||||||
|
<div class="flex items-start justify-between mb-5">
|
||||||
|
<div>
|
||||||
|
<h2 class="text-2xl font-bold text-slate-800">Tambah Pengguna</h2>
|
||||||
|
<p class="text-slate-500 text-sm mt-1">Buat akun pengguna baru untuk sistem SENTARA.</p>
|
||||||
|
</div>
|
||||||
|
<button onclick="closeTambahModal()"
|
||||||
|
class="text-slate-400 hover:text-slate-700 text-2xl leading-none ml-4 flex-shrink-0">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- SESUDAH --}}
|
||||||
|
<form method="POST" action="/kelola-pengguna" id="tambahForm">
|
||||||
|
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Nama Pengguna</label>
|
||||||
|
<input type="text" name="name" placeholder="Masukkan nama lengkap"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Email</label>
|
||||||
|
<input type="email" name="email" placeholder="contoh@email.com"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Role Pengguna</label>
|
||||||
|
<select name="role"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none">
|
||||||
|
<option value="staff">Staff</option>
|
||||||
|
<option value="admin">Admin</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Password</label>
|
||||||
|
<input type="password" name="password" placeholder="Minimal 8 karakter"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Konfirmasi Password</label>
|
||||||
|
<input type="password" name="password_confirmation" placeholder="Ulangi password"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button type="button" onclick="closeTambahModal()"
|
||||||
|
class="flex-1 py-3 rounded-2xl border border-slate-200 text-slate-600 text-sm font-medium hover:bg-slate-100 transition">
|
||||||
|
Batal
|
||||||
|
</button>
|
||||||
|
<button type="submit"
|
||||||
|
class="flex-1 py-3 rounded-2xl bg-blue-600 hover:bg-blue-700 text-white text-sm font-semibold transition">
|
||||||
|
Tambah
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================= MODAL EDIT ================= -->
|
||||||
|
<div id="editModal"
|
||||||
|
class="hidden fixed inset-0 bg-black/40 z-50 flex items-center justify-center px-4">
|
||||||
|
|
||||||
|
<div class="bg-white rounded-3xl p-6 shadow-2xl w-full" style="max-width: 480px;">
|
||||||
|
|
||||||
|
<div class="flex items-start justify-between mb-5">
|
||||||
|
<div>
|
||||||
|
<h2 class="text-2xl font-bold text-slate-800">Edit Pengguna</h2>
|
||||||
|
<p class="text-slate-500 text-sm mt-1">Perbarui informasi akun pengguna SENTARA.</p>
|
||||||
|
</div>
|
||||||
|
<button onclick="closeEditModal()"
|
||||||
|
class="text-slate-400 hover:text-slate-700 text-2xl leading-none ml-4 flex-shrink-0">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" id="editForm">
|
||||||
|
@csrf
|
||||||
|
@method('PUT')
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Nama Pengguna</label>
|
||||||
|
<input type="text" name="name" id="editName"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Email</label>
|
||||||
|
<input type="email" name="email" id="editEmail"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Role Pengguna</label>
|
||||||
|
<select name="role" id="editRole"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none">
|
||||||
|
<option value="admin">Admin</option>
|
||||||
|
<option value="staff">Staff</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button type="button" onclick="closeEditModal()"
|
||||||
|
class="flex-1 py-3 rounded-2xl border border-slate-200 text-slate-600 text-sm font-medium hover:bg-slate-100 transition">
|
||||||
|
Batal
|
||||||
|
</button>
|
||||||
|
<button type="submit"
|
||||||
|
class="flex-1 py-3 rounded-2xl bg-blue-600 hover:bg-blue-700 text-white text-sm font-semibold transition">
|
||||||
|
Simpan
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================= MODAL RESET PASSWORD ================= -->
|
||||||
|
<div id="passwordModal"
|
||||||
|
class="hidden fixed inset-0 bg-black/40 z-50 flex items-center justify-center px-4">
|
||||||
|
|
||||||
|
<div class="bg-white rounded-3xl p-6 shadow-2xl w-full" style="max-width: 480px;">
|
||||||
|
|
||||||
|
<div class="flex items-start justify-between mb-5">
|
||||||
|
<div>
|
||||||
|
<h2 class="text-2xl font-bold text-slate-800">Reset Password</h2>
|
||||||
|
<p class="text-slate-500 text-sm mt-1">Buat password baru pengguna.</p>
|
||||||
|
</div>
|
||||||
|
<button onclick="closePasswordModal()"
|
||||||
|
class="text-slate-400 hover:text-slate-700 text-2xl leading-none ml-4 flex-shrink-0">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-5 bg-amber-50 border border-amber-200 rounded-2xl p-4">
|
||||||
|
<p class="text-sm text-amber-700 leading-relaxed">
|
||||||
|
Password minimal 8 karakter dan harus kombinasi huruf besar, huruf kecil, dan angka.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" id="passwordForm">
|
||||||
|
@csrf
|
||||||
|
@method('PUT')
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Password Baru</label>
|
||||||
|
<input type="password" name="password"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-amber-400 focus:outline-none">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label class="block text-sm font-semibold text-slate-700 mb-2">Konfirmasi Password</label>
|
||||||
|
<input type="password" name="password_confirmation"
|
||||||
|
class="w-full rounded-2xl border border-slate-200 px-4 py-3 text-sm focus:ring-2 focus:ring-amber-400 focus:outline-none">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button type="button" onclick="closePasswordModal()"
|
||||||
|
class="flex-1 py-3 rounded-2xl border border-slate-200 text-slate-600 text-sm font-medium hover:bg-slate-100 transition">
|
||||||
|
Batal
|
||||||
|
</button>
|
||||||
|
<button type="submit"
|
||||||
|
class="flex-1 py-3 rounded-2xl bg-amber-500 hover:bg-amber-600 text-white text-sm font-semibold transition">
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================= MODAL DELETE ================= -->
|
||||||
|
<div id="deleteModal"
|
||||||
|
class="hidden fixed inset-0 bg-black/40 z-50 flex items-center justify-center px-4">
|
||||||
|
|
||||||
|
<div class="bg-white rounded-3xl p-6 shadow-2xl w-full text-center" style="max-width: 400px;">
|
||||||
|
|
||||||
|
<div class="w-16 h-16 mx-auto rounded-full bg-red-100 flex items-center justify-center text-3xl mb-4">🗑️</div>
|
||||||
|
<h2 class="text-2xl font-bold text-slate-800">Hapus Pengguna?</h2>
|
||||||
|
<p class="text-slate-500 text-sm mt-2 leading-relaxed">
|
||||||
|
Data pengguna akan dihapus permanen dan tidak dapat dikembalikan.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" id="deleteForm" class="mt-6">
|
||||||
|
@csrf
|
||||||
|
@method('DELETE')
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button type="button" onclick="closeDeleteModal()"
|
||||||
|
class="flex-1 py-3 rounded-2xl border border-slate-200 text-slate-600 text-sm font-medium hover:bg-slate-100 transition">
|
||||||
|
Batal
|
||||||
|
</button>
|
||||||
|
<button type="submit"
|
||||||
|
class="flex-1 py-3 rounded-2xl bg-red-500 hover:bg-red-600 text-white text-sm font-semibold transition">
|
||||||
|
Hapus
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================= MODAL ADMIN TIDAK BISA DIHAPUS ================= -->
|
||||||
|
<div id="adminAlertModal"
|
||||||
|
class="hidden fixed inset-0 bg-black/40 z-50 flex items-center justify-center px-4">
|
||||||
|
|
||||||
|
<div class="bg-white rounded-3xl p-6 shadow-2xl w-full text-center" style="max-width: 400px;">
|
||||||
|
|
||||||
|
<div class="w-16 h-16 mx-auto rounded-full bg-amber-100 flex items-center justify-center text-3xl mb-4">⚠️</div>
|
||||||
|
<h2 class="text-2xl font-bold text-slate-800">Akses Ditolak</h2>
|
||||||
|
<p class="text-slate-500 text-sm mt-2 leading-relaxed">
|
||||||
|
Akun administrator tidak dapat dihapus demi menjaga keamanan sistem.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button onclick="closeAdminAlertModal()"
|
||||||
|
class="w-full mt-6 py-3 rounded-2xl bg-blue-600 hover:bg-blue-700 text-white text-sm font-semibold transition">
|
||||||
|
Mengerti
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ================= SCRIPT ================= -->
|
||||||
|
<script>
|
||||||
|
|
||||||
|
/* ---- TAMBAH ---- */
|
||||||
|
function openTambahModal() {
|
||||||
|
document.getElementById('tambahModal').classList.remove('hidden');
|
||||||
|
document.getElementById('tambahForm').reset();
|
||||||
|
}
|
||||||
|
function closeTambahModal() {
|
||||||
|
document.getElementById('tambahModal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- EDIT ---- */
|
||||||
|
function openEditModal(id, name, email, role) {
|
||||||
|
document.getElementById('editModal').classList.remove('hidden');
|
||||||
|
document.getElementById('editName').value = name;
|
||||||
|
document.getElementById('editEmail').value = email;
|
||||||
|
document.getElementById('editRole').value = role;
|
||||||
|
document.getElementById('editForm').action = '/kelola-pengguna/' + id;
|
||||||
|
}
|
||||||
|
function closeEditModal() {
|
||||||
|
document.getElementById('editModal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- RESET PASSWORD ---- */
|
||||||
|
function openPasswordModal(id) {
|
||||||
|
document.getElementById('passwordModal').classList.remove('hidden');
|
||||||
|
document.getElementById('passwordForm').action =
|
||||||
|
'/kelola-pengguna/' + id + '/reset-password';
|
||||||
|
}
|
||||||
|
function closePasswordModal() {
|
||||||
|
document.getElementById('passwordModal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- DELETE ---- */
|
||||||
|
function openDeleteModal(id, role) {
|
||||||
|
if (role === 'admin') {
|
||||||
|
document.getElementById('adminAlertModal').classList.remove('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.getElementById('deleteModal').classList.remove('hidden');
|
||||||
|
document.getElementById('deleteForm').action = '/kelola-pengguna/' + id;
|
||||||
|
}
|
||||||
|
function closeDeleteModal() {
|
||||||
|
document.getElementById('deleteModal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
function closeAdminAlertModal() {
|
||||||
|
document.getElementById('adminAlertModal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|
@ -86,11 +86,27 @@ class="block hover:text-gray-300">
|
||||||
</header>
|
</header>
|
||||||
@endisset
|
@endisset
|
||||||
|
|
||||||
|
|
||||||
<!-- Page Content -->
|
<!-- Page Content -->
|
||||||
<main class="p-6">
|
<main class="p-6">
|
||||||
@yield('content')
|
|
||||||
</main>
|
|
||||||
|
|
||||||
|
{{-- ALERT SUCCESS --}}
|
||||||
|
@if(session('success'))
|
||||||
|
<div class="mb-4 bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-2xl">
|
||||||
|
{{ session('success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- ALERT ERROR --}}
|
||||||
|
@if(session('error'))
|
||||||
|
<div class="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-2xl">
|
||||||
|
{{ session('error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@yield('content')
|
||||||
|
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,14 @@
|
||||||
|
|
||||||
@vite(['resources/css/app.css','resources/js/app.js'])
|
@vite(['resources/css/app.css','resources/js/app.js'])
|
||||||
|
|
||||||
|
<!-- FEATHER ICON -->
|
||||||
<script src="https://unpkg.com/feather-icons"></script>
|
<script src="https://unpkg.com/feather-icons"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-gray-100">
|
<body class="bg-gray-100 overflow-x-hidden">
|
||||||
|
|
||||||
<div class="min-h-screen">
|
<div class="min-h-screen flex">
|
||||||
|
|
||||||
<!-- SIDEBAR -->
|
<!-- SIDEBAR -->
|
||||||
<aside id="sidebar"
|
<aside id="sidebar"
|
||||||
|
|
@ -25,7 +27,8 @@ class="fixed top-0 left-0 h-full w-64
|
||||||
<!-- LOGO -->
|
<!-- LOGO -->
|
||||||
<div class="flex justify-center mt-6 mb-8">
|
<div class="flex justify-center mt-6 mb-8">
|
||||||
<img src="{{ asset('images/tulisan-sentara.png') }}"
|
<img src="{{ asset('images/tulisan-sentara.png') }}"
|
||||||
class="w-44">
|
class="w-44"
|
||||||
|
alt="Logo SENTARA">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- MENU -->
|
<!-- MENU -->
|
||||||
|
|
@ -39,7 +42,9 @@ class="flex items-center gap-4 px-4 py-3 rounded-xl hover:bg-blue-800 transition
|
||||||
<i data-feather="grid" class="w-5 h-5"></i>
|
<i data-feather="grid" class="w-5 h-5"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="text-[15px]">Dashboard Sentimen</span>
|
<span class="text-[15px] leading-5">
|
||||||
|
Dashboard Sentimen
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- DATA ULASAN -->
|
<!-- DATA ULASAN -->
|
||||||
|
|
@ -50,7 +55,9 @@ class="flex items-center gap-4 px-4 py-3 rounded-xl hover:bg-blue-800 transition
|
||||||
<i data-feather="file-text" class="w-5 h-5"></i>
|
<i data-feather="file-text" class="w-5 h-5"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="text-[15px]">Data Ulasan</span>
|
<span class="text-[15px] leading-5">
|
||||||
|
Data Ulasan
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- RIWAYAT -->
|
<!-- RIWAYAT -->
|
||||||
|
|
@ -61,54 +68,103 @@ class="flex items-center gap-4 px-4 py-3 rounded-xl hover:bg-blue-800 transition
|
||||||
<i data-feather="clock" class="w-5 h-5"></i>
|
<i data-feather="clock" class="w-5 h-5"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="text-[15px]">Riwayat Analisis</span>
|
<span class="text-[15px] leading-5">
|
||||||
|
Riwayat Analisis
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<!-- KHUSUS ADMIN -->
|
||||||
|
@if(auth()->user()->role == 'admin')
|
||||||
|
|
||||||
|
<a href="{{ route('kelola-pengguna.index') }}"
|
||||||
|
class="flex items-center gap-4 px-4 py-3 rounded-xl hover:bg-blue-800 transition">
|
||||||
|
|
||||||
|
<div class="w-10 h-10 flex items-center justify-center bg-blue-800 rounded-lg">
|
||||||
|
<i data-feather="users" class="w-5 h-5"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="text-[15px] leading-5">
|
||||||
|
Kelola Pengguna
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
@endif
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- USER SECTION -->
|
<!-- USER -->
|
||||||
<div class="px-6 pb-6">
|
<div class="px-6 pb-6">
|
||||||
|
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
|
|
||||||
<!-- USER BUTTON -->
|
<!-- USER BUTTON -->
|
||||||
<button onclick="toggleUserMenu()"
|
<button onclick="toggleUserMenu()"
|
||||||
class="w-full flex items-center gap-3 bg-blue-800 hover:bg-blue-900 px-4 py-3 rounded-xl transition">
|
class="w-full flex items-center gap-3
|
||||||
|
bg-blue-800 hover:bg-blue-900
|
||||||
|
px-4 py-3 rounded-2xl transition">
|
||||||
|
|
||||||
|
<!-- ICON -->
|
||||||
|
<div class="w-11 h-11 rounded-full bg-blue-700
|
||||||
|
flex items-center justify-center">
|
||||||
|
|
||||||
|
<i data-feather="user"
|
||||||
|
class="w-5 h-5"></i>
|
||||||
|
|
||||||
<div class="w-10 h-10 flex items-center justify-center bg-blue-700 rounded-full">
|
|
||||||
<i data-feather="user" class="w-5 h-5"></i>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-left">
|
<!-- TEXT -->
|
||||||
<p class="text-sm font-semibold">
|
<div class="text-left flex-1">
|
||||||
|
|
||||||
|
<p class="text-sm font-semibold capitalize">
|
||||||
{{ auth()->user()->name }}
|
{{ auth()->user()->name }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-xs text-blue-200">Staff</p>
|
|
||||||
|
<p class="text-xs text-blue-200 capitalize">
|
||||||
|
{{ auth()->user()->role }}
|
||||||
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ARROW -->
|
||||||
|
<i data-feather="chevron-up"
|
||||||
|
class="w-4 h-4 text-blue-200"></i>
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- DROPDOWN -->
|
<!-- DROPDOWN -->
|
||||||
<div id="userMenu"
|
<div id="userMenu"
|
||||||
class="hidden absolute bottom-16 left-0 w-full
|
class="hidden absolute bottom-16 left-0 w-full
|
||||||
bg-white text-gray-700 rounded-xl shadow-lg overflow-hidden">
|
bg-white text-gray-700 rounded-2xl shadow-2xl overflow-hidden">
|
||||||
|
|
||||||
<!-- PROFILE -->
|
<!-- PROFILE -->
|
||||||
<a href="{{ route('profile') }}"
|
<a href="{{ route('profile') }}"
|
||||||
class="flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-100">
|
class="flex items-center gap-3 px-4 py-4 text-sm hover:bg-gray-100 transition">
|
||||||
<i data-feather="user" class="w-4 h-4"></i>
|
|
||||||
|
<i data-feather="user"
|
||||||
|
class="w-4 h-4"></i>
|
||||||
|
|
||||||
Profile
|
Profile
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- LOGOUT -->
|
<!-- LOGOUT -->
|
||||||
<form id="logoutForm" method="POST" action="{{ route('logout') }}">
|
<form id="logoutForm"
|
||||||
@csrf
|
method="POST"
|
||||||
<button type="button"
|
action="{{ route('logout') }}">
|
||||||
onclick="confirmLogout()"
|
|
||||||
class="w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-100">
|
@csrf
|
||||||
<i data-feather="log-out" class="w-4 h-4"></i>
|
|
||||||
Logout
|
<button type="submit"
|
||||||
</button>
|
class="w-full flex items-center gap-3 px-4 py-4 text-sm hover:bg-gray-100 transition text-left">
|
||||||
</form>
|
|
||||||
|
<i data-feather="log-out"
|
||||||
|
class="w-4 h-4"></i>
|
||||||
|
|
||||||
|
Logout
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -119,11 +175,14 @@ class="w-full flex items-center gap-3 px-4 py-3 text-sm hover:bg-gray-100">
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- CONTENT -->
|
<!-- CONTENT -->
|
||||||
<div class="flex-1 p-4 md:p-6 md:ml-64 ml-0">
|
<div class="flex-1 p-4 md:p-6 md:ml-64">
|
||||||
|
|
||||||
|
<!-- MOBILE BUTTON -->
|
||||||
<button onclick="toggleSidebar()"
|
<button onclick="toggleSidebar()"
|
||||||
class="md:hidden mb-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow">
|
class="md:hidden mb-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow">
|
||||||
|
|
||||||
☰
|
☰
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@yield('content')
|
@yield('content')
|
||||||
|
|
@ -134,28 +193,44 @@ class="md:hidden mb-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow">
|
||||||
|
|
||||||
<!-- MODAL LOGOUT -->
|
<!-- MODAL LOGOUT -->
|
||||||
<div id="logoutModal"
|
<div id="logoutModal"
|
||||||
class="hidden fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center z-50">
|
class="hidden fixed inset-0 bg-black/40 flex items-center justify-center z-50 px-4">
|
||||||
|
|
||||||
<div class="bg-white rounded-xl p-6 w-80 text-center shadow-lg">
|
<div class="bg-white rounded-2xl p-6 w-96 text-center shadow-2xl">
|
||||||
|
|
||||||
<h3 class="text-lg font-semibold mb-2">
|
<!-- ICON -->
|
||||||
|
<div class="w-16 h-16 rounded-full
|
||||||
|
flex items-center justify-center mx-auto mb-5">
|
||||||
|
|
||||||
|
<i data-feather="log-out"
|
||||||
|
class="w-7 h-7 text-red-500"></i>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TITLE -->
|
||||||
|
<h3 class="text-xl font-bold text-gray-800 mb-2">
|
||||||
Konfirmasi Logout
|
Konfirmasi Logout
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p class="text-sm text-gray-600 mb-4">
|
<!-- TEXT -->
|
||||||
Apakah Anda ingin keluar?
|
<p class="text-sm text-gray-500 mb-6">
|
||||||
|
Apakah Anda ingin keluar dari sistem?
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<!-- BUTTON -->
|
||||||
<div class="flex justify-center gap-3">
|
<div class="flex justify-center gap-3">
|
||||||
|
|
||||||
<button onclick="closeModal()"
|
<button onclick="closeModal()"
|
||||||
class="px-4 py-2 bg-gray-200 rounded-lg">
|
class="px-5 py-2 rounded-xl bg-gray-200 hover:bg-gray-300 transition">
|
||||||
|
|
||||||
Tidak
|
Tidak
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button onclick="submitLogout()"
|
<button onclick="submitLogout()"
|
||||||
class="px-4 py-2 bg-red-500 text-white rounded-lg">
|
class="px-5 py-2 rounded-xl bg-red-500 hover:bg-red-600 text-white transition">
|
||||||
|
|
||||||
Ya
|
Ya
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -166,28 +241,48 @@ class="px-4 py-2 bg-red-500 text-white rounded-lg">
|
||||||
|
|
||||||
<!-- SCRIPT -->
|
<!-- SCRIPT -->
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
function toggleSidebar() {
|
function toggleSidebar() {
|
||||||
document.getElementById('sidebar').classList.toggle('-translate-x-full');
|
document.getElementById('sidebar')
|
||||||
|
.classList.toggle('-translate-x-full');
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleUserMenu() {
|
function toggleUserMenu() {
|
||||||
document.getElementById('userMenu').classList.toggle('hidden');
|
document.getElementById('userMenu')
|
||||||
|
.classList.toggle('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmLogout(){
|
function confirmLogout() {
|
||||||
document.getElementById('logoutModal').classList.remove('hidden');
|
document.getElementById('logoutModal')
|
||||||
|
.classList.remove('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeModal(){
|
function closeModal() {
|
||||||
document.getElementById('logoutModal').classList.add('hidden');
|
document.getElementById('logoutModal')
|
||||||
|
.classList.add('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitLogout(){
|
function submitLogout() {
|
||||||
document.getElementById('logoutForm').submit();
|
document.getElementById('logoutForm').submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CLOSE DROPDOWN KETIKA KLIK LUAR */
|
||||||
|
window.addEventListener('click', function(e){
|
||||||
|
|
||||||
|
const button = e.target.closest('button');
|
||||||
|
const menu = document.getElementById('userMenu');
|
||||||
|
|
||||||
|
if(!e.target.closest('#userMenu') &&
|
||||||
|
!e.target.closest('[onclick="toggleUserMenu()"]')) {
|
||||||
|
|
||||||
|
menu.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
feather.replace();
|
feather.replace();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -6,14 +6,20 @@
|
||||||
|
|
||||||
<!-- HEADER -->
|
<!-- HEADER -->
|
||||||
<div class="flex items-center gap-4 mb-6">
|
<div class="flex items-center gap-4 mb-6">
|
||||||
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-3xl font-bold text-gray-800">Profile Saya</h1>
|
<h1 class="text-3xl font-bold text-gray-800">
|
||||||
|
Profile Saya
|
||||||
|
</h1>
|
||||||
|
|
||||||
<p class="text-gray-500 text-sm">
|
<p class="text-gray-500 text-sm">
|
||||||
Informasi akun pengguna <span class="text-blue-600 font-medium">SENTARA</span>
|
Informasi akun pengguna
|
||||||
|
<span class="text-blue-600 font-medium">
|
||||||
|
SENTARA
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- PROFILE CARD -->
|
<!-- PROFILE CARD -->
|
||||||
|
|
@ -21,30 +27,53 @@
|
||||||
|
|
||||||
<div class="absolute right-0 bottom-0 w-64 h-64 bg-blue-100 opacity-30 rounded-full"></div>
|
<div class="absolute right-0 bottom-0 w-64 h-64 bg-blue-100 opacity-30 rounded-full"></div>
|
||||||
|
|
||||||
|
<!-- FOTO -->
|
||||||
<div class="w-24 h-24 rounded-full bg-gradient-to-r from-blue-500 to-blue-700
|
<div class="w-24 h-24 rounded-full bg-gradient-to-r from-blue-500 to-blue-700
|
||||||
flex items-center justify-center text-white text-3xl font-bold shadow-md">
|
flex items-center justify-center text-white text-3xl font-bold shadow-md">
|
||||||
|
|
||||||
{{ strtoupper(substr(Auth::user()->name, 0, 1)) }}
|
{{ strtoupper(substr(Auth::user()->name, 0, 1)) }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- INFO -->
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
<!-- NAMA -->
|
||||||
<h2 class="text-2xl font-semibold text-gray-800">
|
<h2 class="text-2xl font-semibold text-gray-800">
|
||||||
{{ Auth::user()->name }}
|
{{ Auth::user()->name }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p class="text-gray-500 mt-1">
|
<!-- ROLE -->
|
||||||
Staff - Dinas Pariwisata Jember
|
<p class="text-gray-500 mt-1 capitalize">
|
||||||
|
{{ Auth::user()->role }} - Dinas Pariwisata Jember
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<span class="inline-block mt-3 text-xs bg-blue-100 text-blue-600 px-3 py-1 rounded-full font-medium">
|
<!-- BADGE ROLE -->
|
||||||
STAFF
|
<span class="
|
||||||
|
inline-block mt-3 text-xs px-3 py-1 rounded-full font-medium uppercase
|
||||||
|
|
||||||
|
@if(Auth::user()->role == 'admin')
|
||||||
|
bg-purple-100 text-purple-700
|
||||||
|
@else
|
||||||
|
bg-blue-100 text-blue-600
|
||||||
|
@endif
|
||||||
|
">
|
||||||
|
|
||||||
|
{{ Auth::user()->role }}
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- INFORMASI AKUN -->
|
<!-- INFORMASI AKUN -->
|
||||||
<div class="mb-4 flex items-center gap-2 text-blue-600 font-semibold">
|
<div class="mb-4 flex items-center gap-2 text-blue-600 font-semibold">
|
||||||
<span class="text-xl">📋</span> Informasi Akun
|
|
||||||
|
<span class="text-xl">📋</span>
|
||||||
|
|
||||||
|
Informasi Akun
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- GRID -->
|
<!-- GRID -->
|
||||||
|
|
@ -52,48 +81,101 @@
|
||||||
|
|
||||||
<!-- NAMA -->
|
<!-- NAMA -->
|
||||||
<div class="bg-white rounded-xl shadow-sm p-5 text-center hover:shadow-md transition">
|
<div class="bg-white rounded-xl shadow-sm p-5 text-center hover:shadow-md transition">
|
||||||
<div class="w-12 h-12 mx-auto mb-3 bg-blue-100 rounded-full flex items-center justify-center text-blue-600">
|
|
||||||
|
<div class="w-12 h-12 mx-auto mb-3 bg-blue-100 rounded-full
|
||||||
|
flex items-center justify-center text-blue-600">
|
||||||
|
|
||||||
👤
|
👤
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-400 text-sm">Nama Lengkap</p>
|
|
||||||
|
<p class="text-gray-400 text-sm">
|
||||||
|
Nama Lengkap
|
||||||
|
</p>
|
||||||
|
|
||||||
<p class="font-semibold text-gray-800 mt-1">
|
<p class="font-semibold text-gray-800 mt-1">
|
||||||
{{ Auth::user()->name }}
|
{{ Auth::user()->name }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- EMAIL -->
|
<!-- EMAIL -->
|
||||||
<div class="bg-white rounded-xl shadow-sm p-5 text-center hover:shadow-md transition">
|
<div class="bg-white rounded-xl shadow-sm p-5 text-center hover:shadow-md transition">
|
||||||
<div class="w-12 h-12 mx-auto mb-3 bg-blue-100 rounded-full flex items-center justify-center text-blue-600">
|
|
||||||
|
<div class="w-12 h-12 mx-auto mb-3 bg-blue-100 rounded-full
|
||||||
|
flex items-center justify-center text-blue-600">
|
||||||
|
|
||||||
✉️
|
✉️
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-400 text-sm">Email</p>
|
|
||||||
|
<p class="text-gray-400 text-sm">
|
||||||
|
Email
|
||||||
|
</p>
|
||||||
|
|
||||||
<p class="font-semibold text-gray-800 mt-1">
|
<p class="font-semibold text-gray-800 mt-1">
|
||||||
{{ Auth::user()->email }}
|
{{ Auth::user()->email }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ROLE (FIX ICON) -->
|
<!-- ROLE -->
|
||||||
<div class="bg-white rounded-xl shadow-sm p-5 text-center hover:shadow-md transition">
|
<div class="bg-white rounded-xl shadow-sm p-5 text-center hover:shadow-md transition">
|
||||||
<div class="w-12 h-12 mx-auto mb-3 bg-blue-100 rounded-full flex items-center justify-center text-blue-600">
|
|
||||||
<!-- ICON GROUP -->
|
<div class="w-12 h-12 mx-auto mb-3 bg-blue-100 rounded-full
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
flex items-center justify-center text-blue-600">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M17 20h5v-1a4 4 0 00-3-3.87M9 20H4v-1a4 4 0 013-3.87m10-2.13a4 4 0 10-8 0m8 0a4 4 0 01-8 0"/>
|
<!-- ICON -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-5 h-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
|
||||||
|
<path stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
|
||||||
|
d="M17 20h5v-1a4 4 0 00-3-3.87
|
||||||
|
M9 20H4v-1a4 4 0 013-3.87
|
||||||
|
m10-2.13a4 4 0 10-8 0
|
||||||
|
m8 0a4 4 0 01-8 0"/>
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-400 text-sm">Role</p>
|
|
||||||
<p class="font-semibold text-gray-800 mt-1">Staff</p>
|
<p class="text-gray-400 text-sm">
|
||||||
|
Role
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="font-semibold text-gray-800 mt-1 capitalize">
|
||||||
|
{{ Auth::user()->role }}
|
||||||
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- STATUS -->
|
<!-- STATUS -->
|
||||||
<div class="bg-white rounded-xl shadow-sm p-5 text-center hover:shadow-md transition">
|
<div class="bg-white rounded-xl shadow-sm p-5 text-center hover:shadow-md transition">
|
||||||
<div class="w-12 h-12 mx-auto mb-3 bg-green-100 rounded-full flex items-center justify-center text-green-600">
|
|
||||||
|
<div class="w-12 h-12 mx-auto mb-3 bg-green-100 rounded-full
|
||||||
|
flex items-center justify-center text-green-600">
|
||||||
|
|
||||||
✔
|
✔
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-400 text-sm">Status Akun</p>
|
|
||||||
<span class="inline-block mt-1 text-sm bg-green-100 text-green-600 px-3 py-1 rounded-full">
|
<p class="text-gray-400 text-sm">
|
||||||
|
Status Akun
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<span class="inline-block mt-1 text-sm bg-green-100
|
||||||
|
text-green-600 px-3 py-1 rounded-full">
|
||||||
|
|
||||||
Aktif
|
Aktif
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -102,22 +184,37 @@
|
||||||
<div class="bg-white rounded-xl shadow-sm p-6 flex items-center justify-between">
|
<div class="bg-white rounded-xl shadow-sm p-6 flex items-center justify-between">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2 text-blue-600 font-semibold mb-2">
|
<div class="flex items-center gap-2 text-blue-600 font-semibold mb-2">
|
||||||
<span class="text-xl">ℹ</span> Informasi Sistem
|
|
||||||
|
<span class="text-xl">ℹ</span>
|
||||||
|
|
||||||
|
Informasi Sistem
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-gray-500 text-sm max-w-xl">
|
<p class="text-gray-500 text-sm max-w-xl">
|
||||||
|
|
||||||
Akun ini digunakan untuk mengakses sistem SENTARA
|
Akun ini digunakan untuk mengakses sistem SENTARA
|
||||||
(Sistem Analisis Sentimen Ulasan Dinas Pariwisata Jember).
|
(Sistem Analisis Sentimen Ulasan Dinas Pariwisata Jember).
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- FOOTER -->
|
<!-- FOOTER -->
|
||||||
<div class="flex justify-between text-sm text-gray-400 mt-6">
|
<div class="flex justify-between text-sm text-gray-400 mt-6">
|
||||||
<span>© 2025 SENTARA. All rights reserved.</span>
|
|
||||||
<span>Dinas Pariwisata Jember</span>
|
<span>
|
||||||
|
© 2025 SENTARA. All rights reserved.
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
Dinas Pariwisata Jember
|
||||||
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
115
routes/web.php
115
routes/web.php
|
|
@ -6,6 +6,8 @@
|
||||||
use App\Http\Controllers\UlasanController;
|
use App\Http\Controllers\UlasanController;
|
||||||
use App\Http\Controllers\AnalisisController;
|
use App\Http\Controllers\AnalisisController;
|
||||||
use App\Http\Controllers\RekomendasiController;
|
use App\Http\Controllers\RekomendasiController;
|
||||||
|
use App\Http\Controllers\UserController;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
@ -13,7 +15,6 @@
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// hanya SATU route '/'
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
return redirect('/login');
|
return redirect('/login');
|
||||||
});
|
});
|
||||||
|
|
@ -26,48 +27,114 @@
|
||||||
|
|
||||||
Route::middleware(['auth', 'verified'])->group(function () {
|
Route::middleware(['auth', 'verified'])->group(function () {
|
||||||
|
|
||||||
// ✅ DASHBOARD
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| DASHBOARD
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
Route::get('/dashboard', [DashboardController::class, 'index'])
|
Route::get('/dashboard', [DashboardController::class, 'index'])
|
||||||
->name('dashboard');
|
->name('dashboard');
|
||||||
|
|
||||||
// ✅ PROFILE (ini yang kamu tanya)
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| PROFILE
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
Route::get('/profile', function () {
|
Route::get('/profile', function () {
|
||||||
return view('profile.index');
|
return view('profile.index');
|
||||||
})->name('profile');
|
})->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'])
|
Route::get('/ulasan', [UlasanController::class, 'index'])
|
||||||
->name('ulasan.index');
|
->name('ulasan.index');
|
||||||
|
|
||||||
Route::get('/riwayat', [UlasanController::class, 'riwayat'])
|
Route::get('/riwayat', [UlasanController::class, 'riwayat'])
|
||||||
->name('riwayat.index');
|
->name('riwayat.index');
|
||||||
|
|
||||||
Route::post('/riwayat/import', [UlasanController::class, 'importRiwayat'])
|
Route::post('/riwayat/import',
|
||||||
->name('riwayat.import');
|
[UlasanController::class, 'importRiwayat']
|
||||||
|
)->name('riwayat.import');
|
||||||
|
|
||||||
Route::post('/ulasan/ambil', [UlasanController::class, 'ambilData'])
|
Route::post('/ulasan/ambil',
|
||||||
->name('ulasan.ambil');
|
[UlasanController::class, 'ambilData']
|
||||||
|
)->name('ulasan.ambil');
|
||||||
|
|
||||||
Route::delete('/ulasan/periode/kosongkan', [UlasanController::class, 'kosongkanPeriode'])
|
Route::delete('/ulasan/periode/kosongkan',
|
||||||
->name('ulasan.kosongkan-periode');
|
[UlasanController::class, 'kosongkanPeriode']
|
||||||
|
)->name('ulasan.kosongkan-periode');
|
||||||
|
|
||||||
// ✅ PROSES ANALISIS
|
/*
|
||||||
Route::post('/proses-analisis', [UlasanController::class, 'analisisData'])
|
|--------------------------------------------------------------------------
|
||||||
->name('proses.analisis');
|
| PROSES ANALISIS
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
// ✅ HASIL ANALISIS
|
Route::post('/proses-analisis',
|
||||||
Route::get('/analisis', [AnalisisController::class, 'index'])
|
[UlasanController::class, 'analisisData']
|
||||||
->name('analisis.index');
|
)->name('proses.analisis');
|
||||||
|
|
||||||
// ✅ REKOMENDASI
|
/*
|
||||||
Route::get('/rekomendasi', function() {
|
|--------------------------------------------------------------------------
|
||||||
return redirect('/dashboard?tab=rekomendasi');
|
| HASIL ANALISIS
|
||||||
})->name('rekomendasi.index');
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
@ -65,7 +65,8 @@ def read_laravel_env():
|
||||||
|
|
||||||
|
|
||||||
def env_value(env, key, default=""):
|
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"}:
|
if value in {None, "", "null", "None"}:
|
||||||
return default
|
return default
|
||||||
return value
|
return value
|
||||||
|
|
@ -734,13 +735,12 @@ def main():
|
||||||
scraper = scraper_config()
|
scraper = scraper_config()
|
||||||
destinations = selected_destinations(args.wisata)
|
destinations = selected_destinations(args.wisata)
|
||||||
conn = pymysql.connect(
|
conn = pymysql.connect(
|
||||||
host=config["host"],
|
host='localhost',
|
||||||
port=config["port"],
|
port=3306,
|
||||||
user=config["user"],
|
user='root',
|
||||||
password=config["password"],
|
password='', # isi password kalau ada
|
||||||
database=config["database"],
|
database='sistem_analisis',
|
||||||
charset="utf8mb4",
|
)
|
||||||
)
|
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -767,4 +767,4 @@ def main():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
Loading…
Reference in New Issue