From 89e54538ded9481ff9663bf3defa8bc9d2c6991e Mon Sep 17 00:00:00 2001 From: kalugin66 Date: Mon, 11 May 2026 16:31:22 +0500 Subject: [PATCH] =?UTF-8?q?=D0=BD=D0=BE=D0=BC=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/index.js | 103 ++++++++- modules/rooms/index.js | 126 ++++++++++- package.json | 1 + public/admin.html | 375 +++++++++++++++++++++++++++++++ public/css/style.css | 12 + public/index.html | 144 +----------- public/js/main.js | 38 +++- public/js/rooms-public.js | 130 +++++++++++ server.js | 153 ++++++++++++- uploads/rooms/1778498276572.webp | Bin 0 -> 54352 bytes 10 files changed, 923 insertions(+), 159 deletions(-) create mode 100644 public/js/rooms-public.js create mode 100644 uploads/rooms/1778498276572.webp diff --git a/config/index.js b/config/index.js index e91feb6..a82572d 100644 --- a/config/index.js +++ b/config/index.js @@ -5,6 +5,29 @@ const ROOM_TYPES = { vip: { id: 'Люкс', name: 'Люкс', pricePerGuest: 4500 } }; +const FURNITURE_TYPES = [ + { id: 'double_bed', name: 'Двуспальная кровать' }, + { id: 'single_beds', name: 'Односпальные кровати' }, + { id: 'sofa', name: 'Диван' }, + { id: 'wardrobe', name: 'Шкаф' }, + { id: 'table', name: 'Стол' }, + { id: 'chairs', name: 'Стулья' }, + { id: 'nightstands', name: 'Прикроватные тумбочки' }, + { id: 'hanger', name: 'Вешалка' }, + { id: 'mirror', name: 'Зеркало' } +]; + +const AMENITY_TYPES = [ + { id: 'has_ac', name: 'Кондиционер', icon: 'snowflake' }, + { id: 'has_tv', name: 'Телевизор', icon: 'tv' }, + { id: 'has_fridge', name: 'Холодильник', icon: 'sink' }, + { id: 'has_wifi', name: 'Wi-Fi интернет', icon: 'wifi' }, + { id: 'has_kettle', name: 'Электрический чайник', icon: 'mug-hot' }, + { id: 'has_hairdryer', name: 'Фен', icon: 'wind' }, + { id: 'has_shower', name: 'Душ в номере', icon: 'shower' }, + { id: 'has_sea_view', name: 'Вид на море', icon: 'water' } +]; + const BOOKING_STATUSES = { NEW: 'новая', PAID: 'оплачена', @@ -15,10 +38,66 @@ const BOOKING_STATUSES = { }; const DEFAULT_ROOMS = [ - { type: ROOM_TYPES.economy.id, name: '2x-местный 1', description: 'Номер на двоих', rooms_count: 15, single_beds: 0, double_beds: 1, has_sofa: 0, has_ac: 1, has_wifi: 1, has_shower: 1, max_guests: 2, price_per_guest: ROOM_TYPES.economy.pricePerGuest }, - { type: ROOM_TYPES.standard.id, name: '3х-местный 1', description: 'Номер на троих', rooms_count: 8, single_beds: 0, double_beds: 1, has_sofa: 0, has_ac: 1, has_wifi: 1, has_shower: 1, max_guests: 3, price_per_guest: ROOM_TYPES.standard.pricePerGuest }, - { type: ROOM_TYPES.family.id, name: 'Семейный 1', description: 'Семейный номер', rooms_count: 4, single_beds: 0, double_beds: 2, has_sofa: 0, has_ac: 1, has_wifi: 1, has_shower: 1, max_guests: 4, price_per_guest: ROOM_TYPES.family.pricePerGuest }, - { type: ROOM_TYPES.vip.id, name: 'Люкс 1', description: 'Люкс', rooms_count: 3, single_beds: 0, double_beds: 1, has_sofa: 1, has_ac: 1, has_wifi: 1, has_shower: 1, max_guests: 4, price_per_guest: ROOM_TYPES.vip.pricePerGuest } + { + type: ROOM_TYPES.economy.id, + name: '2x-местный', + description: 'Уютный номер на двоих с одной или двумя кроватями. Идеально для пары или друзей. Кондиционер, TV, холодильник, Wi-Fi.', + rooms_count: 15, + area_sqm: 20, + furniture: ['double_bed', 'wardrobe', 'chairs', 'nightstands', 'hanger', 'mirror'], + amenities: ['has_ac', 'has_tv', 'has_fridge', 'has_wifi', 'has_kettle'], + floors: [1, 2, 3], + max_guests: 2, + price_per_night: 1500, + image_path: 'img/1faae356b-9f79-489d-8165-c37a47b82040.webp', + extra_beds: 15, + extra_bed_price: 1000 + }, + { + type: ROOM_TYPES.standard.id, + name: '3х-местный', + description: 'Комфортный трёхместный номер с односпальными или двуспальной кроватью. Просторный и светлый с видом на море.', + rooms_count: 8, + area_sqm: 20, + furniture: ['single_beds', 'double_bed', 'table', 'wardrobe', 'chairs', 'nightstands', 'hanger', 'mirror'], + amenities: ['has_ac', 'has_tv', 'has_fridge', 'has_wifi', 'has_kettle', 'has_sea_view'], + floors: [1, 2, 3], + max_guests: 3, + price_per_night: 2000, + image_path: 'img/1006bc9be-64c6-43e3-9625-1a862d04930c.webp', + extra_beds: 0, + extra_bed_price: 0 + }, + { + type: ROOM_TYPES.family.id, + name: 'Семейный', + description: 'Просторный двухкомнатный номер для семьи до 4 человек. Две спальни с двуспальными кроватями. Возможны дополнительные места.', + rooms_count: 4, + area_sqm: 40, + furniture: ['double_bed', 'wardrobe', 'chairs', 'nightstands', 'hanger', 'mirror'], + amenities: ['has_ac', 'has_tv', 'has_fridge', 'has_wifi', 'has_kettle', 'has_hairdryer'], + floors: [2], + max_guests: 4, + price_per_night: 3000, + image_path: 'img/1eae46658-cfca-4b65-82f0-e5868af5541b.webp', + extra_beds: 15, + extra_bed_price: 1000 + }, + { + type: ROOM_TYPES.vip.id, + name: 'Люкс', + description: 'Элегантный двухкомнатный люкс с двуспальной кроватью, гостиной зоной и великолепным видом на море. Премиум-комфорт.', + rooms_count: 3, + area_sqm: 50, + furniture: ['double_bed', 'sofa', 'wardrobe', 'chairs', 'nightstands', 'hanger', 'mirror'], + amenities: ['has_ac', 'has_tv', 'has_fridge', 'has_wifi', 'has_kettle', 'has_hairdryer', 'has_shower', 'has_sea_view'], + floors: [2], + max_guests: 4, + price_per_night: 4500, + image_path: 'img/1eae46658-cfca-4b65-82f0-e5868af5541b.webp', + extra_beds: 0, + extra_bed_price: 0 + } ]; const ROOM_PRICES = { @@ -38,10 +117,10 @@ const STATUS_LIST = [ ]; const ROOM_TYPE_LIST = [ - '2x-местный', - '3х-местный', - 'Семейный', - 'Люкс' + ROOM_TYPES.economy.id, + ROOM_TYPES.standard.id, + ROOM_TYPES.family.id, + ROOM_TYPES.vip.id ]; function getRoomPrice(roomType) { @@ -60,14 +139,22 @@ function calculateBasePrice(roomType, checkin, checkout) { return pricePerNight * nights; } +function getRoomBasePriceByType(type) { + const room = DEFAULT_ROOMS.find(r => r.type === type); + return room ? room.price_per_night : 0; +} + module.exports = { ROOM_TYPES, + FURNITURE_TYPES, + AMENITY_TYPES, BOOKING_STATUSES, DEFAULT_ROOMS, ROOM_PRICES, STATUS_LIST, ROOM_TYPE_LIST, getRoomPrice, + getRoomBasePriceByType, calculateNights, calculateBasePrice }; \ No newline at end of file diff --git a/modules/rooms/index.js b/modules/rooms/index.js index 0268b62..54a0331 100644 --- a/modules/rooms/index.js +++ b/modules/rooms/index.js @@ -1,18 +1,140 @@ let db; +const path = require('path'); +const fs = require('fs'); function init(database) { db = database; } +function parseRoomFields(row) { + if (!row) return null; + try { row.furniture = JSON.parse(row.furniture || '[]'); } catch { row.furniture = []; } + try { row.amenities = JSON.parse(row.amenities || '[]'); } catch { row.amenities = []; } + try { row.floors = JSON.parse(row.floors || '[]'); } catch { row.floors = []; } + return row; +} + function getAll(req, res) { - db.all(`SELECT * FROM rooms WHERE is_active = 1 ORDER BY price_per_guest ASC`, [], (err, rows) => { + db.all(`SELECT * FROM rooms WHERE is_active = 1 ORDER BY price_per_night ASC`, [], (err, rows) => { if (err) { console.error('Rooms API error:', err); return res.status(500).json({ error: 'Database error' }); } + rows = rows.map(parseRoomFields); res.json(rows); }); } -function setupRoutes(app) { +function getAllForAdmin(req, res) { + db.all(`SELECT * FROM rooms ORDER BY price_per_night ASC`, [], (err, rows) => { + if (err) { console.error('Admin rooms API error:', err); return res.status(500).json({ error: 'Database error' }); } + rows = rows.map(parseRoomFields); + res.json(rows); + }); +} + +function createRoom(req, res) { + const { type, name, description, rooms_count, area_sqm, max_guests, furniture, amenities, floors, price_per_night, extra_beds, extra_bed_price, is_active } = req.body; + + if (!type || !name || !price_per_night) { + return res.status(400).json({ error: 'type, name и price_per_night обязательны' }); + } + + db.run(`INSERT INTO rooms (type, name, description, rooms_count, area_sqm, max_guests, furniture, amenities, floors, price_per_night, extra_beds, extra_bed_price, is_active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [type, name, description || '', rooms_count || 1, area_sqm || 20, max_guests || 2, JSON.stringify(furniture || []), JSON.stringify(amenities || []), JSON.stringify(floors || []), price_per_night, extra_beds || 0, extra_bed_price || 0, is_active !== undefined ? (is_active ? 1 : 0) : 1], + function(err) { + if (err) { console.error('Create room error:', err); return res.status(500).json({ error: 'Database error' }); } + db.get(`SELECT * FROM rooms WHERE id = ?`, [this.lastID], (err, row) => { + if (err) return res.status(500).json({ error: 'Database error' }); + res.status(201).json(parseRoomFields(row)); + }); + } + ); +} + +function updateRoom(req, res) { + const { id } = req.params; + const { type, name, description, rooms_count, area_sqm, max_guests, furniture, amenities, floors, price_per_night, image_path, extra_beds, extra_bed_price, is_active } = req.body; + + db.get(`SELECT * FROM rooms WHERE id = ?`, [id], (err, row) => { + if (err) return res.status(500).json({ error: 'Database error' }); + if (!row) return res.status(404).json({ error: 'Номер не найден' }); + + db.run(`UPDATE rooms SET type = ?, name = ?, description = ?, rooms_count = ?, area_sqm = ?, max_guests = ?, furniture = ?, amenities = ?, floors = ?, price_per_night = ?, image_path = ?, extra_beds = ?, extra_bed_price = ?, is_active = ? WHERE id = ?`, + [ + type ?? row.type, + name ?? row.name, + description ?? row.description, + rooms_count ?? row.rooms_count, + area_sqm ?? row.area_sqm, + max_guests ?? row.max_guests, + furniture ? JSON.stringify(furniture) : row.furniture, + amenities ? JSON.stringify(amenities) : row.amenities, + floors ? JSON.stringify(floors) : row.floors, + price_per_night ?? row.price_per_night, + image_path !== undefined ? image_path : row.image_path, + extra_beds ?? row.extra_beds, + extra_bed_price ?? row.extra_bed_price, + is_active !== undefined ? (is_active ? 1 : 0) : row.is_active, + id + ], + function(err) { + if (err) { console.error('Update room error:', err); return res.status(500).json({ error: 'Database error' }); } + db.get(`SELECT * FROM rooms WHERE id = ?`, [id], (err, row) => { + if (err) return res.status(500).json({ error: 'Database error' }); + res.json(parseRoomFields(row)); + }); + } + ); + }); +} + +function deleteRoom(req, res) { + const { id } = req.params; + + db.get(`SELECT * FROM rooms WHERE id = ?`, [id], (err, row) => { + if (err) return res.status(500).json({ error: 'Database error' }); + if (!row) return res.status(404).json({ error: 'Номер не найден' }); + + db.run(`UPDATE rooms SET is_active = 0 WHERE id = ?`, [id], function(err) { + if (err) { console.error('Delete room error:', err); return res.status(500).json({ error: 'Database error' }); } + res.json({ message: 'Номер удалён' }); + }); + }); +} + +function uploadRoomImage(req, res) { + if (!req.file) { + return res.status(400).json({ error: 'Файл не загружен' }); + } + + let imagePath = 'uploads/rooms/' + req.file.filename; + + if (req.file.mimetype !== 'image/webp') { + const inputPath = req.file.path; + const outputPath = path.join(path.dirname(inputPath), req.file.filename.replace(/\.[^.]+$/, '.webp')); + + require('sharp')(inputPath) + .webp({ quality: 85 }) + .toFile(outputPath) + .then(() => { + try { fs.unlinkSync(inputPath); } catch {} + imagePath = 'uploads/rooms/' + path.basename(outputPath); + res.json({ path: imagePath }); + }) + .catch(err => { + console.error('Image conversion error:', err); + res.json({ path: imagePath }); + }); + } else { + res.json({ path: imagePath }); + } +} + +function setupRoutes(app, authenticateToken, requireAdmin, upload) { app.get('/api/rooms', getAll); + app.get('/api/admin/rooms', authenticateToken, requireAdmin, getAllForAdmin); + app.post('/api/admin/rooms', authenticateToken, requireAdmin, createRoom); + app.put('/api/admin/rooms/:id', authenticateToken, requireAdmin, updateRoom); + app.delete('/api/admin/rooms/:id', authenticateToken, requireAdmin, deleteRoom); + app.post('/api/admin/rooms/upload', authenticateToken, requireAdmin, upload.single('image'), uploadRoomImage); } module.exports = { init, setupRoutes }; \ No newline at end of file diff --git a/package.json b/package.json index 9da9832..97dfb29 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "dotenv": "^17.4.2", "express": "^5.2.1", "jsonwebtoken": "^9.0.3", + "multer": "^1.4.5-lts.1", "prom-client": "^15.1.3", "sharp": "^0.34.5", "sqlite3": "^6.0.1" diff --git a/public/admin.html b/public/admin.html index bc5dabe..ec1a589 100644 --- a/public/admin.html +++ b/public/admin.html @@ -177,6 +177,7 @@ tr.row-checkout-today { background: #fef2f2 !important; border-left: 4px solid # Дашборд Пользователи Бронирования + Номера Промокоды Отзывы Настройки @@ -285,6 +286,18 @@ tr.row-checkout-today { background: #fef2f2 !important; border-left: 4px solid # +
+
+

Номера

+ +
+
+
+
+
+
+
+

Промокоды

@@ -541,6 +554,7 @@ function initTabs() { if (tab === 'dashboard') loadDashboard(); if (tab === 'users') loadUsers(); if (tab === 'bookings') { bookingsLoaded = false; loadBookings(); } + if (tab === 'rooms') loadRooms(); if (tab === 'promocodes') loadPromocodes(); if (tab === 'reviews') loadReviews(); if (tab === 'settings') loadSettings(); @@ -935,6 +949,7 @@ async function deleteUser(id) { } function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; } +function escAttr(s) { if (s === null || s === undefined) return ''; return String(s).replace(/'/g, "\\'").replace(/"/g, '"'); } async function changeComment(id, comment) { try { @@ -1183,6 +1198,366 @@ function toggleCodeShow() { display.textContent = '******'; } } + +// ===== ROOMS MODULE ===== + +const ROOM_TYPES_LIST = ['2x-местный', '3х-местный', 'Семейный', 'Люкс']; + +const FURNITURE_OPTIONS = [ + { id: 'double_bed', name: 'Двуспальная кровать' }, + { id: 'single_beds', name: 'Односпальные кровати' }, + { id: 'sofa', name: 'Диван' }, + { id: 'wardrobe', name: 'Шкаф' }, + { id: 'table', name: 'Стол' }, + { id: 'chairs', name: 'Стулья' }, + { id: 'nightstands', name: 'Прикроватные тумбочки' }, + { id: 'hanger', name: 'Вешалка' }, + { id: 'mirror', name: 'Зеркало' } +]; + +const AMENITY_OPTIONS = [ + { id: 'has_ac', name: 'Кондиционер', icon: 'snowflake' }, + { id: 'has_tv', name: 'Телевизор', icon: 'tv' }, + { id: 'has_fridge', name: 'Холодильник', icon: 'sink' }, + { id: 'has_wifi', name: 'Wi-Fi интернет', icon: 'wifi' }, + { id: 'has_kettle', name: 'Электрический чайник', icon: 'mug-hot' }, + { id: 'has_hairdryer', name: 'Фен', icon: 'wind' }, + { id: 'has_shower', name: 'Душ в номере', icon: 'shower' }, + { id: 'has_sea_view', name: 'Вид на море', icon: 'water' } +]; + +function getAmenityIcon(id) { + const opt = AMENITY_OPTIONS.find(a => a.id === id); + return opt ? opt.icon : 'check'; +} + +async function loadRooms() { + try { + const rooms = await api('/api/admin/rooms'); + currentRooms = rooms; + renderRooms(rooms); + } catch(err) { showToast('Ошибка загрузки номеров: ' + err.message, 'error'); } +} + +function renderRooms(rooms) { + const grid = document.getElementById('roomsGrid'); + if (!rooms || rooms.length === 0) { + grid.innerHTML = '
Нет номеров. Нажмите "Добавить номер" для создания.
'; + return; + } + grid.innerHTML = rooms.map(r => { + const furniture = Array.isArray(r.furniture) ? r.furniture : []; + const amenities = Array.isArray(r.amenities) ? r.amenities : []; + const floors = Array.isArray(r.floors) ? r.floors : []; + const imageSrc = r.image_path ? (r.image_path.startsWith('uploads') ? '/' + r.image_path : r.image_path) : 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 250"%3E%3Crect fill="%23274151" width="400" height="250"/%3E%3Ctext fill="%2364748b" font-family="sans-serif" font-size="16" x="50%25" y="50%25" text-anchor="middle" dy=".3em"%3EБез фото%3C/text%3E%3C/svg%3E'; + const statusClass = r.is_active ? 'badge-status-оплачена' : 'badge-status-отменена'; + const statusText = r.is_active ? 'Активен' : 'Скрыт'; + const extraBedsText = r.extra_beds > 0 ? `+${r.extra_beds} доп. мест (${r.extra_bed_price}₽)` : ''; + + return '
' + + '
' + + '' + esc(r.name) + '' + + '
' + esc(r.type) + '
' + + '' + statusText + '' + + '
' + + '
' + + '

' + esc(r.name) + '

' + + '
' + r.price_per_night + ' ₽ / ночь
' + + '
' + + ' ' + (r.area_sqm || 0) + ' м²' + + ' до ' + r.max_guests + ' чел.' + + ' ' + r.rooms_count + ' номеров' + + '
' + + '
Этажи: ' + floors.join(', ') + '
' + + '
' + amenities.map(a => '').join('') + '
' + + (extraBedsText ? '
' + extraBedsText + '
' : '') + + '
' + + '' + + '' + + '
' + + '
' + + '
'; + }).join(''); +} + +let editingRoomId = null; +let currentRooms = []; + +function showRoomModal(id) { + editingRoomId = id; + const modal = document.getElementById('roomModal'); + const form = document.getElementById('roomForm'); + const title = document.getElementById('roomModalTitle'); + + form.reset(); + document.getElementById('roomImagePreview').src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 120"%3E%3Crect fill="%23274151" width="200" height="120"/%3E%3Ctext fill="%2364748b" font-family="sans-serif" font-size="12" x="50%25" y="50%25" text-anchor="middle" dy=".3em"%3EВыберите фото%3C/text%3E%3C/svg%3E'; + document.getElementById('roomImagePreview').removeAttribute('data-path'); + + document.querySelectorAll('#roomForm input[type="checkbox"]').forEach(cb => cb.checked = false); + + if (id) { + title.textContent = 'Редактировать номер'; + const room = currentRooms.find(r => r.id === id); + if (room) fillRoomForm(room); + } else { + title.textContent = 'Добавить номер'; + } + + modal.classList.add('show'); + document.body.style.overflow = 'hidden'; +} + +function fillRoomForm(r) { + document.getElementById('roomName').value = r.name || ''; + document.getElementById('roomType').value = r.type || '2x-местный'; + document.getElementById('roomDescription').value = r.description || ''; + document.getElementById('roomPrice').value = r.price_per_night || 0; + document.getElementById('roomArea').value = r.area_sqm || 20; + document.getElementById('roomMaxGuests').value = r.max_guests || 2; + document.getElementById('roomCount').value = r.rooms_count || 1; + document.getElementById('roomFloors').value = (Array.isArray(r.floors) ? r.floors.join(', ') : ''); + document.getElementById('roomExtraBeds').value = r.extra_beds || 0; + document.getElementById('roomExtraBedPrice').value = r.extra_bed_price || 0; + document.getElementById('roomIsActive').checked = r.is_active !== 0; + + if (r.image_path) { + const src = r.image_path.startsWith('uploads') ? '/' + r.image_path : r.image_path; + document.getElementById('roomImagePreview').src = src; + document.getElementById('roomImagePreview').dataset.path = r.image_path; + } + + const furniture = Array.isArray(r.furniture) ? r.furniture : []; + furniture.forEach(f => { + const cb = document.querySelector('#roomForm input[name="furniture"][value="' + f + '"]'); + if (cb) cb.checked = true; + }); + + const amenities = Array.isArray(r.amenities) ? r.amenities : []; + amenities.forEach(a => { + const cb = document.querySelector('#roomForm input[name="amenities"][value="' + a + '"]'); + if (cb) cb.checked = true; + }); +} + +function closeRoomModal() { + document.getElementById('roomModal').classList.remove('show'); + document.body.style.overflow = ''; + editingRoomId = null; +} + +async function saveRoom() { + const name = document.getElementById('roomName').value.trim(); + const type = document.getElementById('roomType').value; + const description = document.getElementById('roomDescription').value.trim(); + const price_per_night = parseInt(document.getElementById('roomPrice').value) || 0; + const area_sqm = parseInt(document.getElementById('roomArea').value) || 20; + const max_guests = parseInt(document.getElementById('roomMaxGuests').value) || 2; + const rooms_count = parseInt(document.getElementById('roomCount').value) || 1; + const extra_beds = parseInt(document.getElementById('roomExtraBeds').value) || 0; + const extra_bed_price = parseInt(document.getElementById('roomExtraBedPrice').value) || 0; + const is_active = document.getElementById('roomIsActive').checked ? 1 : 0; + + const floorsInput = document.getElementById('roomFloors').value.trim(); + const floors = floorsInput ? floorsInput.split(',').map(f => parseInt(f.trim())).filter(f => !isNaN(f)) : []; + + const furniture = []; + document.querySelectorAll('#roomForm input[name="furniture"]:checked').forEach(cb => furniture.push(cb.value)); + + const amenities = []; + document.querySelectorAll('#roomForm input[name="amenities"]:checked').forEach(cb => amenities.push(cb.value)); + + if (!name || !price_per_night) { + showToast('Название и цена обязательны', 'error'); + return; + } + + const image_path = document.getElementById('roomImagePreview').dataset.path || ''; + + try { + const data = { + type, name, description, price_per_night, area_sqm, max_guests, rooms_count, + floors, furniture, amenities, extra_beds, extra_bed_price, is_active + }; + + if (editingRoomId) { + await api('/api/admin/rooms/' + editingRoomId, { method: 'PUT', body: JSON.stringify({ ...data, image_path }) }); + showToast('Номер обновлён'); + } else { + await api('/api/admin/rooms', { method: 'POST', body: JSON.stringify(data) }); + showToast('Номер создан'); + } + + closeRoomModal(); + loadRooms(); + } catch(err) { showToast(err.message, 'error'); } +} + +async function deleteRoom(id, name) { + if (!confirm('Удалить номер "' + name + '"? Это скроет номер из списка.')) return; + try { + await api('/api/admin/rooms/' + id, { method: 'DELETE' }); + showToast('Номер удалён'); + loadRooms(); + } catch(err) { showToast(err.message, 'error'); } +} + +async function uploadRoomImage() { + const fileInput = document.getElementById('roomImageInput'); + const file = fileInput.files[0]; + if (!file) { showToast('Выберите файл', 'error'); return; } + + const formData = new FormData(); + formData.append('image', file); + + try { + const res = await fetch(API + '/api/admin/rooms/upload', { + method: 'POST', + headers: { 'Authorization': 'Bearer ' + token }, + body: formData + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Ошибка загрузки'); + + document.getElementById('roomImagePreview').src = '/' + data.path; + document.getElementById('roomImagePreview').dataset.path = data.path; + showToast('Фото загружено'); + } catch(err) { showToast(err.message, 'error'); } +} + + + + + + diff --git a/public/css/style.css b/public/css/style.css index c513295..f53aa6d 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -558,6 +558,14 @@ h1, h2, h3, h4 { background: #fef3e2; border-radius: 6px; } +.room-extra-hint { + font-size: 0.75rem; + color: #e67e22; + margin-bottom: 12px; + padding: 6px 10px; + background: #fef3e2; + border-radius: 6px; +} .room-feature-tag { display: inline-flex; align-items: center; @@ -1027,6 +1035,10 @@ h1, h2, h3, h4 { transform: translateY(40px); transition: all 0.8s ease; } +.animate-on-scroll.visible { + opacity: 1; + transform: translateY(0); +} .animate-on-scroll.animated { opacity: 1; transform: translateY(0); diff --git a/public/index.html b/public/index.html index c4514b1..75a2f9c 100644 --- a/public/index.html +++ b/public/index.html @@ -162,143 +162,12 @@

Четыре категории номеров — от 2x-местного до люкса. Каждый номер оснащён всем необходимым для идеального отдыха.

-
- -
-
-
- 2x-местный номер -
Стандарт
-
-
-

2x-местный

-

Уютный номер на двоих с одной или двумя кроватями. Идеально для пары или друзей. Кондиционер, TV, холодильник, Wi-Fi.

-
- 20 м² - Этажи 1-3 - Душ в номере -
-
- 2 кровати - Кондиционер - ТВ - WiFi - Холодильник - Чайник -
- -
-
-
- -
-
-
- 3х-местный номер -
Стандарт
-
-
-

3х-местный

-

Комфортный трёхместный номер с односпальными или двуспальной кроватью. Просторный и светлый с видом на море.

-
- 20 м² - Этажи 1-3 - Душ в номере -
-
- 3 кровати - Кондиционер - ТВ - WiFi - Холодильник - Чайник - Вид на море -
- -
-
-
- -
- -
- -
-
-
- Люкс -
Люкс
-
-
-

Люкс

-

Элегантный двухкомнатный люкс с двуспальной кроватью, гостиной зоной и великолепным видом на море. Премиум-комфорт.

-
- 50 м² - Этаж 2 - Душ в номере -
-
- 4 кровати - 2 комнаты - Кондиционер - ТВ - WiFi - Холодильник - Чайник - Вид на море -
- -
-
-
+
+
@@ -666,6 +535,7 @@
+ diff --git a/public/js/main.js b/public/js/main.js index 0c340b5..66081bc 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -71,15 +71,48 @@ document.querySelectorAll('.btn-book').forEach(btn => { btn.addEventListener('click', function() { const room = this.getAttribute('data-room'); document.getElementById('selectedRoom').value = room; - updateGuestOptions(room); + const maxGuests = parseInt(this.getAttribute('data-max-guests')) || ROOM_MAX_GUESTS[room] || 4; + updateGuestOptionsFallback(room); hidePriceInfo(); }); }); +function updateGuestOptionsDynamic(roomType, maxGuests) { + const guestsSelect = document.querySelector('[name="guests"]'); + if (!guestsSelect) return; + + let options = []; + for (let i = 1; i <= maxGuests; i++) { + let text = i === 1 ? '1 гость' : (i < 5 ? `${i} гостя` : `${i} гостей`); + options.push(``); + } + + guestsSelect.innerHTML = options.join(''); +} + +window.updateRoomPricesData = function(prices, maxGuests) { + Object.assign(ROOM_PRICES, prices); + Object.assign(ROOM_MAX_GUESTS, maxGuests); +}; + function updateGuestOptions(room) { const guestsSelect = document.querySelector('[name="guests"]'); if (!guestsSelect) return; + const maxGuests = ROOM_MAX_GUESTS[room] || 4; + let options = []; + for (let i = 1; i <= maxGuests; i++) { + let text = i === 1 ? '1 гость' : (i < 5 ? `${i} гостя` : `${i} гостей`); + options.push(``); + } + + guestsSelect.innerHTML = options.join(''); +} + +function updateGuestOptionsFallback(room) { + const guestsSelect = document.querySelector('[name="guests"]'); + if (!guestsSelect) return; + const options2x = [ { value: 1, text: '1 гость' }, { value: 2, text: '2 гостя' } @@ -105,8 +138,11 @@ function updateGuestOptions(room) { guestsSelect.innerHTML = options.map(o => ``).join(''); } +function hideGuestOptionsUpdate() {} + // Price calculation const ROOM_PRICES = { '2x-местный': 1500, '3х-местный': 2000, 'Семейный': 3000, 'Люкс': 4500 }; +const ROOM_MAX_GUESTS = { '2x-местный': 2, '3х-местный': 3, 'Семейный': 4, 'Люкс': 4 }; let currentPromocodeData = null; function calculateNights(checkin, checkout) { diff --git a/public/js/rooms-public.js b/public/js/rooms-public.js new file mode 100644 index 0000000..1ca8149 --- /dev/null +++ b/public/js/rooms-public.js @@ -0,0 +1,130 @@ +const AMENITY_ICONS = { + has_ac: { icon: 'fa-snowflake', label: 'Кондиционер' }, + has_tv: { icon: 'fa-tv', label: 'ТВ' }, + has_fridge: { icon: 'fa-sink', label: 'Холодильник' }, + has_wifi: { icon: 'fa-wifi', label: 'WiFi' }, + has_kettle: { icon: 'fa-mug-hot', label: 'Чайник' }, + has_hairdryer: { icon: 'fa-wind', label: 'Фен' }, + has_shower: { icon: 'fa-shower', label: 'Душ в номере' }, + has_sea_view: { icon: 'fa-water', label: 'Вид на море' } +}; + +let cachedRooms = []; + +async function loadRoomsPublic() { + try { + const res = await fetch('/api/rooms'); + if (!res.ok) throw new Error('Failed to load rooms'); + cachedRooms = await res.json(); + renderRoomsPublic(cachedRooms); + updateRoomPrices(cachedRooms); + } catch (err) { + console.error('Error loading rooms:', err); + document.getElementById('roomsEmpty').style.display = 'block'; + } +} + +function renderRoomsPublic(rooms) { + const grid = document.getElementById('roomsGrid'); + const empty = document.getElementById('roomsEmpty'); + + if (!rooms || rooms.length === 0) { + grid.innerHTML = ''; + empty.style.display = 'block'; + return; + } + + empty.style.display = 'none'; + + grid.innerHTML = rooms.map((room, index) => { + const amenities = Array.isArray(room.amenities) ? room.amenities : []; + const floors = Array.isArray(room.floors) ? room.floors : []; + + const imageSrc = room.image_path || 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 250"%3E%3Crect fill="%23274151" width="400" height="250"/%3E%3Ctext fill="%2364748b" font-family="sans-serif" font-size="16" x="50%25" y="50%25" text-anchor="middle" dy=".3em"%3EФото%3C/text%3E%3C/svg%3E'; + const fullImageSrc = imageSrc.startsWith('uploads') ? '/' + imageSrc : imageSrc; + + const amenitiesHtml = amenities.map(a => { + const info = AMENITY_ICONS[a]; + if (!info) return ''; + return ` ${info.label}`; + }).join(''); + + const floorsStr = floors.length > 0 ? 'Этажи ' + floors.join(', ') : ''; + + const extraBedsHtml = room.extra_beds > 0 + ? `
Возможно +${room.extra_beds} доп. мест (${room.extra_bed_price} ₽)
` + : ''; + + const featuredClass = room.type === 'Семейный' ? ' featured' : ''; + + return ` +
+
+
+ ${room.name} +
${room.type}
+
+
+

${escapeHtml(room.name)}

+

${escapeHtml(room.description || '')}

+
+ ${room.area_sqm || 20} м² + ${floorsStr ? ` ${floorsStr}` : ''} + ${amenities.includes('has_shower') ? ' Душ в номере' : ''} +
+
+ ${amenitiesHtml} +
+ ${extraBedsHtml} + +
+
+
+ `; + }).join(''); + + initRoomBookingHandlers(); +} + +function initRoomBookingHandlers() { + document.querySelectorAll('.btn-book').forEach(btn => { + btn.addEventListener('click', function() { + const room = this.getAttribute('data-room'); + const maxGuests = parseInt(this.getAttribute('data-max-guests')) || 4; + document.getElementById('selectedRoom').value = room; + updateGuestOptionsDynamic(room, maxGuests); + hidePriceInfo(); + }); + }); +} + +function updateGuestOptionsDynamic(roomType, maxGuests) { + const guestsSelect = document.querySelector('[name="guests"]'); + if (!guestsSelect) return; + + let options = []; + for (let i = 1; i <= maxGuests; i++) { + let text = i === 1 ? '1 гость' : (i < 5 ? `${i} гостя` : `${i} гостей`); + options.push(``); + } + + guestsSelect.innerHTML = options.join(''); +} + +function escapeHtml(text) { + if (!text) return ''; + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; +} + +document.addEventListener('DOMContentLoaded', () => { + loadRoomsPublic(); +}); \ No newline at end of file diff --git a/server.js b/server.js index 7766018..6a44cfe 100644 --- a/server.js +++ b/server.js @@ -6,6 +6,7 @@ const sharp = require('sharp'); const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const client = require('prom-client'); +const multer = require('multer'); require('dotenv').config(); const config = require('./config'); @@ -19,6 +20,31 @@ const JWT_SECRET = process.env.JWT_SECRET; const MONITORING_USER = process.env.MONITORING_USER || 'monitoring'; const MONITORING_PASSWORD = process.env.MONITORING_PASSWORD || 'monitoring123'; +const uploadsDir = path.join(__dirname, 'uploads'); +if (!fs.existsSync(uploadsDir)) fs.mkdirSync(uploadsDir, { recursive: true }); +const roomsUploadsDir = path.join(uploadsDir, 'rooms'); +if (!fs.existsSync(roomsUploadsDir)) fs.mkdirSync(roomsUploadsDir, { recursive: true }); + +const storage = multer.diskStorage({ + destination: (req, file, cb) => cb(null, roomsUploadsDir), + filename: (req, file, cb) => { + const ext = path.extname(file.originalname).toLowerCase(); + const timestamp = Date.now(); + cb(null, `${timestamp}${ext}`); + } +}); + +const upload = multer({ + storage, + limits: { fileSize: 5 * 1024 * 1024 }, + fileFilter: (req, file, cb) => { + const allowed = ['.jpg', '.jpeg', '.png', '.webp']; + const ext = path.extname(file.originalname).toLowerCase(); + if (allowed.includes(ext)) cb(null, true); + else cb(new Error('Только изображения: jpg, jpeg, png, webp')); + } +}); + if (!JWT_SECRET) { console.error('FATAL: JWT_SECRET environment variable not set'); process.exit(1); @@ -94,6 +120,7 @@ app.use((req, res, next) => { next(); }); app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'uploads'))); app.use((req, res, next) => { const start = Date.now(); @@ -224,19 +251,95 @@ db.serialize(() => { name TEXT NOT NULL, description TEXT, rooms_count INTEGER DEFAULT 1, - single_beds INTEGER DEFAULT 0, - double_beds INTEGER DEFAULT 0, - has_sofa INTEGER DEFAULT 0, - has_ac INTEGER DEFAULT 0, - has_wifi INTEGER DEFAULT 0, - has_shower INTEGER DEFAULT 0, + area_sqm INTEGER DEFAULT 20, max_guests INTEGER DEFAULT 2, - price_per_guest INTEGER NOT NULL, + furniture TEXT DEFAULT '[]', + amenities TEXT DEFAULT '[]', + floors TEXT DEFAULT '[]', + price_per_night INTEGER NOT NULL, image_path TEXT, + extra_beds INTEGER DEFAULT 0, + extra_bed_price INTEGER DEFAULT 0, is_active INTEGER DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`); - setupBookingHistoryTable(); + + db.all("PRAGMA table_info(rooms)", [], (err, cols) => { + if (err) { setupBookingHistoryTable(); return; } + const colNames = cols.map(c => c.name); + + const migrations = [ + ['area_sqm', 'INTEGER DEFAULT 20'], + ['furniture', "TEXT DEFAULT '[]'"], + ['amenities', "TEXT DEFAULT '[]'"], + ['floors', "TEXT DEFAULT '[]'"], + ['extra_beds', 'INTEGER DEFAULT 0'], + ['extra_bed_price', 'INTEGER DEFAULT 0'] + ]; + + let pending = migrations.length; + if (pending === 0) { + migratePricePerNight(); + return; + } + + migrations.forEach(([colName, colDef], i) => { + if (!colNames.includes(colName)) { + db.run(`ALTER TABLE rooms ADD COLUMN ${colName} ${colDef}`, (err) => { + if (err && !err.message.includes('duplicate') && !err.message.includes('NOT NULL')) { + console.log('Room migration note:', err.message); + } + }); + } + if (--pending === 0) migratePricePerNight(); + }); + }); + } + + function migratePricePerNight() { + db.all("PRAGMA table_info(rooms)", [], (err, cols) => { + if (err) { setupBookingHistoryTable(); return; } + const colNames = cols.map(c => c.name); + + if (!colNames.includes('price_per_night')) { + db.run(`ALTER TABLE rooms ADD COLUMN price_per_night INTEGER DEFAULT 0`, (err) => { + if (err && !err.message.includes('duplicate') && !err.message.includes('NOT NULL')) { + console.log('Room migration note (add price_per_night):', err.message); + } + }); + } + + db.run(`CREATE TABLE rooms_backup AS SELECT id, type, name, description, rooms_count, area_sqm, max_guests, furniture, amenities, floors, price_per_night, image_path, extra_beds, extra_bed_price, is_active, created_at FROM rooms`, (err) => { + if (err) { + console.log('Room backup failed:', err.message); + setupBookingHistoryTable(); + return; + } + + db.run(`DROP TABLE rooms`, (err) => { + if (err) { + console.log('Room drop failed:', err.message); + setupBookingHistoryTable(); + return; + } + + db.run(`CREATE TABLE rooms (id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, name TEXT NOT NULL, description TEXT, rooms_count INTEGER DEFAULT 1, area_sqm INTEGER DEFAULT 20, max_guests INTEGER DEFAULT 2, furniture TEXT DEFAULT '[]', amenities TEXT DEFAULT '[]', floors TEXT DEFAULT '[]', price_per_night INTEGER NOT NULL, image_path TEXT, extra_beds INTEGER DEFAULT 0, extra_bed_price INTEGER DEFAULT 0, is_active INTEGER DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)`, (err) => { + if (err) { + console.log('Room recreate failed:', err.message); + setupBookingHistoryTable(); + return; + } + + db.run(`INSERT INTO rooms (id, type, name, description, rooms_count, area_sqm, max_guests, furniture, amenities, floors, price_per_night, image_path, extra_beds, extra_bed_price, is_active, created_at) SELECT id, type, name, description, rooms_count, area_sqm, max_guests, COALESCE(furniture, '[]'), COALESCE(amenities, '[]'), COALESCE(floors, '[]'), COALESCE(price_per_night, 0) as price_per_night, image_path, COALESCE(extra_beds, 0), COALESCE(extra_bed_price, 0), is_active, created_at FROM rooms_backup`, (err) => { + if (err) console.log('Room data restore failed:', err.message); + + db.run(`DROP TABLE rooms_backup`, (err) => {}); + setupBookingHistoryTable(); + }); + }); + }); + }); + }); } function setupBookingHistoryTable() { @@ -363,8 +466,27 @@ function initDefaultRooms() { db.get("SELECT COUNT(*) as count FROM rooms", (err, row) => { if (err) return console.error('Check rooms count error:', err); if (row.count > 0) return; - const stmt = db.prepare(`INSERT INTO rooms (type, name, description, rooms_count, single_beds, double_beds, has_sofa, has_ac, has_wifi, has_shower, max_guests, price_per_guest, image_path, is_active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`); - config.DEFAULT_ROOMS.forEach(r => stmt.run(r.type, r.name, r.description, r.rooms_count, r.single_beds, r.double_beds, r.has_sofa, r.has_ac, r.has_wifi, r.has_shower, r.max_guests, r.price_per_guest, r.image_path, r.is_active)); + + const stmt = db.prepare(`INSERT INTO rooms (type, name, description, rooms_count, area_sqm, max_guests, furniture, amenities, floors, price_per_night, image_path, extra_beds, extra_bed_price, is_active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)`); + + config.DEFAULT_ROOMS.forEach(r => { + stmt.run( + r.type, + r.name, + r.description, + r.rooms_count, + r.area_sqm, + r.max_guests, + JSON.stringify(r.furniture || []), + JSON.stringify(r.amenities || []), + JSON.stringify(r.floors || []), + r.price_per_night, + r.image_path, + r.extra_beds || 0, + r.extra_bed_price || 0 + ); + }); + stmt.finalize(); console.log('✅ Default rooms initialized'); }); @@ -402,7 +524,7 @@ authModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmi bookingsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin); adminBookingsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin); promocodesModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin); -roomsModule.setupRoutes(app); +roomsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin, upload); usersModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin); settingsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin); reviewsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin); @@ -479,6 +601,15 @@ app.get('/api/cities/:countryCode', (req, res) => { } }); +app.get('/uploads/rooms/:filename', (req, res) => { + const filePath = path.join(roomsUploadsDir, req.params.filename); + if (fs.existsSync(filePath)) { + res.sendFile(filePath); + } else { + res.status(404).json({ error: 'File not found' }); + } +}); + app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); diff --git a/uploads/rooms/1778498276572.webp b/uploads/rooms/1778498276572.webp new file mode 100644 index 0000000000000000000000000000000000000000..8158374404b7e52f0640e3fee6065a2b0773f065 GIT binary patch literal 54352 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ð4yIJdHNAR`fBW(gGe6)Js@x#fNK({m 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