|
Server IP : 45.113.226.95 / Your IP : 216.73.216.239 Web Server : LiteSpeed System : Linux bharat.hostitbro.com 5.14.0-611.13.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 11 04:57:59 EST 2025 x86_64 User : digita21 ( 1701) PHP Version : 8.3.30 Disable Function : mail MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF Directory (0750) : /home2/digita21/worldwideengineers.in/../mail/../ssl/../realpropertyindia.com/ |
| [ Home ] | [ C0mmand ] | [ Upload File ] |
|---|
<?php
session_start();
include("config.php");
?>
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Webartinfo">
<meta name="author" content="Webartinfo">
<title>Shiv Ganga Real Estate</title>
<!-- Favicon Icon -->
<link rel="icon" type="image/png" href="img/favicon.png">
<!-- Bootstrap core CSS -->
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Material Design Icons -->
<link href="vendor/icons/css/materialdesignicons.min.css" media="all" rel="stylesheet" type="text/css" />
<!-- Select2 CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css" />
<link href="vendor/select2/css/select2-bootstrap.css" />
<link href="vendor/select2/css/select2.min.css" rel="stylesheet" />
<!-- Custom styles for this template -->
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<?php
include('./inc/header.php');
?>
<?php
if (!isset($_SESSION['uid']) || !isset($_SESSION['role'])) {
header("Location: login.php");
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])) {
$uid = $_SESSION['uid'];
$role = $_SESSION['role']; // 🔥 SESSION ROLE
// सुरक्षा validation
$allowed_roles = ['user','agent','builder'];
if (!in_array($role, $allowed_roles)) {
$role = 'user';
}
// Basic fields
$title = mysqli_real_escape_string($con, trim($_POST['title'] ?? ''));
$type = mysqli_real_escape_string($con, trim($_POST['type'] ?? ''));
$price_type = $_POST['priceType'] ?? 'FIXED';
$area_input_type = $_POST['areaInputType'] ?? 'area';
// Price
$price = !empty($_POST['price']) ? (float)$_POST['price'] : 0;
$min_price = !empty($_POST['min_price']) ? (float)$_POST['min_price'] : 0;
$max_price = !empty($_POST['max_price']) ? (float)$_POST['max_price'] : 0;
// Area (NO NULL)
$area = 0;
$length = 0;
$breadth = 0;
if ($area_input_type === 'dimensions') {
$length = !empty($_POST['length']) ? (float)$_POST['length'] : 0;
$breadth = !empty($_POST['breadth']) ? (float)$_POST['breadth'] : 0;
if ($length > 0 && $breadth > 0) {
$area = $length * $breadth;
}
} else {
$area = !empty($_POST['area']) ? (float)$_POST['area'] : 0;
if ($area > 0) {
$length = $area;
$breadth = 1;
}
}
// Location
$address = mysqli_real_escape_string($con, trim($_POST['location'] ?? ''));
$city = mysqli_real_escape_string($con, trim($_POST['city'] ?? ''));
$pincode = $_POST['pincode'] ?? '';
$latitude = $_POST['latitude'] ?? '';
$longitude = $_POST['longitude'] ?? '';
// Description & contact
$description = mysqli_real_escape_string($con, trim($_POST['description'] ?? ''));
$contact_person = mysqli_real_escape_string($con, trim($_POST['contactPerson'] ?? ''));
$contact_phone = preg_replace('/[^0-9]/', '', $_POST['contactPhone'] ?? '');
// Validation
if ($title == '' || $type == '' || $city == '' || $description == '' || $contact_person == '' || $contact_phone == '') {
$_SESSION['error'] = "Please fill all fields.";
header("Location: ".$_SERVER['PHP_SELF']);
exit;
}
// Price logic
if ($price_type == 'FIXED') {
if ($price <= 0) {
$_SESSION['error'] = "Enter valid price";
header("Location: ".$_SERVER['PHP_SELF']);
exit;
}
$min_price = 0;
$max_price = 0;
} elseif ($price_type == 'RANGE') {
if ($min_price <= 0 || $max_price <= 0 || $min_price > $max_price) {
$_SESSION['error'] = "Invalid price range";
header("Location: ".$_SERVER['PHP_SELF']);
exit;
}
$price = $min_price;
}
// Cover Image
$cover_image = '';
if (!empty($_FILES['cover_image']['name'])) {
$cover_image = uploadSingleImage($_FILES['cover_image'], 'cover');
}
// Multiple Images
$images_array = [];
if (!empty($_FILES['additional_images']['name'][0])) {
$images_array = uploadMultipleImages($_FILES['additional_images']);
}
$images = !empty($images_array) ? json_encode($images_array) : '';
// Video
$video = '';
if (!empty($_FILES['property_video']['name'])) {
$video = uploadVideo($_FILES['property_video']);
}
// 🔥 FINAL INSERT QUERY (NO NULL)
$sql = "INSERT INTO properties
(uid, role, title, type, price, min_price, max_price, price_type, area, area_unit, length, breadth, address, city, pincode, latitude, longitude, description, cover_image, images, video, contact_person, contact_phone, status, created_at)
VALUES
('$uid','$role','$title','$type',
'$price','$min_price','$max_price','$price_type',
'$area','sqft',
'$length','$breadth',
'$address','$city','$pincode','$latitude','$longitude',
'$description','$cover_image','$images','$video',
'$contact_person','$contact_phone','active',NOW())";
if (mysqli_query($con, $sql)) {
$_SESSION['success'] = "Property Added Successfully!";
} else {
$_SESSION['error'] = mysqli_error($con);
}
header("Location: ".$_SERVER['PHP_SELF']);
exit;
}
// ================= FUNCTIONS =================
function uploadSingleImage($file, $type='cover') {
$dir = "uploads/properties/$type/";
if (!is_dir($dir)) mkdir($dir, 0777, true);
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, ['jpg','jpeg','png','webp'])) return '';
$name = uniqid().".".$ext;
$path = $dir.$name;
move_uploaded_file($file['tmp_name'], $path);
return $path;
}
function uploadMultipleImages($files) {
$uploaded = [];
$dir = "uploads/properties/images/";
if (!is_dir($dir)) mkdir($dir, 0777, true);
for ($i=0; $i<count($files['name']); $i++) {
if ($files['error'][$i] == 0) {
$ext = strtolower(pathinfo($files['name'][$i], PATHINFO_EXTENSION));
if (!in_array($ext, ['jpg','jpeg','png','webp'])) continue;
$name = uniqid().".".$ext;
$path = $dir.$name;
if (move_uploaded_file($files['tmp_name'][$i], $path)) {
$uploaded[] = $path;
}
}
}
return $uploaded;
}
function uploadVideo($file) {
$dir = "uploads/properties/video/";
if (!is_dir($dir)) mkdir($dir, 0777, true);
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, ['mp4','webm','ogg','mov'])) return '';
$name = uniqid().".".$ext;
$path = $dir.$name;
move_uploaded_file($file['tmp_name'], $path);
return $path;
}
?>
<div class="bg-gradient-to-br from-gray-50 via-white to-primary-50 min-h-screen">
<div class="flex-grow">
<div class="bg-white shadow-sm border-b border-gray-100">
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-4 md:py-6">
<div class="flex items-center gap-3">
<button onclick="history.back()"
class="md:hidden p-2 -ml-2 rounded-lg hover:bg-gray-100 transition-colors"
aria-label="Go back">
<svg class="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 19l-7-7 7-7"></path>
</svg>
</button>
<div>
<h1 class="text-xl sm:text-2xl md:text-3xl font-bold text-gray-900">List Your Property</h1>
<p class="text-sm text-gray-600 mt-1">Create a new property listing</p>
</div>
</div>
</div>
</div>
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<form id="propertyForm"
class="bg-white rounded-2xl shadow-lg border border-gray-100 overflow-hidden p-4 sm:p-6 lg:p-8 space-y-8"
method="post" enctype="multipart/form-data">
<input type="hidden" name="cover_image_url" id="cover_image_url">
<input type="hidden" name="additional_image_urls" id="additional_image_urls">
<input type="hidden" name="video_url" id="video_url">
<input type="hidden" name="latitude" id="latitude">
<input type="hidden" name="longitude" id="longitude">
<div class="bg-gradient-to-br from-blue-50 to-indigo-50 rounded-2xl p-6 border border-blue-100 fade-in">
<div class="flex items-center gap-3 mb-6">
<div class="bg-blue-600 p-3 rounded-xl">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-4m-5 0H3m2 0h3M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10V9a1 1 0 011-1h4a1 1 0 011 1v12M9 21h6"></path>
</svg>
</div>
<div>
<h2 class="text-lg font-bold text-gray-900">Basic Information</h2>
<p class="text-sm text-gray-600">Property title and type</p>
</div>
</div>
<div class="space-y-6">
<div>
<label for="title" class="block text-sm font-semibold text-gray-700 mb-3">Property Title *</label>
<input id="title" name="title"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-base"
placeholder="e.g., Beautiful 2BHK Apartment in Prime Location" maxlength="150" autocomplete="off">
<div class="flex justify-between items-center mt-2">
<p class="text-xs text-gray-500">Make it descriptive and appealing</p>
<span class="text-xs text-gray-400 character-counter" id="titleCounter">0/150</span>
</div>
</div>
<div>
<label for="type" class="block text-sm font-semibold text-gray-700 mb-3">Property Type *</label>
<select id="type" name="type"
class="w-full px-4 py-3 text-base border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white"
>
<option value="">Select property type</option>
<option value="FARMLAND">Farmland</option>
<option value="PLOT">Plot/Land</option>
<option value="HOUSE">House/Villa</option>
<option value="FLAT">Flat/Apartment</option>
<option value="SHOP">Commercial Shop</option>
<option value="OTHER">Other</option>
</select>
</div>
</div>
</div>
<div class="bg-gradient-to-br from-green-50 to-emerald-50 rounded-2xl p-6 border border-green-100 fade-in">
<div class="flex items-center gap-3 mb-6">
<div class="bg-green-600 p-3 rounded-xl">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1"></path>
</svg>
</div>
<div>
<h2 class="text-lg font-bold text-gray-900">Pricing Details</h2>
<p class="text-sm text-gray-600">Set your property price</p>
</div>
</div>
<div class="space-y-6">
<div>
<label class="block text-sm font-semibold text-gray-700 mb-4">Pricing Strategy *</label>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<label class="relative cursor-pointer radio-card">
<input type="radio" name="priceType" class="sr-only" value="FIXED" checked>
<div class="p-4 rounded-xl border-2 transition-all border-green-500 bg-green-100 shadow-md">
<div class="text-center">
<div class="font-semibold text-gray-900 mb-1">Fixed Price</div>
<div class="text-xs text-gray-600">Set a fixed price</div>
</div>
</div>
</label>
<label class="relative cursor-pointer radio-card">
<input type="radio" name="priceType" class="sr-only" value="RANGE">
<div class="p-4 rounded-xl border-2 transition-all border-gray-200 bg-white hover:border-green-300">
<div class="text-center">
<div class="font-semibold text-gray-900 mb-1">Price Range</div>
<div class="text-xs text-gray-600">Set min-max range</div>
</div>
</div>
</label>
<label class="relative cursor-pointer radio-card">
<input type="radio" name="priceType" class="sr-only" value="NEGOTIABLE">
<div class="p-4 rounded-xl border-2 transition-all border-gray-200 bg-white hover:border-green-300">
<div class="text-center">
<div class="font-semibold text-gray-900 mb-1">Negotiable</div>
<div class="text-xs text-gray-600">Price is negotiable</div>
</div>
</div>
</label>
</div>
</div>
<div class="bg-white rounded-xl p-4 border border-gray-200" id="priceFields"></div>
</div>
</div>
<div class="bg-gradient-to-br from-amber-50 to-orange-50 rounded-2xl p-6 border border-amber-100 fade-in">
<div class="flex items-center gap-3 mb-6">
<div class="bg-amber-600 p-3 rounded-xl">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"></path>
</svg>
</div>
<div>
<h2 class="text-lg font-bold text-gray-900">Area & Dimensions</h2>
<p class="text-sm text-gray-600">Specify property size</p>
</div>
</div>
<div class="space-y-6">
<div>
<label class="block text-sm font-semibold text-gray-700 mb-4">How would you like to specify the area? *</label>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<label class="relative cursor-pointer radio-card">
<input type="radio" name="areaInputType" class="sr-only" value="area" checked>
<div class="p-4 rounded-xl border-2 transition-all border-amber-500 bg-amber-100 shadow-md">
<div class="text-center">
<div class="font-semibold text-gray-900 mb-1">Total Area</div>
<div class="text-xs text-gray-600">Direct area input</div>
</div>
</div>
</label>
<label class="relative cursor-pointer radio-card">
<input type="radio" name="areaInputType" class="sr-only" value="dimensions">
<div class="p-4 rounded-xl border-2 transition-all border-gray-200 bg-white hover:border-amber-300">
<div class="text-center">
<div class="font-semibold text-gray-900 mb-1">Length × Breadth</div>
<div class="text-xs text-gray-600">Calculate from dimensions</div>
</div>
</div>
</label>
</div>
</div>
<div class="bg-white rounded-xl p-4 border border-gray-200">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4" id="areaInputs"></div>
</div>
</div>
</div>
<div class="space-y-4">
<label class="block text-sm font-semibold text-gray-700 mb-2">Location (Optional - Use map OR fill address below)</label>
<div class="bg-blue-50 border border-blue-200 rounded-lg p-3 mb-3">
<p class="text-xs sm:text-sm text-blue-800"><strong>Tip:</strong> Use the map selector for precise location or fill in the address fields below manually.</p>
</div>
<button type="button" onclick="openMapSelector()"
class="font-semibold rounded-lg transition-colors bg-primary-600 hover:bg-primary-700 text-white px-4 py-3 text-sm sm:text-base w-full flex items-center justify-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
<circle cx="12" cy="10" r="3"></circle>
</svg>
<span>Select Location on Map</span>
</button>
</div>
<div class="space-y-4">
<label for="location" class="block text-sm font-semibold text-gray-700 mb-2">Full Address</label>
<input id="location" name="location"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="e.g., Near Railway Station, Main Road, or paste from Google Maps">
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="city" class="block text-sm font-semibold text-gray-700 mb-2">City *</label>
<input id="city" name="city"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="e.g., Mumbai" >
</div>
<div>
<label for="pincode" class="block text-sm font-semibold text-gray-700 mb-2">Pincode</label>
<input id="pincode" name="pincode"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="e.g., 400001" maxlength="6">
</div>
</div>
<div>
<label for="description" class="block text-sm font-semibold text-gray-700 mb-2">Property Description *</label>
<textarea id="description" name="description" rows="6"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-vertical"
placeholder="Describe your property in detail..." ></textarea>
</div>
<div class="bg-gradient-to-br from-primary-50 to-blue-50 border-2 border-primary-200 rounded-xl p-4 sm:p-6">
<div class="flex items-center gap-3 mb-4">
<div class="bg-primary-600 p-2.5 rounded-lg">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<circle cx="8.5" cy="8.5" r="1.5"></circle>
<polyline points="21 15 16 10 5 21"></polyline>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-900">Cover Photo *</h3>
<p class="text-sm text-gray-600">Main display image for your property</p>
</div>
</div>
<div class="bg-white rounded-lg p-4 border-2 border-dashed border-gray-300 hover:border-blue-400 transition-colors">
<div class="flex flex-col sm:flex-row gap-3">
<input id="coverImageUrl"
class="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
placeholder="Paste image URL here">
<div class="flex gap-2">
<button type="button" onclick="setCoverImage()"
class="bg-gray-200 text-gray-800 hover:bg-gray-300 px-4 py-3 text-sm font-semibold rounded-lg transition-colors">
Set
</button>
<input id="coverImageUpload" name="cover_image" type="file" accept="image/*" class="hidden">
<button type="button" onclick="document.getElementById('coverImageUpload').click()"
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-3 text-sm font-semibold rounded-lg transition-colors">
Upload
</button>
</div>
</div>
<div id="coverPreview" class="mt-4 flex flex-wrap gap-2 hidden"></div>
</div>
</div>
<div class="bg-gradient-to-br from-orange-50 to-amber-50 border-2 border-orange-200 rounded-xl p-4 sm:p-6">
<div class="flex items-center justify-between mb-4">
<div>
<h3 class="text-lg font-bold text-gray-900">Additional Images (Max 6)</h3>
<p class="text-sm text-gray-600">Upload up to 6 images (max 5MB each)</p>
</div>
<div class="bg-white px-3 py-1.5 rounded-lg border border-orange-300">
<span class="text-sm font-bold text-gray-700" id="imageCount">0/6</span>
</div>
</div>
<input type="file" id="additionalImages" name="additional_images[]" multiple accept="image/*" class="hidden">
<div class="bg-white rounded-lg p-4 border-2 border-dashed border-gray-300 hover:border-orange-400 transition-colors mb-4">
<div class="flex flex-col sm:flex-row gap-3">
<input id="additionalImageUrl"
class="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
placeholder="Paste image URL or press Enter">
<div class="flex gap-2">
<button type="button" onclick="addAdditionalImage()"
class="bg-gray-200 text-gray-800 hover:bg-gray-300 px-4 py-3 text-sm font-semibold rounded-lg transition-colors">
Add
</button>
<button type="button" onclick="document.getElementById('additionalImages').click()"
class="bg-orange-500 hover:bg-orange-600 text-white px-4 py-3 text-sm font-semibold rounded-lg transition-colors">
Upload
</button>
</div>
</div>
</div>
<div id="additionalImagesPreview" class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3"></div>
</div>
<div class="bg-gradient-to-br from-purple-50 to-indigo-50 border-2 border-purple-200 rounded-xl p-4 sm:p-6">
<div class="flex items-center gap-3 mb-4">
<div class="bg-purple-600 p-2.5 rounded-lg">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<polygon points="23 7 16 12 23 17 23 7"></polygon>
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>
</svg>
</div>
<div>
<h3 class="text-base sm:text-lg font-bold text-gray-900">Property Video (Optional)</h3>
<p class="text-xs sm:text-sm text-gray-600">Add a video tour (max 100MB)</p>
</div>
</div>
<input type="file" id="propertyVideoUpload" name="property_video" accept="video/*" class="hidden" aria-label="Upload property video">
<div class="bg-white rounded-lg p-4 border-2 border-dashed border-gray-300">
<div class="flex flex-col sm:flex-row gap-3">
<input id="propertyVideoUrlInput"
class="w-full px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 border-gray-300 flex-1 text-sm"
placeholder="Paste video URL here">
<div class="flex gap-2">
<button type="button" onclick="setPropertyVideo()"
class="bg-gray-200 text-gray-800 hover:bg-gray-300 px-4 py-3 text-sm font-semibold rounded-lg transition-colors">
Set
</button>
<button type="button" onclick="document.getElementById('propertyVideoUpload').click()"
class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-3 text-sm font-semibold rounded-lg transition-colors">
Upload
</button>
</div>
</div>
<div id="videoPreview" class="mt-4 hidden"></div>
</div>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-2">Contact Information *</label>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="contactPerson" class="block text-sm font-medium text-gray-700 mb-2">Contact Person</label>
<input id="contactPerson" name="contactPerson"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Your name" >
</div>
<div>
<label for="contactPhone" class="block text-sm font-medium text-gray-700 mb-2">Contact Phone</label>
<input id="contactPhone" name="contactPhone"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Your phone number" >
</div>
</div>
</div>
<div class="bg-gradient-to-r from-primary-600 to-blue-600 rounded-2xl p-6 text-white">
<div class="text-center mb-6">
<h3 class="text-2xl font-bold mb-2">Ready to List Your Property?</h3>
<p class="text-primary-100 text-sm">Review details and publish your listing</p>
</div>
<div class="space-y-4">
<button type="submit" name="submit" value="1"
class="w-full bg-white text-black hover:bg-gray-100 py-4 text-base font-black rounded-xl shadow-lg transition-all border-2 border-gray-300 flex items-center justify-center gap-3">
Publish Listing
</button>
<button type="button" onclick="window.history.back()"
class="w-full border-2 border-white text-white hover:bg-white hover:text-primary-600 py-3 font-semibold rounded-xl transition-all">
Cancel
</button>
</div>
</div>
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('propertyForm');
const titleInput = document.getElementById('title');
const titleCounter = document.getElementById('titleCounter');
const priceFields = document.getElementById('priceFields');
const areaInputs = document.getElementById('areaInputs');
const coverUpload = document.getElementById('coverImageUpload');
const additionalUpload = document.getElementById('additionalImages');
const videoUpload = document.getElementById('propertyVideoUpload');
const coverPreview = document.getElementById('coverPreview');
const additionalPreview = document.getElementById('additionalImagesPreview');
const videoPreview = document.getElementById('videoPreview');
const imageCount = document.getElementById('imageCount');
const coverImageUrlInput = document.getElementById('coverImageUrl');
const additionalImageUrlInput = document.getElementById('additionalImageUrl');
const videoUrlInput = document.getElementById('propertyVideoUrlInput');
const hiddenCoverUrl = document.getElementById('cover_image_url');
const hiddenAdditionalUrls = document.getElementById('additional_image_urls');
const hiddenVideoUrl = document.getElementById('video_url');
const maxImages = 6;
let additionalImageUrls = [];
function renderPriceFields(type) {
if (type === 'RANGE') {
priceFields.innerHTML = `
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="minPrice" class="block text-sm font-semibold text-gray-700 mb-2">Minimum Price *</label>
<input id="minPrice" name="min_price" type="number" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-base" placeholder="e.g., 5000000" >
</div>
<div>
<label for="maxPrice" class="block text-sm font-semibold text-gray-700 mb-2">Maximum Price *</label>
<input id="maxPrice" name="max_price" type="number" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-base" placeholder="e.g., 8000000" >
</div>
</div>
<p class="text-xs text-gray-500 mt-3">Enter price range in INR.</p>
`;
} else if (type === 'NEGOTIABLE') {
priceFields.innerHTML = `
<div>
<label for="price" class="block text-sm font-semibold text-gray-700 mb-2">Expected Price</label>
<input id="price" name="price" type="number" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-base" placeholder="Optional">
<p class="text-xs text-gray-500 mt-3">You can leave it empty or set an expected price.</p>
</div>
`;
} else {
priceFields.innerHTML = `
<div>
<label for="price" class="block text-sm font-semibold text-gray-700 mb-2">Property Price *</label>
<input id="price" name="price" type="number" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-base" placeholder="e.g., 6500000" >
<p class="text-xs text-gray-500 mt-3">Enter price in INR.</p>
</div>
`;
}
}
function renderAreaFields(type) {
if (type === 'dimensions') {
areaInputs.innerHTML = `
<div>
<label for="length" class="block text-sm font-semibold text-gray-700 mb-2">Length *</label>
<input id="length" name="length" type="number" step="any" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="e.g., 50" >
</div>
<div>
<label for="breadth" class="block text-sm font-semibold text-gray-700 mb-2">Breadth *</label>
<input id="breadth" name="breadth" type="number" step="any" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="e.g., 24" >
</div>
<div class="md:col-span-2">
<label for="areaUnit" class="block text-sm font-semibold text-gray-700 mb-2">Unit *</label>
<select id="areaUnit" name="areaUnit" class="w-full px-4 py-3 text-base border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
<option value="sqft">sqft</option>
<option value="sqm">sqm</option>
<option value="acre">acre</option>
<option value="bigha">bigha</option>
<option value="katha">katha</option>
</select>
</div>
`;
} else {
areaInputs.innerHTML = `
<div>
<label for="area" class="block text-sm font-semibold text-gray-700 mb-2">Total Area *</label>
<input id="area" name="area" type="number" step="any" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="e.g., 1200" >
</div>
<div>
<label for="areaUnit" class="block text-sm font-semibold text-gray-700 mb-2">Unit *</label>
<select id="areaUnit" name="areaUnit" class="w-full px-4 py-3 text-base border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
<option value="sqft">sqft</option>
<option value="sqm">sqm</option>
<option value="acre">acre</option>
<option value="bigha">bigha</option>
<option value="katha">katha</option>
</select>
</div>
`;
}
}
function syncAdditionalImageUrls() {
hiddenAdditionalUrls.value = JSON.stringify(additionalImageUrls);
}
function showCoverPreview(src) {
coverPreview.innerHTML = `
<div class="relative">
<img src="${src}" alt="Cover Preview" class="w-24 h-24 object-cover rounded-lg shadow-md border">
<button type="button" id="removeCoverBtn" class="absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs hover:bg-red-600">×</button>
</div>
`;
coverPreview.classList.remove('hidden');
document.getElementById('removeCoverBtn').addEventListener('click', function () {
coverPreview.innerHTML = '';
coverPreview.classList.add('hidden');
if (coverUpload) coverUpload.value = '';
coverImageUrlInput.value = '';
hiddenCoverUrl.value = '';
});
}
function renderAdditionalPreviews() {
additionalPreview.innerHTML = '';
additionalImageUrls.forEach((url, index) => {
const div = document.createElement('div');
div.className = 'relative group';
div.innerHTML = `
<img src="${url}" alt="Additional Image ${index + 1}" class="w-full h-24 object-cover rounded-lg shadow-md border">
<button type="button" class="absolute -top-1 -right-1 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs opacity-100 sm:opacity-0 group-hover:opacity-100 transition-all hover:bg-red-600" data-index="${index}">×</button>
`;
additionalPreview.appendChild(div);
});
additionalPreview.querySelectorAll('button[data-index]').forEach(btn => {
btn.addEventListener('click', function () {
const index = parseInt(this.getAttribute('data-index'));
additionalImageUrls.splice(index, 1);
syncAdditionalImageUrls();
renderAdditionalPreviews();
});
});
imageCount.textContent = `${additionalImageUrls.length}/${maxImages}`;
}
function showVideoPreview(src) {
videoPreview.innerHTML = `
<div class="relative max-w-md">
<video controls class="w-full rounded-lg border shadow-md bg-black">
<source src="${src}">
Your browser does not support the video tag.
</video>
<button type="button" id="removeVideoBtn" class="absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs hover:bg-red-600">×</button>
</div>
`;
videoPreview.classList.remove('hidden');
document.getElementById('removeVideoBtn').addEventListener('click', function () {
videoPreview.innerHTML = '';
videoPreview.classList.add('hidden');
if (videoUpload) videoUpload.value = '';
if (videoUrlInput) videoUrlInput.value = '';
if (hiddenVideoUrl) hiddenVideoUrl.value = '';
});
}
window.setCoverImage = function () {
const url = coverImageUrlInput.value.trim();
if (!url) return;
hiddenCoverUrl.value = url;
if (coverUpload) coverUpload.value = '';
showCoverPreview(url);
};
window.addAdditionalImage = function () {
const url = additionalImageUrlInput.value.trim();
if (!url) return;
if (additionalImageUrls.length >= maxImages) {
alert(`Maximum ${maxImages} additional images allowed`);
return;
}
additionalImageUrls.push(url);
syncAdditionalImageUrls();
renderAdditionalPreviews();
additionalImageUrlInput.value = '';
};
window.setPropertyVideo = function () {
const url = videoUrlInput.value.trim();
if (!url) return;
hiddenVideoUrl.value = url;
if (videoUpload) videoUpload.value = '';
showVideoPreview(url);
};
window.openMapSelector = function () {
alert('Map selector integration pending. You can fill address manually for now.');
};
if (titleInput && titleCounter) {
titleInput.addEventListener('input', function () {
const length = this.value.length;
titleCounter.textContent = `${length}/150`;
titleCounter.className = length > 140
? 'text-xs text-red-500 character-counter'
: 'text-xs text-gray-400 character-counter';
});
}
document.querySelectorAll('.radio-card input[type="radio"]').forEach(radio => {
radio.addEventListener('change', function () {
const parentGrid = this.closest('.grid');
if (parentGrid) {
parentGrid.querySelectorAll('.radio-card > div').forEach(card => {
card.classList.remove('border-green-500', 'bg-green-100', 'shadow-md', 'border-amber-500', 'bg-amber-100');
card.classList.add('border-gray-200', 'bg-white');
});
const activeCard = this.nextElementSibling;
activeCard.classList.remove('border-gray-200', 'bg-white');
if (this.name === 'priceType') {
activeCard.classList.add('border-green-500', 'bg-green-100', 'shadow-md');
renderPriceFields(this.value);
} else if (this.name === 'areaInputType') {
activeCard.classList.add('border-amber-500', 'bg-amber-100', 'shadow-md');
renderAreaFields(this.value);
}
}
});
});
if (coverUpload) {
coverUpload.addEventListener('change', function (e) {
const file = e.target.files[0];
if (!file) return;
if (file.size > 5 * 1024 * 1024) {
alert('Cover image must be less than 5MB');
this.value = '';
return;
}
hiddenCoverUrl.value = '';
const reader = new FileReader();
reader.onload = function (ev) {
showCoverPreview(ev.target.result);
};
reader.readAsDataURL(file);
});
}
if (additionalUpload) {
additionalUpload.addEventListener('change', function (e) {
const selectedFiles = Array.from(e.target.files);
if ((additionalImageUrls.length + selectedFiles.length) > maxImages) {
alert(`Maximum ${maxImages} additional images allowed`);
this.value = '';
return;
}
imageCount.textContent = `${additionalImageUrls.length + selectedFiles.length}/${maxImages}`;
});
}
if (additionalImageUrlInput) {
additionalImageUrlInput.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
window.addAdditionalImage();
}
});
}
if (videoUpload) {
videoUpload.addEventListener('change', function (e) {
const file = e.target.files[0];
if (!file) return;
if (file.size > 100 * 1024 * 1024) {
alert('Video must be less than 100MB');
this.value = '';
return;
}
hiddenVideoUrl.value = '';
if (videoUrlInput) videoUrlInput.value = '';
const localUrl = URL.createObjectURL(file);
showVideoPreview(localUrl);
});
}
if (form) {
form.addEventListener('submit', function (e) {
const title = document.getElementById('title')?.value.trim() || '';
const type = document.getElementById('type')?.value.trim() || '';
const city = document.getElementById('city')?.value.trim() || '';
const description = document.getElementById('description')?.value.trim() || '';
const contactPerson = document.getElementById('contactPerson')?.value.trim() || '';
const contactPhone = document.getElementById('contactPhone')?.value.trim() || '';
const selectedPriceType = document.querySelector('input[name="priceType"]:checked')?.value || 'FIXED';
let errors = [];
if (!title) errors.push('Title');
if (!type) errors.push('Property Type');
if (!city) errors.push('City');
if (!description) errors.push('Description');
if (!contactPerson) errors.push('Contact Person');
if (!contactPhone) errors.push('Contact Phone');
if (selectedPriceType === 'FIXED') {
const price = document.getElementById('price')?.value.trim() || '';
if (!price) errors.push('Price');
}
if (selectedPriceType === 'RANGE') {
const minPrice = document.getElementById('minPrice')?.value.trim() || '';
const maxPrice = document.getElementById('maxPrice')?.value.trim() || '';
if (!minPrice) errors.push('Minimum Price');
if (!maxPrice) errors.push('Maximum Price');
if (minPrice && maxPrice && Number(minPrice) > Number(maxPrice)) {
errors.push('Minimum Price cannot be greater than Maximum Price');
}
}
const selectedAreaType = document.querySelector('input[name="areaInputType"]:checked')?.value || 'area';
if (selectedAreaType === 'area') {
const area = document.getElementById('area')?.value.trim() || '';
if (!area) errors.push('Area');
} else {
const length = document.getElementById('length')?.value.trim() || '';
const breadth = document.getElementById('breadth')?.value.trim() || '';
if (!length) errors.push('Length');
if (!breadth) errors.push('Breadth');
}
const hasCoverFile = coverUpload && coverUpload.files.length > 0;
const hasCoverUrl = hiddenCoverUrl.value.trim() !== '';
if (!hasCoverFile && !hasCoverUrl) {
errors.push('Cover Photo');
}
if (errors.length > 0) {
e.preventDefault();
alert('Please fill fields:\n- ' + errors.join('\n- '));
return false;
}
syncAdditionalImageUrls();
});
}
renderPriceFields('FIXED');
renderAreaFields('area');
});
</script>
</div>
<?php
include('./inc/footer.php');
?>