Files
admin/resources/views/items/create.blade.php
Husanjonazamov e0f1989655 classify admin
2026-02-24 12:52:01 +05:00

1053 lines
52 KiB
PHP

@extends('layouts.main')
@section('title')
{{ __('Create Advertisement') }}
@endsection
@section('page-title')
<div class="page-title">
<div class="row">
<div class="col-12 col-md-6 order-md-1 order-last">
<h4>@yield('title')</h4>
</div>
<div class="col-12 col-md-6 order-md-2 order-first"></div>
</div>
</div>
@endsection
@section('content')
<section class="section">
@php
$hasAdminCredentials = !empty($adminUserEmail) && !empty($adminUserPassword);
@endphp
@if (!$hasAdminCredentials)
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<i class="fa fa-exclamation-triangle"></i>
<strong>{{ __('Warning') }}:</strong>
{{ __('Please add email and password in General Settings first to create advertisements on behalf of users.') }}
<a href="{{ route('settings.system', 'system') }}" class="alert-link">{{ __('Go to Settings') }}</a>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
<div class="card">
<div class="card-body">
<form method="POST" action="{{ route('advertisement.store') }}" data-parsley-validate
enctype="multipart/form-data">
@csrf
<ul class="nav nav-tabs" id="addItemTabs" role="tablist">
<li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#categories"
data-tab-index="0">{{ __('Categories') }}</a></li>
<li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#listing"
data-tab-index="2">{{ __('Listing Details') }}</a></li>
<li class="nav-item" id="custom-tab-item" style="display: none;"><a class="nav-link"
data-bs-toggle="tab" href="#custom" data-tab-index="3">{{ __('Other Details') }}</a></li>
<li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#images"
data-tab-index="4">{{ __('Product Images') }}</a></li>
<li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#address"
data-tab-index="5">{{ __('Address') }}</a></li>
</ul>
<div class="tab-content pt-3">
{{-- Listing Details --}}
<div class="tab-pane fade" id="listing">
<div class="row">
<div class="col-6 mb-3">
<label>{{ __('Name') }} <span class="text-danger">*</span></label>
<input type="text" name="name" id="name-input" value="{{ old('name') }}"
class="form-control" required>
</div>
<div class="col-6 mb-3">
<label>{{ __('Slug') }}</label>
<input type="text" name="slug" value="{{ old('slug') }}" class="form-control"
placeholder="auto-generated if blank">
</div>
<div class="col-6 mb-3">
<label>{{ __('Select Currency') }} <span class="text-danger">*</span></label>
<select class="form-control select2" id="currency" name="currency_id" required>
<option value="">{{ __('--Select Currency--') }}</option>
@foreach ($currencies as $currency)
<option value="{{ $currency->id }}">{{ $currency->name }}</option>
@endforeach
</select>
</div>
<div class="col-6 mb-3" id="price-field">
<label>{{ __('Price') }} <span class="text-danger">*</span></label>
<input type="number" name="price" id="price-input" value="{{ old('price') }}"
class="form-control" required>
</div>
<div class="col-6 mb-3" id="salary-fields" style="display: none;">
<label>{{ __('Min Salary') }}</label>
<input type="number" name="min_salary" id="min-salary-input" class="form-control mb-2"
value="{{ old('min_salary') }}">
<label>{{ __('Max Salary') }}</label>
<input type="number" name="max_salary" id="max-salary-input" class="form-control"
value="{{ old('max_salary') }}">
</div>
<div class="col-6 mb-3">
<label>{{ __('Phone Number') }}</label>
<input type="number" name="contact" id="contact-input"
value="{{ old('contact', auth()->user()->phone ?? '') }}" class="form-control"
required>
</div>
<div class="col-12 mb-3">
<label>{{ __('Description') }} <span class="text-danger">*</span></label>
<textarea name="description" id="description-input" class="form-control" required>{{ old('description') }}</textarea>
</div>
</div>
<div class="mt-4 d-flex justify-content-between">
<button type="button" class="btn btn-primary btn-prev-tab"
data-prev-tab="categories">{{ __('Previous') }}</button>
<button type="button" class="btn btn-primary btn-next-tab"
data-next-tab="custom-or-images">{{ __('Next') }}</button>
</div>
</div>
{{-- Categories Tab --}}
<div class="tab-pane fade show active" id="categories">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="row w-100">
<div class="col-md-6 mb-3">
<div class="flex-grow-1">
<label class="form-label mb-0">{{ __('Selected Category') }}</label>
<input type="text" id="selected-category-display" class="form-control"
readonly placeholder="{{ __('No category selected') }}" required>
<input type="hidden" name="category_id" id="category_id" value=""
required>
</div>
</div>
<div class="col-md-6">
<div id="breadcrumb-container" style="display: none;">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0" id="category-breadcrumb"></ol>
</nav>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div id="categories-container">
<label class="form-label mb-3">{{ __('Select Category') }}</label>
<div id="categories-list" class="row"></div>
<div id="load-more-container" class="text-center mt-3" style="display: none;">
<button type="button" class="btn btn-primary" id="load-more-categories">
{{ __('Load More') }}
</button>
</div>
</div>
</div>
</div>
<div class="mt-4 text-end">
<button type="button" class="btn btn-primary btn-next-tab"
data-next-tab="listing">{{ __('Next') }}</button>
</div>
</div>
{{-- Other Details - Custom Fields --}}
<div class="tab-pane fade" id="custom">
<div class="text-muted">{{ __('Select a category to load custom fields.') }}</div>
</div>
{{-- Product Images --}}
<div class="tab-pane fade" id="images">
<div class="row">
<div class="col-12 mb-3">
<label>{{ __('Main Image') }} <span class="text-danger">*</span></label>
<input type="file" name="image" id="image-input" class="form-control" required
accept="image/png,image/jpeg,image/jpg">
</div>
<div class="col-12 mb-3">
<label>{{ __('Other Images') }}</label>
<input type="file" name="gallery_images[]" class="form-control" multiple
accept="image/png,image/jpeg,image/jpg">
<small class="text-muted">{{ __('You can select multiple images at once') }}</small>
</div>
<div class="col-12 mb-3">
<label for="video_link">{{ __('Video Link') }}</label>
<input type="url" name="video_link" id="video_link" class="form-control"
value="{{ old('video_link') }}"
placeholder="https://www.youtube.com/watch?v=...">
</div>
</div>
<div class="mt-4 d-flex justify-content-between">
<button type="button" class="btn btn-primary btn-prev-tab"
data-prev-tab="custom-or-images">{{ __('Previous') }}</button>
<button type="button" class="btn btn-primary btn-next-tab"
data-next-tab="address">{{ __('Next') }}</button>
</div>
</div>
{{-- Address --}}
<div class="tab-pane fade" id="address">
<div class="row">
<div class="col-12">
<div class="form-group mb-3">
<label class="form-label">{{ __('Selected Address') }} <span
class="text-danger">*</span></label>
<input type="text" class="form-control" id="address-input" readonly
placeholder="{{ __('Select a location on the map to see the address') }}"
required>
</div>
<label class="form-label">{{ __('Select Location on Map') }} <span
class="text-danger">*</span></label>
<div id="map"
style="height: 500px; border: 1px solid #ddd; border-radius: 5px;"></div>
<!-- Hidden inputs for form submission -->
<input type="hidden" id="latitude-input" name="latitude" required />
<input type="hidden" id="longitude-input" name="longitude" required />
<input type="hidden" name="country_input" id="country-input">
<input type="hidden" name="state_input" id="state-input">
<input type="hidden" name="city_input" id="city-input">
<input type="hidden" name="address" id="address-hidden">
</div>
</div>
<div class="mt-4 d-flex justify-content-between">
<button type="button" class="btn btn-primary btn-prev-tab"
data-prev-tab="images">{{ __('Previous') }}</button>
<button type="submit" class="btn btn-primary">{{ __('Post') }}</button>
</div>
</div>
</div>
</form>
</div>
</div>
</section>
@endsection
@section('script')
<script>
$(document).ready(function() {
let selectedCategoryId = null;
let selectedCategoryName = '';
let currentParentId = null;
let currentPage = 1;
let hasMoreCategories = true;
let categoryHistory = []; // Track navigation history
// Load initial parent categories when categories tab is shown
$('#addItemTabs a[href="#categories"]').on('shown.bs.tab', function() {
if ($('#categories-list').is(':empty')) {
loadCategories(null, 1);
}
});
// Load categories immediately if categories tab is already active on page load
// Check if categories tab is visible/active
if (($('#categories').hasClass('active') || $('#categories').hasClass('show')) && $('#categories-list')
.is(':empty')) {
loadCategories(null, 1);
}
// Fallback: Load categories after a short delay if still empty (for edge cases)
setTimeout(function() {
if ($('#categories-list').is(':empty') && ($('#categories').is(':visible') || $(
'#categories').hasClass('active'))) {
loadCategories(null, 1);
}
}, 100);
// Load categories function
function loadCategories(parentId, page = 1) {
const url = parentId ?
'{{ route('advertisement.get-subcategories') }}' :
'{{ route('advertisement.get-parent-categories') }}';
$.ajax({
url: url,
type: 'GET',
data: {
category_id: parentId,
page: page,
per_page: 10
},
success: function(response) {
if (page === 1) {
$('#categories-list').html('');
}
if (response.data && response.data.length > 0) {
let html = '';
response.data.forEach(function(category) {
const hasSubcategories = category.subcategories_count > 0;
const categoryImage = category.image ||
'/assets/images/default-category.png';
html += `
<div class="col-md-2 col-lg-2 col-xl-2 mb-3">
<div class="card category-card h-100"
data-category-id="${category.id}"
data-category-name="${category.name}"
data-has-subcategories="${hasSubcategories}"
style="cursor: pointer; border: 2px solid #e0e0e0; transition: all 0.3s;">
<div class="card-body p-2 text-center">
<img src="${categoryImage}" alt="${category.name}" class="img-fluid mb-2" style="max-height: 60px; width: auto; border-radius: 5px;">
<strong class="d-block" style="font-size: 12px;">${category.name}</strong>
${hasSubcategories ? '<small class="text-muted" style="font-size: 10px;"><i class="fas fa-chevron-right"></i> Subcategories</small>' : '<small class="text-success" style="font-size: 10px;"><i class="fas fa-check"></i> Select</small>'}
</div>
</div>
</div>
`;
});
$('#categories-list').append(html);
// Add click handlers
$('.category-card').off('click').on('click', function() {
const categoryId = $(this).data('category-id');
const categoryName = $(this).data('category-name');
const hasSubcategories = $(this).data('has-subcategories') == 1;
// Visual feedback - remove previous selections
$('.category-card').removeClass('border-primary').css({
'border-color': '#e0e0e0',
'background-color': ''
});
// Highlight selected
$(this).addClass('border-primary').css({
'border-color': '#007bff',
'background-color': '#f0f8ff'
});
if (hasSubcategories) {
// Navigate to subcategories
categoryHistory.push({
parentId: currentParentId,
page: currentPage,
html: $('#categories-list').html(),
loadMoreVisible: $('#load-more-container').is(
':visible')
});
currentParentId = categoryId;
currentPage = 1;
hasMoreCategories = true;
updateBreadcrumb(categoryName, categoryId);
loadCategories(categoryId, 1);
} else {
// Select this category (no subcategories)
selectCategory(categoryId, categoryName);
}
});
// Show/hide load more button
hasMoreCategories = response.has_more || false;
if (hasMoreCategories && page === 1) {
$('#load-more-container').show();
} else if (!hasMoreCategories) {
$('#load-more-container').hide();
}
currentPage = page;
} else {
if (page === 1) {
$('#categories-list').html(
'<div class="col-12"><p class="text-muted text-center">No categories found.</p></div>'
);
}
$('#load-more-container').hide();
hasMoreCategories = false;
}
},
error: function() {
$('#categories-list').html(
'<div class="col-12"><p class="text-danger text-center">Error loading categories.</p></div>'
);
}
});
}
// Load more categories
$('#load-more-categories').on('click', function() {
if (hasMoreCategories) {
loadCategories(currentParentId, currentPage + 1);
}
});
// Select category function
function selectCategory(categoryId, categoryName) {
selectedCategoryId = categoryId;
selectedCategoryName = categoryName;
$('#category_id').val(categoryId);
$('#selected-category-display').val(categoryName);
// Load custom fields in the "Other Details" tab
loadCustomFields(categoryId);
// Show success message
showCategorySelectedMessage(categoryName);
}
// Update breadcrumb
function updateBreadcrumb(categoryName, categoryId) {
$('#breadcrumb-container').show();
let breadcrumbHtml =
'<li class="breadcrumb-item"><a href="javascript:void(0)" class="breadcrumb-link" data-parent-id="null">Home</a></li>';
// Add current category to breadcrumb
breadcrumbHtml += `<li class="breadcrumb-item active">${categoryName}</li>`;
$('#category-breadcrumb').html(breadcrumbHtml);
// Add click handler for breadcrumb
$('.breadcrumb-link').off('click').on('click', function() {
const parentId = $(this).data('parent-id') === 'null' ? null : $(this).data(
'parent-id');
goBackToCategory(parentId);
});
}
// Go back to previous category level
function goBackToCategory(parentId) {
if (categoryHistory.length > 0) {
const previous = categoryHistory.pop();
currentParentId = previous.parentId;
currentPage = previous.page;
$('#categories-list').html(previous.html);
if (previous.loadMoreVisible) {
$('#load-more-container').show();
} else {
$('#load-more-container').hide();
}
if (currentParentId === null) {
$('#breadcrumb-container').hide();
} else {
// Update breadcrumb
updateBreadcrumbForBack();
}
// Re-attach click handlers
$('.category-card').off('click').on('click', function() {
const categoryId = $(this).data('category-id');
const categoryName = $(this).data('category-name');
const hasSubcategories = $(this).data('has-subcategories') == 1;
$('.category-card').removeClass('border-primary').css({
'border-color': '#e0e0e0',
'background-color': ''
});
$(this).addClass('border-primary').css({
'border-color': '#007bff',
'background-color': '#f0f8ff'
});
if (hasSubcategories) {
categoryHistory.push({
parentId: currentParentId,
page: currentPage,
html: $('#categories-list').html(),
loadMoreVisible: $('#load-more-container').is(':visible')
});
currentParentId = categoryId;
currentPage = 1;
hasMoreCategories = true;
updateBreadcrumb(categoryName, categoryId);
loadCategories(categoryId, 1);
} else {
selectCategory(categoryId, categoryName);
}
});
} else {
// Go to root
currentParentId = null;
currentPage = 1;
categoryHistory = [];
$('#breadcrumb-container').hide();
loadCategories(null, 1);
}
}
function updateBreadcrumbForBack() {
// Simplified breadcrumb for back navigation
$('#breadcrumb-container').show();
let breadcrumbHtml =
'<li class="breadcrumb-item"><a href="javascript:void(0)" class="breadcrumb-link" data-parent-id="null">Home</a></li>';
$('#category-breadcrumb').html(breadcrumbHtml);
$('.breadcrumb-link').off('click').on('click', function() {
goBackToCategory(null);
});
}
// Show category selected message
function showCategorySelectedMessage(categoryName) {
const alertHtml = `
<div class="alert alert-success alert-dismissible fade show" role="alert">
<strong>Category Selected:</strong> ${categoryName}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
`;
$('#categories-container').prepend(alertHtml);
setTimeout(function() {
$('.alert').fadeOut();
}, 3000);
}
// Function to load custom fields in the "Other Details" tab
function loadCustomFields(categoryId) {
if (!categoryId) {
$('#custom').html(
'<div class="text-muted">{{ __('Select a category to load custom fields.') }}</div>');
$('#custom-tab-item').hide();
return;
}
$.ajax({
url: `/get-custom-fields/${categoryId}`,
type: 'GET',
success: function(response) {
let html = '';
html += `<div class="row">`;
if (response.fields.length === 0) {
html += `<p class="text-muted">No custom fields for this category.</p>`;
html += `<div class="mt-4 d-flex justify-content-between">
<button type="button" class="btn btn-primary btn-prev-tab" data-prev-tab="listing">{{ __('Previous') }}</button>
<button type="button" class="btn btn-primary btn-next-tab" data-next-tab="images">{{ __('Next') }}</button>
</div>`;
$('#custom-tab-item').hide();
hasCustomFields = false;
// Ensure custom tab link is disabled
$('[href="#custom"]').css('pointer-events', 'none').css('cursor',
'not-allowed');
} else {
$('#custom-tab-item').show();
hasCustomFields = true;
// Ensure custom tab link is disabled (navigation only via buttons)
$('[href="#custom"]').css('pointer-events', 'none').css('cursor',
'not-allowed');
response.fields.forEach(function(field) {
const isRequired = field.required ? 'required' : '';
html += `<div class="col-md-6 mb-3">`;
html +=
`<label>${field.name}${field.required ? ' <span class="text-danger">*</span>' : ''}</label>`;
if (field.type === 'textbox') {
html +=
`<input type="text" name="custom_fields[${field.id}]" class="form-control custom-field-input" ${isRequired}>`;
} else if (field.type === 'number') {
html +=
`<input type="number" name="custom_fields[${field.id}]" class="form-control custom-field-input" ${isRequired}>`;
} else if (field.type === 'fileinput') {
html +=
`<input type="file" name="custom_field_files[${field.id}]" class="form-control custom-field-input" ${isRequired}>`;
} else if (field.type === 'dropdown' || field.type ===
'radio') {
const options = Array.isArray(field.values) ? field.values :
JSON.parse(field.values ?? '[]');
html +=
`<select name="custom_fields[${field.id}]" class="form-select custom-field-input" ${isRequired}>`;
html += `<option value="">Select</option>`;
options.forEach(option => {
html +=
`<option value="${option}">${option}</option>`;
});
html += `</select>`;
} else if (field.type === 'checkbox') {
const options = Array.isArray(field.values) ? field.values :
JSON.parse(field.values ?? '[]');
options.forEach(option => {
html += `
<div class="form-check">
<input class="form-check-input custom-field-checkbox" type="checkbox" name="custom_fields[${field.id}][]" value="${option}" ${isRequired}>
<label class="form-check-label">${option}</label>
</div>
`;
});
}
html += `</div>`;
});
// Add navigation buttons for custom fields
html += `<div class="mt-4 d-flex justify-content-between">
<button type="button" class="btn btn-primary btn-prev-tab" data-prev-tab="listing">{{ __('Previous') }}</button>
<button type="button" class="btn btn-primary btn-next-tab" data-next-tab="images">{{ __('Next') }}</button>
</div>`;
}
html += `</div>`;
$('#custom').html(html);
updateCustomFieldsStatus();
if (response.is_job_category || response.price_optional) {
$('#price-field').hide();
$('#price-input').removeAttr('required');
$('#salary-fields').show();
} else {
$('#price-field').show();
$('#price-input').attr('required', 'required');
$('#salary-fields').hide();
}
},
error: function() {
$('#custom').html(
'<div class="text-danger">Error loading custom fields.</div>');
$('#custom-tab-item').hide();
}
});
}
// Tab navigation with validation
let hasCustomFields = false;
let navigationAllowed = false; // Flag to track if navigation is from button
// Update hasCustomFields when custom fields are loaded
function updateCustomFieldsStatus() {
hasCustomFields = $('#custom .custom-field-input').length > 0;
}
// Disable all tab links except categories (first tab)
$('#addItemTabs a[data-bs-toggle="tab"]').each(function() {
const href = $(this).attr('href');
if (href !== '#categories') {
$(this).css('pointer-events', 'none').css('cursor', 'not-allowed');
}
});
// Prevent direct tab clicking - use Next/Previous buttons
$(document).on('click', '#addItemTabs a[data-bs-toggle="tab"]', function(e) {
const targetTab = $(this).attr('href');
const currentTab = $('.tab-pane.active').attr('id');
// Allow clicking on categories tab (first tab) only if we're already on it
if (targetTab === '#categories' && currentTab === 'categories') {
return true;
}
// If navigation is not allowed, prevent it
if (!navigationAllowed) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
showErrorToast('Please use the Next/Previous buttons to navigate between tabs.');
return false;
}
});
// Also prevent Bootstrap tab event
$('#addItemTabs a[data-bs-toggle="tab"]').on('show.bs.tab', function(e) {
const targetTab = $(e.target).attr('href');
const currentTab = $('.tab-pane.active').attr('id');
// Only allow if we're going to categories and already on it
if (targetTab === '#categories' && currentTab === 'categories') {
return true;
}
// If navigation is not allowed, prevent it
if (!navigationAllowed) {
e.preventDefault();
showErrorToast('Please use the Next/Previous buttons to navigate between tabs.');
return false;
}
});
// Next button handler
$(document).on('click', '.btn-next-tab', function(e) {
e.preventDefault();
e.stopPropagation();
const nextTab = $(this).data('next-tab');
const currentTab = $('.tab-pane.active').attr('id');
// Validate current tab before moving
if (!validateCurrentTab(currentTab)) {
return false;
}
// Enable navigation temporarily
navigationAllowed = true;
// Re-enable the target tab link temporarily
let targetTabLink = null;
if (nextTab === 'custom-or-images') {
targetTabLink = hasCustomFields ? $('[href="#custom"]') : $('[href="#images"]');
} else {
targetTabLink = $('[href="#' + nextTab + '"]');
}
if (targetTabLink.length) {
targetTabLink.css('pointer-events', 'auto').css('cursor', 'pointer');
}
// Handle special case: custom-or-images
if (nextTab === 'custom-or-images') {
if (hasCustomFields) {
$('[href="#custom"]').tab('show');
} else {
$('[href="#images"]').tab('show');
}
} else {
$('[href="#' + nextTab + '"]').tab('show');
// Initialize map if navigating to address tab
if (nextTab === 'address') {
setTimeout(() => {
initMap();
}, 300);
}
}
// Disable navigation and tab links after tab switch
setTimeout(() => {
navigationAllowed = false;
if (targetTabLink && targetTabLink.length) {
targetTabLink.css('pointer-events', 'none').css('cursor', 'not-allowed');
}
}, 200);
});
// Previous button handler
$(document).on('click', '.btn-prev-tab', function(e) {
e.preventDefault();
e.stopPropagation();
const prevTab = $(this).data('prev-tab');
// Enable navigation temporarily
navigationAllowed = true;
// Re-enable the target tab link temporarily
let targetTabLink = null;
if (prevTab === 'custom-or-images') {
targetTabLink = hasCustomFields ? $('[href="#custom"]') : $('[href="#listing"]');
} else {
targetTabLink = $('[href="#' + prevTab + '"]');
}
if (targetTabLink && targetTabLink.length) {
targetTabLink.css('pointer-events', 'auto').css('cursor', 'pointer');
}
// Handle special case: custom-or-images
if (prevTab === 'custom-or-images') {
if (hasCustomFields) {
$('[href="#custom"]').tab('show');
} else {
$('[href="#listing"]').tab('show');
}
} else {
$('[href="#' + prevTab + '"]').tab('show');
}
// Disable navigation and tab links after tab switch
setTimeout(() => {
navigationAllowed = false;
if (targetTabLink && targetTabLink.length) {
targetTabLink.css('pointer-events', 'none').css('cursor', 'not-allowed');
}
}, 200);
});
// Validation function for current tab
function validateCurrentTab(tabId) {
let isValid = true;
let firstInvalidField = null;
if (tabId === 'categories') {
if (!selectedCategoryId || !$('#category_id').val()) {
showErrorToast('Please select a category first.');
isValid = false;
}
} else if (tabId === 'listing') {
const name = $('#name-input').val().trim();
const description = $('#description-input').val().trim();
const price = $('#price-input').val();
const minSalary = $('#min-salary-input').val();
const maxSalary = $('#max-salary-input').val();
const contact = $('#contact-input').val().trim();
if (!name) {
showErrorToast('Please enter a name.');
$('#name-input').focus();
isValid = false;
} else if (!description) {
showErrorToast('Please enter a description.');
$('#description-input').focus();
isValid = false;
} else if ($('#price-field').is(':visible') && !price) {
showErrorToast('Please enter a price.');
$('#price-input').focus();
isValid = false;
} else if ($('#salary-fields').is(':visible')) {
if (minSalary && maxSalary && parseFloat(minSalary) > parseFloat(maxSalary)) {
showErrorToast('Min salary cannot be greater than max salary.');
$('#min-salary-input').focus();
isValid = false;
}
}
// Always validate contact field
if (isValid && !contact) {
showErrorToast('Please enter a Phone Number.');
$('#contact-input').focus();
isValid = false;
}
} else if (tabId === 'custom') {
// Validate required custom fields
let firstInvalidField = null;
$('#custom .custom-field-input[required]').each(function() {
const $field = $(this);
if ($field.is(':file')) {
if (!$field[0].files || $field[0].files.length === 0) {
if (!firstInvalidField) {
firstInvalidField = $field;
}
isValid = false;
return false;
}
} else if ($field.is('select')) {
if (!$field.val()) {
if (!firstInvalidField) {
firstInvalidField = $field;
}
isValid = false;
return false;
}
} else if (!$field.val().trim()) {
if (!firstInvalidField) {
firstInvalidField = $field;
}
isValid = false;
return false;
}
});
if (!isValid && firstInvalidField) {
showErrorToast('Please fill all required custom fields.');
firstInvalidField.focus();
}
// Validate checkbox groups
$('.custom-field-checkbox[required]').each(function() {
const name = $(this).attr('name');
if (!$('input[name="' + name + '"]:checked').length) {
showErrorToast(
'Please select at least one option for required checkbox fields.');
isValid = false;
return false;
}
});
} else if (tabId === 'images') {
const imageInput = $('#image-input')[0];
if (!imageInput || !imageInput.files || imageInput.files.length === 0) {
showErrorToast('Please select a main image.');
$('#image-input').focus();
isValid = false;
}
} else if (tabId === 'address') {
const lat = $('#latitude-input').val();
const lng = $('#longitude-input').val();
const address = $('#address-input').val();
if (!lat || !lng) {
showErrorToast('Please select a location on the map.');
isValid = false;
} else if (!address) {
showErrorToast('Please select a location on the map to get the address.');
isValid = false;
}
}
return isValid;
}
// Form validation before submit
$('form').on('submit', function(e) {
// Validate all tabs before submit
const tabs = ['categories', 'listing', 'images', 'address'];
if (hasCustomFields) {
tabs.splice(2, 0, 'custom');
}
for (let i = 0; i < tabs.length; i++) {
if (!validateCurrentTab(tabs[i])) {
e.preventDefault();
$('[href="#' + tabs[i] + '"]').tab('show');
return false;
}
}
});
// Email validation helper
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// Toggle password visibility for user details
// $('#toggle-user-password-visibility').on('click', function() {
// const passwordInput = $('#user-password-input');
// const eyeIcon = $('#user-password-eye-icon');
// if (passwordInput.attr('type') === 'password') {
// passwordInput.attr('type', 'text');
// eyeIcon.removeClass('fa-eye').addClass('fa-eye-slash');
// } else {
// passwordInput.attr('type', 'password');
// eyeIcon.removeClass('fa-eye-slash').addClass('fa-eye');
// }
// });
});
// Country/State/City dropdown handlers - kept for map address population
$(document).ready(function() {
// These handlers are now only used for populating dropdowns from map selection
// No manual dropdowns are shown in the UI anymore
});
// Map initialization
let map, marker;
let mapInitialized = false;
function initMap() {
// Check if map is already initialized
if (mapInitialized && map) {
// Invalidate size in case container was hidden
setTimeout(() => {
map.invalidateSize();
}, 100);
return;
}
// Check if map container exists
const mapContainer = document.getElementById('map');
if (!mapContainer) {
console.error('Map container not found');
return;
}
const defaultLat = 20.5937;
const defaultLng = 78.9629;
try {
map = L.map('map').setView([defaultLat, defaultLng], 5);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19
}).addTo(map);
marker = L.marker([defaultLat, defaultLng], {
draggable: true
}).addTo(map);
updateLatLngInputs(defaultLat, defaultLng);
marker.on('dragend', function(e) {
const pos = marker.getLatLng();
updateLatLngInputs(pos.lat, pos.lng);
fetchAddressFromCoords(pos.lat, pos.lng);
});
const provider = new GeoSearch.OpenStreetMapProvider();
const search = new GeoSearch.GeoSearchControl({
provider: provider,
style: 'bar',
autoComplete: true,
searchLabel: 'Enter address',
showMarker: false,
});
map.addControl(search);
map.on('geosearch/showlocation', function(result) {
const {
x: lng,
y: lat,
label
} = result.location;
marker.setLatLng([lat, lng]);
map.setView([lat, lng], 15);
updateLatLngInputs(lat, lng);
document.getElementById("address-input").value = label;
document.getElementById("address-hidden").value = label;
fetchAddressFromCoords(lat, lng);
});
// Also handle map click to update location
map.on('click', function(e) {
const lat = e.latlng.lat;
const lng = e.latlng.lng;
marker.setLatLng([lat, lng]);
updateLatLngInputs(lat, lng);
fetchAddressFromCoords(lat, lng);
});
mapInitialized = true;
} catch (error) {
console.error('Error initializing map:', error);
}
}
function updateLatLngInputs(lat, lng) {
document.getElementById("latitude-input").value = lat;
document.getElementById("longitude-input").value = lng;
}
function fetchAddressFromCoords(lat, lng) {
fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=json`)
.then(res => res.json())
.then(data => {
if (!data || !data.address) {
console.error('Invalid address data received');
return;
}
const address = data.address;
const fullAddress = data.display_name || '';
// Update address display
document.getElementById("address-input").value = fullAddress;
document.getElementById("address-hidden").value = fullAddress;
// Extract address components from OpenStreetMap response structure
const countryName = address.country || '';
const stateName = address.state || address.state_district || '';
const cityName = address.city || address.town || address.village || address.county || address
.state_district || '';
// Update hidden inputs for form submission
document.getElementById("country-input").value = countryName;
document.getElementById("state-input").value = stateName;
document.getElementById("city-input").value = cityName;
})
.catch(error => {
console.error('Error fetching address:', error);
});
}
// Initialize map when address tab is shown
$(document).ready(function() {
// Listen for when address tab is shown
$('a[href="#address"]').on('shown.bs.tab', function() {
setTimeout(() => {
initMap();
}, 300);
});
// Also check if address tab is already active on page load
if ($('#address').hasClass('active') || $('#address').hasClass('show')) {
setTimeout(() => {
initMap();
}, 500);
}
});
</script>
@endsection