344 lines
17 KiB
Plaintext
344 lines
17 KiB
Plaintext
<!-- Content Header (Page header) -->
|
||
<div class="content-header">
|
||
<div class="container-fluid">
|
||
<div class="row mb-2">
|
||
<div class="col-sm-6">
|
||
<h1><i class="fas fa-plus mr-2"></i>Добавить услугу</h1>
|
||
</div>
|
||
<div class="col-sm-6">
|
||
<ol class="breadcrumb float-sm-right">
|
||
<li class="breadcrumb-item"><a href="/admin/dashboard">Админ</a></li>
|
||
<li class="breadcrumb-item"><a href="/admin/services">Услуги</a></li>
|
||
<li class="breadcrumb-item active">Добавить</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main content -->
|
||
<section class="content">
|
||
<div class="container-fluid">
|
||
<form id="serviceForm" action="/api/admin/services" method="POST">
|
||
<div class="row">
|
||
<div class="col-md-8">
|
||
<!-- Basic Information -->
|
||
<div class="card card-primary">
|
||
<div class="card-header">
|
||
<h3 class="card-title">Основная информация</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-group">
|
||
<label for="name">Название услуги *</label>
|
||
<input type="text" class="form-control" id="name" name="name" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="shortDescription">Краткое описание</label>
|
||
<textarea class="form-control" id="shortDescription" name="shortDescription" rows="2" placeholder="Краткое описание для списков и карточек"></textarea>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="description">Полное описание</label>
|
||
<textarea class="form-control" id="description" name="description" rows="6" placeholder="Детальное описание услуги"></textarea>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div class="form-group">
|
||
<label for="category">Категория *</label>
|
||
<select class="form-control" id="category" name="category" required>
|
||
<option value="">Выберите категорию</option>
|
||
<option value="web-development">Веб-разработка</option>
|
||
<option value="mobile-development">Мобильная разработка</option>
|
||
<option value="ui-ux-design">UI/UX Дизайн</option>
|
||
<option value="consulting">Консалтинг</option>
|
||
<option value="support">Поддержка</option>
|
||
<option value="other">Другое</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="form-group">
|
||
<label for="icon">Иконка</label>
|
||
<input type="text" class="form-control" id="icon" name="icon" placeholder="fas fa-code" value="fas fa-cog">
|
||
<small class="form-text text-muted">FontAwesome класс иконки</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="estimatedTime">Время выполнения</label>
|
||
<input type="text" class="form-control" id="estimatedTime" name="estimatedTime" placeholder="2-4 недели">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Pricing -->
|
||
<div class="card card-info">
|
||
<div class="card-header">
|
||
<h3 class="card-title">Ценообразование</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div class="form-group">
|
||
<label for="basePrice">Базовая цена ($)</label>
|
||
<input type="number" class="form-control" id="basePrice" name="pricing[basePrice]" min="0" step="0.01">
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="form-group">
|
||
<label for="currency">Валюта</label>
|
||
<select class="form-control" id="currency" name="pricing[currency]">
|
||
<option value="USD">USD ($)</option>
|
||
<option value="EUR">EUR (€)</option>
|
||
<option value="KRW">KRW (₩)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="pricingType">Тип ценообразования</label>
|
||
<select class="form-control" id="pricingType" name="pricing[type]">
|
||
<option value="fixed">Фиксированная цена</option>
|
||
<option value="hourly">Почасовая оплата</option>
|
||
<option value="project">За проект</option>
|
||
<option value="subscription">Подписка</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Features -->
|
||
<div class="card card-success">
|
||
<div class="card-header">
|
||
<h3 class="card-title">Функции и возможности</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="featuresContainer">
|
||
<div class="feature-item mb-3">
|
||
<div class="row">
|
||
<div class="col-md-8">
|
||
<input type="text" class="form-control" name="features[0][name]" placeholder="Название функции">
|
||
</div>
|
||
<div class="col-md-3">
|
||
<select class="form-control" name="features[0][included]">
|
||
<option value="true">Включено</option>
|
||
<option value="false">Не включено</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-1">
|
||
<button type="button" class="btn btn-danger btn-sm remove-feature">
|
||
<i class="fas fa-trash"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button type="button" class="btn btn-sm btn-outline-primary" id="addFeature">
|
||
<i class="fas fa-plus mr-1"></i> Добавить функцию
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<!-- Status & Settings -->
|
||
<div class="card card-warning">
|
||
<div class="card-header">
|
||
<h3 class="card-title">Настройки</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-group">
|
||
<div class="custom-control custom-switch">
|
||
<input type="checkbox" class="custom-control-input" id="isActive" name="isActive" checked>
|
||
<label class="custom-control-label" for="isActive">Активная услуга</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<div class="custom-control custom-switch">
|
||
<input type="checkbox" class="custom-control-input" id="featured" name="featured">
|
||
<label class="custom-control-label" for="featured">Рекомендуемая услуга</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="order">Порядок отображения</label>
|
||
<input type="number" class="form-control" id="order" name="order" value="0" min="0">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tags -->
|
||
<div class="card card-secondary">
|
||
<div class="card-header">
|
||
<h3 class="card-title">Теги</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-group">
|
||
<label for="tags">Теги (через запятую)</label>
|
||
<input type="text" class="form-control" id="tags" name="tags" placeholder="веб-дизайн, frontend, react">
|
||
<small class="form-text text-muted">Разделите теги запятыми</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SEO -->
|
||
<div class="card card-dark">
|
||
<div class="card-header">
|
||
<h3 class="card-title">SEO настройки</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-group">
|
||
<label for="seoTitle">SEO заголовок</label>
|
||
<input type="text" class="form-control" id="seoTitle" name="seo[title]">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seoDescription">SEO описание</label>
|
||
<textarea class="form-control" id="seoDescription" name="seo[description]" rows="3"></textarea>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="seoKeywords">Ключевые слова</label>
|
||
<input type="text" class="form-control" id="seoKeywords" name="seo[keywords]" placeholder="через, запятую">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Submit Buttons -->
|
||
<div class="row">
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-body">
|
||
<button type="submit" class="btn btn-success">
|
||
<i class="fas fa-save mr-1"></i> Сохранить услугу
|
||
</button>
|
||
<a href="/admin/services" class="btn btn-secondary ml-2">
|
||
<i class="fas fa-times mr-1"></i> Отмена
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
|
||
<script>
|
||
$(document).ready(function() {
|
||
let featureIndex = 1;
|
||
|
||
// Add feature
|
||
$('#addFeature').click(function() {
|
||
const featureHtml = `
|
||
<div class="feature-item mb-3">
|
||
<div class="row">
|
||
<div class="col-md-8">
|
||
<input type="text" class="form-control" name="features[${featureIndex}][name]" placeholder="Название функции">
|
||
</div>
|
||
<div class="col-md-3">
|
||
<select class="form-control" name="features[${featureIndex}][included]">
|
||
<option value="true">Включено</option>
|
||
<option value="false">Не включено</option>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-1">
|
||
<button type="button" class="btn btn-danger btn-sm remove-feature">
|
||
<i class="fas fa-trash"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
$('#featuresContainer').append(featureHtml);
|
||
featureIndex++;
|
||
});
|
||
|
||
// Remove feature
|
||
$(document).on('click', '.remove-feature', function() {
|
||
$(this).closest('.feature-item').remove();
|
||
});
|
||
|
||
// Form submission
|
||
$('#serviceForm').submit(function(e) {
|
||
e.preventDefault();
|
||
|
||
const formData = new FormData(this);
|
||
const data = {};
|
||
|
||
// Convert FormData to object
|
||
for (let [key, value] of formData.entries()) {
|
||
if (key.includes('[') && key.includes(']')) {
|
||
// Handle nested objects (pricing, seo, features)
|
||
const matches = key.match(/(\w+)\[(\w+|\d+)\](?:\[(\w+)\])?/);
|
||
if (matches) {
|
||
const [, parent, child, grandchild] = matches;
|
||
if (!data[parent]) data[parent] = {};
|
||
|
||
if (grandchild) {
|
||
// features array handling
|
||
if (!data[parent][child]) data[parent][child] = {};
|
||
data[parent][child][grandchild] = value;
|
||
} else {
|
||
data[parent][child] = value;
|
||
}
|
||
}
|
||
} else {
|
||
data[key] = value;
|
||
}
|
||
}
|
||
|
||
// Convert features object to array
|
||
if (data.features) {
|
||
const featuresArray = [];
|
||
Object.keys(data.features).forEach(index => {
|
||
if (data.features[index].name) {
|
||
featuresArray.push({
|
||
name: data.features[index].name,
|
||
included: data.features[index].included === 'true'
|
||
});
|
||
}
|
||
});
|
||
data.features = featuresArray;
|
||
}
|
||
|
||
// Convert checkboxes
|
||
data.isActive = $('#isActive').is(':checked');
|
||
data.featured = $('#featured').is(':checked');
|
||
|
||
// Convert tags to array
|
||
if (data.tags) {
|
||
data.tags = data.tags.split(',').map(tag => tag.trim()).filter(tag => tag);
|
||
}
|
||
|
||
fetch('/api/admin/services', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(data)
|
||
})
|
||
.then(response => response.json())
|
||
.then(result => {
|
||
if (result.success) {
|
||
toastr.success('Услуга успешно создана!');
|
||
setTimeout(() => {
|
||
window.location.href = '/admin/services';
|
||
}, 1500);
|
||
} else {
|
||
toastr.error(result.message || 'Ошибка при создании услуги');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
toastr.error('Произошла ошибка при создании услуги');
|
||
});
|
||
});
|
||
});
|
||
</script> |