This commit is contained in:
@@ -6,7 +6,10 @@ let db;
|
||||
const DEFAULT_SETTINGS = {
|
||||
review_code: 'GUEST2026',
|
||||
review_min_length: '20',
|
||||
review_ip_cooldown_minutes: '5'
|
||||
review_ip_cooldown_minutes: '5',
|
||||
hotel_phone_primary: '89409270080',
|
||||
hotel_phone_additional: '',
|
||||
hotel_max_link: 'kalugin66'
|
||||
};
|
||||
|
||||
function init(database) {
|
||||
@@ -201,6 +204,49 @@ function setupRoutes(app, authenticateToken, requireAdmin) {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/admin/settings/phones', authenticateToken, requireAdmin, (req, res) => {
|
||||
getAll((err, settings) => {
|
||||
if (err) return res.status(500).json({ error: 'Database error' });
|
||||
res.json({
|
||||
primary: settings.hotel_phone_primary || '',
|
||||
additional: settings.hotel_phone_additional || '',
|
||||
maxLink: settings.hotel_max_link || ''
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/api/admin/settings/phones', authenticateToken, requireAdmin, (req, res) => {
|
||||
const { primary, additional, maxLink } = req.body;
|
||||
if (!primary || primary.length < 5) {
|
||||
return res.status(400).json({ error: 'Основной номер должен содержать минимум 5 цифр' });
|
||||
}
|
||||
const updates = [];
|
||||
updates.push(['hotel_phone_primary', primary]);
|
||||
updates.push(['hotel_phone_additional', additional || '']);
|
||||
updates.push(['hotel_max_link', maxLink || '']);
|
||||
let processed = 0;
|
||||
updates.forEach(([key, value]) => {
|
||||
set(key, value, (err) => {
|
||||
if (err) console.error('Phone settings update error:', err);
|
||||
processed++;
|
||||
if (processed === updates.length) {
|
||||
res.json({ message: 'Настройки телефонов сохранены' });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/config', (req, res) => {
|
||||
getAll((err, settings) => {
|
||||
if (err) return res.status(500).json({ error: 'Database error' });
|
||||
res.json({
|
||||
phone_primary: settings.hotel_phone_primary || '89409270080',
|
||||
phone_additional: settings.hotel_phone_additional || '',
|
||||
max_link: settings.hotel_max_link || 'kalugin66'
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { init, get, set, getAll, getReviewCode, setReviewCode, checkIpCooldown, setupRoutes };
|
||||
|
||||
@@ -184,6 +184,9 @@ const translations = {
|
||||
'hero.subtitle': 'Ваш идеальный отдых на берегу Чёрного моря в самом сердце Абхазии. Комфорт, природа и гостеприимство — всё, что нужно для незабываемого отпуска.',
|
||||
'hero.btn_rooms': 'Выбрать номер',
|
||||
'hero.btn_call': 'Позвонить',
|
||||
'hero.btn_whatsapp': 'Написать в WhatsApp',
|
||||
'hero.btn_telegram': 'Написать в Telegram',
|
||||
'hero.btn_max': 'Написать в Max',
|
||||
'hero.stat_sea': 'мин до моря',
|
||||
'hero.stat_categories': 'категории',
|
||||
'hero.stat_guests': 'довольных гостей',
|
||||
@@ -507,6 +510,9 @@ const translations = {
|
||||
'hero.subtitle': 'Your perfect getaway on the Black Sea coast in the heart of Abkhazia. Comfort, nature and hospitality — everything you need for an unforgettable vacation.',
|
||||
'hero.btn_rooms': 'Choose a Room',
|
||||
'hero.btn_call': 'Call Us',
|
||||
'hero.btn_whatsapp': 'Write on WhatsApp',
|
||||
'hero.btn_telegram': 'Write on Telegram',
|
||||
'hero.btn_max': 'Write on Max',
|
||||
'hero.stat_sea': 'min to the sea',
|
||||
'hero.stat_categories': 'categories',
|
||||
'hero.stat_guests': 'happy guests',
|
||||
@@ -833,6 +839,9 @@ const translations = {
|
||||
'hero.subtitle': 'Уара уидеалтә ԥсынчыра Амшын Еиқәаҵәа аԥшаҳәаҿы Аԥсны агәҭаны. Акомфорт, аԥсабареи асасӡреи — ахымԥада-ҳәада аусумҭазы иахәҭоу зегьы.',
|
||||
'hero.btn_rooms': 'Апалаҭа алхразы',
|
||||
'hero.btn_call': 'Аҭелефон асра',
|
||||
'hero.btn_whatsapp': 'WhatsApp ала аҩра',
|
||||
'hero.btn_telegram': 'Telegram ала аҩра',
|
||||
'hero.btn_max': 'Max ала аҩра',
|
||||
'hero.stat_sea': 'мин амшын ахь',
|
||||
'hero.stat_categories': 'акатегориақәа',
|
||||
'hero.stat_guests': 'изызҳауа асасцәа',
|
||||
|
||||
@@ -468,11 +468,38 @@ tr.row-checkout-today { background: #fef2f2 !important; border-left: 4px solid #
|
||||
<tr><td colspan="5" class="text-center text-muted">Загрузка...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" id="phoneCard">
|
||||
<div class="card-header-custom"><h3><i class="fas fa-phone-alt me-2"></i>Телефоны и контакты</h3></div>
|
||||
<div class="card-body-custom">
|
||||
<p style="color: #64748b; margin-bottom: 20px;">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
Основной номер используется для звонков, WhatsApp и Telegram. Дополнительный отображается через запятую рядом с основным.
|
||||
</p>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Основной номер</label>
|
||||
<input type="text" class="form-control" id="phonePrimary" placeholder="89409270080">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Дополнительный номер</label>
|
||||
<input type="text" class="form-control" id="phoneAdditional" placeholder="Необязательно">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Ссылка Max (username)</label>
|
||||
<input type="text" class="form-control" id="phoneMaxLink" placeholder="kalugin66">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<button class="btn-gold btn-sm" onclick="savePhoneSettings()"><i class="fas fa-save me-1"></i>Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header-custom"><h3>Кодовое слово для отзывов</h3></div>
|
||||
@@ -2472,6 +2499,32 @@ async function sendTestEmail() {
|
||||
} catch(err) { showToast(err.message, 'error'); }
|
||||
}
|
||||
|
||||
// Phone settings
|
||||
async function loadPhoneSettings() {
|
||||
try {
|
||||
const data = await api('/api/admin/settings/phones');
|
||||
document.getElementById('phonePrimary').value = data.primary || '';
|
||||
document.getElementById('phoneAdditional').value = data.additional || '';
|
||||
document.getElementById('phoneMaxLink').value = data.maxLink || '';
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
async function savePhoneSettings() {
|
||||
const data = {
|
||||
primary: document.getElementById('phonePrimary').value.trim(),
|
||||
additional: document.getElementById('phoneAdditional').value.trim(),
|
||||
maxLink: document.getElementById('phoneMaxLink').value.trim()
|
||||
};
|
||||
if (!data.primary) {
|
||||
showToast('Основной номер обязателен', 'error');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await api('/api/admin/settings/phones', { method: 'PUT', body: JSON.stringify(data) });
|
||||
showToast('Настройки телефонов сохранены');
|
||||
} catch(err) { showToast(err.message, 'error'); }
|
||||
}
|
||||
|
||||
// Export
|
||||
async function exportCSV() {
|
||||
const status = document.getElementById('filterStatus').value;
|
||||
@@ -2501,6 +2554,7 @@ async function loadSettings() {
|
||||
} catch(e) {}
|
||||
loadSecurityKeys();
|
||||
loadEmailSettings();
|
||||
loadPhoneSettings();
|
||||
}
|
||||
|
||||
// Show room modal with images
|
||||
|
||||
@@ -298,6 +298,83 @@ h1, h2, h3, h4 {
|
||||
border-color: #fff;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
.btn-whatsapp {
|
||||
background: #25D366;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: clamp(10px, 2vw, 15px) clamp(20px, 4vw, 40px);
|
||||
border-radius: 50px;
|
||||
font-weight: 700;
|
||||
font-size: clamp(0.85rem, 1.2vw, 1rem);
|
||||
letter-spacing: 0.5px;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-whatsapp:hover {
|
||||
background: #1ebe57;
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 30px rgba(37,211,102,0.4);
|
||||
color: #fff;
|
||||
}
|
||||
.btn-telegram {
|
||||
background: #0088cc;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: clamp(10px, 2vw, 15px) clamp(20px, 4vw, 40px);
|
||||
border-radius: 50px;
|
||||
font-weight: 700;
|
||||
font-size: clamp(0.85rem, 1.2vw, 1rem);
|
||||
letter-spacing: 0.5px;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-telegram:hover {
|
||||
background: #0077b5;
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 30px rgba(0,136,204,0.4);
|
||||
color: #fff;
|
||||
}
|
||||
.btn-max {
|
||||
background: #471AFF;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: clamp(10px, 2vw, 15px) clamp(20px, 4vw, 40px);
|
||||
border-radius: 50px;
|
||||
font-weight: 700;
|
||||
font-size: clamp(0.85rem, 1.2vw, 1rem);
|
||||
letter-spacing: 0.5px;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-max:hover {
|
||||
background: #3b15d9;
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 30px rgba(71,26,255,0.4);
|
||||
color: #fff;
|
||||
}
|
||||
.max-icon-circle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.4em;
|
||||
height: 1.4em;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
color: #471AFF;
|
||||
font-weight: 800;
|
||||
font-size: 0.85em;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.hero-stats {
|
||||
position: absolute;
|
||||
bottom: clamp(20px, 5vw, 60px);
|
||||
@@ -1380,7 +1457,7 @@ h1, h2, h3, h4 {
|
||||
font-size: 0.65rem;
|
||||
padding: 5px 12px;
|
||||
}
|
||||
.btn-gold, .btn-outline-white {
|
||||
.btn-gold, .btn-outline-white, .btn-whatsapp, .btn-telegram, .btn-max {
|
||||
padding: 10px 18px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,10 @@
|
||||
<p class="hero-subtitle" data-i18n="hero.subtitle">Ваш идеальный отдых на берегу Чёрного моря в самом сердце Абхазии. Комфорт, природа и гостеприимство — всё, что нужно для незабываемого отпуска.</p>
|
||||
<div class="hero-buttons">
|
||||
<a href="#rooms" class="btn-gold"><i class="fas fa-bed"></i> <span data-i18n="hero.btn_rooms">Выбрать номер</span></a>
|
||||
<a href="tel:89409270080" class="btn-outline-white"><i class="fas fa-phone"></i> <span data-i18n="hero.btn_call">Позвонить</span></a>
|
||||
<a href="tel:89409270080" class="btn-outline-white" data-phone="tel"><i class="fas fa-phone"></i> <span data-i18n="hero.btn_call">Позвонить</span></a>
|
||||
<a href="https://wa.me/89409270080" class="btn-whatsapp" target="_blank" title="Написать в WhatsApp" data-phone="wa"><i class="fab fa-whatsapp"></i> W</a>
|
||||
<a href="https://t.me/+89409270080" class="btn-telegram" target="_blank" title="Написать в Telegram" data-phone="tg"><i class="fab fa-telegram"></i> T</a>
|
||||
<a href="https://max.ru/kalugin66" class="btn-max" target="_blank" title="Написать в Max" data-phone="max"><span class="max-icon-circle">M</span> M</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -537,7 +540,7 @@
|
||||
<div class="cta-banner animate-on-scroll">
|
||||
<h2 data-i18n="cta.title">Забронируйте номер прямо сейчас!</h2>
|
||||
<p data-i18n="cta.subtitle">Лучшие цены гарантированы при бронировании напрямую. Позвоните Рауфу Алексеевичу или оставьте заявку.</p>
|
||||
<a href="tel:89409270080" class="btn-dark-cta"><i class="fas fa-phone"></i> 8 (940) 927-00-80</a>
|
||||
<a href="tel:89409270080" class="btn-dark-cta" data-phone="tel" data-phone-text><i class="fas fa-phone"></i> 8 (940) 927-00-80</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -591,7 +594,7 @@
|
||||
<p style="font-size: 1rem; color: #666; line-height: 1.7; margin-bottom: 25px;" data-i18n="host.desc2">
|
||||
Домашняя кухня — отдельная достопримечательность гостиницы. Свежие местные продукты, абхазские и русские блюда, приготовленные с душой — абиц, ачапа, хачапури, шашлык и многое другое. Чистые номера, искренняя улыбка и внимание к каждой детали — это то, что делает Hotel 777 особенным.
|
||||
</p>
|
||||
<a href="tel:89409270080" class="host-contact">
|
||||
<a href="tel:89409270080" class="host-contact" data-phone="tel" data-phone-text>
|
||||
<i class="fas fa-phone"></i> 8 (940) 927-00-80
|
||||
</a>
|
||||
</div>
|
||||
@@ -628,7 +631,7 @@
|
||||
<div class="contact-icon"><i class="fas fa-phone"></i></div>
|
||||
<div>
|
||||
<div class="contact-label"><span data-i18n="contact.phone_label">Телефон</span></div>
|
||||
<div class="contact-value"><a href="tel:89409270080">8 (940) 927-00-80</a></div>
|
||||
<div class="contact-value"><a href="tel:89409270080" data-phone="tel" data-phone-text>8 (940) 927-00-80</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-info-item">
|
||||
@@ -646,7 +649,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a href="https://wa.me/89409270080" class="btn-gold" target="_blank" style="text-decoration: none;">
|
||||
<a href="https://wa.me/89409270080" class="btn-gold" target="_blank" style="text-decoration: none;" data-phone="wa">
|
||||
<i class="fab fa-whatsapp"></i> <span data-i18n="contact.whatsapp">Написать Рауфу в WhatsApp</span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -680,7 +683,7 @@
|
||||
<a href="#"><i class="fab fa-whatsapp"></i></a>
|
||||
<a href="#"><i class="fab fa-telegram"></i></a>
|
||||
<a href="#"><i class="fab fa-instagram"></i></a>
|
||||
<a href="tel:89409270080"><i class="fas fa-phone"></i></a>
|
||||
<a href="tel:89409270080" data-phone="tel"><i class="fas fa-phone"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2 col-md-4">
|
||||
@@ -709,7 +712,7 @@
|
||||
<div class="col-lg-3 col-md-4">
|
||||
<div class="footer-title" data-i18n="footer.contact_title">Контакты</div>
|
||||
<ul class="footer-links">
|
||||
<li><a href="tel:89409270080"><i class="fas fa-phone me-2"></i>8 (940) 927-00-80</a></li>
|
||||
<li><a href="tel:89409270080" data-phone="tel" data-phone-text><i class="fas fa-phone me-2"></i>8 (940) 927-00-80</a></li>
|
||||
<li><a href="#"><i class="fas fa-user me-2"></i>Рауф Алексеевич</a></li>
|
||||
<li><a href="#"><i class="fas fa-map-marker-alt me-2"></i>с. Мгудзырхуа</a></li>
|
||||
<li><a href="#"><i class="fas fa-water me-2"></i>Набережная ул., 1</a></li>
|
||||
@@ -724,7 +727,7 @@
|
||||
</footer>
|
||||
|
||||
<!-- Floating Phone Button -->
|
||||
<a href="tel:89409270080" class="float-phone">
|
||||
<a href="tel:89409270080" class="float-phone" data-phone="tel">
|
||||
<i class="fas fa-phone"></i>
|
||||
</a>
|
||||
|
||||
@@ -810,7 +813,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-center mt-3" style="font-size: 0.8rem; color: #999;">
|
||||
Или позвоните Рауфу Алексеевичу: <a href="tel:89409270080" style="color: var(--primary); font-weight: 600;">8 (940) 927-00-80</a>
|
||||
Или позвоните Рауфу Алексеевичу: <a href="tel:89409270080" style="color: var(--primary); font-weight: 600;" data-phone="tel" data-phone-text>8 (940) 927-00-80</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
@@ -823,6 +826,56 @@
|
||||
<script src="js/main.js?v=20260511b"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/reviews.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
function formatPhone(raw) {
|
||||
if (!raw || raw.length < 5) return raw;
|
||||
var d = raw.replace(/\D/g, '');
|
||||
if (d.length === 11 && d[0] === '8') {
|
||||
return '8 (' + d.substring(1, 4) + ') ' + d.substring(4, 7) + '-' + d.substring(7, 9) + '-' + d.substring(9, 11);
|
||||
}
|
||||
if (d.length === 10) {
|
||||
return '8 (' + d.substring(0, 3) + ') ' + d.substring(3, 6) + '-' + d.substring(6, 8) + '-' + d.substring(8, 10);
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
fetch('/api/config')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(cfg) {
|
||||
var primary = cfg.phone_primary || '89409270080';
|
||||
var additional = cfg.phone_additional || '';
|
||||
var maxLink = cfg.max_link || 'kalugin66';
|
||||
var displayText = formatPhone(primary);
|
||||
if (additional) displayText += ', ' + formatPhone(additional);
|
||||
|
||||
document.querySelectorAll('[data-phone]').forEach(function(el) {
|
||||
var type = el.getAttribute('data-phone');
|
||||
var href = '#';
|
||||
if (type === 'tel') href = 'tel:' + primary;
|
||||
else if (type === 'wa') href = 'https://wa.me/' + primary;
|
||||
else if (type === 'tg') href = 'https://t.me/+' + primary;
|
||||
else if (type === 'max') href = 'https://max.ru/' + maxLink;
|
||||
el.setAttribute('href', href);
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-phone-text]').forEach(function(el) {
|
||||
var t = el.textContent.trim();
|
||||
var icon = el.querySelector('i');
|
||||
if (t.match(/^8\s*\(?\d{3}\)?\s*\d{3}[\s-]*\d{2}[\s-]*\d{2}$/) || t.match(/^8\s*\(?\d{3}\)?/)) {
|
||||
if (icon) {
|
||||
el.innerHTML = '';
|
||||
el.appendChild(icon.cloneNode(true));
|
||||
el.appendChild(document.createTextNode(' ' + displayText));
|
||||
} else {
|
||||
el.textContent = displayText;
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(function() {});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Review Modal -->
|
||||
<div class="review-modal" id="reviewModal">
|
||||
|
||||
Reference in New Issue
Block a user