#!/bin/bash
# Setup PPOB Laravel — jalankan di server via SSH / cPanel Terminal
# cd /home/fourflaz/ppob.fourflaz.my.id
# bash setup.sh

set -e

echo "=== Setup PPOB Laravel ==="

# 1. Backup dulu kalo ada file lama
if [ -f "artisan" ]; then
    echo "Backup existing files..."
    mkdir -p backup-$(date +%Y%m%d)
    mv app config database public resources routes .env artisan composer.json backup-$(date +%Y%m%d)/ 2>/dev/null || true
fi

# 2. Download Laravel 10 fresh
echo "=== Download Laravel 10 ==="
composer create-project laravel/laravel:^10.0 tmp-laravel --no-interaction

# 3. Copy custom files kita
echo "=== Copy custom PPOB files ==="
cd tmp-laravel

# ── Models ──
mkdir -p app/Models
cat > app/Models/AppConfig.php << 'PHP'
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class AppConfig extends Model
{
    protected $table = 'app_configs';
    public $timestamps = false;
    protected $fillable = ['key', 'value'];
    protected $primaryKey = 'key';
    public $incrementing = false;

    public static function get(string $key, string $default = ''): string
    {
        $c = self::where('key', $key)->first();
        return $c ? $c->value : $default;
    }

    public static function set(string $key, string $value): void
    {
        self::updateOrCreate(['key' => $key], ['value' => $value]);
    }
}
PHP

cat > app/Models/Deposit.php << 'PHP'
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Deposit extends Model
{
    protected $fillable = ['user_id', 'invoice', 'amount', 'total_payment', 'payment_method', 'payment_number', 'qr_url', 'status', 'expired_at', 'paid_at', 'webhook_raw'];
    protected $casts = ['amount' => 'integer', 'total_payment' => 'integer'];

    public function user() { return $this->belongsTo(User::class); }
}
PHP

cat > app/Models/Order.php << 'PHP'
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected $table = 'transactions';
    protected $fillable = ['user_id', 'product_code', 'product_name', 'target', 'price', 'status', 'sn', 'provider'];
    protected $casts = ['price' => 'integer'];

    public function user() { return $this->belongsTo(User::class); }
}
PHP

cat > app/Models/BalanceLog.php << 'PHP'
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class BalanceLog extends Model
{
    protected $fillable = ['user_id', 'type', 'amount', 'balance_before', 'balance_after', 'description', 'ref_id'];
    protected $casts = ['amount' => 'integer', 'balance_before' => 'integer', 'balance_after' => 'integer'];

    public function user() { return $this->belongsTo(User::class); }
}
PHP

# ── Controllers ──
mkdir -p app/Http/Controllers/Api app/Http/Controllers/Admin app/Http/Middleware

# API AuthController
cat > app/Http/Controllers/Api/AuthController.php << 'PHP'
<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class AuthController extends Controller
{
    public function login(Request $request)
    {
        $request->validate(['username' => 'required', 'password' => 'required']);
        $user = User::where('username', $request->username)->first();
        if (!$user || !Hash::check($request->password, $user->password))
            return response()->json(['status' => false, 'msg' => 'Username atau password salah'], 401);
        if ($user->status !== 'active')
            return response()->json(['status' => false, 'msg' => 'Akun dinonaktifkan'], 403);

        return response()->json([
            'status' => true, 'msg' => 'Login berhasil',
            'data' => ['token' => $user->createToken('api')->plainTextToken, 'user' => $user->only(['id','username','fullname','email','phone','balance','role'])]
        ]);
    }

    public function register(Request $request)
    {
        $request->validate(['username' => 'required|unique:users', 'password' => 'required|min:6', 'fullname' => 'required', 'phone' => 'required']);
        $user = User::create(['username' => $request->username, 'password' => Hash::make($request->password), 'fullname' => $request->fullname, 'email' => $request->email ?? '', 'phone' => $request->phone, 'balance' => 0, 'role' => 'user', 'status' => 'active']);
        return response()->json(['status' => true, 'msg' => 'Registrasi berhasil', 'data' => ['user' => $user->only(['id','username','fullname','phone'])]]);
    }

    public function me(Request $request)
    {
        return response()->json(['status' => true, 'data' => $request->user()->only(['id','username','fullname','email','phone','balance','role'])]);
    }

    public function logout(Request $request)
    {
        $request->user()->currentAccessToken()->delete();
        return response()->json(['status' => true, 'msg' => 'Logout berhasil']);
    }
}
PHP

# API ProductController
cat > app/Http/Controllers/Api/ProductController.php << 'PHP'
<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Product;
use App\Models\Category;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    public function list(Request $request)
    {
        $q = Product::where('status', 'active');
        if ($request->provider) $q->where('provider', $request->provider);
        if ($request->category_id) $q->where('category_id', $request->category_id);
        if ($request->search) $q->where(function($qq) use ($request) { $qq->where('name','like',"%{$request->search}%")->orWhere('code','like',"%{$request->search}%"); });
        return response()->json(['status' => true, 'data' => $q->orderBy('provider')->orderBy('name')->get()]);
    }

    public function categories()
    {
        return response()->json(['status' => true, 'data' => Category::withCount('products')->get()]);
    }
}
PHP

# API DepositController (Pakasir)
cat > app/Http/Controllers/Api/DepositController.php << 'PHP'
<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Deposit;
use App\Models\AppConfig;
use App\Models\User;
use App\Models\BalanceLog;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class DepositController extends Controller
{
    public function create(Request $request)
    {
        $request->validate(['amount' => 'required|integer|min:5000|max:2000000']);
        $slug = AppConfig::get('pakasir_slug');
        $apiKey = AppConfig::get('pakasir_api_key');
        if (!$slug || !$apiKey) return response()->json(['status' => false, 'msg' => 'Pembayaran belum dikonfigurasi admin'], 503);

        $invoice = 'INV-' . strtoupper(Str::random(10));
        $amount = (int) $request->amount;
        $paymentUrl = "https://app.pakasir.com/pay/{$slug}/{$amount}?order_id={$invoice}";

        Deposit::create(['user_id' => $request->user()->id, 'invoice' => $invoice, 'amount' => $amount, 'payment_method' => 'pakasir', 'status' => 'pending', 'payment_number' => $paymentUrl, 'expired_at' => now()->addHour()]);

        return response()->json(['status' => true, 'msg' => 'Silakan lanjutkan pembayaran', 'data' => ['invoice' => $invoice, 'amount' => $amount, 'payment_url' => $paymentUrl, 'expired_at' => now()->addHour()]]);
    }

    public function history(Request $request)
    {
        return response()->json(['status' => true, 'data' => Deposit::where('user_id', $request->user()->id)->orderBy('created_at','desc')->paginate(20)]);
    }

    public function webhook(Request $request)
    {
        $data = $request->all();
        $invoice = $data['order_id'] ?? '';
        $status = $data['status'] ?? '';
        $isPaid = ($status === 'completed');
        if (!$invoice) return response()->json(['status' => false, 'msg' => 'No order_id'], 400);

        $deposit = Deposit::where('invoice', $invoice)->first();
        if (!$deposit) return response()->json(['status' => false, 'msg' => 'Invoice not found'], 404);
        if ($deposit->status === 'paid') return response()->json(['status' => true, 'msg' => 'Already paid']);

        $deposit->update(['webhook_raw' => json_encode($data)]);

        if ($isPaid) {
            \DB::beginTransaction();
            try {
                $deposit->update(['status' => 'paid', 'paid_at' => now()]);
                $user = User::find($deposit->user_id);
                $balBefore = $user->balance;
                $user->increment('balance', $deposit->amount);
                BalanceLog::create(['user_id' => $user->id, 'type' => 'deposit', 'amount' => $deposit->amount, 'balance_before' => $balBefore, 'balance_after' => $user->balance, 'description' => "Deposit via Pakasir - {$invoice}", 'ref_id' => $invoice]);

                $fcmToken = $user->fcm_token;
                if ($fcmToken) { try { \Illuminate\Support\Facades\Http::withHeaders(['Content-Type'=>'application/json','Authorization'=>'key='.AppConfig::get('fcm_server_key','')])->post('https://fcm.googleapis.com/fcm/send',['to'=>$fcmToken,'notification'=>['title'=>'Deposit Berhasil','body'=>'Saldo Rp '.number_format($deposit->amount).' telah ditambahkan','sound'=>'default']]); } catch(\Throwable $e) {} }

                \DB::commit();
                return response()->json(['status' => true, 'msg' => 'OK']);
            } catch (\Throwable $e) { \DB::rollBack(); return response()->json(['status' => false, 'msg' => $e->getMessage()], 500); }
        }
        return response()->json(['status' => true, 'msg' => 'received']);
    }
}
PHP

# API OrderController + BalanceController
cat > app/Http/Controllers/Api/OrderController.php << 'PHP'
<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Order;
use App\Models\Product;
use Illuminate\Http\Request;

class OrderController extends Controller
{
    public function create(Request $request)
    {
        $request->validate(['product_code'=>'required','target'=>'required']);
        $user = $request->user();
        $product = Product::where('code', $request->product_code)->where('status','active')->first();
        if (!$product) return response()->json(['status'=>false,'msg'=>'Produk tidak tersedia'],404);
        if ($product->stock < 1) return response()->json(['status'=>false,'msg'=>'Stok habis'],400);
        if ($user->balance < $product->sell_price) return response()->json(['status'=>false,'msg'=>'Saldo tidak mencukupi'],400);

        $order = Order::create(['user_id'=>$user->id,'product_code'=>$product->code,'product_name'=>$product->name,'target'=>$request->target,'price'=>$product->sell_price,'status'=>'pending','provider'=>$product->provider]);
        $user->decrement('balance', $product->sell_price);
        $order->update(['status'=>'success','sn'=>'SUCCESS-'.strtoupper(\Illuminate\Support\Str::random(8))]);

        return response()->json(['status'=>true,'msg'=>'Pesanan berhasil','data'=>$order]);
    }

    public function history(Request $request)
    {
        return response()->json(['status'=>true,'data'=>Order::where('user_id',$request->user()->id)->orderBy('created_at','desc')->paginate(20)]);
    }

    public function status(Request $request)
    {
        $request->validate(['order_id'=>'required']);
        $order = Order::where('id',$request->order_id)->where('user_id',$request->user()->id)->first();
        if (!$order) return response()->json(['status'=>false,'msg'=>'Pesanan tidak ditemukan'],404);
        return response()->json(['status'=>true,'data'=>$order]);
    }
}
PHP

cat > app/Http/Controllers/Api/BalanceController.php << 'PHP'
<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\BalanceLog;
use Illuminate\Http\Request;

class BalanceController extends Controller
{
    public function show(Request $request) { return response()->json(['status'=>true,'data'=>['balance'=>(int)$request->user()->balance]]); }
    public function history(Request $request) { return response()->json(['status'=>true,'data'=>BalanceLog::where('user_id',$request->user()->id)->orderBy('created_at','desc')->paginate(20)]); }
}
PHP

# API PageController (custom HTML from DB)
cat > app/Http/Controllers/Api/PageController.php << 'PHP'
<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\AppConfig;
use Illuminate\Http\Request;

class PageController extends Controller
{
    public function render(Request $request, $page)
    {
        $key = 'html_' . str_replace('-', '_', $page);
        $html = AppConfig::get($key, '');
        if (!$html) return response()->json(['status'=>false,'msg'=>'Page not found'],404);

        $user = $request->user();
        $saldo = $user ? (int) $user->balance : 0;
        $url = config('app.url');
        $html = str_replace(['{{saldo_user}}','{{url_website_baru}}'], [$saldo, $url], $html);
        $html = preg_replace('/\{\{.*?\}\}/', '', $html);

        return response($html)->header('Content-Type', 'text/html');
    }
}
PHP

# Admin controllers
cat > app/Http/Controllers/Admin/DashboardController.php << 'PHP'
<?php
namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\Order;
use App\Models\Deposit;

class DashboardController extends Controller
{
    public function index()
    {
        return response()->json(['status'=>true,'data'=>[
            'total_users' => User::count(),
            'total_orders' => Order::count(),
            'total_deposits' => Deposit::where('status','paid')->count(),
            'revenue' => (int) Order::where('status','success')->sum('price'),
            'recent_orders' => Order::with('user')->latest()->take(10)->get(),
            'recent_deposits' => Deposit::with('user')->latest()->take(10)->get(),
        ]]);
    }
}
PHP

cat > app/Http/Controllers/Admin/ConfigController.php << 'PHP'
<?php
namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\AppConfig;
use Illuminate\Http\Request;

class ConfigController extends Controller
{
    public function index() { return response()->json(['status'=>true,'data'=>AppConfig::orderBy('key')->get()]); }
    public function update(Request $request) { $request->validate(['key'=>'required']); AppConfig::set($request->key, $request->value??''); return response()->json(['status'=>true,'msg'=>'Config saved']); }
    public function pakasir() { return response()->json(['status'=>true,'data'=>['slug'=>AppConfig::get('pakasir_slug'),'api_key'=>AppConfig::get('pakasir_api_key'),'webhook_url'=>url('/api/webhook/pakasir')]]); }
    public function updatePakasir(Request $request) { $request->validate(['slug'=>'required','api_key'=>'required']); AppConfig::set('pakasir_slug',$request->slug); AppConfig::set('pakasir_api_key',$request->api_key); return response()->json(['status'=>true,'msg'=>'Pengaturan Pakasir disimpan']); }
}
PHP

# ── Routes ──
cat > routes/api.php << 'PHP'
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\ProductController;
use App\Http\Controllers\Api\OrderController;
use App\Http\Controllers\Api\DepositController;
use App\Http\Controllers\Api\BalanceController;
use App\Http\Controllers\Api\PageController;

Route::post('/auth/login', [AuthController::class, 'login']);
Route::post('/auth/register', [AuthController::class, 'register']);

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/auth/me', [AuthController::class, 'me']);
    Route::post('/auth/logout', [AuthController::class, 'logout']);
    Route::get('/products', [ProductController::class, 'list']);
    Route::get('/products/categories', [ProductController::class, 'categories']);
    Route::post('/order', [OrderController::class, 'create']);
    Route::get('/order/history', [OrderController::class, 'history']);
    Route::post('/order/status', [OrderController::class, 'status']);
    Route::get('/balance', [BalanceController::class, 'show']);
    Route::get('/balance/history', [BalanceController::class, 'history']);
    Route::post('/deposit', [DepositController::class, 'create']);
    Route::get('/deposit/history', [DepositController::class, 'history']);
});

Route::post('/webhook/pakasir', [DepositController::class, 'webhook']);
Route::get('/page/{page}', [PageController::class, 'render'])->where('page', '[a-z_-]+');
PHP

# ── Admin Routes (web.php) ──
# Backup original routes/web.php first
cp routes/web.php routes/web.php.backup

# Append admin routes after the default welcome route
cat >> routes/web.php << 'WEB'

// ── Admin Login ──
Route::get('/admin/login', function () { return view('admin.login'); });
Route::post('/admin/login', function (\Illuminate\Http\Request $r) {
    $user = \App\Models\User::where('username', $r->username)->first();
    if ($user && \Illuminate\Support\Facades\Hash::check($r->password, $user->password) && $user->role === 'admin') {
        auth()->login($user); $r->session()->regenerate(); return redirect('/admin');
    }
    return back()->withErrors(['msg' => 'Login gagal']);
});
Route::post('/admin/logout', function (\Illuminate\Http\Request $r) { auth()->logout(); $r->session()->invalidate(); return redirect('/admin/login'); });

Route::middleware(['auth', 'admin'])->prefix('admin')->group(function () {
    Route::get('/', fn() => view('admin.dashboard'));
    Route::get('/users', fn() => view('admin.users'));
    Route::get('/products', fn() => view('admin.products'));
    Route::get('/deposits', fn() => view('admin.deposits'));
    Route::get('/orders', fn() => view('admin.orders'));
    Route::get('/config', fn() => view('admin.config'));
    Route::get('/notifications', fn() => view('admin.notifications'));
    Route::get('/pembayaran', fn() => view('admin.pembayaran'));

    Route::get('/api/dashboard', [App\Http\Controllers\Admin\DashboardController::class, 'index']);
    Route::get('/api/config', [App\Http\Controllers\Admin\ConfigController::class, 'index']);
    Route::post('/api/config', [App\Http\Controllers\Admin\ConfigController::class, 'update']);
    Route::get('/api/config/pakasir', [App\Http\Controllers\Admin\ConfigController::class, 'pakasir']);
    Route::post('/api/config/pakasir', [App\Http\Controllers\Admin\ConfigController::class, 'updatePakasir']);
    Route::get('/api/users', function() { return response()->json(['status'=>true,'data'=>\App\Models\User::latest()->paginate(50)]); });
    Route::post('/api/users/{id}', function(\Illuminate\Http\Request $r, $id) { $user = \App\Models\User::findOrFail($id); $user->update($r->only(['fullname','email','phone','balance','role','status'])); return response()->json(['status'=>true,'msg'=>'User updated']); });
    Route::get('/api/products', function() { return response()->json(['status'=>true,'data'=>\App\Models\Product::latest()->paginate(100)]); });
    Route::post('/api/products', function(\Illuminate\Http\Request $r) { return response()->json(['status'=>true,'msg'=>'Created','data'=>\App\Models\Product::create($r->all())]); });
    Route::post('/api/products/{id}', function(\Illuminate\Http\Request $r, $id) { \App\Models\Product::findOrFail($id)->update($r->all()); return response()->json(['status'=>true,'msg'=>'Updated']); });
    Route::delete('/api/products/{id}', function($id) { \App\Models\Product::findOrFail($id)->delete(); return response()->json(['status'=>true,'msg'=>'Deleted']); });
    Route::get('/api/deposits', function() { return response()->json(['status'=>true,'data'=>\App\Models\Deposit::with('user')->latest()->paginate(50)]); });
    Route::get('/api/orders', function() { return response()->json(['status'=>true,'data'=>\App\Models\Order::with('user')->latest()->paginate(50)]); });
    Route::post('/api/notifications/send', [App\Http\Controllers\Admin\NotificationController::class, 'send']);
});

// ── Custom HTML pages ──
Route::get('/{page}', function ($page) {
    $html = \App\Models\AppConfig::get('html_' . str_replace('-', '_', $page), '');
    if (!$html) abort(404);
    $user = auth()->user(); $saldo = $user ? (int)$user->balance : 0;
    $html = str_replace(['{{saldo_user}}','{{url_website_baru}}'], [$saldo, config('app.url')], $html);
    $html = preg_replace('/\{\{.*?\}\}/', '', $html);
    return response($html)->header('Content-Type', 'text/html');
})->where('page', '(?!admin|api).*');
WEB

# ── Admin Middleware ──
cat > app/Http/Middleware/AdminMiddleware.php << 'PHP'
<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class AdminMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        if (!$request->user() || $request->user()->role !== 'admin')
            return response()->json(['status'=>false,'msg'=>'Akses ditolak'],403);
        if ($request->user()->status !== 'active')
            return response()->json(['status'=>false,'msg'=>'Akun dinonaktifkan'],403);
        return $next($request);
    }
}
PHP

cat > app/Http/Controllers/Admin/NotificationController.php << 'PHP'
<?php
namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\AppConfig;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class NotificationController extends Controller
{
    public function send(Request $request)
    {
        $request->validate(['title'=>'required','body'=>'required']);
        $count = 0;
        $key = AppConfig::get('fcm_server_key', '');
        User::whereNotNull('fcm_token')->where('fcm_token','!=','')->chunk(100, function($users) use ($request, $key, &$count) {
            foreach ($users as $user) {
                if (!$user->fcm_token) continue;
                try {
                    Http::withHeaders(['Content-Type'=>'application/json','Authorization'=>'key='.$key])->post('https://fcm.googleapis.com/fcm/send', ['to'=>$user->fcm_token,'notification'=>['title'=>$request->title,'body'=>$request->body,'sound'=>'default']]);
                    $count++;
                } catch(\Throwable $e) {}
            }
        });
        return response()->json(['status'=>true,'msg'=>"Notifikasi terkirim ke {$count} perangkat"]);
    }
}
PHP

# ── Register AdminMiddleware in Kernel ──
sed -i "s/'auth' => \\\\Illuminate\\\\Auth\\\\Middleware\\\\Authenticate::class/'auth' => \\\\Illuminate\\\\Auth\\\\Middleware\\\\Authenticate::class,\\n        'admin' => \\\\App\\\\Http\\\\Middleware\\\\AdminMiddleware::class,/' app/Http/Kernel.php

# ── Admin Views ──
mkdir -p resources/views/admin

# Login view
cat > resources/views/admin/login.blade.php << 'HTML'
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Login - Admin PPOB</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><style>body{display:flex;align-items:center;justify-content:center;min-height:100vh;background:#f5f5f9} .card{width:400px;border:none;border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,.08);padding:2rem}</style></head><body><div class="card"><h4 class="fw-bold mb-1">Admin PPOB</h4><p class="text-muted mb-3">Silakan login</p>@if($errors->any())<div class="alert alert-danger">{{$errors->first('msg')??'Login gagal'}}</div>@endif<form method="POST">@csrf<div class="mb-3"><label class="form-label">Username</label><input type="text" name="username" class="form-control" required></div><div class="mb-3"><label class="form-label">Password</label><input type="password" name="password" class="form-control" required></div><button type="submit" class="btn btn-primary w-100">Login</button></form></div></body></html>
HTML

# Layout
cat > resources/views/admin/layouts/app.blade.php << 'HTML'
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>@yield('title','Admin PPOB')</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><link href="https://cdn.jsdelivr.net/npm/remixicon@4.6.0/fonts/remixicon.min.css" rel="stylesheet"><style>body{display:flex;min-height:100vh;background:#f5f5f9;font-family:'Inter',sans-serif;margin:0}.sidebar{width:240px;background:#1e1e2d;color:#a2a3b7;display:flex;flex-direction:column;position:fixed;height:100vh;z-index:100}.sidebar .brand{padding:1.2rem;border-bottom:1px solid rgba(255,255,255,.08)}.sidebar .brand h5{color:#fff;font-weight:700;margin:0}.sidebar nav{padding:.6rem}.sidebar nav a{display:flex;align-items:center;gap:.7rem;padding:.55rem .7rem;border-radius:6px;color:#a2a3b7;text-decoration:none;font-size:.85rem}.sidebar nav a:hover{background:rgba(255,255,255,.06);color:#fff}.sidebar nav a.active{background:#5867dd;color:#fff}.sidebar nav a i{font-size:1.1rem;width:20px}.sidebar .user-info{padding:.8rem 1rem;border-top:1px solid rgba(255,255,255,.08);margin-top:auto}.main{margin-left:240px;flex:1;padding:1.5rem 2rem}.topbar h4{font-weight:600}.table{font-size:.85rem}.btn-primary{background:#5867dd;border-color:#5867dd}.ri-22px{font-size:1.4rem}@media(max-width:768px){.sidebar{width:60px}.sidebar .brand h5,.sidebar nav a span,.sidebar .user-info{display:none}.sidebar nav a{justify-content:center;padding:.55rem}.main{margin-left:60px;padding:.8rem}}</style></head><body><div class="sidebar"><div class="brand"><h5>PPOB</h5></div><nav><a href="{{url('/admin')}}" class="{{request()->is('admin')?'active':''}}"><i class="ri ri-dashboard-line"></i><span>Dashboard</span></a><a href="{{url('/admin/users')}}" class="{{request()->is('admin/users*')?'active':''}}"><i class="ri ri-group-line"></i><span>Users</span></a><a href="{{url('/admin/products')}}" class="{{request()->is('admin/products*')?'active':''}}"><i class="ri ri-box-3-line"></i><span>Produk</span></a><a href="{{url('/admin/deposits')}}" class="{{request()->is('admin/deposits*')?'active':''}}"><i class="ri ri-bank-card-line"></i><span>Deposit</span></a><a href="{{url('/admin/orders')}}" class="{{request()->is('admin/orders*')?'active':''}}"><i class="ri ri-exchange-dollar-line"></i><span>Transaksi</span></a><a href="{{url('/admin/config')}}" class="{{request()->is('admin/config*')?'active':''}}"><i class="ri ri-settings-4-line"></i><span>Konfigurasi</span></a><a href="{{url('/admin/notifications')}}" class="{{request()->is('admin/notifications*')?'active':''}}"><i class="ri ri-notification-3-line"></i><span>Notifikasi</span></a><a href="{{url('/admin/pembayaran')}}" class="{{request()->is('admin/pembayaran*')?'active':''}}"><i class="ri ri-bank-card-line"></i><span>Pembayaran</span></a></nav><div class="user-info"><small>{{auth()->user()->fullname??auth()->user()->username}}</small><a href="#" onclick="event.preventDefault();document.getElementById('lf').submit();">Logout</a><form id="lf" action="{{url('/admin/logout')}}" method="POST" style="display:none">@csrf</form></div></div><div class="main">@yield('content')</div><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>@stack('scripts')</body></html>
HTML

# Dashboard view
cat > resources/views/admin/dashboard.blade.php << 'HTML'
@extends('admin.layouts.app')@section('title','Dashboard')@section('content')
<h4 class="fw-bold py-3 mb-4">Dashboard</h4>
<div class="row g-4 mb-4" id="statsRow">
  <div class="col-sm-6 col-xl-3"><div class="card"><div class="card-body"><h6 class="text-muted">Users</h6><h3 id="statUsers">-</h3></div></div></div>
  <div class="col-sm-6 col-xl-3"><div class="card"><div class="card-body"><h6 class="text-muted">Transaksi</h6><h3 id="statOrders">-</h3></div></div></div>
  <div class="col-sm-6 col-xl-3"><div class="card"><div class="card-body"><h6 class="text-muted">Deposit Paid</h6><h3 id="statDeposits">-</h3></div></div></div>
  <div class="col-sm-6 col-xl-3"><div class="card"><div class="card-body"><h6 class="text-muted">Revenue</h6><h3 id="statRevenue">-</h3></div></div></div>
</div>
<div class="row"><div class="col-md-6"><div class="card"><h5 class="card-header">Transaksi Terbaru</h5><div class="table-responsive"><table class="table table-sm mb-0" id="recentOrders"><tbody></tbody></table></div></div></div><div class="col-md-6"><div class="card"><h5 class="card-header">Deposit Terbaru</h5><div class="table-responsive"><table class="table table-sm mb-0" id="recentDeposits"><tbody></tbody></table></div></div></div></div>
@push('scripts')
<script>
async function load(){try{
  const r=await(await fetch('/admin/api/dashboard')).json();if(!r.status)return;const d=r.data;
  document.getElementById('statUsers').textContent=(d.total_users||0).toLocaleString();
  document.getElementById('statOrders').textContent=(d.total_orders||0).toLocaleString();
  document.getElementById('statDeposits').textContent=(d.total_deposits||0).toLocaleString();
  document.getElementById('statRevenue').textContent='Rp '+(d.revenue||0).toLocaleString('id-ID');
  document.getElementById('recentOrders').querySelector('tbody').innerHTML=(d.recent_orders||[]).map(o=>'<tr><td><strong>'+(o.user?.username||'')+'</strong><br><small>'+o.product_name+'</small></td><td><span class="badge bg-'+(o.status==='success'?'success':'warning')+'">'+o.status+'</span></td><td><strong>Rp '+(o.price||0).toLocaleString('id-ID')+'</strong></td></tr>').join('')||'<tr><td class="text-center text-muted py-3">Belum ada</td></tr>';
  document.getElementById('recentDeposits').querySelector('tbody').innerHTML=(d.recent_deposits||[]).map(dp=>'<tr><td><strong>'+(dp.user?.username||'')+'</strong><br><small>'+dp.invoice+'</small></td><td><span class="badge bg-'+(dp.status==='paid'?'success':'warning')+'">'+dp.status+'</span></td><td><strong>Rp '+(dp.amount||0).toLocaleString('id-ID')+'</strong></td></tr>').join('')||'<tr><td class="text-center text-muted py-3">Belum ada</td></tr>';
}catch(e){}}
load();
</script>
@endpush
@endsection
HTML

# Pembayaran view
cat > resources/views/admin/pembayaran.blade.php << 'HTML'
@extends('admin.layouts.app')@section('title','Pembayaran')@section('content')
<h4 class="fw-bold py-3 mb-4">Pembayaran (Pakasir)</h4>
<div class="row"><div class="col-md-6"><div class="card"><div class="card-body"><h5>Pengaturan Pakasir</h5>
<form id="pakasirForm"><div class="mb-3"><label>Project Slug</label><input type="text" id="slug" class="form-control" required></div><div class="mb-3"><label>API Key</label><input type="text" id="apiKey" class="form-control" required></div><button type="submit" class="btn btn-primary">Simpan</button></form></div></div></div>
<div class="col-md-6"><div class="card"><div class="card-body"><h5>Webhook URL</h5><p class="text-muted">Isi di dashboard Pakasir:</p><div class="input-group"><input type="text" id="whUrl" class="form-control" readonly><button class="btn btn-outline-primary" onclick="navigator.clipboard.writeText(document.getElementById('whUrl').value)">Copy</button></div></div></div></div></div>
@push('scripts')
<script>
async function load(){try{const r=await(await fetch('/admin/api/config/pakasir')).json();if(!r.status)return;document.getElementById('slug').value=r.data.slug||'';document.getElementById('apiKey').value=r.data.api_key||'';document.getElementById('whUrl').value=r.data.webhook_url||'';}catch(e){}}
document.getElementById('pakasirForm').addEventListener('submit',async function(e){e.preventDefault();await fetch('/admin/api/config/pakasir',{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-TOKEN':'{{csrf_token()}}'},body:JSON.stringify({slug:document.getElementById('slug').value,api_key:document.getElementById('apiKey').value})});alert('Disimpan');});
load();
</script>
@endpush
@endsection
HTML

# Other admin views (minimal)
cat > resources/views/admin/users.blade.php << 'HTML'
@extends('admin.layouts.app')@section('title','Users')@section('content')
<h4 class="fw-bold py-3 mb-4">Data Users</h4>
<div class="card"><table class="table table-hover" id="usersTable"><thead><tr><th>ID</th><th>Username</th><th>Nama</th><th>Saldo</th><th>Role</th><th>Status</th></tr></thead><tbody id="usersBody"></tbody></table></div>
@push('scripts')
<script>
(async function(){try{const r=await(await fetch('/admin/api/users')).json();if(!r.status)return;document.getElementById('usersBody').innerHTML=(r.data.data||[]).map(u=>'<tr><td>'+u.id+'</td><td><strong>'+u.username+'</strong></td><td>'+(u.fullname||'')+'</td><td>Rp '+(u.balance||0).toLocaleString('id-ID')+'</td><td><span class="badge bg-'+(u.role==='admin'?'primary':'secondary')+'">'+u.role+'</span></td><td><span class="badge bg-'+(u.status==='active'?'success':'danger')+'">'+u.status+'</span></td></tr>').join('');}catch(e){}})();
</script>
@endpush
@endsection
HTML

cat > resources/views/admin/products.blade.php << 'HTML'
@extends('admin.layouts.app')@section('title','Produk')@section('content')
<h4 class="fw-bold py-3 mb-4">Produk PPOB</h4>
<div class="card"><table class="table table-hover"><thead><tr><th>ID</th><th>Provider</th><th>Kode</th><th>Nama</th><th>Modal</th><th>Jual</th><th>Stok</th><th>Status</th></tr></thead><tbody id="productsBody"></tbody></table></div>
@push('scripts')
<script>
(async function(){try{const r=await(await fetch('/admin/api/products')).json();if(!r.status)return;document.getElementById('productsBody').innerHTML=(r.data.data||[]).map(p=>'<tr><td>'+p.id+'</td><td>'+p.provider+'</td><td>'+p.code+'</td><td>'+p.name+'</td><td>Rp '+(p.base_price||0).toLocaleString('id-ID')+'</td><td>Rp '+(p.sell_price||0).toLocaleString('id-ID')+'</td><td>'+p.stock+'</td><td><span class="badge bg-'+(p.status==='active'?'success':'secondary')+'">'+p.status+'</span></td></tr>').join('');}catch(e){}})();
</script>
@endpush
@endsection
HTML

cat > resources/views/admin/deposits.blade.php << 'HTML'
@extends('admin.layouts.app')@section('title','Deposit')@section('content')
<h4 class="fw-bold py-3 mb-4">Riwayat Deposit</h4>
<div class="card"><table class="table table-hover"><thead><tr><th>ID</th><th>User</th><th>Jumlah</th><th>Invoice</th><th>Status</th><th>Tanggal</th></tr></thead><tbody id="depositsBody"></tbody></table></div>
@push('scripts')
<script>
(async function(){try{const r=await(await fetch('/admin/api/deposits')).json();if(!r.status)return;document.getElementById('depositsBody').innerHTML=(r.data.data||[]).map(d=>'<tr><td>'+d.id+'</td><td>'+(d.user?.username||'')+'</td><td>Rp '+(d.amount||0).toLocaleString('id-ID')+'</td><td>'+d.invoice+'</td><td><span class="badge bg-'+(d.status==='paid'?'success':'warning')+'">'+d.status+'</span></td><td>'+d.created_at+'</td></tr>').join('');}catch(e){}})();
</script>
@endpush
@endsection
HTML

cat > resources/views/admin/orders.blade.php << 'HTML'
@extends('admin.layouts.app')@section('title','Transaksi')@section('content')
<h4 class="fw-bold py-3 mb-4">Riwayat Transaksi</h4>
<div class="card"><table class="table table-hover"><thead><tr><th>ID</th><th>User</th><th>Produk</th><th>Target</th><th>Harga</th><th>Status</th></tr></thead><tbody id="ordersBody"></tbody></table></div>
@push('scripts')
<script>
(async function(){try{const r=await(await fetch('/admin/api/orders')).json();if(!r.status)return;document.getElementById('ordersBody').innerHTML=(r.data.data||[]).map(o=>'<tr><td>'+o.id+'</td><td>'+(o.user?.username||'')+'</td><td>'+o.product_name+'</td><td>'+o.target+'</td><td>Rp '+(o.price||0).toLocaleString('id-ID')+'</td><td><span class="badge bg-'+(o.status==='success'?'success':'warning')+'">'+o.status+'</span></td></tr>').join('');}catch(e){}})();
</script>
@endpush
@endsection
HTML

cat > resources/views/admin/config.blade.php << 'HTML'
@extends('admin.layouts.app')@section('title','Konfigurasi')@section('content')
<h4 class="fw-bold py-3 mb-4">Konfigurasi Aplikasi</h4>
<div class="card"><table class="table table-hover"><thead><tr><th>Key</th><th>Value</th></tr></thead><tbody id="configsBody"></tbody></table></div>
@push('scripts')
<script>
(async function(){try{const r=await(await fetch('/admin/api/config')).json();if(!r.status)return;document.getElementById('configsBody').innerHTML=(r.data||[]).map(c=>'<tr><td><code>'+c.key+'</code></td><td style="max-width:400px;overflow:hidden"><small>'+(c.value||'').substring(0,200)+'</small></td></tr>').join('');}catch(e){}})();
</script>
@endpush
@endsection
HTML

cat > resources/views/admin/notifications.blade.php << 'HTML'
@extends('admin.layouts.app')@section('title','Notifikasi')@section('content')
<h4 class="fw-bold py-3 mb-4">Kirim Notifikasi</h4>
<div class="card" style="max-width:600px"><div class="card-body">
<form id="notifForm"><div class="mb-3"><label>Judul</label><input type="text" id="ntitle" class="form-control" required></div><div class="mb-3"><label>Pesan</label><textarea id="nbody" class="form-control" rows="4" required></textarea></div><button type="submit" class="btn btn-primary">Kirim</button></form>
<div id="notifResult" class="mt-3"></div></div></div>
@push('scripts')
<script>
document.getElementById('notifForm').addEventListener('submit',async function(e){e.preventDefault();const b=this.querySelector('button');b.disabled=true;b.textContent='Mengirim...';const r=await(await fetch('/admin/api/notifications/send',{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-TOKEN':'{{csrf_token()}}'},body:JSON.stringify({title:document.getElementById('ntitle').value,body:document.getElementById('nbody').value})})).json();document.getElementById('notifResult').innerHTML='<div class="alert alert-success">'+(r.msg||'Terkirim')+'</div>';b.disabled=false;b.textContent='Kirim';});
</script>
@endpush
@endsection
HTML

# ── Create admin layout directory ──
mkdir -p resources/views/admin/layouts
# Already created layout above

# ── Copy .env & configure DB ──
cp .env.example .env
# Set DB credentials
sed -i 's/DB_DATABASE=laravel/DB_DATABASE=fourflaz_jaja/' .env
sed -i 's/DB_USERNAME=root/DB_USERNAME=fourflaz_arsadcell/' .env
sed -i 's/DB_PASSWORD=/DB_PASSWORD=@Rambutan1/' .env

# ── Generate key ──
php artisan key:generate

# ── Clean up ──
rm -f resources/views/welcome.blade.php
rm -f routes/web.php.backup

cd ..
# Move custom files out of temp directory
cp -r tmp-laravel/* .
rm -rf tmp-laravel

# ── Done ──
echo ""
echo "=== SETUP SELESAI ==="
echo "Admin: https://ppob.fourflaz.my.id/admin/login"
echo "Login dengan user yg role=admin"
echo ""
echo "Langkah selanjutnya:"
echo "1. Buka menu Pembayaran → isi slug & API key Pakasir"
echo "2. Upload file topup_final.html & update_topup.php"
echo "3. Set webhook di dashboard Pakasir"
