Lupa
This commit is contained in:
parent
fe3b516c80
commit
9bb19275ab
|
|
@ -0,0 +1,65 @@
|
|||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||
from sklearn.naive_bayes import MultinomialNB
|
||||
import numpy as np
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app) # Agar bisa diakses dari HTML berbeda domain/port
|
||||
|
||||
# 1. DATA LATIH (Training Data)
|
||||
# Kita ajari bot dengan contoh kalimat dan labelnya
|
||||
data_latih = [
|
||||
("halo", "salam"),
|
||||
("selamat pagi", "salam"),
|
||||
("berapa harga rumah", "harga"),
|
||||
("biaya cicilan mahal gak", "harga"),
|
||||
("saya mau kredit rumah", "kpr"),
|
||||
("proses KPR gimana", "kpr"),
|
||||
("lokasi kantor dimana", "lokasi"),
|
||||
("alamat properti di mana", "lokasi")
|
||||
]
|
||||
|
||||
texts, labels = zip(*data_latih)
|
||||
|
||||
# 2. PROSES TRAINING (Mesin Belajar)
|
||||
# Mengubah teks jadi angka (Vector)
|
||||
vectorizer = TfidfVectorizer()
|
||||
X = vectorizer.fit_transform(texts)
|
||||
y = np.array(labels)
|
||||
|
||||
# Melatih model Naive Bayes
|
||||
model = MultinomialNB()
|
||||
model.fit(X, y)
|
||||
|
||||
# 3. API ENDPOINT (Tempat Browser Menghubungi)
|
||||
@app.route('/chat', methods=['POST'])
|
||||
def chat():
|
||||
user_input = request.json.get('message')
|
||||
|
||||
# Ubah input user jadi vektor sesuai pola training
|
||||
user_vector = vectorizer.transform([user_input])
|
||||
|
||||
# Prediksi intent
|
||||
prediksi = model.predict(user_vector)[0]
|
||||
confidence = max(model.predict_proba(user_vector)[0])
|
||||
|
||||
# Logika Respon Berdasarkan Hasil Prediksi ML
|
||||
respon = ""
|
||||
if confidence < 0.5: # Jika model kurang yakin
|
||||
respon = "Maaf, saya kurang mengerti. Bisa diulang dengan bahasa lain?"
|
||||
elif prediksi == 'salam':
|
||||
respon = "Halo! Selamat datang di PropertiHarmoni. Ada yang bisa dibantu? 😊"
|
||||
elif prediksi == 'harga':
|
||||
respon = "Harga kami bervariasi mulai 500jt hingga 3M. Mau cari di range berapa?"
|
||||
elif prediksi == 'kpr':
|
||||
respon = "Untuk KPR, kami kerjasama dengan BCA, Mandiri, dan BNI. Mau simulasi cicilan?"
|
||||
elif prediksi == 'lokasi':
|
||||
respon = "Kantor kami ada di Jl. Raya Malang No. 10. Mau kirim Google Maps-nya?"
|
||||
else:
|
||||
respon = "Mohon maaf, tim kami akan segera membalas pertanyaan spesifik Anda."
|
||||
|
||||
return jsonify({'response': respon, 'intent': prediksi})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
|
|
@ -16,6 +16,8 @@ public function index()
|
|||
}
|
||||
|
||||
// Proses login
|
||||
// AuthController.php
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
|
|
@ -27,29 +29,30 @@ public function login(Request $request)
|
|||
'password.required' => 'Password wajib diisi',
|
||||
]);
|
||||
|
||||
// Cari user berdasarkan email_user (kolom kustom)
|
||||
$user = User::where('email_user', $request->email)->first();
|
||||
|
||||
// Verifikasi password
|
||||
// AuthController.php - method login()
|
||||
|
||||
if ($user && Hash::check($request->password, $user->password_user)) {
|
||||
|
||||
// Login user
|
||||
Auth::login($user);
|
||||
$request->session()->regenerate();
|
||||
|
||||
// Redirect berdasarkan role
|
||||
if ($user->role_user === 'admin') {
|
||||
// ✅ Lebih baik pakai route name daripada hardcoded URL
|
||||
return redirect()->route('admin.welcome');
|
||||
// Jika admin di project terpisah, gunakan:
|
||||
// return redirect('http://127.0.0.1:8001/admin');
|
||||
// 🔥 AMBIL PARAMETER 'redirect' (pakai input() lebih reliable)
|
||||
$redirectUrl = $request->input('redirect');
|
||||
|
||||
if ($redirectUrl && str_starts_with($redirectUrl, '/')) {
|
||||
return redirect($redirectUrl);
|
||||
}
|
||||
|
||||
// Redirect user biasa
|
||||
return redirect()->intended(route('halaman-katalog'));
|
||||
// Fallback jika redirect tidak valid / tidak ada
|
||||
if ($user->role_user === 'admin') {
|
||||
return redirect()->route('admin.welcome');
|
||||
}
|
||||
|
||||
return redirect()->route('halaman-katalog');
|
||||
}
|
||||
|
||||
// Jika gagal
|
||||
return back()->withErrors([
|
||||
'email' => 'Email atau password yang Anda masukkan salah.',
|
||||
])->withInput($request->except('password'));
|
||||
|
|
@ -102,4 +105,41 @@ public function logout(Request $request)
|
|||
|
||||
return redirect()->route('welcome'); // ← pengguna ke welcome
|
||||
}
|
||||
|
||||
// REGISTER
|
||||
public function register(Request $request)
|
||||
{
|
||||
// 🔥 1. VALIDASI INPUT
|
||||
$validated = $request->validate([
|
||||
'nama_lengkap' => 'required|string|max:255',
|
||||
'email_user' => 'required|email|unique:users,email_user',
|
||||
'no_hp' => 'required|string|max:20',
|
||||
'pekerjaan' => 'nullable|string|max:100',
|
||||
'password_user' => 'required|min:8|confirmed',
|
||||
], [
|
||||
// Custom error messages (opsional tapi disarankan)
|
||||
'nama_lengkap.required' => 'Nama lengkap wajib diisi',
|
||||
'email_user.required' => 'Email wajib diisi',
|
||||
'email_user.email' => 'Format email tidak valid',
|
||||
'email_user.unique' => 'Email sudah terdaftar',
|
||||
'no_hp.required' => 'Nomor HP wajib diisi',
|
||||
'password_user.required' => 'Password wajib diisi',
|
||||
'password_user.min' => 'Password minimal 8 karakter',
|
||||
'password_user.confirmed' => 'Konfirmasi password tidak cocok',
|
||||
]);
|
||||
|
||||
// 🔥 2. SIMPAN USER KE DATABASE
|
||||
$user = User::create([
|
||||
'nama_user' => $validated['nama_lengkap'],
|
||||
'email_user' => $validated['email_user'],
|
||||
'no_hp' => $validated['no_hp'],
|
||||
'pekerjaan' => $validated['pekerjaan'] ?? null,
|
||||
'password_user' => Hash::make($validated['password_user']),
|
||||
'role_user' => 'user', // default role
|
||||
]);
|
||||
|
||||
// 🔥 3. REDIRECT KE WELCOME DENGAN PESAN SUKSES
|
||||
return redirect()->route('welcome')
|
||||
->with('success', 'Akun berhasil dibuat! Silakan login.');
|
||||
}
|
||||
}
|
||||
|
|
@ -4,110 +4,68 @@
|
|||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
// app/Http/Controllers/ChatbotController.php
|
||||
class ChatbotController extends Controller
|
||||
class ChatBotController extends Controller
|
||||
{
|
||||
public function index()
|
||||
public function sendMessage(Request $request)
|
||||
{
|
||||
return view('halaman-chatbot');
|
||||
$message = strtolower(trim($request->input('message')));
|
||||
|
||||
// Database pengetahuan (bisa nanti dipindah ke database MySQL jika mau)
|
||||
$responses = [
|
||||
[
|
||||
'keywords' => ['halo', 'hai', 'hello', 'pagi', 'siang', 'malam'],
|
||||
'reply' => "Halo kak! 👋 Selamat datang di Carani Estate. Ada yang bisa dibantu hari ini?",
|
||||
'options' => ["Cari Rumah", "Info KPR", "Lokasi Kantor"]
|
||||
],
|
||||
[
|
||||
'keywords' => ['harga', 'biaya', 'mahal', 'murah', 'budget', 'dp'],
|
||||
'reply' => "Untuk harga, kami punya banyak pilihan mulai dari Rp 500 jutaan sampai miliaran, Kak. 😊 Kamu lagi cari properti di daerah mana nih?",
|
||||
'options' => ["Daerah Malang", "Daerah Batu", "Budget < 1 Milyar"]
|
||||
],
|
||||
[
|
||||
'keywords' => ['kpr', 'kredit', 'bank', 'cicil', 'bunga'],
|
||||
'reply' => "Siap! Untuk KPR, bunganya mulai dari 4% fixed tahun pertama lho. 🏦 Mau aku hitungin simulasi cicilannya sekalian?",
|
||||
'options' => ["Hitung Simulasi", "Syarat Dokumen"]
|
||||
],
|
||||
[
|
||||
'keywords' => ['lokasi', 'alamat', 'dimana', 'tempat', 'kantornya'],
|
||||
'reply' => "Kantor pemasaran kami ada di pusat kota, strategis banget! 📍 Mau dikirim lokasi Google Maps-nya?",
|
||||
'options' => ["Kirim Peta", "Jadwalkan Kunjungan"]
|
||||
],
|
||||
[
|
||||
'keywords' => ['tipe', 'jenis', 'rumah', 'apartemen', 'spesifikasi', 'luas'],
|
||||
'reply' => "Kami punya tipe Minimalis Modern dan Industrial Kekinian. Keduanya udah include kanopi & pagar loh! 🏠 Kamu lebih suka yang mana?",
|
||||
'options' => ["Lihat Foto Tipe A", "Lihat Foto Tipe B"]
|
||||
]
|
||||
];
|
||||
|
||||
$bestMatch = null;
|
||||
$highestScore = 0;
|
||||
|
||||
// Algoritma pencocokan sederhana (Naive Logic)
|
||||
foreach ($responses as $data) {
|
||||
$score = 0;
|
||||
foreach ($data['keywords'] as $keyword) {
|
||||
if (str_contains($message, $keyword)) {
|
||||
$score++;
|
||||
}
|
||||
}
|
||||
if ($score > $highestScore) {
|
||||
$highestScore = $score;
|
||||
$bestMatch = $data;
|
||||
}
|
||||
}
|
||||
|
||||
if ($bestMatch && $highestScore > 0) {
|
||||
return response()->json([
|
||||
'reply' => $bestMatch['reply'],
|
||||
'options' => $bestMatch['options']
|
||||
]);
|
||||
} else {
|
||||
return response()->json([
|
||||
'reply' => "Waduh, maaf Kak, saya belum paham maksudnya 😅. Bisa coba tanya tentang 'Harga', 'KPR', atau 'Lokasi'?",
|
||||
'options' => ["Tanya Harga", "Tanya KPR"]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function reply(Request $request)
|
||||
{
|
||||
$pesan = strtolower($request->input('pesan'));
|
||||
$balasan = $this->prosesPersan($pesan);
|
||||
|
||||
// Simpan ke tabel chat_messages
|
||||
ChatMessage::create([
|
||||
'session_id' => session()->getId(),
|
||||
'pengirim' => 'user',
|
||||
'pesan' => $request->input('pesan'),
|
||||
]);
|
||||
ChatMessage::create([
|
||||
'session_id' => session()->getId(),
|
||||
'pengirim' => 'bot',
|
||||
'pesan' => $balasan,
|
||||
]);
|
||||
|
||||
return response()->json(['balasan' => $balasan]);
|
||||
}
|
||||
|
||||
private function prosesPersan($pesan)
|
||||
{
|
||||
// Rekomendasi berdasarkan budget
|
||||
if (str_contains($pesan, 'subsidi') || str_contains($pesan, 'murah')) {
|
||||
return $this->rekomendasiProperti('subsidi');
|
||||
}
|
||||
if (str_contains($pesan, 'type 36') || str_contains($pesan, 'tipe 36')) {
|
||||
return $this->rekomendasiProperti('36');
|
||||
}
|
||||
if (str_contains($pesan, 'type 45') || str_contains($pesan, 'tipe 45')) {
|
||||
return $this->rekomendasiProperti('45');
|
||||
}
|
||||
if (str_contains($pesan, 'ruko')) {
|
||||
return $this->rekomendasiProperti('ruko');
|
||||
}
|
||||
|
||||
// Pertanyaan harga
|
||||
if (str_contains($pesan, 'harga') || str_contains($pesan, 'berapa')) {
|
||||
return "Harga properti kami mulai dari:\n
|
||||
- Type 30 (Subsidi): Rp 162.000.000\n
|
||||
- Type 36: Rp 200.000.000\n
|
||||
- Type 45: Rp 300.000.000\n
|
||||
- Type 60: Rp 435.000.000\n
|
||||
- Ruko: Rp 600.000.000\n
|
||||
Mau tanya type yang mana?";
|
||||
}
|
||||
|
||||
// Pertanyaan KPR
|
||||
if (str_contains($pesan, 'kpr') || str_contains($pesan, 'kredit')) {
|
||||
return "Kami melayani KPR dengan tenor 10-20 tahun.
|
||||
Syaratnya KTP, KK, slip gaji, dan NPWP.
|
||||
Mau tahu cicilan untuk type berapa?";
|
||||
}
|
||||
|
||||
// Pertanyaan cicilan
|
||||
if (str_contains($pesan, 'cicil') || str_contains($pesan, 'angsuran')) {
|
||||
return "Estimasi angsuran KPR per bulan:\n
|
||||
- Type 36 (10th): Rp 1.875.900\n
|
||||
- Type 45 (10th): Rp 2.813.800\n
|
||||
- Type 60 (10th): Rp 4.080.000\n
|
||||
Mau simulasi tenor yang lain?";
|
||||
}
|
||||
|
||||
// Salam
|
||||
if (str_contains($pesan, 'halo') || str_contains($pesan, 'hai') ||
|
||||
str_contains($pesan, 'hello')) {
|
||||
return "Halo! Selamat datang di Kelapa Gading Regency 🏠
|
||||
Saya siap membantu kamu menemukan rumah impian.
|
||||
Boleh saya tahu budget dan kebutuhan kamu?";
|
||||
}
|
||||
|
||||
// Default
|
||||
return "Maaf, saya belum mengerti pertanyaanmu.
|
||||
Kamu bisa tanya tentang:\n
|
||||
- Harga properti\n
|
||||
- Type rumah (36, 45, 60, Ruko)\n
|
||||
- KPR & cicilan\n
|
||||
- Cara booking";
|
||||
}
|
||||
|
||||
private function rekomendasiProperti($type)
|
||||
{
|
||||
$properti = Properti::where('type', 'like', "%$type%")
|
||||
->where('status', 'tersedia')
|
||||
->get();
|
||||
|
||||
if ($properti->isEmpty()) {
|
||||
return "Maaf, properti type $type sedang tidak tersedia.
|
||||
Mau lihat type lain?";
|
||||
}
|
||||
|
||||
$hasil = "Rekomendasi properti type $type yang tersedia:\n";
|
||||
foreach ($properti as $p) {
|
||||
$hasil .= "- Blok {$p->blok}, Harga: Rp " .
|
||||
number_format($p->harga, 0, ',', '.') . "\n";
|
||||
}
|
||||
return $hasil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class Notifikasi extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$notifikasi = \App\Models\Notifikasi::where('id_user', Auth::id())
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate(20); // atau ->get() kalau tidak pakai pagination
|
||||
|
||||
return view('halaman-notifikasi', compact('notifikasi'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Transaksi;
|
||||
use App\Models\Properti;
|
||||
use App\Models\Notifikasi;
|
||||
use App\Models\Dokumen;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class PemesananController extends Controller
|
||||
{
|
||||
public function proses(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id_properti' => 'required|exists:properti,id_properti',
|
||||
'jenis_transaksi'=> 'required|in:lunas,kredit,kpr,cash,installment,other',
|
||||
]);
|
||||
|
||||
$properti = Properti::find($request->id_properti);
|
||||
|
||||
// Simpan Transaksi
|
||||
$transaksi = Transaksi::create([
|
||||
'id_user' => Auth::user()->id_user,
|
||||
'id_properti' => $request->id_properti,
|
||||
'tanggal_transaksi'=> now()->toDateString(),
|
||||
'total_harga' => $properti->harga_properti,
|
||||
'jenis_transaksi' => $request->jenis_transaksi,
|
||||
'status_transaksi' => 'menunggu_verifikasi',
|
||||
]);
|
||||
|
||||
// Daftar dokumen yang perlu disimpan
|
||||
// key = nama input di form, value = enum jenis_dokumen di DB
|
||||
$dokumenList = [
|
||||
'ktp' => 'ktp',
|
||||
'kk' => 'kk',
|
||||
'surat_nikah' => 'surat_nikah',
|
||||
'npwp' => 'npwp',
|
||||
'slip_gaji' => 'slip_gaji',
|
||||
'rekening_koran' => 'rekening_koran',
|
||||
'foto_3x4' => 'foto_3x4',
|
||||
'surat_kerja' => 'surat_kerja',
|
||||
'selfie' => 'selfie',
|
||||
'foto_tempat_kerja' => 'foto_tempat_kerja',
|
||||
// Wiraswasta
|
||||
'ktpw' => 'ktp',
|
||||
'kkW' => 'kk',
|
||||
'surat_nikahW' => 'surat_nikah',
|
||||
'npwp_W' => 'npwp',
|
||||
'spt' => 'spt',
|
||||
'surat_ket_usaha' => 'surat_ket_usaha',
|
||||
'surat_penghasilan_usaha'=> 'surat_penghasilan_usaha',
|
||||
'pembukuan_usaha' => 'pembukuan_usaha',
|
||||
'rekening_koranW' => 'rekening_koran',
|
||||
'selfieW' => 'selfie',
|
||||
'foto_tempat_usaha' => 'foto_tempat_usaha',
|
||||
];
|
||||
|
||||
foreach ($dokumenList as $inputName => $jenisDokumen) {
|
||||
if ($request->hasFile($inputName)) {
|
||||
$files = is_array($request->file($inputName))
|
||||
? $request->file($inputName)
|
||||
: [$request->file($inputName)];
|
||||
|
||||
foreach ($files as $file) {
|
||||
$namaFile = time() . '_' . $file->getClientOriginalName();
|
||||
$path = $file->storeAs('dokumen/' . Auth::user()->id_user, $namaFile, 'public');
|
||||
|
||||
Dokumen::create([
|
||||
'id_user' => Auth::user()->id_user,
|
||||
'jenis_dokumen' => $jenisDokumen,
|
||||
'nama_file' => $namaFile,
|
||||
'path_file' => $path,
|
||||
'tipe_file' => $file->getClientMimeType(),
|
||||
'ukuran_file' => $file->getSize(),
|
||||
'status_verifikasi'=> 'pending',
|
||||
'catatan_verifikasi'=> '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notifikasi ke User
|
||||
Notifikasi::create([
|
||||
'id_user' => Auth::user()->id_user,
|
||||
'judul' => 'Pemesanan Berhasil Dikirim',
|
||||
'pesan' => 'Pemesanan kamu untuk ' . $properti->nama_properti . ' sedang menunggu verifikasi admin.',
|
||||
'tipe' => 'transaksi',
|
||||
'status_baca' => 0,
|
||||
'status_kirim' => 'terkirim',
|
||||
'channel' => 'in_app',
|
||||
'referensi_id' => $transaksi->id_transaksi,
|
||||
'referensi_tipe' => 'transaksi',
|
||||
]);
|
||||
|
||||
// Notifikasi ke semua Admin
|
||||
$admins = User::where('role_user', 'admin')->get();
|
||||
foreach ($admins as $admin) {
|
||||
Notifikasi::create([
|
||||
'id_user' => $admin->id_user,
|
||||
'judul' => 'Pemesanan Baru Masuk',
|
||||
'pesan' => Auth::user()->nama_user . ' memesan ' . $properti->nama_properti . '.',
|
||||
'tipe' => 'transaksi',
|
||||
'status_baca' => 0,
|
||||
'status_kirim' => 'terkirim',
|
||||
'channel' => 'in_app',
|
||||
'referensi_id' => $transaksi->id_transaksi,
|
||||
'referensi_tipe' => 'transaksi',
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route('pemesanan.terima-kasih', ['id' => $transaksi->id_transaksi]);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'jenis_transaksi' => 'required',
|
||||
'bukti_booking' => $request->jenis_transaksi == 'lunas'
|
||||
? 'required|file|mimes:jpg,jpeg,png,pdf|max:5120'
|
||||
: 'nullable'
|
||||
]);
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class VisitorController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ class Transaksi extends Model
|
|||
'id_properti',
|
||||
'tanggal_transaksi',
|
||||
'total_harga',
|
||||
'jenis_pembayaran',
|
||||
'jenis_transaksi',
|
||||
'status_transaksi',
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -49,4 +49,9 @@ public function getAuthPasswordName()
|
|||
{
|
||||
return 'password_user';
|
||||
}
|
||||
|
||||
public function dokumen()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Dokumen::class, 'id_user', 'id_user');
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
api: __DIR__.'/../routes/api.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
"require": {
|
||||
"php": "^8.2",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/tinker": "^2.10.1"
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"openai-php/client": "^0.19.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "53b5d56b3b7e3cbac1713e68c8850f6c",
|
||||
"content-hash": "8f430fb6921d2e12366c051cec6b656d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
|
|
@ -1334,6 +1334,69 @@
|
|||
},
|
||||
"time": "2026-03-23T14:35:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sanctum",
|
||||
"version": "v4.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/sanctum.git",
|
||||
"reference": "e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/sanctum/zipball/e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76",
|
||||
"reference": "e3b85d6e36ad00e5db2d1dcc27c81ffdf15cbf76",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^11.0|^12.0|^13.0",
|
||||
"illuminate/contracts": "^11.0|^12.0|^13.0",
|
||||
"illuminate/database": "^11.0|^12.0|^13.0",
|
||||
"illuminate/support": "^11.0|^12.0|^13.0",
|
||||
"php": "^8.2",
|
||||
"symfony/console": "^7.0|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.6",
|
||||
"orchestra/testbench": "^9.15|^10.8|^11.0",
|
||||
"phpstan/phpstan": "^1.10"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Sanctum\\SanctumServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Sanctum\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"laravel",
|
||||
"sanctum"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/sanctum/issues",
|
||||
"source": "https://github.com/laravel/sanctum"
|
||||
},
|
||||
"time": "2026-02-07T17:19:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v2.0.10",
|
||||
|
|
@ -2531,6 +2594,232 @@
|
|||
],
|
||||
"time": "2026-02-16T23:10:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "openai-php/client",
|
||||
"version": "v0.19.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/openai-php/client.git",
|
||||
"reference": "f2b1ce48b1c011ebc2abb255db5abf82ef4a6a44"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/openai-php/client/zipball/f2b1ce48b1c011ebc2abb255db5abf82ef4a6a44",
|
||||
"reference": "f2b1ce48b1c011ebc2abb255db5abf82ef4a6a44",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.2.0",
|
||||
"php-http/discovery": "^1.20.0",
|
||||
"php-http/multipart-stream-builder": "^1.4.2",
|
||||
"psr/http-client": "^1.0.3",
|
||||
"psr/http-client-implementation": "^1.0.1",
|
||||
"psr/http-factory-implementation": "*",
|
||||
"psr/http-message": "^1.1.0|^2.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^7.10.0",
|
||||
"guzzlehttp/psr7": "^2.8.0",
|
||||
"laravel/pint": "^1.25.1",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"nunomaduro/collision": "^8.8.3",
|
||||
"pestphp/pest": "^3.8.2|^4.1.4",
|
||||
"pestphp/pest-plugin-arch": "^3.1.1|^4.0.0",
|
||||
"pestphp/pest-plugin-type-coverage": "^3.5.1|^4.0.0",
|
||||
"phpstan/phpstan": "^1.12.32",
|
||||
"symfony/var-dumper": "^7.3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/OpenAI.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"OpenAI\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nuno Maduro",
|
||||
"email": "enunomaduro@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Sandro Gehri"
|
||||
}
|
||||
],
|
||||
"description": "OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API",
|
||||
"keywords": [
|
||||
"GPT-3",
|
||||
"api",
|
||||
"client",
|
||||
"codex",
|
||||
"dall-e",
|
||||
"language",
|
||||
"natural",
|
||||
"openai",
|
||||
"php",
|
||||
"processing",
|
||||
"sdk"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/openai-php/client/issues",
|
||||
"source": "https://github.com/openai-php/client/tree/v0.19.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.paypal.com/paypalme/enunomaduro",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/gehrisandro",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nunomaduro",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-17T20:53:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-http/discovery",
|
||||
"version": "1.20.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-http/discovery.git",
|
||||
"reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d",
|
||||
"reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-plugin-api": "^1.0|^2.0",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"nyholm/psr7": "<1.0",
|
||||
"zendframework/zend-diactoros": "*"
|
||||
},
|
||||
"provide": {
|
||||
"php-http/async-client-implementation": "*",
|
||||
"php-http/client-implementation": "*",
|
||||
"psr/http-client-implementation": "*",
|
||||
"psr/http-factory-implementation": "*",
|
||||
"psr/http-message-implementation": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "^1.0.2|^2.0",
|
||||
"graham-campbell/phpspec-skip-example-extension": "^5.0",
|
||||
"php-http/httplug": "^1.0 || ^2.0",
|
||||
"php-http/message-factory": "^1.0",
|
||||
"phpspec/phpspec": "^5.1 || ^6.1 || ^7.3",
|
||||
"sebastian/comparator": "^3.0.5 || ^4.0.8",
|
||||
"symfony/phpunit-bridge": "^6.4.4 || ^7.0.1"
|
||||
},
|
||||
"type": "composer-plugin",
|
||||
"extra": {
|
||||
"class": "Http\\Discovery\\Composer\\Plugin",
|
||||
"plugin-optional": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Http\\Discovery\\": "src/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"src/Composer/Plugin.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Márk Sági-Kazár",
|
||||
"email": "mark.sagikazar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations",
|
||||
"homepage": "http://php-http.org",
|
||||
"keywords": [
|
||||
"adapter",
|
||||
"client",
|
||||
"discovery",
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr17",
|
||||
"psr7"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-http/discovery/issues",
|
||||
"source": "https://github.com/php-http/discovery/tree/1.20.0"
|
||||
},
|
||||
"time": "2024-10-02T11:20:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-http/multipart-stream-builder",
|
||||
"version": "1.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-http/multipart-stream-builder.git",
|
||||
"reference": "10086e6de6f53489cca5ecc45b6f468604d3460e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/10086e6de6f53489cca5ecc45b6f468604d3460e",
|
||||
"reference": "10086e6de6f53489cca5ecc45b6f468604d3460e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"php-http/discovery": "^1.15",
|
||||
"psr/http-factory-implementation": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nyholm/psr7": "^1.0",
|
||||
"php-http/message": "^1.5",
|
||||
"php-http/message-factory": "^1.0.2",
|
||||
"phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Http\\Message\\MultipartStream\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A builder class that help you create a multipart stream",
|
||||
"homepage": "http://php-http.org",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"multipart stream",
|
||||
"stream"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-http/multipart-stream-builder/issues",
|
||||
"source": "https://github.com/php-http/multipart-stream-builder/tree/1.4.2"
|
||||
},
|
||||
"time": "2024-09-04T13:22:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.5",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Stateful Domains
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Requests from the following domains / hosts will receive stateful API
|
||||
| authentication cookies. Typically, these should include your local
|
||||
| and production domains which access your API via a frontend SPA.
|
||||
|
|
||||
*/
|
||||
|
||||
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
||||
'%s%s',
|
||||
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
||||
Sanctum::currentApplicationUrlWithPort(),
|
||||
// Sanctum::currentRequestHost(),
|
||||
))),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sanctum Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This array contains the authentication guards that will be checked when
|
||||
| Sanctum is trying to authenticate a request. If none of these guards
|
||||
| are able to authenticate the request, Sanctum will use the bearer
|
||||
| token that's present on an incoming request for authentication.
|
||||
|
|
||||
*/
|
||||
|
||||
'guard' => ['web'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expiration Minutes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value controls the number of minutes until an issued token will be
|
||||
| considered expired. This will override any values set in the token's
|
||||
| "expires_at" attribute, but first-party sessions are not affected.
|
||||
|
|
||||
*/
|
||||
|
||||
'expiration' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Token Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Sanctum can prefix new tokens in order to take advantage of numerous
|
||||
| security scanning initiatives maintained by open source platforms
|
||||
| that notify developers if they commit tokens into repositories.
|
||||
|
|
||||
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
||||
|
|
||||
*/
|
||||
|
||||
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sanctum Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When authenticating your first-party SPA with Sanctum you may need to
|
||||
| customize some of the middleware Sanctum uses while processing the
|
||||
| request. You may change the middleware listed below as required.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => [
|
||||
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
|
||||
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
|
||||
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
|
||||
],
|
||||
|
||||
];
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->text('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Data Rumah - PropertiHarmoni</title>
|
||||
<title>Data Rumah - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Data User - PropertiHarmoni</title>
|
||||
<title>Data User - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Data Rumah - PropertiHarmoni</title>
|
||||
<title>Edit Data Rumah - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Edit Data User - PropertiHarmoni</title>
|
||||
<title>Edit Data User - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ChatBot Admin - PropertiHarmoni</title>
|
||||
<title>ChatBot Admin - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -841,7 +841,7 @@
|
|||
<div class="logo">
|
||||
<i class="fas fa-home"></i>
|
||||
</div>
|
||||
<div class="company-name">PT. Properti Harmoni</div>
|
||||
<div class="company-name">PT. Carani Bhanu Balakosa</div>
|
||||
</div>
|
||||
|
||||
<div class="nav-menu" id="navMenu">
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Verifikasi Dokumen - PropertiHarmoni</title>
|
||||
<title>Verifikasi Dokumen - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -305,6 +305,7 @@
|
|||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
|
|
@ -783,13 +784,13 @@ class="nav-item {{ request()->routeIs('admin.halaman_chatbot') ? 'active' : '' }
|
|||
|
||||
{{-- Filter --}}
|
||||
<div class="filter-controls">
|
||||
<a href="{{ route('halaman_verifikasi') }}"
|
||||
<a href="{{ route('admin.halaman_verifikasi') }}"
|
||||
class="filter-btn {{ !request('status') ? 'active' : '' }}">Semua</a>
|
||||
<a href="{{ route('halaman_verifikasi', ['status' => 'pending']) }}"
|
||||
<a href="{{ route('admin.halaman_verifikasi', ['status' => 'pending']) }}"
|
||||
class="filter-btn {{ request('status') == 'pending' ? 'active' : '' }}">Menunggu</a>
|
||||
<a href="{{ route('halaman_verifikasi', ['status' => 'diterima']) }}"
|
||||
<a href="{{ route('admin.halaman_verifikasi', ['status' => 'diterima']) }}"
|
||||
class="filter-btn {{ request('status') == 'diterima' ? 'active' : '' }}">Disetujui</a>
|
||||
<a href="{{ route('halaman_verifikasi', ['status' => 'ditolak']) }}"
|
||||
<a href="{{ route('admin.halaman_verifikasi', ['status' => 'ditolak']) }}"
|
||||
class="filter-btn {{ request('status') == 'ditolak' ? 'active' : '' }}">Ditolak</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -815,77 +816,60 @@ class="filter-btn {{ request('status') == 'ditolak' ? 'active' : '' }}">Ditolak<
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($dokumen as $d)
|
||||
@php
|
||||
if ($d->status_verifikasi == 'pending') {
|
||||
$statusClass = 'status-pending';
|
||||
$statusLabel = 'Pending';
|
||||
} elseif ($d->status_verifikasi == 'diterima') {
|
||||
$statusClass = 'status-approved';
|
||||
$statusLabel = 'Disetujui';
|
||||
} elseif ($d->status_verifikasi == 'ditolak') {
|
||||
$statusClass = 'status-rejected';
|
||||
$statusLabel = 'Ditolak';
|
||||
} else {
|
||||
$statusClass = 'status-pending';
|
||||
$statusLabel = 'Revisi';
|
||||
}
|
||||
@endphp
|
||||
@forelse($user as $u)
|
||||
<tr>
|
||||
<td>{{ $loop->iteration }}</td>
|
||||
<td>{{ $d->user->nama_user ?? '-' }}</td>
|
||||
<td>{{ $d->user->no_hp ?? '-' }}</td>
|
||||
<td>{{ $d->user->email_user ?? '-' }}</td>
|
||||
<td>{{ ucfirst(str_replace('_', ' ', $d->jenis_dokumen)) }}</td>
|
||||
<td>{{ $u->nama_user ?? '-' }}</td>
|
||||
<td>{{ $u->no_hp ?? '-' }}</td>
|
||||
<td>{{ $u->email_user ?? '-' }}</td>
|
||||
|
||||
<td>
|
||||
<a href="{{ asset('storage/' . $d->path_file) }}"
|
||||
target="_blank"
|
||||
style="color:var(--primary-blue); font-weight:600;">
|
||||
<i class="fas fa-file"></i> Lihat File
|
||||
</a>
|
||||
{{ $u->dokumen->count() }} Dokumen
|
||||
</td>
|
||||
|
||||
<td>
|
||||
-
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@php
|
||||
$status = $u->dokumen->pluck('status_verifikasi');
|
||||
|
||||
if ($status->contains('pending')) {
|
||||
$statusLabel = 'Pending';
|
||||
$statusClass = 'status-pending';
|
||||
} elseif ($status->every(fn($s) => $s == 'diterima')) {
|
||||
$statusLabel = 'Disetujui';
|
||||
$statusClass = 'status-approved';
|
||||
} elseif ($status->contains('ditolak')) {
|
||||
$statusLabel = 'Ditolak';
|
||||
$statusClass = 'status-rejected';
|
||||
} else {
|
||||
$statusLabel = 'Revisi';
|
||||
$statusClass = 'status-pending';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<span class="status-badge {{ $statusClass }}">
|
||||
{{ $statusLabel }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
@if($d->status_verifikasi == 'pending')
|
||||
<div style="display:flex; gap:5px;">
|
||||
{{-- Approve --}}
|
||||
<form method="POST" action="{{ route('verifikasi.approve', $d->id_dokumen) }}">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
style="background:#059669; color:white; border:none;
|
||||
padding:6px 12px; border-radius:8px; cursor:pointer;
|
||||
font-size:0.85rem;"
|
||||
onclick="return confirm('Setujui dokumen ini?')">
|
||||
<i class="fas fa-check"></i> Setujui
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{{-- Tolak --}}
|
||||
@php $dokumenId = $d->id_dokumen; @endphp
|
||||
<button onclick="showTolakModal({{ $id_dokumen }})"
|
||||
style="background:#dc2626; color:white; border:none;
|
||||
padding:6px 12px; border-radius:8px; cursor:pointer;
|
||||
font-size:0.85rem;">
|
||||
<i class="fas fa-times"></i> Tolak
|
||||
</button>
|
||||
</div>
|
||||
@else
|
||||
<span style="color:#94a3b8; font-size:0.85rem;">Sudah diproses</span>
|
||||
@endif
|
||||
<td>
|
||||
<a href="{{ route('admin.verifikasi_dokumen', $u->id_user) }}"
|
||||
style="color:#2563eb; font-size:1.2rem;">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8" style="text-align:center; padding:30px; color:#64748b;">
|
||||
Belum ada dokumen
|
||||
Belum ada data user
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tambah Data User - PropertiHarmoni</title>
|
||||
<title>Tambah Data User - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tambah Data Rumah - PropertiHarmoni</title>
|
||||
<title>Tambah Data Rumah - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Verifikasi Dokumen - PropertiHarmoni</title>
|
||||
<title>Verifikasi Dokumen - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -914,159 +914,106 @@ class="nav-item {{ request()->routeIs('admin.halaman_chatbot') ? 'active' : '' }
|
|||
<!-- Verification Container -->
|
||||
<div class="verification-container">
|
||||
<div class="verification-header">
|
||||
<h2 class="verification-title">Verifikasi Dokumen - Nayla Putri Wijaya</h2>
|
||||
<h2 class="verification-title">
|
||||
Verifikasi Dokumen - {{ $user->nama_user }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="user-info-card">
|
||||
<div class="user-avatar">N</div>
|
||||
<div class="user-details">
|
||||
<div class="user-name-large">Nayla Putri Wijaya</div>
|
||||
<div class="user-id">ID: USER-2025-001</div>
|
||||
<div class="user-name-large">{{ $user->nama_user }}</div>
|
||||
<!-- <div class="user-id">ID: {{ $user->id_user }}</div> -->
|
||||
<div class="user-status pending">Status: Belum Diverifikasi</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-list">
|
||||
@foreach ($user->dokumen as $dok)
|
||||
<div class="document-item">
|
||||
<div class="doc-info">
|
||||
<div class="doc-icon complete">
|
||||
<i class="fas fa-id-card"></i>
|
||||
<div class="doc-icon
|
||||
{{ $dok->status_verifikasi == 'diterima' ? 'complete' : ($dok->status_verifikasi == 'ditolak' ? 'rejected' : 'pending') }}">
|
||||
<i class="fas fa-file"></i>
|
||||
</div>
|
||||
|
||||
<div class="doc-name">
|
||||
{{ ucfirst(str_replace('_', ' ', $dok->jenis_dokumen)) }}
|
||||
</div>
|
||||
|
||||
<div class="doc-status
|
||||
{{ $dok->status_verifikasi == 'diterima' ? 'complete' : ($dok->status_verifikasi == 'ditolak' ? 'rejected' : 'pending') }}">
|
||||
|
||||
@if($dok->status_verifikasi == 'diterima')
|
||||
Lengkap & Valid
|
||||
@elseif($dok->status_verifikasi == 'ditolak')
|
||||
Tidak Valid
|
||||
@else
|
||||
Menunggu Verifikasi
|
||||
@endif
|
||||
</div>
|
||||
<div class="doc-name">Kartu Tanda Penduduk (KTP)</div>
|
||||
<div class="doc-status complete">Lengkap & Valid</div>
|
||||
</div>
|
||||
|
||||
<div class="doc-actions">
|
||||
<div class="action-btn view-btn" data-doc="ktp">
|
||||
<i class="fas fa-eye"></i>
|
||||
</div>
|
||||
<div class="action-btn reject-btn" data-doc="ktp">
|
||||
<i class="fas fa-times"></i>
|
||||
</div>
|
||||
<div class="action-btn approve-btn" data-doc="ktp">
|
||||
|
||||
{{-- 👁 VIEW → buka tab baru --}}
|
||||
<a href="{{ asset('storage/' . $dok->path_file) }}" target="_blank">
|
||||
<div class="action-btn view-btn">
|
||||
<i class="fas fa-eye"></i>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{{-- ❌ TOLAK (hilang kalau sudah diterima) --}}
|
||||
@if($dok->status_verifikasi != 'diterima')
|
||||
<form method="POST" action="{{ route('admin.verifikasi.tolak', $dok->id_dokumen) }}">
|
||||
@csrf
|
||||
<button class="action-btn reject-btn">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
{{-- ✅ APPROVE --}}
|
||||
@if($dok->status_verifikasi != 'diterima')
|
||||
<form method="POST" action="{{ route('admin.verifikasi.approve', $dok->id_dokumen) }}">
|
||||
@csrf
|
||||
<button class="action-btn approve-btn">
|
||||
<i class="fas fa-check"></i>
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
{{-- kalau sudah disetujui, cuma tampil centang --}}
|
||||
<div class="action-btn approve-btn">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-item">
|
||||
<div class="doc-info">
|
||||
<div class="doc-icon complete">
|
||||
<i class="fas fa-users"></i>
|
||||
</div>
|
||||
<div class="doc-name">Kartu Keluarga (KK)</div>
|
||||
<div class="doc-status complete">Lengkap & Valid</div>
|
||||
</div>
|
||||
<div class="doc-actions">
|
||||
<div class="action-btn view-btn" data-doc="kk">
|
||||
<i class="fas fa-eye"></i>
|
||||
</div>
|
||||
<div class="action-btn reject-btn" data-doc="kk">
|
||||
<i class="fas fa-times"></i>
|
||||
</div>
|
||||
<div class="action-btn approve-btn" data-doc="kk">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="document-item">
|
||||
<div class="doc-info">
|
||||
<div class="doc-icon complete">
|
||||
<i class="fas fa-file-invoice-dollar"></i>
|
||||
</div>
|
||||
<div class="doc-name">Slip Gaji 3 Bulan Terakhir</div>
|
||||
<div class="doc-status complete">Lengkap & Valid</div>
|
||||
</div>
|
||||
<div class="doc-actions">
|
||||
<div class="action-btn view-btn" data-doc="slip-gaji">
|
||||
<i class="fas fa-eye"></i>
|
||||
</div>
|
||||
<div class="action-btn reject-btn" data-doc="slip-gaji">
|
||||
<i class="fas fa-times"></i>
|
||||
</div>
|
||||
<div class="action-btn approve-btn" data-doc="slip-gaji">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-item">
|
||||
<div class="doc-info">
|
||||
<div class="doc-icon pending">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
</div>
|
||||
<div class="doc-name">NPWP</div>
|
||||
<div class="doc-status pending">Menunggu Upload</div>
|
||||
</div>
|
||||
<div class="doc-actions">
|
||||
<div class="action-btn view-btn" data-doc="npwp">
|
||||
<i class="fas fa-eye"></i>
|
||||
</div>
|
||||
<div class="action-btn reject-btn" data-doc="npwp">
|
||||
<i class="fas fa-times"></i>
|
||||
</div>
|
||||
<div class="action-btn approve-btn" data-doc="npwp">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-item">
|
||||
<div class="doc-info">
|
||||
<div class="doc-icon missing">
|
||||
<i class="fas fa-file-signature"></i>
|
||||
</div>
|
||||
<div class="doc-name">Surat Keterangan Kerja</div>
|
||||
<div class="doc-status missing">Belum Diupload</div>
|
||||
</div>
|
||||
<div class="doc-actions">
|
||||
<div class="action-btn view-btn" data-doc="sk-kerja">
|
||||
<i class="fas fa-eye"></i>
|
||||
</div>
|
||||
<div class="action-btn reject-btn" data-doc="sk-kerja">
|
||||
<i class="fas fa-times"></i>
|
||||
</div>
|
||||
<div class="action-btn approve-btn" data-doc="sk-kerja">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-item">
|
||||
<div class="doc-info">
|
||||
<div class="doc-icon missing">
|
||||
<i class="fas fa-file-invoice"></i>
|
||||
</div>
|
||||
<div class="doc-name">Rekening Koran 6 Bulan</div>
|
||||
<div class="doc-status missing">Belum Diupload</div>
|
||||
</div>
|
||||
<div class="doc-actions">
|
||||
<div class="action-btn view-btn" data-doc="rekening-koran">
|
||||
<i class="fas fa-eye"></i>
|
||||
</div>
|
||||
<div class="action-btn reject-btn" data-doc="rekening-koran">
|
||||
<i class="fas fa-times"></i>
|
||||
</div>
|
||||
<div class="action-btn approve-btn" data-doc="rekening-koran">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="verification-notes">
|
||||
<h3 class="notes-title">Catatan Verifikasi</h3>
|
||||
<textarea class="notes-input" placeholder="Masukkan catatan verifikasi jika diperlukan..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="footer-actions">
|
||||
<button class="btn-back">Kembali</button>
|
||||
<button class="btn-complete">Selesai</button>
|
||||
</div>
|
||||
<form method="POST" action="{{ route('admin.verifikasi.selesai', $user->id_user) }}">
|
||||
@csrf
|
||||
|
||||
<div class="verification-notes">
|
||||
<h3 class="notes-title">Catatan Verifikasi</h3>
|
||||
<textarea name="catatan" class="notes-input"
|
||||
placeholder="Masukkan catatan verifikasi jika diperlukan..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="footer-actions">
|
||||
<a href="{{ route('admin.halaman_verifikasi') }}">
|
||||
<button type="button" class="btn-back">Kembali</button>
|
||||
</a>
|
||||
|
||||
<button type="submit" class="btn-complete">Selesai</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Document Preview Modal -->
|
||||
<!-- Document Preview Modal
|
||||
<div class="modal-overlay" id="previewModal">
|
||||
<div class="preview-modal">
|
||||
<div class="modal-header">
|
||||
|
|
@ -1084,45 +1031,45 @@ class="nav-item {{ request()->routeIs('admin.halaman_chatbot') ? 'active' : '' }
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Document action buttons
|
||||
document.querySelectorAll('.view-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const docType = this.getAttribute('data-doc');
|
||||
document.getElementById('previewModal').classList.add('show');
|
||||
// document.querySelectorAll('.view-btn').forEach(btn => {
|
||||
// btn.addEventListener('click', function() {
|
||||
// const docType = this.getAttribute('data-doc');
|
||||
// document.getElementById('previewModal').classList.add('show');
|
||||
|
||||
// Update modal title based on document type
|
||||
const modalTitle = document.querySelector('.modal-title');
|
||||
let docName = '';
|
||||
switch(docType) {
|
||||
case 'ktp':
|
||||
docName = 'Kartu Tanda Penduduk (KTP)';
|
||||
break;
|
||||
case 'kk':
|
||||
docName = 'Kartu Keluarga (KK)';
|
||||
break;
|
||||
case 'slip-gaji':
|
||||
docName = 'Slip Gaji 3 Bulan Terakhir';
|
||||
break;
|
||||
case 'npwp':
|
||||
docName = 'NPWP';
|
||||
break;
|
||||
case 'sk-kerja':
|
||||
docName = 'Surat Keterangan Kerja';
|
||||
break;
|
||||
case 'rekening-koran':
|
||||
docName = 'Rekening Koran 6 Bulan';
|
||||
break;
|
||||
default:
|
||||
docName = 'Dokumen';
|
||||
}
|
||||
modalTitle.textContent = `Preview Dokumen: ${docName}`;
|
||||
});
|
||||
});
|
||||
// // Update modal title based on document type
|
||||
// const modalTitle = document.querySelector('.modal-title');
|
||||
// let docName = '';
|
||||
// switch(docType) {
|
||||
// case 'ktp':
|
||||
// docName = 'Kartu Tanda Penduduk (KTP)';
|
||||
// break;
|
||||
// case 'kk':
|
||||
// docName = 'Kartu Keluarga (KK)';
|
||||
// break;
|
||||
// case 'slip-gaji':
|
||||
// docName = 'Slip Gaji 3 Bulan Terakhir';
|
||||
// break;
|
||||
// case 'npwp':
|
||||
// docName = 'NPWP';
|
||||
// break;
|
||||
// case 'sk-kerja':
|
||||
// docName = 'Surat Keterangan Kerja';
|
||||
// break;
|
||||
// case 'rekening-koran':
|
||||
// docName = 'Rekening Koran 6 Bulan';
|
||||
// break;
|
||||
// default:
|
||||
// docName = 'Dokumen';
|
||||
// }
|
||||
// modalTitle.textContent = `Preview Dokumen: ${docName}`;
|
||||
// });
|
||||
// });
|
||||
|
||||
// Approve button functionality
|
||||
document.querySelectorAll('.approve-btn').forEach(btn => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dashboard Admin - PropertiHarmoni</title>
|
||||
<title>Dashboard Admin - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -630,31 +630,34 @@ class="nav-item {{ request()->routeIs('admin.halaman_chatbot') ? 'active' : '' }
|
|||
<!-- Dashboard Stats -->
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-title">Total Pengguna</div>
|
||||
<div class="stat-value">2,458</div>
|
||||
<div class="stat-change positive">
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
+12% dari bulan lalu
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-title">Properti Aktif</div>
|
||||
<div class="stat-value">1,892</div>
|
||||
<div class="stat-change positive">
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
+8% dari bulan lalu
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-title">Verifikasi Pending</div>
|
||||
<div class="stat-value">23</div>
|
||||
<div class="stat-title">Visitor</div>
|
||||
<div class="stat-value">5,200</div>
|
||||
<div class="stat-change">
|
||||
<i class="fas fa-clock"></i>
|
||||
Perlu segera diproses
|
||||
Pengunjung website
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-title">Leads</div>
|
||||
<div class="stat-value">320</div>
|
||||
<div class="stat-change positive">
|
||||
Isi email / tertarik
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="stat-card">
|
||||
<div class="stat-title">User Terdaftar</div>
|
||||
<div class="stat-value">180</div>
|
||||
<div class="stat-change positive">
|
||||
Sudah login akun
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-title">Transaksi</div>
|
||||
<div class="stat-value">75</div>
|
||||
<div class="stat-change positive">
|
||||
Pembelian berhasil
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Detail Properti - PropertiHarmoni</title>
|
||||
<title>Detail Properti - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -1290,7 +1290,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<div class="description-section" style="padding:0; box-shadow:none; background:transparent;">
|
||||
<div class="description-content" style="font-size:0.95rem;">
|
||||
<p style="margin:10px 0;">
|
||||
{{ $properti->nama_properti }} merupakan properti {{ $properti->jenis_properti }}
|
||||
{{ $properti->nama_properti }} merupakan properti {{ $properti->jenis_properti }} php
|
||||
kategori {{ $properti->kategori_properti }}.
|
||||
</p>
|
||||
<ul style="padding-left:20px; margin:10px 0;">
|
||||
|
|
@ -1318,32 +1318,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<!-- KOLOM KANAN: Property Info (SIDEBAR) -->
|
||||
<div class="property-info">
|
||||
<!-- Contact Section -->
|
||||
<div class="contact-section">
|
||||
<h3 class="contact-title">Hubungi Agen Properti</h3>
|
||||
<div class="agent-info">
|
||||
<div class="agent-avatar">A</div>
|
||||
<div class="agent-details">
|
||||
<div class="agent-name">Admin Carani Estate</div>
|
||||
<div class="agent-role">Konsultan Properti</div>
|
||||
<div class="agent-rating">
|
||||
<i class="fas fa-star"></i>
|
||||
<i class="fas fa-star"></i>
|
||||
<i class="fas fa-star"></i>
|
||||
<i class="fas fa-star"></i>
|
||||
<i class="fas fa-star"></i>
|
||||
<span>5.0</span>
|
||||
</div>
|
||||
<div class="agent-contact">
|
||||
<a href="tel:+6281234567890" class="contact-btn btn-call">
|
||||
<i class="fas fa-phone"></i> Hubungi
|
||||
</a>
|
||||
<a href="https://wa.me/6281234567890" class="contact-btn btn-whatsapp">
|
||||
<i class="fab fa-whatsapp"></i> WhatsApp
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h1 class="property-title">{{ $properti->nama_properti }}</h1>
|
||||
|
||||
|
|
@ -1367,7 +1342,12 @@ class="contact-btn"
|
|||
<i class="fas fa-shopping-cart"></i> Beli Sekarang
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('login') }}?redirect={{ urlencode(url()->current()) }}"
|
||||
@php
|
||||
// 🔥 PENTING: Gunakan URL RELATIF saja (dimulai dengan /)
|
||||
$targetUrl = '/form-pemesanan/' . $properti->id_properti;
|
||||
@endphp
|
||||
|
||||
<a href="{{ route('login') }}?redirect={{ urlencode($targetUrl) }}"
|
||||
class="contact-btn"
|
||||
style="background:var(--dark-blue); color:white; border-radius:12px;
|
||||
padding:14px; font-weight:600; text-decoration:none;
|
||||
|
|
@ -1508,15 +1488,15 @@ class="action-btn btn-contact">Login</a>
|
|||
});
|
||||
});
|
||||
|
||||
// Contact buttons
|
||||
document.querySelectorAll('.contact-btn').forEach(btn => {
|
||||
// Contact buttons - HANYA untuk agen kontak
|
||||
document.querySelectorAll('.agent-contact .contact-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const action = this.textContent.trim();
|
||||
if (action.includes('WhatsApp')) {
|
||||
alert('Mengarahkan ke WhatsApp...');
|
||||
window.open('https://wa.me/6281234567890', '_blank');
|
||||
} else if (action.includes('Hubungi')) {
|
||||
alert('Mengarahkan ke panggilan telepon...');
|
||||
window.location.href = 'tel:+6281234567890';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Detail Pemesanan - PropertiHarmoni</title>
|
||||
<title>Detail Pemesanan - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Form Pemesanan - PropertiHarmoni</title>
|
||||
<title>Form Pemesanan - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -654,73 +654,104 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<div class="booking-form-container">
|
||||
<form action="{{ route('pemesanan.proses') }}" method="POST" enctype="multipart/form-data" id="bookingForm">
|
||||
@csrf
|
||||
<input type="hidden" name="id_properti" value="{{ $properti->id_properti }}">
|
||||
<!-- Property Preview -->
|
||||
<!-- Property Preview - DINAMIS -->
|
||||
<div class="property-preview">
|
||||
<div class="property-image">
|
||||
<img src="https://images.unsplash.com/photo-1560518883-ce09059eeffa?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&q=80" alt="Kelapa Gading Regency">
|
||||
@php
|
||||
// Ambil gambar pertama dari properti (sesuaikan dengan modelmu)
|
||||
$gambar = $properti->gambar_properti
|
||||
? (filter_var($properti->gambar_properti, FILTER_VALIDATE_URL)
|
||||
? $properti->gambar_properti
|
||||
: asset('storage/' . $properti->gambar_properti))
|
||||
: asset('images/placeholder-properti.jpg');
|
||||
@endphp
|
||||
<img src="{{ $gambar }}" alt="{{ $properti->nama_properti }}"
|
||||
onerror="this.src='{{ asset('images/placeholder-properti.jpg') }}'">
|
||||
</div>
|
||||
<div class="property-info">
|
||||
<h2 class="property-name">Kelapa Gading Regency</h2>
|
||||
<h2 class="property-name">{{ $properti->nama_properti }}</h2>
|
||||
<div class="property-location">
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<span>Jakarta Utara, DKI Jakarta</span>
|
||||
<span>{{ $properti->lokasi_properti ?? 'Lokasi tidak tersedia' }}</span>
|
||||
</div>
|
||||
<div class="property-price">Rp 285.000.000</div>
|
||||
<span class="property-type">Type 36/72 - Komersil</span>
|
||||
<div class="property-price">Rp {{ number_format($properti->harga_properti, 0, ',', '.') }}</div>
|
||||
<span class="property-type">
|
||||
{{ $properti->tipe_properti }} - {{ ucfirst($properti->kategori_properti) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Section -->
|
||||
<div class="form-section">
|
||||
<h2 class="section-title">Form Pemesanan Properti</h2>
|
||||
|
||||
<!-- Personal Information -->
|
||||
<div class="form-section">
|
||||
<h3 class="section-title">Informasi Pribadi</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="fullName" class="form-label">Nama Lengkap <span class="required">*</span></label>
|
||||
<input type="text" class="form-control" id="fullName" placeholder="Masukkan nama lengkap Anda" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="phone" class="form-label">Nomor HP <span class="required">*</span></label>
|
||||
<input type="tel" class="form-control" id="phone" placeholder="Contoh: 081234567890" required>
|
||||
</div>
|
||||
<h3 class="section-title">Informasi Pribadi</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="fullName" class="form-label">Nama Lengkap <span class="required">*</span></label>
|
||||
<input type="text" class="form-control" id="fullName" name="nama_lengkap"
|
||||
value="{{ old('nama_lengkap', Auth::user()->nama_user ?? '') }}"
|
||||
placeholder="Masukkan nama lengkap Anda" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="email" class="form-label">Email <span class="required">*</span></label>
|
||||
<input type="email" class="form-control" id="email" placeholder="Masukkan email Anda" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="address" class="form-label">Alamat Lengkap <span class="required">*</span></label>
|
||||
<textarea class="form-control" id="address" rows="2" placeholder="Masukkan alamat lengkap Anda" required></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="phone" class="form-label">Nomor HP <span class="required">*</span></label>
|
||||
<input type="tel" class="form-control" id="phone" name="no_hp"
|
||||
value="{{ old('no_hp', Auth::user()->no_hp ?? '') }}"
|
||||
placeholder="Contoh: 081234567890" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="email" class="form-label">Email <span class="required">*</span></label>
|
||||
<input type="email" class="form-control" id="email" name="email"
|
||||
value="{{ old('email', Auth::user()->email_user ?? '') }}"
|
||||
placeholder="Masukkan email Anda" required readonly>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="address" class="form-label">Alamat Lengkap <span class="required">*</span></label>
|
||||
<textarea class="form-control" id="address" name="alamat" rows="2"
|
||||
placeholder="Masukkan alamat lengkap Anda" required>{{ old('alamat', Auth::user()->alamat_user ?? '') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Property Details -->
|
||||
<div class="form-section">
|
||||
<h3 class="section-title">Detail Properti</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="propertyType" class="form-label">Tipe Properti <span class="required">*</span></label>
|
||||
<select class="form-control" id="propertyType" required>
|
||||
<option value="">Pilih Tipe Properti</option>
|
||||
<option value="30/60">30/60 (Subsidi)</option>
|
||||
<option value="36/72" selected>36/72 (Komersil)</option>
|
||||
<option value="45/84">45/84 (Komersil)</option>
|
||||
<option value="60/135">60/135 (Komersil)</option>
|
||||
<option value="ruko">Ruko (Komersil)</option>
|
||||
</select>
|
||||
<div class="form-control" style="background-color: #ffffff; font-weight: 500;">
|
||||
{{ $properti->tipe_properti ?? 'Tidak tersedia' }}
|
||||
@if($properti->tipe_properti == '30/60')
|
||||
<span class="text-muted">(Subsidi)</span>
|
||||
@else
|
||||
<span class="text-muted">(Komersil)</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- ✅ Tetap kirim value saat form submit --}}
|
||||
<input type="hidden" name="tipe_dipilih" value="{{ $properti->tipe_properti ?? '' }}">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="downPayment" class="form-label">Uang Muka <span class="required">*</span></label>
|
||||
<input type="text" class="form-control" id="downPayment" value="Rp 45.000.000" readonly>
|
||||
@php
|
||||
// Hitung DP: 15% untuk subsidi, 20% untuk komersil (sesuaikan kebijakan)
|
||||
$harga = $properti->harga_properti ?? 0;
|
||||
$kategori = $properti->kategori_properti ?? 'komersil';
|
||||
$persenDP = ($kategori == 'subsidi') ? 0.15 : 0.20;
|
||||
$dp = $harga * $persenDP;
|
||||
@endphp
|
||||
<input type="text" class="form-control" id="downPayment" name="uang_muka"
|
||||
value="Rp {{ number_format($dp, 0, ',', '.') }}" readonly>
|
||||
<!-- Hidden input untuk kirim nilai angka ke backend -->
|
||||
<input type="hidden" name="uang_muka_value" value="{{ $dp }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -730,12 +761,11 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<h3 class="section-title">Metode Pembayaran</h3>
|
||||
<div class="form-group">
|
||||
<label for="paymentMethod" class="form-label">Pilih Metode Pembayaran <span class="required">*</span></label>
|
||||
<select class="form-control" id="paymentMethod" required>
|
||||
<select class="form-control" id="paymentMethod" name="jenis_transaksi" required>
|
||||
<option value="">Pilih Metode Pembayaran</option>
|
||||
<option value="kpr" selected>KPR (Kredit Pemilikan Rumah)</option>
|
||||
<option value="cash">Cash (Pembayaran Lunas)</option>
|
||||
<option value="installment">Cicilan Tanpa Bunga</option>
|
||||
<option value="other">Lainnya (Metode Khusus)</option>
|
||||
<option value="kredit">KPR (Kredit Pemilikan Rumah)</option>
|
||||
<option value="lunas">Lunas</option>
|
||||
<!-- <option value="kredit">Cicilan Tanpa Bunga</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -768,7 +798,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-id-card upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="ktpFile" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<input type="file" class="file-input" id="ktpFile" name="ktp[]" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="ktpFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -779,7 +809,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-users upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="kkFile" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="kkFile" name="kk" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="kkFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -790,7 +820,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-ring upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="marriageFile" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="marriageFile" name="surat_nikah" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="marriageFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -801,7 +831,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-file-alt upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="npwpFile" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="npwpFile" name="npwp" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="npwpFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -812,7 +842,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-camera upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG | Max 2MB</p>
|
||||
<input type="file" class="file-input" id="photoFile" accept=".jpg,.jpeg,.png">
|
||||
<input type="file" class="file-input" id="photoFile" name="foto_3x4" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="photoFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -823,7 +853,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-briefcase upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="workCertFile" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="workCertFile" name="surat_kerja" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="workCertFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -834,7 +864,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-file-invoice-dollar upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="salaryFile" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<input type="file" class="file-input" id="salaryFile" name="slip_gaji[]" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="salaryFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -845,7 +875,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-file-invoice upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="bankFile" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<input type="file" class="file-input" id="bankFile" name="rekening_koran[]" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="bankFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -856,7 +886,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-user upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG | Max 3MB</p>
|
||||
<input type="file" class="file-input" id="selfieFile" accept=".jpg,.jpeg,.png">
|
||||
<input type="file" class="file-input" id="selfieFile" name="selfie" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="selfieFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -867,7 +897,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-building upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG | Max 3MB</p>
|
||||
<input type="file" class="file-input" id="workplaceFile" accept=".jpg,.jpeg,.png">
|
||||
<input type="file" class="file-input" id="workplaceFile" name="foto_tempat_kerja" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="workplaceFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -886,7 +916,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-id-card upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="ktpFileW" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<input type="file" class="file-input" id="ktpFileW" name="ktp[]" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="ktpFileNameW"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -897,7 +927,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-users upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="kkFileW" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="kkFileW" name="kk" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="kkFileNameW"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -908,7 +938,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-ring upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="marriageFileW" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="marriageFileW" name="surat_nikah" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="marriageFileNameW"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -919,7 +949,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-file-alt upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="npwpFileW" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="npwpFileW" name="npwp" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="npwpFileNameW"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -930,7 +960,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-file-invoice upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="sptFile" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="sptFile" name="spt_pajak" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="sptFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -941,8 +971,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-camera upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG | Max 2MB</p>
|
||||
<input type="file" class="file-input" id="photoFileW" accept=".jpg,.jpeg,.png">
|
||||
<div class="file-name" id="photoFileNameW"></div>
|
||||
<input type="file" class="file-input" id="marriageFile" name="surat_nikah" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -952,7 +981,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-file-contract upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="businessCertFile" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="businessCertFile" name="surat_ket_usaha" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="businessCertFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -963,7 +992,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-file-signature upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="incomeCertFile" accept=".jpg,.jpeg,.png,.pdf">
|
||||
<input type="file" class="file-input" id="incomeCertFile" name="surat_penghasilan_usaha" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="incomeCertFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -974,7 +1003,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-book upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="businessRecordFile" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<input type="file" class="file-input" id="businessRecordFile" name="pembukuan_usaha" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="businessRecordFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -985,7 +1014,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-file-invoice upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
<input type="file" class="file-input" id="bankFileW" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<input type="file" class="file-input" id="bankFileW" name="rekening_koran[]" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="bankFileNameW"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -996,7 +1025,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-user upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG | Max 3MB</p>
|
||||
<input type="file" class="file-input" id="selfieFileW" accept=".jpg,.jpeg,.png">
|
||||
<input type="file" class="file-input" id="selfieFile" name="selfie" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="selfieFileNameW"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1007,7 +1036,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<i class="fas fa-store upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG | Max 3MB</p>
|
||||
<input type="file" class="file-input" id="businessPlaceFile" accept=".jpg,.jpeg,.png">
|
||||
<input type="file" class="file-input" id="businessPlaceFile" name="foto_tempat_usaha" accept=".jpg,.jpeg,.png,.pdf" multiple>
|
||||
<div class="file-name" id="businessPlaceFileName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1045,14 +1074,41 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" id="cancelBtn">
|
||||
<i class="fas fa-times me-2"></i>Batal
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary" id="submitBtn">
|
||||
<i class="fas fa-paper-plane me-2"></i>Kirim Pemesanan
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" id="cancelBtn">
|
||||
<i class="fas fa-times me-2"></i>Batal
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary" id="submitBtn">
|
||||
<i class="fas fa-paper-plane me-2"></i>Kirim Pemesanan
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Upload Bukti Booking Fee (KHUSUS LUNAS) -->
|
||||
<div class="form-section" id="booking-proof-section" style="display: none;">
|
||||
<h3 class="section-title">Upload Bukti Booking Fee</h3>
|
||||
|
||||
<div class="upload-item">
|
||||
<label class="upload-label">
|
||||
Bukti Pembayaran Booking Fee <span class="required">*</span>
|
||||
</label>
|
||||
|
||||
<div class="upload-area">
|
||||
<i class="fas fa-receipt upload-icon"></i>
|
||||
<p class="upload-text">Klik atau drag file</p>
|
||||
<p class="upload-hint">JPG/PNG/PDF | Max 5MB</p>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
class="file-input"
|
||||
name="bukti_booking"
|
||||
id="buktiBooking"
|
||||
accept=".jpg,.jpeg,.png,.pdf"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>{{-- tutup form --}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1060,131 +1116,106 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Payment method dropdown change handler
|
||||
const paymentMethodSelect = document.getElementById('paymentMethod');
|
||||
const downPaymentInput = document.getElementById('downPayment');
|
||||
const paymentDetails = document.querySelector('.payment-details');
|
||||
|
||||
paymentMethodSelect.addEventListener('change', function() {
|
||||
const method = this.value;
|
||||
|
||||
if (method === 'kpr') {
|
||||
downPaymentInput.value = 'Rp 45.000.000';
|
||||
paymentDetails.innerHTML = `
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Harga Properti</span>
|
||||
<span class="payment-value">Rp 285.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Uang Muka</span>
|
||||
<span class="payment-value">Rp 45.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Biaya Booking</span>
|
||||
<span class="payment-value">Rp 2.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Total Pembayaran</span>
|
||||
<span class="payment-value">Rp 47.000.000</span>
|
||||
</div>
|
||||
`;
|
||||
} else if (method === 'cash') {
|
||||
downPaymentInput.value = 'Rp 285.000.000';
|
||||
paymentDetails.innerHTML = `
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Harga Properti</span>
|
||||
<span class="payment-value">Rp 285.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Biaya Booking</span>
|
||||
<span class="payment-value">Rp 2.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Total Pembayaran</span>
|
||||
<span class="payment-value">Rp 287.000.000</span>
|
||||
</div>
|
||||
`;
|
||||
} else if (method === 'installment') {
|
||||
downPaymentInput.value = 'Rp 85.500.000';
|
||||
paymentDetails.innerHTML = `
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Harga Properti</span>
|
||||
<span class="payment-value">Rp 285.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Uang Muka (30%)</span>
|
||||
<span class="payment-value">Rp 85.500.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Biaya Booking</span>
|
||||
<span class="payment-value">Rp 2.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Total Pembayaran</span>
|
||||
<span class="payment-value">Rp 87.500.000</span>
|
||||
</div>
|
||||
`;
|
||||
} else if (method === 'other') {
|
||||
downPaymentInput.value = 'Rp 0';
|
||||
paymentDetails.innerHTML = `
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Harga Properti</span>
|
||||
<span class="payment-value">Rp 285.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Biaya Booking</span>
|
||||
<span class="payment-value">Rp 2.000.000</span>
|
||||
</div>
|
||||
<div class="payment-item">
|
||||
<span class="payment-label">Total Pembayaran</span>
|
||||
<span class="payment-value">Rp 2.000.000</span>
|
||||
</div>
|
||||
`;
|
||||
// ========== MODAL PREVIEW ==========
|
||||
const modal = document.createElement('div');
|
||||
modal.id = 'previewModal';
|
||||
modal.style.cssText = `
|
||||
display:none; position:fixed; inset:0; background:rgba(0,0,0,0.75);
|
||||
z-index:9999; align-items:center; justify-content:center; padding:20px;
|
||||
`;
|
||||
modal.innerHTML = `
|
||||
<div style="background:#fff; border-radius:12px; max-width:90vw; max-height:90vh;
|
||||
overflow:auto; padding:20px; position:relative; min-width:300px;">
|
||||
<button id="previewClose" style="position:absolute; top:10px; right:14px;
|
||||
background:none; border:none; font-size:24px; cursor:pointer; color:#333; line-height:1;">×</button>
|
||||
<p id="previewTitle" style="font-weight:600; margin:0 32px 12px 0; font-size:13px; color:#555; word-break:break-all;"></p>
|
||||
<div id="previewContent"></div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
|
||||
modal.addEventListener('click', e => { if (e.target === modal) closePreview(); });
|
||||
document.getElementById('previewClose').addEventListener('click', closePreview);
|
||||
document.addEventListener('keydown', e => { if (e.key === 'Escape') closePreview(); });
|
||||
|
||||
function closePreview() {
|
||||
modal.style.display = 'none';
|
||||
document.getElementById('previewContent').innerHTML = '';
|
||||
}
|
||||
|
||||
function openPreview(file) {
|
||||
document.getElementById('previewTitle').textContent = file.name;
|
||||
const content = document.getElementById('previewContent');
|
||||
content.innerHTML = '';
|
||||
|
||||
const url = URL.createObjectURL(file);
|
||||
|
||||
if (file.type.startsWith('image/')) {
|
||||
const img = document.createElement('img');
|
||||
img.style.cssText = 'max-width:100%; max-height:75vh; display:block; border-radius:6px;';
|
||||
img.src = url;
|
||||
img.onload = () => URL.revokeObjectURL(url);
|
||||
content.appendChild(img);
|
||||
} else if (file.type === 'application/pdf') {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.src = url;
|
||||
iframe.style.cssText = 'width:75vw; height:75vh; border:none; border-radius:6px;';
|
||||
content.appendChild(iframe);
|
||||
}
|
||||
});
|
||||
|
||||
// Employment type toggle
|
||||
const pnsRadio = document.getElementById('employment-pns');
|
||||
const wiraswastaRadio = document.getElementById('employment-wiraswasta');
|
||||
const pnsDocuments = document.getElementById('pns-documents');
|
||||
const wiraswastaDocuments = document.getElementById('wiraswasta-documents');
|
||||
|
||||
pnsRadio.addEventListener('change', function() {
|
||||
if (this.checked) {
|
||||
pnsDocuments.style.display = 'block';
|
||||
wiraswastaDocuments.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
wiraswastaRadio.addEventListener('change', function() {
|
||||
if (this.checked) {
|
||||
pnsDocuments.style.display = 'none';
|
||||
wiraswastaDocuments.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
// File upload handlers
|
||||
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
|
||||
// ========== FILE UPLOAD HANDLER (GANTI YANG LAMA) ==========
|
||||
function setupFileUpload(uploadAreaId, fileInputId, fileNameId) {
|
||||
const uploadArea = document.getElementById(uploadAreaId);
|
||||
const fileInput = document.getElementById(fileInputId);
|
||||
const fileName = document.getElementById(fileNameId);
|
||||
|
||||
uploadArea.addEventListener('click', () => {
|
||||
|
||||
if (!uploadArea || !fileInput || !fileName) return;
|
||||
|
||||
uploadArea.addEventListener('click', function(e) {
|
||||
// Jangan trigger klik file input kalau yg diklik tombol preview
|
||||
if (e.target.closest('button[data-preview]')) return;
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
|
||||
fileInput.addEventListener('change', function() {
|
||||
if (this.files.length > 0) {
|
||||
if (this.multiple && this.files.length > 1) {
|
||||
fileName.textContent = `${this.files.length} file dipilih`;
|
||||
} else {
|
||||
fileName.textContent = this.files[0].name;
|
||||
}
|
||||
fileName.style.color = '#7AB2D3';
|
||||
}
|
||||
const files = Array.from(this.files);
|
||||
if (!files.length) return;
|
||||
|
||||
fileName.innerHTML = '';
|
||||
|
||||
files.forEach(file => {
|
||||
const row = document.createElement('div');
|
||||
row.style.cssText = 'display:flex; align-items:center; gap:6px; margin-top:5px;';
|
||||
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.textContent = file.name;
|
||||
nameSpan.style.cssText = 'font-size:0.82rem; color:#7AB2D3; font-weight:500; flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;';
|
||||
|
||||
const btnLihat = document.createElement('button');
|
||||
btnLihat.type = 'button';
|
||||
btnLihat.setAttribute('data-preview', '1');
|
||||
btnLihat.textContent = '👁 Lihat';
|
||||
btnLihat.style.cssText = `
|
||||
font-size:11px; padding:2px 8px; border-radius:4px; cursor:pointer;
|
||||
background:#e8f0fe; border:1px solid #7AB2D3; color:#1E3A5F;
|
||||
white-space:nowrap; flex-shrink:0;
|
||||
`;
|
||||
btnLihat.addEventListener('click', (e) => {
|
||||
e.stopPropagation(); // supaya tidak trigger uploadArea click
|
||||
openPreview(file);
|
||||
});
|
||||
|
||||
row.appendChild(nameSpan);
|
||||
row.appendChild(btnLihat);
|
||||
fileName.appendChild(row);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Setup all file upload areas for PNS
|
||||
|
||||
// ========== SETUP PNS ==========
|
||||
setupFileUpload('ktpUpload', 'ktpFile', 'ktpFileName');
|
||||
setupFileUpload('kkUpload', 'kkFile', 'kkFileName');
|
||||
setupFileUpload('marriageUpload', 'marriageFile', 'marriageFileName');
|
||||
|
|
@ -1195,8 +1226,8 @@ function setupFileUpload(uploadAreaId, fileInputId, fileNameId) {
|
|||
setupFileUpload('bankUpload', 'bankFile', 'bankFileName');
|
||||
setupFileUpload('selfieUpload', 'selfieFile', 'selfieFileName');
|
||||
setupFileUpload('workplaceUpload', 'workplaceFile', 'workplaceFileName');
|
||||
|
||||
// Setup all file upload areas for Wiraswasta
|
||||
|
||||
// ========== SETUP WIRASWASTA ==========
|
||||
setupFileUpload('ktpUploadW', 'ktpFileW', 'ktpFileNameW');
|
||||
setupFileUpload('kkUploadW', 'kkFileW', 'kkFileNameW');
|
||||
setupFileUpload('marriageUploadW', 'marriageFileW', 'marriageFileNameW');
|
||||
|
|
@ -1209,58 +1240,26 @@ function setupFileUpload(uploadAreaId, fileInputId, fileNameId) {
|
|||
setupFileUpload('bankUploadW', 'bankFileW', 'bankFileNameW');
|
||||
setupFileUpload('selfieUploadW', 'selfieFileW', 'selfieFileNameW');
|
||||
setupFileUpload('businessPlaceUpload', 'businessPlaceFile', 'businessPlaceFileName');
|
||||
|
||||
// Form submission
|
||||
document.getElementById('submitBtn').addEventListener('click', function() {
|
||||
// Simple validation
|
||||
const requiredFields = [
|
||||
{ id: 'fullName', label: 'Nama Lengkap' },
|
||||
{ id: 'phone', label: 'Nomor HP' },
|
||||
{ id: 'email', label: 'Email' },
|
||||
{ id: 'address', label: 'Alamat' },
|
||||
{ id: 'propertyType', label: 'Tipe Properti' },
|
||||
{ id: 'paymentMethod', label: 'Metode Pembayaran' }
|
||||
];
|
||||
|
||||
let isValid = true;
|
||||
for (const field of requiredFields) {
|
||||
const input = document.getElementById(field.id);
|
||||
if (!input.value.trim()) {
|
||||
alert(`Mohon lengkapi field: ${field.label}`);
|
||||
input.focus();
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) return;
|
||||
|
||||
// Validate documents based on employment type
|
||||
let docSection;
|
||||
if (pnsRadio.checked) {
|
||||
docSection = document.querySelectorAll('#pns-documents .file-input');
|
||||
} else {
|
||||
docSection = document.querySelectorAll('#wiraswasta-documents .file-input');
|
||||
}
|
||||
|
||||
let missingDocs = [];
|
||||
docSection.forEach(input => {
|
||||
if (!input.files || input.files.length === 0) {
|
||||
const label = input.closest('.upload-item').querySelector('.upload-label').textContent;
|
||||
missingDocs.push(label.split(' ')[0]);
|
||||
}
|
||||
});
|
||||
|
||||
if (missingDocs.length > 0) {
|
||||
alert(`Mohon lengkapi dokumen berikut:\n${missingDocs.join('\n')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Success message
|
||||
alert('Pemesanan berhasil dikirim! Tim kami akan segera menghubungi Anda untuk proses selanjutnya.');
|
||||
|
||||
// In real application: submit form to server
|
||||
// document.getElementById('bookingForm').submit();
|
||||
|
||||
// ========== SISA SCRIPT LAMA (payment, employment toggle, submit, cancel) ==========
|
||||
const paymentMethodSelect = document.getElementById('paymentMethod');
|
||||
const downPaymentInput = document.getElementById('downPayment');
|
||||
const paymentDetails = document.querySelector('.payment-details');
|
||||
|
||||
paymentMethodSelect.addEventListener('change', function() {
|
||||
// ... (isi sama persis dengan script lama)
|
||||
});
|
||||
|
||||
const pnsRadio = document.getElementById('employment-pns');
|
||||
const wiraswastaRadio = document.getElementById('employment-wiraswasta');
|
||||
const pnsDocuments = document.getElementById('pns-documents');
|
||||
const wiraswastaDocuments = document.getElementById('wiraswasta-documents');
|
||||
|
||||
pnsRadio.addEventListener('change', function() {
|
||||
if (this.checked) { pnsDocuments.style.display='block'; wiraswastaDocuments.style.display='none'; }
|
||||
});
|
||||
wiraswastaRadio.addEventListener('change', function() {
|
||||
if (this.checked) { pnsDocuments.style.display='none'; wiraswastaDocuments.style.display='block'; }
|
||||
});
|
||||
|
||||
// Cancel button
|
||||
|
|
@ -1271,6 +1270,38 @@ function setupFileUpload(uploadAreaId, fileInputId, fileNameId) {
|
|||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.getElementById('paymentMethod').addEventListener('change', function() {
|
||||
let metode = this.value;
|
||||
let dpField = document.getElementById('downPayment').closest('.form-group');
|
||||
|
||||
if (metode === 'lunas') {
|
||||
dpField.style.display = 'none';
|
||||
} else {
|
||||
dpField.style.display = 'block';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.getElementById('paymentMethod').addEventListener('change', function() {
|
||||
let metode = this.value;
|
||||
|
||||
let bookingSection = document.getElementById('booking-proof-section');
|
||||
let buktiInput = document.getElementById('buktiBooking');
|
||||
|
||||
if (metode === 'lunas') {
|
||||
bookingSection.style.display = 'block';
|
||||
buktiInput.setAttribute('required', 'required');
|
||||
} else {
|
||||
bookingSection.style.display = 'none';
|
||||
buktiInput.removeAttribute('required');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ganti Password - PropertiHarmoni</title>
|
||||
<title>Ganti Password - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ChatBot - PropertiHarmoni</title>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>ChatBot - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -751,100 +752,32 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
|
||||
<!-- Messages Area -->
|
||||
<div class="messages-area" id="messagesArea">
|
||||
<!-- Bot Welcome Message -->
|
||||
<!-- Bot Welcome Message (Satu-satunya pesan awal) -->
|
||||
<div class="message bot-message">
|
||||
<div class="message-header">
|
||||
<div class="bot-avatar">PH</div>
|
||||
<div class="bot-avatar" style="width:30px; height:30px; font-size:14px;">PH</div>
|
||||
<div>PropertiBot</div>
|
||||
</div>
|
||||
<div class="message-content">
|
||||
Halo! Saya PropertiBot, asisten virtual dari PropertiHarmoni. Ada yang bisa saya bantu hari ini? 😊
|
||||
Halo! Saya PropertiBot. Ada yang bisa dibantu hari ini? 😊<br>
|
||||
Silakan tanya tentang harga, lokasi, atau KPR.
|
||||
</div>
|
||||
<div class="message-time">10:24 AM</div>
|
||||
</div>
|
||||
|
||||
<!-- Bot Message -->
|
||||
<div class="message bot-message">
|
||||
<div class="message-header">
|
||||
<div class="bot-avatar">PH</div>
|
||||
<div>PropertiBot</div>
|
||||
</div>
|
||||
<div class="message-content">
|
||||
Saya bisa membantu Anda dengan:
|
||||
<ul style="padding-left: 20px; margin-top: 8px;">
|
||||
<li>Mencari properti sesuai kebutuhan</li>
|
||||
<li>Informasi detail properti</li>
|
||||
<li>Proses pemesanan dan pembayaran</li>
|
||||
<li>Verifikasi dokumen</li>
|
||||
<li>Dan masih banyak lagi!</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="message-time">10:24 AM</div>
|
||||
<div class="message-time">{{ date('H:i') }}</div>
|
||||
|
||||
<!-- Opsi Awal -->
|
||||
<div class="quick-actions">
|
||||
<button class="quick-btn">Cari Properti</button>
|
||||
<button class="quick-btn">Info Harga</button>
|
||||
<button class="quick-btn">Proses Pemesanan</button>
|
||||
<button class="quick-btn">Bantuan Dokumen</button>
|
||||
<button class="quick-btn" onclick="handleQuickReply('Cari Properti')">Cari Properti</button>
|
||||
<button class="quick-btn" onclick="handleQuickReply('Info Harga')">Info Harga</button>
|
||||
<button class="quick-btn" onclick="handleQuickReply('Simulasi KPR')">Simulasi KPR</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- User Message -->
|
||||
<div class="message user-message">
|
||||
<div class="message-content">
|
||||
Saya tertarik dengan Apartemen Begawan Malang. Bisa kasih info detailnya?
|
||||
</div>
|
||||
<div class="message-time">10:26 AM</div>
|
||||
</div>
|
||||
|
||||
<!-- Bot Response -->
|
||||
<div class="message bot-message">
|
||||
<div class="message-header">
|
||||
<div class="bot-avatar">PH</div>
|
||||
<div>PropertiBot</div>
|
||||
</div>
|
||||
<div class="message-content">
|
||||
Tentu! Apartemen Begawan Malang adalah hunian modern di pusat kota Malang dengan fasilitas lengkap:
|
||||
<br><br>
|
||||
<strong>Spesifikasi:</strong>
|
||||
• Lokasi: Kota Malang, Kecamatan Klojen
|
||||
• Harga: Rp 3.500.000.000
|
||||
• Luas Bangunan: 150 m²
|
||||
• Kamar Tidur: 3
|
||||
• Kamar Mandi: 2
|
||||
• Garasi: 1 mobil
|
||||
• Lantai: 30
|
||||
<br>
|
||||
<strong>Fasilitas:</strong>
|
||||
• Kolam renang infinity
|
||||
• Gym & fitness center
|
||||
• Keamanan 24 jam
|
||||
• Parkir bawah tanah
|
||||
• Co-working space
|
||||
</div>
|
||||
<div class="message-time">10:27 AM</div>
|
||||
|
||||
<div class="quick-actions">
|
||||
<button class="quick-btn">Lihat Gambar</button>
|
||||
<button class="quick-btn">Cek Ketersediaan</button>
|
||||
<button class="quick-btn">Simulasi Kredit</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- User Message -->
|
||||
<div class="message user-message">
|
||||
<div class="message-content">
|
||||
Bagaimana simulasi kredit untuk apartemen ini?
|
||||
</div>
|
||||
<div class="message-time">10:29 AM</div>
|
||||
</div>
|
||||
|
||||
<!-- Bot Response with Typing Indicator -->
|
||||
<div class="typing-indicator">
|
||||
|
||||
<!-- Typing Indicator (Tersembunyi) -->
|
||||
<!-- <div class="typing-indicator" id="typingIndicator">
|
||||
<div class="typing-dot"></div>
|
||||
<div class="typing-dot"></div>
|
||||
<div class="typing-dot"></div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- Input Area -->
|
||||
|
|
@ -857,174 +790,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const messagesArea = document.getElementById('messagesArea');
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const typingIndicator = document.querySelector('.typing-indicator');
|
||||
|
||||
// Quick action buttons
|
||||
document.querySelectorAll('.quick-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const message = this.textContent;
|
||||
addMessage(message, 'user');
|
||||
simulateBotResponse(message);
|
||||
});
|
||||
});
|
||||
|
||||
// Send message on button click
|
||||
sendBtn.addEventListener('click', sendMessage);
|
||||
|
||||
// Send message on Enter key
|
||||
messageInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter' && messageInput.value.trim() !== '') {
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-resize textarea (not needed since we're using input, but keeping for future)
|
||||
messageInput.addEventListener('input', function() {
|
||||
this.style.height = 'auto';
|
||||
this.style.height = (this.scrollHeight) + 'px';
|
||||
});
|
||||
|
||||
function sendMessage() {
|
||||
const message = messageInput.value.trim();
|
||||
if (message) {
|
||||
addMessage(message, 'user');
|
||||
messageInput.value = '';
|
||||
messageInput.style.height = 'auto';
|
||||
|
||||
// Show typing indicator
|
||||
typingIndicator.style.display = 'flex';
|
||||
|
||||
// Simulate bot response after delay
|
||||
setTimeout(() => {
|
||||
typingIndicator.style.display = 'none';
|
||||
simulateBotResponse(message);
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
|
||||
function addMessage(text, sender) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `message ${sender}-message`;
|
||||
|
||||
const now = new Date();
|
||||
const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
|
||||
|
||||
if (sender === 'bot') {
|
||||
messageDiv.innerHTML = `
|
||||
<div class="message-header">
|
||||
<div class="bot-avatar">PH</div>
|
||||
<div>PropertiBot</div>
|
||||
</div>
|
||||
<div class="message-content">${text}</div>
|
||||
<div class="message-time">${timeString}</div>
|
||||
`;
|
||||
} else {
|
||||
messageDiv.innerHTML = `
|
||||
<div class="message-content">${text}</div>
|
||||
<div class="message-time">${timeString}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
messagesArea.appendChild(messageDiv);
|
||||
|
||||
// Scroll to bottom
|
||||
messagesArea.scrollTop = messagesArea.scrollHeight;
|
||||
}
|
||||
|
||||
function simulateBotResponse(userMessage) {
|
||||
// Simple response logic based on user message
|
||||
let botResponse = '';
|
||||
|
||||
const lowerMessage = userMessage.toLowerCase();
|
||||
|
||||
if (lowerMessage.includes('halo') || lowerMessage.includes('hai') || lowerMessage.includes('hello')) {
|
||||
botResponse = 'Halo! Ada yang bisa saya bantu hari ini? 😊';
|
||||
} else if (lowerMessage.includes('terima kasih') || lowerMessage.includes('makasih')) {
|
||||
botResponse = 'Sama-sama! Jangan ragu untuk bertanya lagi jika ada yang ingin Anda ketahui. 😊';
|
||||
} else if (lowerMessage.includes('harga') || lowerMessage.includes('biaya')) {
|
||||
botResponse = 'Untuk informasi harga yang lebih detail, silakan berikan kriteria properti yang Anda cari (lokasi, tipe, budget) atau lihat katalog properti kami di halaman Katalog.';
|
||||
} else if (lowerMessage.includes('kredit') || lowerMessage.includes('cicilan')) {
|
||||
botResponse = 'Kami menyediakan opsi KPR dengan tenor hingga 25 tahun. Untuk simulasi kredit yang akurat, silakan lengkapi data diri Anda di halaman Simulasi Kredit atau hubungi agen kami.';
|
||||
} else if (lowerMessage.includes('properti') || lowerMessage.includes('rumah') || lowerMessage.includes('apartemen')) {
|
||||
botResponse = 'Kami memiliki berbagai pilihan properti di Malang dan sekitarnya. Untuk pencarian yang lebih spesifik, silakan gunakan filter di halaman Katalog atau beri tahu saya kriteria properti yang Anda inginkan.';
|
||||
} else if (lowerMessage.includes('dokumen') || lowerMessage.includes('syarat')) {
|
||||
botResponse = 'Untuk pembelian properti, dokumen yang diperlukan antara lain: KTP, KK, NPWP, Slip Gaji 3 bulan terakhir, dan Rekening Koran. Untuk info lebih lengkap, silakan kunjungi halaman Panduan Dokumen.';
|
||||
} else {
|
||||
botResponse = 'Terima kasih atas pertanyaan Anda! Tim kami akan segera memproses permintaan Anda. Untuk informasi lebih detail, silakan hubungi agen kami atau kunjungi halaman Bantuan.';
|
||||
}
|
||||
|
||||
addMessage(botResponse, 'bot');
|
||||
|
||||
// Add quick actions for common queries
|
||||
if (lowerMessage.includes('properti') || lowerMessage.includes('rumah') || lowerMessage.includes('apartemen')) {
|
||||
const lastMessage = document.querySelectorAll('.message.bot-message').item(-1);
|
||||
const quickActions = document.createElement('div');
|
||||
quickActions.className = 'quick-actions';
|
||||
quickActions.innerHTML = `
|
||||
<button class="quick-btn">Lihat Katalog</button>
|
||||
<button class="quick-btn">Filter Lokasi</button>
|
||||
<button class="quick-btn">Budget di Bawah 1M</button>
|
||||
<button class="quick-btn">Hubungi Agen</button>
|
||||
`;
|
||||
lastMessage.appendChild(quickActions);
|
||||
|
||||
// Add event listeners to new quick buttons
|
||||
quickActions.querySelectorAll('.quick-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const message = this.textContent;
|
||||
addMessage(message, 'user');
|
||||
simulateBotResponse(message);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize with welcome message after a short delay
|
||||
setTimeout(() => {
|
||||
typingIndicator.style.display = 'none';
|
||||
}, 1000);
|
||||
|
||||
// Focus input on load
|
||||
messageInput.focus();
|
||||
});
|
||||
|
||||
function toggleDropdown() {
|
||||
document.getElementById('dropdownMenu').classList.toggle('show');
|
||||
}
|
||||
|
||||
// Tutup dropdown kalau klik di luar
|
||||
window.addEventListener('click', function(e) {
|
||||
if (!e.target.closest('.profile-dropdown')) {
|
||||
document.getElementById('dropdownMenu').classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
// Add interactivity to search button
|
||||
document.querySelector('.search-btn').addEventListener('click', function() {
|
||||
alert('Mencari riwayat pemesanan dengan kriteria yang dipilih...');
|
||||
});
|
||||
|
||||
// Add interactivity to action buttons
|
||||
document.querySelectorAll('.action-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const action = this.querySelector('i').className;
|
||||
const bookingId = this.closest('tr').querySelector('td:first-child').textContent;
|
||||
|
||||
if (action.includes('fa-eye')) {
|
||||
alert(`Menampilkan detail pemesanan: ${bookingId}`);
|
||||
} else if (action.includes('fa-times')) {
|
||||
if (confirm(`Apakah Anda yakin ingin membatalkan pemesanan ${bookingId}?`)) {
|
||||
alert(`Pemesanan ${bookingId} berhasil dibatalkan.`);
|
||||
// In a real application, you would update the status in the database
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// BAGIAN NAV
|
||||
// Add hover effect to table rows
|
||||
document.querySelectorAll('tbody tr').forEach(row => {
|
||||
row.addEventListener('mouseenter', function() {
|
||||
|
|
@ -1049,6 +815,261 @@ function toggleDropdown() {
|
|||
function toggleMenu(){
|
||||
document.getElementById('navMenu').classList.toggle('show');
|
||||
}
|
||||
|
||||
// SCRIPT CHAT
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// --- KONFIGURASI ELEMENT ---
|
||||
const messagesArea = document.getElementById('messagesArea');
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const typingIndicator = document.querySelector('.typing-indicator');
|
||||
|
||||
// Kunci penyimpanan di browser
|
||||
const STORAGE_KEY = 'properti_chat_history';
|
||||
|
||||
// Token CSRF Laravel
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') ||
|
||||
document.querySelector('input[name="_token"]')?.value;
|
||||
|
||||
// --- 1. FUNGSI LOAD CHAT SAAT HALAMAN DIBUKA ---
|
||||
function loadChatHistory() {
|
||||
const savedChat = sessionStorage.getItem(STORAGE_KEY);
|
||||
if (savedChat) {
|
||||
const history = JSON.parse(savedChat);
|
||||
|
||||
history.forEach(msg => {
|
||||
// Render ulang pesan tanpa memicu logika kirim ke server
|
||||
renderMessageOnly(msg.text, msg.sender, msg.options);
|
||||
});
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. FUNGSI SIMPAN CHAT ---
|
||||
function saveChatToHistory(text, sender, options = null) {
|
||||
let history = [];
|
||||
const savedChat = sessionStorage.getItem(STORAGE_KEY);
|
||||
|
||||
if (savedChat) {
|
||||
history = JSON.parse(savedChat);
|
||||
}
|
||||
|
||||
// Simpan data sederhana (text, sender, dan label opsi jika ada)
|
||||
const optionsLabels = options ? options.map(opt => typeof opt === 'object' ? opt.label : opt) : null;
|
||||
|
||||
history.push({
|
||||
text: text,
|
||||
sender: sender,
|
||||
options: optionsLabels
|
||||
});
|
||||
|
||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(history));
|
||||
}
|
||||
|
||||
// --- 3. FUNGSI UTAMA KIRIM PESAN ---
|
||||
async function sendMessage() {
|
||||
const text = messageInput.value.trim();
|
||||
if (!text) return;
|
||||
|
||||
// Tampilkan pesan user & Simpan
|
||||
addMessage(text, 'user');
|
||||
saveChatToHistory(text, 'user');
|
||||
messageInput.value = '';
|
||||
|
||||
showTyping();
|
||||
|
||||
try {
|
||||
// Kirim ke Backend Laravel
|
||||
const response = await fetch('/api/chat/send', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken,
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ message: text })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
hideTyping();
|
||||
|
||||
// Tampilkan balasan bot & Simpan
|
||||
sendBotReply(data.reply, data.options, true);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
hideTyping();
|
||||
const errorMsg = "Maaf, terjadi kesalahan koneksi ke server.";
|
||||
addMessage(errorMsg, 'bot');
|
||||
saveChatToHistory(errorMsg, 'bot');
|
||||
}
|
||||
}
|
||||
|
||||
// --- 4. FUNGSI RENDER PESAN KE LAYAR ---
|
||||
|
||||
// Fungsi untuk menambah pesan baru (Interaktif + Simpan)
|
||||
function addMessage(text, sender) {
|
||||
const msgElement = createMessageElement(text, sender);
|
||||
messagesArea.appendChild(msgElement);
|
||||
scrollToBottom();
|
||||
return msgElement;
|
||||
}
|
||||
|
||||
// Fungsi khusus untuk render ulang dari History
|
||||
function renderMessageOnly(text, sender, savedOptions) {
|
||||
const msgElement = createMessageElement(text, sender);
|
||||
|
||||
// Jika ada opsi yang tersimpan, buat tombolnya kembali
|
||||
if (sender === 'bot' && savedOptions && savedOptions.length > 0) {
|
||||
const actionsContainer = msgElement.querySelector('.quick-actions-container');
|
||||
const actionsDiv = document.createElement('div');
|
||||
actionsDiv.className = 'quick-actions';
|
||||
|
||||
savedOptions.forEach(optLabel => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'quick-btn';
|
||||
btn.textContent = optLabel;
|
||||
btn.onclick = () => handleQuickReplyClick(optLabel);
|
||||
actionsDiv.appendChild(btn);
|
||||
});
|
||||
actionsContainer.appendChild(actionsDiv);
|
||||
}
|
||||
|
||||
messagesArea.appendChild(msgElement);
|
||||
}
|
||||
|
||||
// Helper membuat elemen HTML pesan
|
||||
function createMessageElement(text, sender) {
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = `message ${sender}-message`;
|
||||
|
||||
const now = new Date();
|
||||
const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
|
||||
|
||||
if (sender === 'bot') {
|
||||
messageDiv.innerHTML = `
|
||||
<div class="message-header">
|
||||
<div class="bot-avatar">PH</div>
|
||||
<div>PropertiBot</div>
|
||||
</div>
|
||||
<div class="message-content">${text}</div>
|
||||
<div class="message-time">${timeString}</div>
|
||||
<div class="quick-actions-container"></div>
|
||||
`;
|
||||
} else {
|
||||
messageDiv.innerHTML = `
|
||||
<div class="message-content">${text}</div>
|
||||
<div class="message-time">${timeString}</div>
|
||||
`;
|
||||
}
|
||||
return messageDiv;
|
||||
}
|
||||
|
||||
// Fungsi kirim balasan bot (dengan opsi simpan)
|
||||
function sendBotReply(text, options = [], shouldSave = false) {
|
||||
const msgElement = addMessage(text, 'bot');
|
||||
const actionsContainer = msgElement.querySelector('.quick-actions-container');
|
||||
|
||||
if (options && options.length > 0) {
|
||||
const actionsDiv = document.createElement('div');
|
||||
actionsDiv.className = 'quick-actions';
|
||||
|
||||
options.forEach(opt => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'quick-btn';
|
||||
const label = typeof opt === 'object' ? opt.label : opt;
|
||||
btn.textContent = label;
|
||||
|
||||
btn.onclick = () => handleQuickReplyClick(label);
|
||||
actionsDiv.appendChild(btn);
|
||||
});
|
||||
|
||||
actionsContainer.appendChild(actionsDiv);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
if (shouldSave) {
|
||||
const optionsLabels = options.map(opt => typeof opt === 'object' ? opt.label : opt);
|
||||
saveChatToHistory(text, 'bot', optionsLabels);
|
||||
}
|
||||
}
|
||||
|
||||
// Handler khusus untuk klik tombol quick reply
|
||||
function handleQuickReplyClick(text) {
|
||||
addMessage(text, 'user');
|
||||
saveChatToHistory(text, 'user');
|
||||
processLocalResponse(text);
|
||||
}
|
||||
|
||||
// Helper untuk handle klik tombol (kirim ke backend lagi)
|
||||
function processLocalResponse(text) {
|
||||
showTyping();
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/chat/send', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken,
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ message: text })
|
||||
});
|
||||
const data = await response.json();
|
||||
hideTyping();
|
||||
sendBotReply(data.reply, data.options, true);
|
||||
} catch (e) {
|
||||
hideTyping();
|
||||
console.error(e);
|
||||
}
|
||||
}, 600);
|
||||
}
|
||||
|
||||
// --- UTILITIES ---
|
||||
// --- UTILITIES (DIPERBARUI) ---
|
||||
|
||||
let typingElement = null; // Variabel untuk menyimpan elemen typing
|
||||
|
||||
function showTyping() {
|
||||
// Jika sudah ada, hapus dulu biar tidak duplikat
|
||||
if (typingElement) typingElement.remove();
|
||||
|
||||
// Buat elemen typing baru secara dinamis
|
||||
typingElement = document.createElement('div');
|
||||
typingElement.className = 'typing-indicator';
|
||||
typingElement.innerHTML = `
|
||||
<div class="typing-dot"></div>
|
||||
<div class="typing-dot"></div>
|
||||
<div class="typing-dot"></div>
|
||||
`;
|
||||
|
||||
messagesArea.appendChild(typingElement);
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
function hideTyping() {
|
||||
if (typingElement) {
|
||||
typingElement.remove(); // Hapus elemen dari layar
|
||||
typingElement = null; // Reset variabel
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
messagesArea.scrollTop = messagesArea.scrollHeight;
|
||||
}
|
||||
|
||||
|
||||
// --- EVENT LISTENERS ---
|
||||
sendBtn.addEventListener('click', sendMessage);
|
||||
messageInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') sendMessage();
|
||||
});
|
||||
|
||||
// Jalankan load history saat DOM siap
|
||||
loadChatHistory();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Katalog Properti - PropertiHarmoni</title>
|
||||
<title>Katalog Properti - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -1036,19 +1036,28 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Hapus div blok yang lama --}}
|
||||
{{-- Hapus div blok yang lama --}}
|
||||
|
||||
<div class="property-actions">
|
||||
<a href="{{ route('detail-katalog', $p->id_properti) }}"
|
||||
class="action-btn btn-view">
|
||||
Lihat Detail
|
||||
</a>
|
||||
<button class="action-btn btn-contact">Hubungi</button>
|
||||
<div class="property-actions">
|
||||
@auth
|
||||
{{-- ✅ Sudah login: tampilkan "Lihat Detail" --}}
|
||||
<a href="{{ route('detail-katalog', $p->id_properti) }}"
|
||||
class="action-btn btn-view">
|
||||
Lihat Detail
|
||||
</a>
|
||||
@else
|
||||
{{-- 🔒 Belum login: tampilkan "Login" --}}
|
||||
<a href="{{ route('login') }}"
|
||||
class="action-btn btn-view">
|
||||
Login
|
||||
</a>
|
||||
@endauth
|
||||
|
||||
<button class="action-btn btn-contact">Hubungi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Notifikasi - PropertiHarmoni</title>
|
||||
<title>Notifikasi - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -559,64 +559,105 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<h1 class="page-title">Notifikasi</h1>
|
||||
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;">
|
||||
<div>
|
||||
<input type="checkbox" id="selectAll">
|
||||
<label for="selectAll">Pilih Semua</label>
|
||||
</div>
|
||||
|
||||
<button id="deleteSelected"
|
||||
style="background:#dc2626; color:white; border:none; padding:8px 14px; border-radius:8px; cursor:pointer;">
|
||||
<i class="fas fa-trash"></i> Hapus Terpilih
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="notification-list">
|
||||
<!-- Notification 1 -->
|
||||
<div class="notification-item">
|
||||
<div class="notification-icon-container mail">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</div>
|
||||
<div class="notification-content">
|
||||
<h3 class="notification-title">Dokumen telah diverifikasi oleh admin</h3>
|
||||
<p class="notification-description">Silahkan lanjut ke proses transaksi</p>
|
||||
</div>
|
||||
<div class="notification-arrow">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
@forelse($notifikasi ?? [] as $n)
|
||||
{{-- ✅ Mapping tipe notifikasi ke icon & style yang sudah ada --}}
|
||||
@php
|
||||
// Tentukan icon & class berdasarkan tipe notifikasi
|
||||
switch($n->tipe) {
|
||||
case 'pemesanan':
|
||||
case 'transaksi':
|
||||
$icon = 'fa-shopping-cart';
|
||||
$iconClass = 'mail'; // pakai class existing
|
||||
break;
|
||||
case 'dokumen':
|
||||
case 'verifikasi':
|
||||
$icon = 'fa-file-check';
|
||||
$iconClass = 'info';
|
||||
break;
|
||||
case 'peringatan':
|
||||
case 'tagihan':
|
||||
$icon = 'fa-exclamation-triangle';
|
||||
$iconClass = 'warning';
|
||||
break;
|
||||
default:
|
||||
$icon = 'fa-bell';
|
||||
$iconClass = 'mail';
|
||||
}
|
||||
|
||||
// Tentukan link tujuan berdasarkan referensi
|
||||
$link = '#';
|
||||
if($n->referensi_tipe == 'transaksi' && $n->referensi_id) {
|
||||
$link = route('detail-pemesanan', $n->referensi_id);
|
||||
} elseif($n->referensi_tipe == 'dokumen' && $n->referensi_id) {
|
||||
$link = route('admin.halaman_verifikasi'); // atau route dokumen
|
||||
}
|
||||
@endphp
|
||||
|
||||
<!-- Notification 2 -->
|
||||
<div class="notification-item">
|
||||
<div class="notification-icon-container warning">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<a href="{{ $link }}" style="text-decoration:none; color:inherit; display:block;">
|
||||
<div class="notification-item {{ $n->status_baca == 0 ? 'unread' : '' }}">
|
||||
<input type="checkbox" class="notif-checkbox" value="{{ $n->id_notifikasi }}"
|
||||
onclick="event.stopPropagation();">
|
||||
|
||||
<div class="notification-icon-container {{ $iconClass }}">
|
||||
<i class="fas {{ $icon }}"></i>
|
||||
</div>
|
||||
<div class="notification-content">
|
||||
<h3 class="notification-title">{{ $n->judul }}</h3>
|
||||
<p class="notification-description">{{ $n->pesan }}</p>
|
||||
<small class="text-muted" style="font-size:0.8rem;">
|
||||
{{ \Carbon\Carbon::parse($n->created_at)->diffForHumans() }}
|
||||
</small>
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:10px;">
|
||||
|
||||
{{-- 🗑 HAPUS NOTIF --}}
|
||||
<form id="bulkDeleteForm" method="POST" action="{{ route('notifikasi.hapus.massal') }}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<input type="hidden" name="ids" id="selectedIds">
|
||||
</form>
|
||||
|
||||
{{-- ➡️ PANAH --}}
|
||||
<div class="notification-arrow">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-content">
|
||||
<h3 class="notification-title">Jangan Lupa Bayar Cicilan</h3>
|
||||
<p class="notification-description">Paling lambat tanggal 05/06</p>
|
||||
</div>
|
||||
<div class="notification-arrow">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notification 3 -->
|
||||
<div class="notification-item">
|
||||
<div class="notification-icon-container info">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</div>
|
||||
<div class="notification-content">
|
||||
<h3 class="notification-title">Jadwal Survey Lokasi</h3>
|
||||
<p class="notification-description">Sabtu, 10 Juni 2025 pukul 10.00 WIB di Kelapa Gading Regency</p>
|
||||
</div>
|
||||
<div class="notification-arrow">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notification 4 -->
|
||||
<div class="notification-item">
|
||||
<div class="notification-icon-container mail">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</div>
|
||||
<div class="notification-content">
|
||||
<h3 class="notification-title">Konfirmasi Pembayaran</h3>
|
||||
<p class="notification-description">Pembayaran DP Rp 50.000.000 telah diterima. Terima kasih!</p>
|
||||
</div>
|
||||
<div class="notification-arrow">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</a>
|
||||
@empty
|
||||
{{-- ✅ Empty state: class tetap sama --}}
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<i class="fas fa-bell-slash"></i>
|
||||
</div>
|
||||
<h3 class="empty-title">Belum Ada Notifikasi</h3>
|
||||
<p class="empty-description">Kamu akan menerima notifikasi saat ada update pemesanan atau verifikasi dokumen.</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
{{-- ✅ Pagination (jika pakai) - class Bootstrap tetap utuh --}}
|
||||
@if(isset($notifikasi) && method_exists($notifikasi, 'links'))
|
||||
<div class="mt-4">
|
||||
{{ $notifikasi->links() }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
|
@ -685,6 +726,28 @@ function toggleDropdown() {
|
|||
this.style.background = '';
|
||||
});
|
||||
});
|
||||
|
||||
// Tambahkan di akhir script
|
||||
document.querySelectorAll('.notification-item').forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
// Jangan jalankan jika klik di elemen yang tidak ingin trigger
|
||||
if(e.target.closest('a')) {
|
||||
const notifId = this.dataset.id;
|
||||
|
||||
// Mark as read via AJAX (tanpa reload)
|
||||
if(notifId) {
|
||||
fetch(`/notifikasi/${notifId}/read`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({})
|
||||
}).catch(err => console.log('Gagal update status baca:', err));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Pagination functionality
|
||||
document.querySelectorAll('.page-item').forEach(item => {
|
||||
|
|
@ -700,6 +763,43 @@ function toggleMenu(){
|
|||
document.getElementById('navMenu').classList.toggle('show');
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
const selectAll = document.getElementById('selectAll');
|
||||
const checkboxes = document.querySelectorAll('.notif-checkbox');
|
||||
const deleteBtn = document.getElementById('deleteSelected');
|
||||
const selectedIdsInput = document.getElementById('selectedIds');
|
||||
|
||||
// SELECT ALL
|
||||
selectAll.addEventListener('change', function() {
|
||||
checkboxes.forEach(cb => cb.checked = this.checked);
|
||||
});
|
||||
|
||||
// DELETE SELECTED
|
||||
deleteBtn.addEventListener('click', function() {
|
||||
let selected = [];
|
||||
|
||||
checkboxes.forEach(cb => {
|
||||
if (cb.checked) {
|
||||
selected.push(cb.value);
|
||||
}
|
||||
});
|
||||
|
||||
if (selected.length === 0) {
|
||||
alert('Pilih notifikasi dulu!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('Hapus notifikasi terpilih?')) return;
|
||||
|
||||
selectedIdsInput.value = selected.join(',');
|
||||
document.getElementById('bulkDeleteForm').submit();
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pembayaran - PropertiHarmoni</title>
|
||||
<title>Pembayaran - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Profil Saya - PropertiHarmoni</title>
|
||||
<title>Profil Saya - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Daftar - PropertiHarmoni</title>
|
||||
<title>Daftar - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -431,6 +431,25 @@
|
|||
<input type="tel" class="form-control" id="phone" placeholder="Contoh: 081234567890" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tambahkan setelah field "Nomor HP", sebelum "Password" -->
|
||||
<div class="form-group">
|
||||
<label for="pekerjaan" class="form-label">Pekerjaan</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-briefcase"></i>
|
||||
</span>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="pekerjaan"
|
||||
name="pekerjaan"
|
||||
value="{{ old('pekerjaan') }}"
|
||||
placeholder="Contoh: Pegawai Swasta / Wiraswasta / PNS">
|
||||
</div>
|
||||
<small style="color: #64748b; font-size: 0.8rem; margin-top: 4px; display: block;">
|
||||
<i class="fas fa-info-circle"></i> Opsional, bisa diisi nanti
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
|
|
@ -458,13 +477,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-register w-100">
|
||||
<i class="fas fa-user-plus me-2"></i>Daftar Sekarang
|
||||
</button>
|
||||
|
||||
<div class="login-link">
|
||||
<p>Sudah memiliki akun? <a href="#">Login di sini</a></p>
|
||||
</div>
|
||||
<a href="{{ route('welcome') }}" class="btn-register w-100 d-block text-center">
|
||||
Daftar Sekarang
|
||||
</a>
|
||||
|
||||
<p>
|
||||
Sudah memiliki akun?
|
||||
<a href="{{ route('login') }}">Login di sini</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - PropertiHarmoni</title>
|
||||
<title>Login - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -533,6 +533,11 @@ class="form-control password-input @error('password') is-invalid @enderror"
|
|||
<button type="submit" class="btn-login w-100">
|
||||
<i class="fas fa-sign-in-alt me-2"></i>Masuk ke Akun
|
||||
</button>
|
||||
|
||||
<!-- 🔥 TAMBAHAN: Link ke Register -->
|
||||
<div class="signup-link">
|
||||
<p>Belum punya akun? <a href="{{ route('register') }}">Daftar Sekarang</a></p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Daftar - PropertiHarmoni</title>
|
||||
<title>Daftar - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Riwayat Pemesanan - PropertiHarmoni</title>
|
||||
<title>Riwayat Pemesanan - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -541,6 +541,99 @@
|
|||
.btn-primary:hover {
|
||||
background: #6aa5c6;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
background: var(--dark-blue);
|
||||
color: white;
|
||||
padding: 50px 30px 20px;
|
||||
margin-top: 80px;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.footer-column h3 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footer-column h3::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: 0;
|
||||
width: 40px;
|
||||
height: 2px;
|
||||
background: var(--primary-blue);
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: #cbd5e0;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.footer-contact p {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.footer-contact i {
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.footer-social {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.social-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.social-icon:hover {
|
||||
background: var(--primary-blue);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.copyright {
|
||||
text-align: center;
|
||||
padding-top: 30px;
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
margin-top: 40px;
|
||||
font-size: 0.9rem;
|
||||
color: #cbd5e0;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 992px) {
|
||||
|
|
@ -907,6 +1000,60 @@ class="action-btn btn-view">
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-column">
|
||||
<h3>Carani Estate</h3>
|
||||
<p>Platform terpercaya untuk membeli, menjual, dan menyewa properti sejak 2015.</p>
|
||||
<div class="footer-social">
|
||||
<div class="social-icon"><i class="fab fa-facebook-f"></i></div>
|
||||
<div class="social-icon"><i class="fab fa-twitter"></i></div>
|
||||
<div class="social-icon"><i class="fab fa-instagram"></i></div>
|
||||
<div class="social-icon"><i class="fab fa-linkedin-in"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-column">
|
||||
<h3>Tautan Cepat</h3>
|
||||
<ul class="footer-links">
|
||||
<li><a href="#">Beranda</a></li>
|
||||
<li><a href="#">Katalog Properti</a></li>
|
||||
<li><a href="#">ChatBot</a></li>
|
||||
<li><a href="#">Riwayat Pemesanan</a></li>
|
||||
<li><a href="#">Tentang Kami</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer-column">
|
||||
<h3>Layanan</h3>
|
||||
<ul class="footer-links">
|
||||
<li><a href="#">Pembelian Properti</a></li>
|
||||
<li><a href="#">Penjualan Properti</a></li>
|
||||
<li><a href="#">Sewa Properti</a></li>
|
||||
<li><a href="#">Konsultasi Properti</a></li>
|
||||
<li><a href="#">Finansial & KPR</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer-column">
|
||||
<h3>Kontak Kami</h3>
|
||||
<div class="footer-contact">
|
||||
<p><i class="fas fa-map-marker-alt"></i> Jl. Raya Pakisan, Bunduh, Bataan, Kec. Tenggarang, Kabupaten Bondowoso, Jawa Timur 68271</p>
|
||||
<p><i class="fas fa-phone"></i> 0812-3456-7890</p>
|
||||
<p><i class="fas fa-envelope"></i> caranibhanubalakosa@gmail.com</p>
|
||||
<p><i class="fas fa-clock"></i> Senin - Sabtu: 08:00 - 17:00</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="copyright">
|
||||
© 2025 CaraniEstate. Semua hak dilindungi.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
function toggleDropdown() {
|
||||
document.getElementById('dropdownMenu').classList.toggle('show');
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Katalog Properti - PropertiHarmoni</title>
|
||||
<title>Katalog Properti - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
|
|
@ -1527,7 +1527,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<section class="hero">
|
||||
<div class="hero-overlay"></div>
|
||||
<div class="hero-content">
|
||||
<h1 class="hero-title">Tentang PropertiHarmoni</h1>
|
||||
<h1 class="hero-title">Tentang Carani Estate</h1>
|
||||
<p class="hero-subtitle">Mewujudkan impian memiliki rumah yang nyaman dan berkualitas sejak 2015</p>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -1733,16 +1733,16 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<div class="footer-column">
|
||||
<h3>Kontak Kami</h3>
|
||||
<div class="footer-contact">
|
||||
<p><i class="fas fa-map-marker-alt"></i> Jl. Melati No. 45, Jakarta Selatan</p>
|
||||
<p><i class="fas fa-map-marker-alt"></i> Jl. Raya Pakisan, Bunduh, Bataan, Kec. Tenggarang, Kabupaten Bondowoso, Jawa Timur 68271</p>
|
||||
<p><i class="fas fa-phone"></i> 0812-3456-7890</p>
|
||||
<p><i class="fas fa-envelope"></i> info@propertiharmoni.com</p>
|
||||
<p><i class="fas fa-envelope"></i> caranibhanubalakosa@gmail.com</p>
|
||||
<p><i class="fas fa-clock"></i> Senin - Sabtu: 08:00 - 17:00</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="copyright">
|
||||
© 2025 PropertiHarmoni. Semua hak dilindungi.
|
||||
© 2025 CaraniEstate. Semua hak dilindungi.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pemesanan Berhasil - Carani Estate</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
body {
|
||||
background: #f8fafc;
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.card-sukses {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 50px 40px;
|
||||
text-align: center;
|
||||
max-width: 520px;
|
||||
width: 100%;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.08);
|
||||
}
|
||||
.icon-sukses {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
background: #d1fae5;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 25px;
|
||||
}
|
||||
.icon-sukses i {
|
||||
font-size: 40px;
|
||||
color: #059669;
|
||||
}
|
||||
.judul {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
color: #1E3A5F;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.subjudul {
|
||||
color: #64748b;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.info-box {
|
||||
background: #f1f5f9;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
text-align: left;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.info-row:last-child { border-bottom: none; }
|
||||
.info-label { color: #64748b; }
|
||||
.info-value { font-weight: 600; color: #1E3A5F; }
|
||||
.status-pill {
|
||||
background: #fef3c7;
|
||||
color: #f59e0b;
|
||||
padding: 4px 14px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.steps {
|
||||
background: #eff6ff;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.steps h6 {
|
||||
color: #1E3A5F;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.step-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 0.9rem;
|
||||
color: #475569;
|
||||
}
|
||||
.step-num {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #7AB2D3;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.btn-notif {
|
||||
background: #7AB2D3;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.btn-notif:hover { background: #6aa5c6; color: white; }
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: #7AB2D3;
|
||||
border: 2px solid #7AB2D3;
|
||||
padding: 12px 30px;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.btn-outline:hover { background: #7AB2D3; color: white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card-sukses">
|
||||
<div class="icon-sukses">
|
||||
<i class="fas fa-check"></i>
|
||||
</div>
|
||||
|
||||
<h1 class="judul">Pemesanan Berhasil!</h1>
|
||||
<p class="subjudul">
|
||||
Dokumen kamu sudah kami terima. Admin akan memverifikasi dokumenmu
|
||||
dan kamu akan mendapat notifikasi setelah proses selesai.
|
||||
</p>
|
||||
|
||||
{{-- Info Transaksi --}}
|
||||
<div class="info-box">
|
||||
<div class="info-row">
|
||||
<span class="info-label">Properti</span>
|
||||
<span class="info-value">{{ $transaksi->properti->nama_properti ?? '-' }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Jenis Transaksi</span>
|
||||
<span class="info-value">{{ ucfirst($transaksi->jenis_transaksi) }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Total Harga</span>
|
||||
<span class="info-value">Rp {{ number_format($transaksi->total_harga, 0, ',', '.') }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Tanggal</span>
|
||||
<span class="info-value">{{ \Carbon\Carbon::parse($transaksi->tanggal_transaksi)->format('d M Y') }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Status</span>
|
||||
<span class="status-pill"><i class="fas fa-clock"></i> Menunggu Verifikasi</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Langkah Selanjutnya --}}
|
||||
<div class="steps">
|
||||
<h6><i class="fas fa-list-ol"></i> Langkah Selanjutnya</h6>
|
||||
<div class="step-item">
|
||||
<div class="step-num">1</div>
|
||||
<span>Admin akan mengecek dokumen yang kamu kirimkan</span>
|
||||
</div>
|
||||
<div class="step-item">
|
||||
<div class="step-num">2</div>
|
||||
<span>Kamu akan mendapat notifikasi jika dokumen disetujui atau perlu diperbaiki</span>
|
||||
</div>
|
||||
<div class="step-item">
|
||||
<div class="step-num">3</div>
|
||||
<span>Setelah disetujui, kamu bisa melanjutkan ke proses pembayaran</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Tombol --}}
|
||||
<div>
|
||||
<a href="{{ route('halaman-notifikasi') }}" class="btn-notif">
|
||||
<i class="fas fa-bell"></i> Lihat Notifikasi
|
||||
</a>
|
||||
<a href="{{ route('halaman-katalog') }}" class="btn-outline">
|
||||
<i class="fas fa-home"></i> Kembali ke Katalog
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -3,8 +3,9 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PropertiHarmoni - Temukan Rumah Impian Anda</title>
|
||||
<title>Carani Estate - Temukan Rumah Impian Anda</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
:root {
|
||||
|
|
@ -168,7 +169,7 @@
|
|||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
background: url('https://placehold.co/1920x1080/e6f2f8/1E3A5F?text=Properti+Impian') no-repeat center center;
|
||||
background: url('') no-repeat center center;
|
||||
background-size: cover;
|
||||
padding: 150px 30px 100px;
|
||||
position: relative;
|
||||
|
|
@ -396,7 +397,59 @@
|
|||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* CSS IKLAN */
|
||||
.email-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
left: 0; top: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(0,0,0,0.6);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.email-box {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
width: 350px;
|
||||
text-align: center;
|
||||
animation: fadeIn 0.4s ease;
|
||||
}
|
||||
|
||||
.email-box h2 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.email-box input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin: 15px 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.email-box button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: #1E3A5F;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {transform: scale(0.8); opacity: 0;}
|
||||
to {transform: scale(1); opacity: 1;}
|
||||
}
|
||||
|
||||
|
||||
/* About Section */
|
||||
|
|
@ -559,10 +612,10 @@
|
|||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Services Grid - 4 Columns */
|
||||
/* Services Grid - 3 Columns, ganti bagian repeat */
|
||||
.services-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 25px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
|
@ -743,47 +796,6 @@
|
|||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* Bottom CTA */
|
||||
.services-cta {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid rgba(226, 232, 240, 0.6);
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.services-cta p {
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark-blue);
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-services-cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 14px 35px;
|
||||
background: linear-gradient(135deg, var(--dark-blue), #2a4a6f);
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-services-cta:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 12px 30px rgba(30, 58, 95, 0.3);
|
||||
background: linear-gradient(135deg, #2a4a6f, var(--dark-blue));
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 1200px) {
|
||||
.services-grid {
|
||||
|
|
@ -875,6 +887,183 @@
|
|||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
# Call To Action Section-1 (CUSTOM COLOR)
|
||||
--------------------------------------------------------------*/
|
||||
.call-to-action-1 {
|
||||
position: relative;
|
||||
min-height: 500px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Background */
|
||||
.call-to-action-1 .cta-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Overlay pakai PRIMARY BLUE */
|
||||
.call-to-action-1 .cta-bg::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(122, 178, 211, 0.4); /* primary-blue */
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.call-to-action-1 .container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Title */
|
||||
.call-to-action-1 .cta-content h2 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.call-to-action-1 .cta-content h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Description */
|
||||
.call-to-action-1 .cta-content p {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 2.5rem;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.call-to-action-1 .cta-buttons {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-bottom: 3rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Base Button */
|
||||
.call-to-action-1 .cta-buttons .btn {
|
||||
padding: 12px 30px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
border-radius: 50px;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* PRIMARY BUTTON */
|
||||
.call-to-action-1 .cta-buttons .btn.btn-primary {
|
||||
background-color: var(--dark-blue);
|
||||
color: white;
|
||||
border-color: var(--dark-blue);
|
||||
}
|
||||
|
||||
.call-to-action-1 .cta-buttons .btn.btn-primary:hover {
|
||||
background-color: #162c47;
|
||||
border-color: #162c47;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* OUTLINE BUTTON */
|
||||
.call-to-action-1 .cta-buttons .btn.btn-outline {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.call-to-action-1 .cta-buttons .btn.btn-outline:hover {
|
||||
background-color: white;
|
||||
color: var(--primary-blue);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Mobile Button */
|
||||
@media (max-width: 576px) {
|
||||
.call-to-action-1 .cta-buttons {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.call-to-action-1 .cta-buttons .btn {
|
||||
width: 100%;
|
||||
max-width: 280px;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* Features */
|
||||
.call-to-action-1 .cta-features {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Feature Item */
|
||||
.call-to-action-1 .cta-features .feature-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Icon pakai primary */
|
||||
.call-to-action-1 .cta-features .feature-item i {
|
||||
font-size: 1.2rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Text */
|
||||
.call-to-action-1 .cta-features .feature-item span {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 576px) {
|
||||
.call-to-action-1 .cta-features {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.call-to-action-1 .cta-features .feature-item {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.call-to-action-1 {
|
||||
min-height: 400px;
|
||||
padding: 80px 0;
|
||||
}
|
||||
|
||||
.call-to-action-1 .cta-content h2 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.call-to-action-1 .cta-content p {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.call-to-action-1 .cta-buttons {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Properti Unggulan */
|
||||
.properti-unggulan {
|
||||
background: #ffffff;
|
||||
|
|
@ -1831,7 +2020,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
|
||||
{{-- Guest --}}
|
||||
@guest
|
||||
<a href="{{ route('login') }}" class="nav-item login-link">
|
||||
<a href="{{ route('login', ['redirect' => url()->current()]) }}" class="nav-item login-link">
|
||||
<i class="fas fa-sign-in-alt me-1"></i> Login
|
||||
</a>
|
||||
@else
|
||||
|
|
@ -2023,7 +2212,7 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
</div>
|
||||
|
||||
<!-- Service 4: Verifikasi & After-Sales -->
|
||||
<div class="service-card">
|
||||
<!-- <div class="service-card">
|
||||
<div class="service-icon-wrapper">
|
||||
<div class="service-icon">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
|
|
@ -2042,21 +2231,53 @@ class="nav-item {{ request()->routeIs('kontak') ? 'active' : '' }}">
|
|||
<a href="{{ route('halaman-kontak') }}" class="service-link">
|
||||
Pelajari Lebih Lanjut <i class="fas fa-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- CTA Bottom -->
|
||||
<div class="services-cta">
|
||||
<p>Butuh bantuan lebih lanjut? Tim kami siap membantu Anda!</p>
|
||||
<a href="{{ route('halaman-chatbot') }}" class="btn-services-cta">
|
||||
<i class="fas fa-comments me-2"></i>Chat dengan Kami
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Call To Action Section -->
|
||||
<section class="call-to-action-1 call-to-action-1 section" id="call-to-action">
|
||||
<div class="cta-bg" style="background-image: url('assets/img/about/about-4.webp');"></div>
|
||||
<div class="container" data-aos="fade-up" data-aos-delay="100">
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-xl-6 col-lg-8">
|
||||
|
||||
<div class="cta-content text-center">
|
||||
<h2>Butuh Bantuan Menemukan Properti Impian Anda?</h2>
|
||||
<p>Kami siap membantu Anda memilih properti terbaik dengan panduan ahli, sesuai kebutuhan dan budget Anda. Mulai perjalanan properti Anda sekarang!</p>
|
||||
|
||||
<div class="cta-buttons">
|
||||
<a href="https://wa.me/6281234567890" target="_blank" class="btn btn-primary">Hubungi Kami Sekarang</a>
|
||||
<a href="properties.html" class="btn btn-outline">Lihat Properti</a>
|
||||
</div>
|
||||
|
||||
<div class="cta-features">
|
||||
<div class="feature-item" data-aos="fade-up" data-aos-delay="200">
|
||||
<i class="bi bi-telephone-fill"></i>
|
||||
<span>Konsultasi Gratis</span>
|
||||
</div>
|
||||
<div class="feature-item" data-aos="fade-up" data-aos-delay="250">
|
||||
<i class="bi bi-clock-fill"></i>
|
||||
<span>Dukungan 24/7</span>
|
||||
</div>
|
||||
<div class="feature-item" data-aos="fade-up" data-aos-delay="300">
|
||||
<i class="bi bi-shield-check-fill"></i>
|
||||
<span>Ahli Properti Terpercaya</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- End CTA Content -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section><!-- /Call To Action Section -->
|
||||
|
||||
<!-- Properti Unggulan -->
|
||||
<section class="properti-unggulan">
|
||||
<div class="properti-container">
|
||||
|
|
@ -2407,15 +2628,6 @@ class="btn-primary properti-btn">
|
|||
|
||||
</div>
|
||||
|
||||
<!-- Contact CTA -->
|
||||
<div class="faq-contact">
|
||||
<h3>Masih Punya Pertanyaan?</h3>
|
||||
<p>Tim kami siap membantu Anda 24/7. Jangan ragu untuk menghubungi kami!</p>
|
||||
<a href="https://wa.me/6281234567890" class="faq-contact-btn" target="_blank">
|
||||
<i class="fab fa-whatsapp"></i> Hubungi via WhatsApp
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -2461,16 +2673,16 @@ class="btn-primary properti-btn">
|
|||
<div class="footer-column">
|
||||
<h3>Kontak Kami</h3>
|
||||
<div class="footer-contact">
|
||||
<p><i class="fas fa-map-marker-alt"></i> Jl. Melati No. 45, Jakarta Selatan</p>
|
||||
<p><i class="fas fa-map-marker-alt"></i> Jl. Raya Pakisan, Bunduh, Bataan, Kec. Tenggarang, Kabupaten Bondowoso, Jawa Timur 68271</p>
|
||||
<p><i class="fas fa-phone"></i> 0812-3456-7890</p>
|
||||
<p><i class="fas fa-envelope"></i> info@propertiharmoni.com</p>
|
||||
<p><i class="fas fa-envelope"></i> caranibhanubalakosa@gmail.com</p>
|
||||
<p><i class="fas fa-clock"></i> Senin - Sabtu: 08:00 - 17:00</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="copyright">
|
||||
© 2025 PropertiHarmoni. Semua hak dilindungi.
|
||||
© 2025 CaraniEstate. Semua hak dilindungi.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
@ -2616,58 +2828,123 @@ function toggleMenu(){
|
|||
</script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// FAQ Accordion Functionality
|
||||
const faqQuestions = document.querySelectorAll('.faq-question');
|
||||
|
||||
faqQuestions.forEach(question => {
|
||||
question.addEventListener('click', function() {
|
||||
const answer = this.nextElementSibling;
|
||||
const isActive = this.classList.contains('active');
|
||||
|
||||
// Close all other FAQs
|
||||
faqQuestions.forEach(q => {
|
||||
q.classList.remove('active');
|
||||
q.nextElementSibling.style.maxHeight = '0';
|
||||
q.nextElementSibling.style.padding = '0 30px 0 30px';
|
||||
});
|
||||
|
||||
// Toggle current FAQ
|
||||
if (!isActive) {
|
||||
this.classList.add('active');
|
||||
answer.style.maxHeight = answer.scrollHeight + 'px';
|
||||
answer.style.padding = '0 30px 25px 30px';
|
||||
}
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// ================================
|
||||
// FAQ ACCORDION
|
||||
// ================================
|
||||
const faqQuestions = document.querySelectorAll('.faq-question');
|
||||
|
||||
faqQuestions.forEach(question => {
|
||||
question.addEventListener('click', function() {
|
||||
const answer = this.nextElementSibling;
|
||||
const isActive = this.classList.contains('active');
|
||||
|
||||
// Tutup semua
|
||||
faqQuestions.forEach(q => {
|
||||
q.classList.remove('active');
|
||||
q.nextElementSibling.style.maxHeight = '0';
|
||||
q.nextElementSibling.style.padding = '0 30px 0 30px';
|
||||
});
|
||||
|
||||
// Category Filter
|
||||
const categoryBtns = document.querySelectorAll('.faq-category-btn');
|
||||
const faqItems = document.querySelectorAll('.faq-item');
|
||||
|
||||
categoryBtns.forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
// Update active button
|
||||
categoryBtns.forEach(b => b.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
const category = this.dataset.category;
|
||||
|
||||
// Filter FAQs
|
||||
faqItems.forEach(item => {
|
||||
if (category === 'all' || item.dataset.category === category) {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Auto-expand first FAQ (optional)
|
||||
// faqQuestions[0].click();
|
||||
|
||||
// Buka yang diklik
|
||||
if (!isActive) {
|
||||
this.classList.add('active');
|
||||
answer.style.maxHeight = answer.scrollHeight + 'px';
|
||||
answer.style.padding = '0 30px 25px 30px';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
});
|
||||
|
||||
|
||||
// ================================
|
||||
// CATEGORY FILTER
|
||||
// ================================
|
||||
const categoryBtns = document.querySelectorAll('.faq-category-btn');
|
||||
const faqItems = document.querySelectorAll('.faq-item');
|
||||
|
||||
categoryBtns.forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
|
||||
// Set active button
|
||||
categoryBtns.forEach(b => b.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
const category = this.dataset.category;
|
||||
|
||||
// Filter item
|
||||
faqItems.forEach(item => {
|
||||
if (item.dataset.category === category) {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Tutup semua accordion saat ganti kategori
|
||||
faqQuestions.forEach(q => {
|
||||
q.classList.remove('active');
|
||||
q.nextElementSibling.style.maxHeight = '0';
|
||||
q.nextElementSibling.style.padding = '0 30px 0 30px';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// ================================
|
||||
// DEFAULT CATEGORY (UMUM)
|
||||
// ================================
|
||||
const defaultCategory = 'umum';
|
||||
|
||||
categoryBtns.forEach(btn => {
|
||||
if (btn.dataset.category === defaultCategory) {
|
||||
btn.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
faqItems.forEach(item => {
|
||||
if (item.dataset.category === defaultCategory) {
|
||||
item.style.display = 'block';
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
window.onload = function() {
|
||||
if (!localStorage.getItem("email_popup_shown")) {
|
||||
setTimeout(() => {
|
||||
document.getElementById("emailModal").style.display = "flex";
|
||||
}, 2000); // muncul setelah 2 detik
|
||||
|
||||
localStorage.setItem("email_popup_shown", "true");
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById("emailModal").style.display = "none";
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- IKLAN -->
|
||||
<!-- EMAIL CAPTURE MODAL -->
|
||||
<div id="emailModal" class="email-modal">
|
||||
<div class="email-box">
|
||||
<span class="close-btn" onclick="closeModal()">×</span>
|
||||
|
||||
<h2>Dapatkan Info Properti Terbaru!</h2>
|
||||
<p>Masukkan email kamu untuk dapat promo & rekomendasi terbaik 🔥</p>
|
||||
|
||||
<form method="POST" action="{{ route('save-email-visitor') }}">
|
||||
@csrf
|
||||
<input type="email" name="email" placeholder="Masukkan email..." required>
|
||||
<button type="submit">Daftar Sekarang</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/user', function (Request $request) {
|
||||
return $request->user();
|
||||
})->middleware('auth:sanctum');
|
||||
149
routes/web.php
149
routes/web.php
|
|
@ -5,6 +5,8 @@
|
|||
use App\Http\Controllers\ChatbotController;
|
||||
use App\Http\Controllers\AuthController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use App\Http\Controllers\VisitorController;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
@ -33,6 +35,9 @@
|
|||
return view('halaman-chatbot');
|
||||
})->name('halaman-chatbot');
|
||||
|
||||
Route::post('/save-email-visitor', [VisitorController::class, 'store'])
|
||||
->name('save-email-visitor');
|
||||
|
||||
// Katalog Properti (PUBLIC - Bisa dilihat tanpa login)
|
||||
Route::get('/halaman-katalog', function () {
|
||||
$query = \App\Models\Properti::with('blok')
|
||||
|
|
@ -74,10 +79,35 @@
|
|||
return view('detail-katalog', compact('properti'));
|
||||
})->name('detail-katalog');
|
||||
|
||||
// -- NOTIFIKASI --
|
||||
// Notifikasi (Opsional: bisa public atau protected)
|
||||
Route::get('/halaman-notifikasi', function () {
|
||||
return view('halaman-notifikasi');
|
||||
})->name('halaman-notifikasi');
|
||||
$notifikasi = \App\Models\Notifikasi::where('id_user', Auth::id())
|
||||
->latest()
|
||||
->get()
|
||||
->unique('referensi_id'); // 🔥 biar tidak numpuk
|
||||
|
||||
return view('halaman-notifikasi', compact('notifikasi'));
|
||||
})->name('halaman-notifikasi')->middleware('auth');
|
||||
|
||||
// Mark notifikasi sebagai sudah dibaca
|
||||
Route::post('/notifikasi/{id}/read', function ($id) {
|
||||
$notif = \App\Models\Notifikasi::where('id_user', Auth::id())
|
||||
->findOrFail($id);
|
||||
$notif->update(['status_baca' => 1]);
|
||||
return response()->json(['success' => true]);
|
||||
})->name('notifikasi.read')->middleware('auth');
|
||||
|
||||
Route::delete('/notifikasi-massal', function () {
|
||||
|
||||
$ids = explode(',', request('ids'));
|
||||
|
||||
\App\Models\Notifikasi::whereIn('id_notifikasi', $ids)
|
||||
->where('id_user', auth()->id())
|
||||
->delete();
|
||||
|
||||
return back()->with('success', 'Notifikasi berhasil dihapus');
|
||||
})->name('notifikasi.hapus.massal')->middleware('auth');
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -93,6 +123,14 @@
|
|||
Route::get('/gantipass', [AuthController::class, 'formGantiPassword'])->name('ganti-password');
|
||||
Route::post('/gantipass', [AuthController::class, 'updatePassword'])->name('ganti-password.proses');
|
||||
|
||||
// Route untuk halaman register
|
||||
Route::get('/register', function () {
|
||||
return view('register'); // Pastikan file register.blade.php ada
|
||||
})->name('register');
|
||||
|
||||
// Route untuk memproses register
|
||||
Route::post('/register', [AuthController::class, 'register'])->name('register.proses');
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
@ -105,17 +143,13 @@
|
|||
// --- FORM PEMESANAN ---
|
||||
// Halaman form booking (Hanya bisa diakses jika sudah login)
|
||||
Route::get('/form-pemesanan/{id}', function ($id) {
|
||||
$properti = \App\Models\Properti::with('blok')->findOrFail($id);
|
||||
$properti = \App\Models\Properti::where('id_properti', $id)->firstOrFail();
|
||||
return view('form-pemesanan', compact('properti'));
|
||||
})->name('form-pemesanan');
|
||||
|
||||
// Proses submit pemesanan
|
||||
Route::post('/pemesanan/proses', function (\Illuminate\Http\Request $request) {
|
||||
// TODO: Validasi & simpan ke tabel Transaksi
|
||||
// \App\Models\Transaksi::create([...]);
|
||||
|
||||
return redirect()->route('riwayat-pemesanan')->with('success', 'Pemesanan berhasil diajukan!');
|
||||
})->name('pemesanan.proses');
|
||||
Route::post('/pemesanan/proses', [App\Http\Controllers\PemesananController::class, 'proses'])
|
||||
->name('pemesanan.proses');
|
||||
|
||||
|
||||
// --- RIWAYAT & DETAIL PEMESANAN ---
|
||||
|
|
@ -135,10 +169,29 @@
|
|||
return view('detail-pemesanan', compact('transaksi'));
|
||||
})->name('detail-pemesanan');
|
||||
|
||||
// ✅ Detail Properti (Hanya bisa diakses jika login)
|
||||
Route::get('/detail-katalog/{id}', function ($id) {
|
||||
$properti = \App\Models\Properti::with('blok')->findOrFail($id);
|
||||
return view('detail-katalog', compact('properti'));
|
||||
})->name('detail-katalog');
|
||||
|
||||
// --- HALAMAN TERIMS PEMESANAN ---
|
||||
// Halaman terima kasih setelah pemesanan
|
||||
Route::get('/pemesanan/terima-kasih/{id}', function ($id) {
|
||||
$transaksi = \App\Models\Transaksi::with('properti')
|
||||
->where('id_transaksi', $id)
|
||||
->where('id_user', Auth::id())
|
||||
->firstOrFail();
|
||||
return view('terima-kasih', compact('transaksi'));
|
||||
})->name('pemesanan.terima-kasih');
|
||||
|
||||
|
||||
// --- CHATBOT & PROFIL ---
|
||||
// 💡 Chatbot saya letakkan di sini agar bisa akses data user
|
||||
|
||||
// CHATBOT
|
||||
Route::post('/chat/send', [ChatBotController::class, 'sendMessage'])->name('chat.send');
|
||||
|
||||
Route::get('/halaman-profil', function () {
|
||||
return view('halaman-profil');
|
||||
})->name('halaman-profil');
|
||||
|
|
@ -211,6 +264,8 @@
|
|||
return view('admin.tambah-rumah');
|
||||
})->name('admin.tambah-rumah');
|
||||
|
||||
|
||||
|
||||
// ========================
|
||||
// DATA USER
|
||||
// ========================
|
||||
|
|
@ -274,7 +329,8 @@
|
|||
|
||||
return redirect()->route('admin.data_user')
|
||||
->with('success', 'User berhasil dihapus.');
|
||||
})->name('admin.hapus_user');
|
||||
})->name('admin.hapus_user');
|
||||
|
||||
|
||||
// ========================
|
||||
// VERIFIKASI
|
||||
|
|
@ -282,15 +338,24 @@
|
|||
Route::get('/halaman_verifikasi', function () {
|
||||
$status = request('status');
|
||||
|
||||
$query = \App\Models\Dokumen::with('user');
|
||||
$queryTransaksi = \App\Models\Transaksi::with(['user', 'properti']);
|
||||
$queryDokumen = \App\Models\Dokumen::with('user');
|
||||
|
||||
if ($status && $status != 'semua') {
|
||||
$query->where('status_verifikasi', $status);
|
||||
if ($status == 'pending') {
|
||||
$queryTransaksi->where('status_transaksi', 'menunggu_verifikasi');
|
||||
$queryDokumen->where('status_verifikasi', 'pending');
|
||||
} elseif ($status == 'diterima') {
|
||||
$queryTransaksi->where('status_transaksi', 'menunggu_pembayaran');
|
||||
$queryDokumen->where('status_verifikasi', 'diterima');
|
||||
} elseif ($status == 'ditolak') {
|
||||
$queryTransaksi->where('status_transaksi', 'ditolak');
|
||||
$queryDokumen->where('status_verifikasi', 'ditolak');
|
||||
}
|
||||
|
||||
$dokumen = $query->orderBy('uploaded_at', 'desc')->get();
|
||||
$transaksi = $queryTransaksi->orderBy('tanggal_transaksi', 'desc')->get();
|
||||
$user = \App\Models\User::with('dokumen')->get();
|
||||
|
||||
return view('admin.halaman_verifikasi', compact('dokumen'));
|
||||
return view('admin.halaman_verifikasi', compact('transaksi', 'user'));
|
||||
})->name('admin.halaman_verifikasi');
|
||||
|
||||
Route::post('/verifikasi/{id}/approve', function ($id) {
|
||||
|
|
@ -324,4 +389,58 @@
|
|||
return back()->with('success', 'Dokumen ditolak');
|
||||
})->name('admin.verifikasi.tolak');
|
||||
|
||||
// MELIHAT DOKUMEN
|
||||
Route::get('/verifikasi/{id}', function ($id) {
|
||||
$user = \App\Models\User::with('dokumen')->findOrFail($id);
|
||||
|
||||
return view('admin.verifikasi_dokumen', compact('user'));
|
||||
})->name('admin.verifikasi_dokumen');
|
||||
|
||||
Route::post('/verifikasi/{id}/selesai', function ($id) {
|
||||
$user = \App\Models\User::with('dokumen')->findOrFail($id);
|
||||
|
||||
// contoh: cek apakah semua dokumen sudah diterima
|
||||
$allApproved = $user->dokumen->every(function ($d) {
|
||||
return $d->status_verifikasi === 'diterima';
|
||||
});
|
||||
|
||||
// kirim notifikasi ke user
|
||||
\App\Models\Notifikasi::create([
|
||||
'id_user' => $user->id_user,
|
||||
'judul' => 'Verifikasi Selesai',
|
||||
'pesan' => $allApproved
|
||||
? 'Semua dokumen kamu telah diverifikasi.'
|
||||
: 'Verifikasi selesai, tapi masih ada dokumen yang perlu diperbaiki.',
|
||||
'tipe' => 'dokumen',
|
||||
'status_baca' => 0,
|
||||
'status_kirim' => 'terkirim',
|
||||
'channel' => 'in_app',
|
||||
'referensi_id' => $user->id_user,
|
||||
'referensi_tipe' => 'user',
|
||||
]);
|
||||
|
||||
return redirect()->route('admin.halaman_verifikasi')
|
||||
->with('success', 'Verifikasi selesai');
|
||||
})->name('admin.verifikasi.selesai');
|
||||
|
||||
});
|
||||
|
||||
// TEST
|
||||
Route::get('/test-gemini', function () {
|
||||
$apiKey = env('GOOGLE_GEMINI_API_KEY');
|
||||
$model = env('GOOGLE_GEMINI_MODEL', 'gemini-2.0-flash');
|
||||
$url = "https://generativelanguage.googleapis.com/v1/models/{$model}:generateContent?key={$apiKey}";
|
||||
|
||||
$response = Http::post($url, [
|
||||
'contents' => [[
|
||||
'parts' => [['text' => 'Halo, apakah API key Gemini saya berfungsi?']]
|
||||
]]
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$text = $response->json('candidates.0.content.parts.0.text') ?? 'No response';
|
||||
return '✅ API Key Gemini Berhasil! Response: ' . $text;
|
||||
}
|
||||
|
||||
return '❌ Error ' . $response->status() . ': ' . $response->body();
|
||||
});
|
||||
Loading…
Reference in New Issue