<!DOCTYPE html>
<html lang="ru">
<голова>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ЭКСПТРСС ТУР | Биржа смотрит на дом</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:корень {
--bg-dark: #0a0a0f;
--bg-card: #111118;
--bg-hover: #1a1a24;
--primary: #7c3aed;
--primary-hover: #8b5cf6;
--вторичный: #10b981;
--текст: #f8fafc;
--text-muted: #94a3b8;
--border: #2d2d3d;
--опасность: #ef4444;
--предупреждение: #f59e0b;
--золото: #fbbf24;
}
* {
отступ: 0;
отступ: 0;
box-izing: border-box;
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
}
тело {
фон: var(--bg-dark);
цвет: var(--text);
минимальная высота: 100vh;
межстрочный интервал: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* Заголовок */
заголовок {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
border-bottom: 1px Solid var(--border);
margin-bottom: 40px;
}
.logo {
display: flex;
align-items: center;
зазор: 12 пикселей;
font-size: 1.8rem;
font-weight: 700;
цвет: var(--text);
}
.logo-icon {
цвет: var(--primary);
font-size: 2rem;
}
.mode-switcher {
display: flex;
background: var(--bg-card);
border-radius: 12px;
padding: 4px;
граница: сплошная 1 пиксель var(--border);
}
.mode-btn {
padding: 10px 24px;
border-radius: 8px;
граница: отсутствует;
фон: прозрачный;
цвет: var(--text-muted);
font-weight: 600;
курсор: указатель;
Переход: плавность 0,3 с;
}
.mode-btn.active {
background: var(--primary);
цвет: белый;
}
/* Раздел аутентификации */
.auth-section {
max-width: 400px;
margin: 60px auto;
padding: 40px;
background: var(--bg-card);
border-radius: 20px;
граница: сплошная 1 пиксель var(--border);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.auth-title {
font-size: 1.8rem;
margin-bottom: 10px;
text-align: center;
}
.auth-subtitle {
цвет: var(--text-muted);
margin-bottom: 30px;
text-align: center;
}
.input-group {
margin-bottom: 24px;
}
.input-label {
display: block;
margin-bottom: 8px;
font-weight: 600;
цвет: var(--text);
}
.input-field {
ширина: 100%;
padding: 14px 16px;
фон: var(--bg-dark);
граница: сплошная 1 пиксель var(--border);
border-radius: 10px;
цвет: var(--text);
font-size: 1rem;
переход: граница 0,3 с;
}
.input-field:focus {
план: отсутствует;
border-color: var(--primary);
}
.btn {
ширина: 100%;
padding: 16px;
background: var(--primary);
цвет: белый;
граница: отсутствует;
border-radius: 10px;
font-size: 1rem;
font-weight: 600;
курсор: указатель;
переход: фон 0,3 с;
display: flex;
justify-content: center;
align-items: center;
зазор: 10 пикселей;
}
.btn:hover {
фон: var (--primary-hover);
}
.btn-secondary {
фон: прозрачный;
граница: сплошная 1 пиксель var(--border);
цвет: var(--text);
}
.btn-secondary:hover {
background: var(--bg-hover);
}
.btn-success {
фон: var(--secondary);
}
.btn-success:hover {
фон: #34d399;
}
.btn-warning {
background: var(--warning);
}
.btn-warning:hover {
фон: #fbbf24;
}
.btn-gold {
background: linear-gradient(135deg, #fbbf24, #f59e0b);
}
.btn-gold:hover {
background: linear-gradient(135deg, #fcd34d, #fbbf24);
}
.btn-small {
padding: 8px 16px;
font-size: 0.9rem;
ширина: авто;
пробел: nowrap;
}
.btn-danger {
фон: var(--danger);
}
.btn-danger:hover {
фон: #dc2626;
}
.btn-info {
фон: #3b82f6;
}
.btn-info:hover {
фон: #2563eb;
}
.divider {
text-align: center;
margin: 30px 0;
положение: относительное;
}
.divider::before {
содержание: '';
положение: абсолютное;
верхний предел: 50%;
слева: 0;
справа: 0;
высота: 1 пиксель;
background: var(--border);
}
.divider span {
background: var(--bg-card);
padding: 0 15px;
цвет: var(--text-muted);
}
/* Форма для гостей */
.guest-form {
background: var(--bg-card);
border-radius: 20px;
padding: 40px;
граница: сплошная 1 пиксель var(--border);
margin-bottom: 40px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.form-title {
font-size: 1.8rem;
margin-bottom: 10px;
}
.form-subtitle {
цвет: var(--text-muted);
margin-bottom: 30px;
}
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
зазор: 20 пикселей;
margin-bottom: 30px;
}
.date-inputs {
display: flex;
зазор: 10 пикселей;
ширина: 100%;
}
.date-inputs .input-field {
минимальная ширина: 0;
}
/* Исправление количества гостевых полей */
.guests-input-container {
положение: относительное;
ширина: 100%;
}
.guests-input-container .input-field {
ширина: 100%;
}
.form-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px Solid var(--border);
}
/* Список запросов */
.requests-list {
margin-top: 30px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.section-title {
font-size: 1.5rem;
}
.stats {
цвет: var(--text-muted);
font-size: 0.9rem;
}
.requests-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
зазор: 20 пикселей;
}
.request-card {
background: var(--bg-card);
border-radius: 16px;
padding: 24px;
граница: сплошная 1 пиксель var(--border);
Переход: плавность 0,3 с;
положение: относительное;
переполнение: скрыто;
}
.request-card.featured {
border-color: var(--gold);
box-shadow: 0 0 0 1px var(--gold), 0 10px 30px rgba(251, 191, 36, 0.1);
}
.featured-badge {
положение: абсолютное;
top: -10px;
справа: 20 пикселей;
background: linear-gradient(135deg, var(--gold), #f59e0b);
цвет: #0a0a0f;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 700;
display: flex;
align-items: center;
зазор: 5 пикселей;
}
.request-card:hover {
border-color: var(--primary);
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.request-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.request-id {
font-size: 0.8rem;
цвет: var(--text-muted);
фон: var(--bg-dark);
padding: 4px 10px;
border-radius: 20px;
}
.request-date {
font-size: 0.9rem;
цвет: var(--text-muted);
}
.request-location {
display: flex;
align-items: center;
зазор: 10 пикселей;
margin-bottom: 12px;
font-weight: 600;
}
.request-dates {
display: flex;
align-items: center;
зазор: 10 пикселей;
margin-bottom: 12px;
цвет: var(--text-muted);
}
.request-guests, .request-budget {
display: flex;
align-items: center;
зазор: 10 пикселей;
margin-bottom: 12px;
}
.request-stats {
display: flex;
зазор: 20 пикселей;
margin: 15px 0;
padding: 15px;
фон: var(--bg-dark);
border-radius: 10px;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.stat-value {
font-size: 1.2rem;
font-weight: 700;
цвет: var(--primary);
}
.stat-label {
font-size: 0.8rem;
цвет: var(--text-muted);
}
.request-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20px;
padding-top: 20px;
border-top: 1px Solid var(--border);
flex-wrap: wrap;
зазор: 10 пикселей;
}
.action-buttons {
display: flex;
зазор: 10 пикселей;
flex-wrap: wrap;
justify-content: flex-end;
max-width: 100%;
}
.action-buttons .btn-small {
min-width: 120px;
flex-shrink: 0;
}
.contact-info {
margin-top: 20px;
padding: 15px;
фон: var(--bg-dark);
border-radius: 10px;
display: none;
}
.contact-info.show {
display: block;
}
.contact-item {
display: flex;
align-items: center;
зазор: 10 пикселей;
margin-bottom: 10px;
}
/* Панель управления владельца */
.owner-dashboard {
display: none;
}
.dashboard-header {
background: var(--bg-card);
border-radius: 16px;
padding: 30px;
margin-bottom: 30px;
граница: сплошная 1 пиксель var(--border);
положение: относительное;
}
.dashboard-header-actions {
положение: абсолютное;
top: 30px;
справа: 30 пикселей;
display: flex;
зазор: 10 пикселей;
}
.dashboard-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
зазор: 20 пикселей;
margin-top: 30px;
}
.stat-card {
фон: var(--bg-dark);
padding: 20px;
border-radius: 12px;
граница: сплошная 1 пиксель var(--border);
}
.owner-stat-value {
font-size: 2rem;
font-weight: 700;
цвет: var(--primary);
}
.owner-stat-label {
цвет: var(--text-muted);
font-size: 0.9rem;
}
.ценник {
background: linear-gradient(135deg, var(--primary), #8b5cf6);
цвет: белый;
padding: 6px 12px;
border-radius: 20px;
font-weight: 600;
font-size: 0.9rem;
}
/* Вкладки */
.tabs {
display: flex;
зазор: 10 пикселей;
margin-bottom: 30px;
border-bottom: 1px Solid var(--border);
padding-bottom: 10px;
flex-wrap: wrap;
}
.tab {
padding: 10px 20px;
фон: прозрачный;
граница: отсутствует;
цвет: var(--text-muted);
font-weight: 600;
курсор: указатель;
border-radius: 8px;
переход: все 0,3 с;
}
.tab.active {
background: var(--primary);
цвет: белый;
}
/* Новые стили для вкладок Заработок и Админ */
.earnings-section, .admin-section {
background: var(--bg-card);
border-radius: 20px;
padding: 40px;
граница: сплошная 1 пиксель var(--border);
margin-bottom: 40px;
}
.referral-link-container {
фон: var(--bg-dark);
border-radius: 12px;
padding: 20px;
margin: 20px 0;
граница: сплошная 1 пиксель var(--border);
}
.link-display {
display: flex;
зазор: 10 пикселей;
margin-bottom: 15px;
}
.link-input {
flex: 1;
padding: 12px;
фон: var(--bg-dark);
граница: сплошная 1 пиксель var(--border);
border-radius: 8px;
цвет: var(--text);
font-size: 0.9rem;
}
.link-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
зазор: 15 пикселей;
margin: 20px 0;
}
.stat-item-earnings {
фон: var(--bg-dark);
padding: 15px;
border-radius: 10px;
text-align: center;
}
.stat-value-earnings {
font-size: 1.5rem;
font-weight: 700;
цвет: var(--secondary);
}
.stat-label-earnings {
font-size: 0.9rem;
цвет: var(--text-muted);
margin-top: 5px;
}
.ad-campaign {
фон: var(--bg-dark);
border-radius: 12px;
padding: 20px;
margin: 15px 0;
граница: сплошная 1 пиксель var(--border);
}
.ad-price-tag {
background: linear-gradient(135deg, #f59e0b, #d97706);
цвет: белый;
padding: 8px 16px;
border-radius: 20px;
font-weight: 600;
display: inline-block;
margin: 10px 0;
}
.roi-badge {
background: linear-gradient(135deg, #10b981, #059669);
цвет: белый;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
margin-left: 10px;
}
.admin-controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
зазор: 15 пикселей;
margin: 20px 0;
}
.admin-table {
ширина: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.admin-table th, .admin-table td {
padding: 12px;
text-align: left;
border-bottom: 1px Solid var(--border);
}
.admin-table th {
фон: var(--bg-dark);
цвет: var(--text-muted);
font-weight: 600;
}
.admin-actions {
display: flex;
зазор: 8 пикселей;
}
.ctr-indicator {
display: inline-flex;
align-items: center;
зазор: 5 пикселей;
padding: 4px 12px;
background: rgba(59, 130, 246, 0.1);
border: 1px solid #3b82f6;
border-radius: 20px;
font-size: 0.9rem;
}
.requests-history {
max-height: 300px;
overflow-y: auto;
margin: 20px 0;
}
.history-item {
padding: 10px;
border-bottom: 1px Solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
/* Вспомогательные функции */
.скрытый {
display: none !important;
}
.text-center {
text-align: center;
}
.mt-20 {
margin-top: 20px;
}
.mt-30 {
margin-top: 30px;
}
.mb-20 {
margin-bottom: 20px;
}
.икона {
ширина: 20 пикселей;
цвет: var(--primary);
}
.тревога {
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
display: none;
}
.alert-success {
background: rgba(16, 185, 129, 0.1);
border: 1px solid var(--secondary);
цвет: var(--secondary);
}
.alert-error {
background: rgba(239, 68, 68, 0.1);
граница: 1 пиксель, сплошная var(--danger);
цвет: var(--danger);
}
.alert-warning {
background: rgba(245, 158, 11, 0.1);
граница: 1 пиксель, сплошная var(--предупреждение);
цвет: var(--warning);
}
/* Модальное окно */
.modal {
display: none;
положение: фиксированное;
верх: 0;
слева: 0;
ширина: 100%;
высота: 100%;
background: rgba(0, 0, 0, 0.8);
z-индекс: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background: var(--bg-card);
border-radius: 20px;
padding: 40px;
max-width: 500px;
ширина: 90%;
граница: сплошная 1 пиксель var(--border);
максимальная высота: 90vh;
overflow-y: auto;
}
.modal-title {
font-size: 1.5rem;
margin-bottom: 20px;
}
.close-modal {
положение: абсолютное;
top: 20px;
справа: 20 пикселей;
фон: отсутствует;
граница: отсутствует;
цвет: var(--text-muted);
font-size: 1.5rem;
курсор: указатель;
}
/* Идентификатор гостя */
.guest-identity {
background: var(--bg-card);
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
граница: сплошная 1 пиксель var(--border);
положение: относительное;
}
.guest-info {
display: flex;
align-items: center;
зазор: 15 пикселей;
}
.guest-avatar {
ширина: 50 пикселей;
высота: 50 пикселей;
background: linear-gradient(135deg, var(--primary), #8b5cf6);
радиус границы: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
font-weight: 700;
}
.guest-actions {
положение: абсолютное;
top: 20px;
справа: 20 пикселей;
display: flex;
зазор: 10 пикселей;
}
/* Адаптивный дизайн */
@media (max-width: 768px) {
.container {
padding: 15px;
}
.guest-form, .auth-section {
padding: 25px;
}
.form-grid {
grid-template-columns: 1fr;
}
.date-inputs {
display: flex;
flex-direction: column;
зазор: 15 пикселей;
}
.date-inputs .input-field {
ширина: 100%;
}
.guests-input-container .input-field {
ширина: 100%;
}
.requests-grid {
grid-template-columns: 1fr;
}
.form-footer, .request-actions {
flex-direction: column;
зазор: 15 пикселей;
}
.action-buttons {
ширина: 100%;
justify-content: center;
}
.action-buttons .btn-small {
min-width: 140px;
ширина: 100%;
max-width: 200px;
}
.tabs {
flex-direction: column;
}
.request-actions {
align-items: stretch;
}
.dashboard-header-actions {
положение: статическое;
margin-top: 20px;
justify-content: flex-end;
}
.guest-actions {
положение: статическое;
margin-top: 15px;
justify-content: flex-end;
}
}
/* Дополнительные исправления для средних экранов */
@media (max-width: 1024px) and (min-width: 769px) {
.form-grid {
grid-template-columns: repeat(2, 1fr);
}
.date-inputs {
grid-column: span 2;
}
.guests-input-container {
grid-column: span 1;
}
.action-buttons {
justify-content: center;
}
.action-buttons .btn-small {
min-width: 110px;
font-size: 0.85rem;
padding: 8px 12px;
}
}
</style>
</head>
<тело>
<div class="container">
<!-- Заголовок -->
<заголовок>
<div class="logo">
<i class="fas fa-crown logo-icon"></i>
<span>Экспресс ТУР</span>
</div>
<div class="mode-switcher">
<button class="mode-btn active" id="guestModeBtn">Гость</button>
<button class="mode-btn" id="ownerModeBtn">Заявки</button>
<button class="mode-btn" id="adminModeBtn" style="display: none;">Админ</button>
</div>
</header>
<!-- Идентификатор гостя -->
<div id="guestIdentity" class="guest-identity">
<div class="guest-info">
<div class="guest-avatar" id="guestAvatar">Г</div>
<div>
<h3 id="guestName">Гость</h3>
<p style="color: var(--text-muted); размер шрифта: 0.9rem;" id="guestContact">Войдите, чтобы сохранить заявки</p>
</div>
</div>
<div class="guest-actions">
<button class="btn btn-small" id="guestLoginBtn" style="display: none;">
<i class="fas fa-sign-in-alt"></i> Войти
</button>
<button class="btn btn-small btn-danger" id="guestLogoutBtn" style="display: none;">
<i class="fas fa-sign-out-alt"></i> Выйти
</button>
</div>
</div>
<!-- Раздел для гостей (виден по умолчанию) -->
<div id="guestSection">
<!-- Форма для гостей -->
<div class="guest-form">
<h2 class="form-title" id="formTitle">Найдите идеальное жилье без поиска</h2>
<p class="form-subtitle" id="formSubtitle">Оставьте заявку — держите сами предложат вам лучшие варианты</p>
<div id="guestAlert" class="alert"></div>
<div class="form-grid">
<div class="input-group">
<label class="input-label">Куда едете?</label>
<input type="text" class="input-field" id="destination" Placeholder="Сочи, Адлер, Геленджик...">
</div>
<div class="input-group date-inputs">
<div style="flex: 1;">
<label class="input-label">Данные отображаются</label>
<input type="date" class="input-field" id="checkin">
</div>
<div style="flex: 1;">
<label class="input-label">Данные выезда</label>
<input type="date" class="input-field" id="checkout">
</div>
</div>
<div class="input-group guests-input-container">
<label class="input-label">Количество гостей</label>
<input type="number" class="input-field" id="guests" min="1" value="2" placeholder="2">
</div>
<div class="input-group">
<label class="input-label">Бюджет в сутки (₽)</label>
<input type="number" class="input-field" id="budget" placeholder="3000" value="3000">
</div>
</div>
<div class="input-group">
<label class="input-label">Особые пожелания</label>
<textarea class="input-field" id="wishes" rows="3" Placeholder="1-я линия, вид на море, парковка, с животными..."></textarea>
</div>
<div class="form-footer">
<div>
<p style="color: var(--text-muted); font-size: 0.9rem;">
<i class="fas fa-shield-alt"></i> Ваши контакты подключены только платным подписчикам
</p>
</div>
<div style="display: flex; gap: 10px;">
<button class="btn btn-secondary" id="cancelEditBtn" style="display: none;">
<i class="fas fa-times"></i> Отмена
</button>
<button class="btn btn-success" id="submitRequestBtn">
<i class="fas fa-paper-plane"></i> Оставить заявку
</button>
</div>
</div>
</div>
<!-- Мои запросы -->
<div class="requests-list">
<div class="section-header">
<h3 class="section-title">Мои заявки</h3>
<div class="stats" id="guestStats">У вас 0 активных заявок</div>
</div>
<div class="requests-grid" id="guestRequestsList">
<div class="text-center" style="color: var(--text-muted); padding: 40px;">
<i class="fas fa-inbox" style="font-size: 3rem; margin-bottom: 15px;"></i>
<p>У вас пока нет активных заявок</p>
</div>
</div>
</div>
</div>
<!-- Раздел владельца -->
<div id="ownerSection" class="owner-dashboard">
<!-- Форма авторизации для владельца -->
<div id="ownerAuth">
<div class="auth-section">
<h2 class="auth-title">Вход для владельцев</h2>
<p class="auth-subtitle">Доступ к базе заявок на проживание</p>
<div id="ownerAuthAlert" class="alert"></div>
<div class="input-group">
<label class="input-label">Электронная почта</label>
<input type="email" class="input-field" id="ownerEmail" placeholder="ваш@email.com">
</div>
<div class="input-group">
<label class="input-label">Пароль</label>
<input type="password" class="input-field" id="ownerPassword" placeholder="••••••••">
</div>
<button class="btn" id="ownerLoginBtn">
<i class="fas fa-sign-in-alt"></i> Войти
</button>
<div class="divider mt-20"><span>или</span></div>
<button class="btn btn-secondary" id="ownerRegisterBtn">
<i class="fas fa-user-plus"></i> Зарегистрироваться
</button>
<p style="text-align: center; margin-top: 20px; color: var(--text-muted); font-size: 0.9rem;">
<i class="fas fa-info-circle"></i> После регистрации вы получаете доступ к заявкам на 7 дней бесплатно
</p>
</div>
</div>
<!-- Панель управления владельца (после входа в систему) -->
<div id="ownerDashboard" class="hidden">
<div class="dashboard-header">
<h2>Панель владельца</h2>
<p style="color: var(--text-muted); Margin-top: 5px;">Управляйте подпиской и просматривайте заявки</p>
<div class="dashboard-header-actions">
<button class="btn btn-small btn-danger" id="ownerLogoutBtn">
<i class="fas fa-sign-out-alt"></i> Выйти
</button>
</div>
<div class="dashboard-stats">
<div class="stat-card">
<div class="owner-stat-value" id="totalRequests">0</div>
<div class="owner-stat-label">Всего заявок</div>
</div>
<div class="stat-card">
<div class="owner-stat-value" id="availableRequests">0</div>
<div class="owner-stat-label">Доступно сегодня</div>
</div>
<div class="stat-card">
<div class="owner-stat-value" id="subscriptionDays">0</div>
<div class="owner-stat-label">Дней подписки</div>
</div>
<div class="stat-card">
<div class="owner-stat-value" id="contactsBought">0</div>
<div class="owner-stat-label">Контакты куплено</div>
</div>
</div>
</div>
<div class="tabs">
<button class="tab active" data-tab="allRequests">Все заявки</button>
<button class="tab" data-tab="filteredRequests">По фильтру</button>
<button class="tab" data-tab="boughtContacts">Купленные контакты</button>
<button class="tab" data-tab="subscription">Подписка</button>
<button class="tab" data-tab="earnings">Заработок</button>
<button class="tab" data-tab="admin" id="adminTabBtn" style="display: none;">Админ</button>
</div>
<!-- Раздел фильтров -->
<div id="filterSection" class="mb-20" style="display: none;">
<div style="background: var(--bg-card); padding: 20px; border-radius: 12px;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<input type="text" class="input-field" id="filterCity" placeholder="Город">
<input type="number" class="input-field" id="filterGuests" placeholder="Гости (мин.)">
<input type="number" class="input-field" id="filterBudget" placeholder="Бюджет (макс.)">
<button class="btn btn-small" id="applyFilterBtn">Применить фильтр</button>
</div>
</div>
</div>
<!-- Содержимое вкладки «Заработок» -->
<div id="earningsTabContent" class="hidden">
<div class="earnings-section">
<h2>Панель заработка</h2>
<p style="color: var(--text-muted);margin-bottom: 20px;">Реферальная система и покупка рекламы для заявок</p>
<div class="referral-link-container">
<h3>Ваша реферальная ссылка</h3>
<div class="link-display">
<input type="text" class="link-input" id="referralLink" readonly value="Загрузка...">
<button class="btn btn-small" onclick="copyReferralLink()">
<i class="fas fa-copy"></i> Копировать
</button>
</div>
<p style="color: var(--text-muted); font-size: 0.9rem;">
Делитесь этой ссылкой с другими владельцами. Вы добавили 49₽ с каждого представителя рекламы по вашей ссылке.
</p>
</div>
<div class="link-stats">
<div class="stat-item-earnings">
<div class="stat-value-earnings" id="refEarnings">0₽</div>
<div class="stat-label-earnings">Заработано всего</div>
</div>
<div class="stat-item-earnings">
<div class="stat-value-earnings" id="refClicks">0</div>
<div class="stat-label-earnings">Переходы по ссылке</div>
</div>
<div class="stat-item-earnings">
<div class="stat-value-earnings" id="refConversions">0</div>
<div class="stat-label-earnings">Куплено рекламы</div>
</div>
<div class="stat-item-earnings">
<div class="stat-value-earnings" id="ctrRate">0%</div>
<div class="stat-label-earnings">CTR (конверсия)</div>
</div>
</div>
<h3>Покупка рекламы для заявок</h3>
<p style="color: var(--text-muted); margin-bottom: 20px;">
Купите рекламу для заявок по 199₽ за штуку. Прибыль с контактом 250₽. Ваша заработка 49₽.
</p>
<div id="adCampaignsList">
<!-- Список заявок на рекламу будет здесь -->
</div>
<div class="ad-campaign">
<h4>Как это работает?</h4>
<p style="color: var(--text-muted); font-size: 0.9rem;">
1. Вы покупаете рекламу по любой заявке за 199₽<br>
2. Когда по этой рекламе покупают контакт за 250₽<br>
3. Вы постоянно получаете 49₽ прибыли с каждой продажи<br>
4. ROI (возврат инвестиций): ~25%<br>
5. Вы можете купить любое количество рекламы.
</p>
</div>
</div>
</div>
<!-- Содержимое вкладки "Администратор" -->
<div id="adminTabContent" class="hidden">
<div class="admin-section">
<h2>Панель администратора</h2>
<p style="color: var(--text-muted);margin-bottom: 20px;">Система управления и статистика</p>
<div class="admin-controls">
<button class="btn btn-small btn-warning" onclick="exportAllData()">
<i class="fas fa-download"></i> Экспорт данных
</button>
<button class="btn btn-small btn-danger" onclick="clearAllData()">
<i class="fas fa-trash"></i> Очистить все данные
</button>
<button class="btn btn-small btn-info" onclick="generateTestData()">
<i class="fas fa-vial"></i> Создать тестовые данные
</button>
</div>
<h3>Статистические системы</h3>
<div class="dashboard-stats">
<div class="stat-card">
<div class="owner-stat-value" id="adminTotalUsers">0</div>
<div class="owner-stat-label">Всех пользователей</div>
</div>
<div class="stat-card">
<div class="owner-stat-value" id="adminTotalEarnings">0₽</div>
<div class="owner-stat-label">Общий оборот</div>
</div>
<div class="stat-card">
<div class="owner-stat-value" id="adminActiveAds">0</div>
<div class="owner-stat-label">Активной рекламы</div>
</div>
<div class="stat-card">
<div class="owner-stat-value" id="adminCTR">0%</div>
<div class="owner-stat-label">Объемный CTR</div>
</div>
</div>
<h3>Управление пользователями</h3>
<div class="requests-history">
<table class="admin-table">
<thead>
<tr>
<th>Электронная почта</th>
<th>Тип</th>
<th>Баланс</th>
<th>Регистрация данных</th>
<th>Действия</th>
</tr>
</thead>
<tbody id="adminUsersList">
<!-- Список пользователей -->
</tbody>
</table>
</div>
<h3>История транзакций</h3>
<div class="requests-history">
<div id="adminTransactionsList">
<!-- История транзакций -->
</div>
</div>
</div>
</div>
<!-- Список запросов для владельца -->
<div class="requests-list" id="ownerRequestsContainer">
<div class="section-header">
<h3 class="section-title" id="requestsListTitle">Всех посетителей</h3>
<div class="stats" id="ownerStats">Показано 0 из 0 заявок</div>
</div>
<div class="requests-grid" id="ownerRequestsList">
<!-- Запросы будут загружены здесь -->
</div>
</div>
</div>
</div>
</div>
<!-- Модальное окно для входа гостя -->
<div class="modal" id="guestLoginModal">
<div class="modal-content">
<button class="close-modal" id="closeGuestLoginModalBtn">×</button>
<h3 class="modal-title">Вход для гостей</h3>
<p style="color: var(--text-muted); margin-bottom: 20px;">
Войдите, чтобы сохранить и сохранить свои заявки.
</p>
<div id="guestLoginAlert" class="alert"></div>
<div class="input-group">
<label class="input-label">Ваше имя</label>
<input type="text" class="input-field" id="guestLoginName" placeholder="Иван">
</div>
<div class="input-group">
<label class="input-label">Телефон или Telegram</label>
<input type="text" class="input-field" id="guestLoginContact" placeholder="+7 999 123-45-67 or @username">
</div>
<button class="btn" id="guestLoginSubmitBtn">
<i class="fas fa-sign-in-alt"></i> Войти
</button>
<p style="text-align: center; margin-top: 20px; color: var(--text-muted); font-size: 0.9rem;">
<i class="fas fa-info-circle"></i> Вы всегда можете просмотреть и сохранить все свои заявки.
</p>
</div>
</div>
<!-- Контактное модальное окно -->
<div class="modal" id="contactModal">
<div class="modal-content">
<button class="close-modal" id="closeModalBtn">×</button>
<h3 class="modal-title">Контактные данные</h3>
<div id="modalContactInfo">
<!-- Контактная информация будет загружена здесь -->
</div>
<div class="mt-20">
<p style="color: var(--text-muted); font-size: 0.9rem;">
<i class="fas fa-exclamation-circle"></i> Свяжитесь с клиентом в течение 24 часов, чтобы повысить шансы на успех
</p>
</div>
<button class="btn mt-20" id="closeContactModalBtn">Закрыть</button>
</div>
</div>
<!-- Модальное окно подписки -->
<div class="modal" id="subscriptionModal">
<div class="modal-content">
<button class="close-modal" id="closeSubModalBtn">×</button>
<h3 class="modal-title">Управление подпиской</h3>
<div style="margin: 30px 0;">
<div style="background: var(--bg-dark); padding: 20px; border-radius: 12px; margin-bottom: 20px;">
<h4 style="margin-bottom: 10px;">Текущий статус</h4>
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<div class="owner-stat-value" id="subStatus">Неактивна</div>
<div class="owner-stat-label" id="subExpiry">Истекает: --</div>
</div>
<div class="price-tag" id="subPlan">БЕСПЛАТНО</div>
</div>
</div>
<div style="background: var(--bg-dark); padding: 20px; border-radius: 12px;">
<h4 style="margin-bottom: 15px;">Доступные планы</h4>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<div style="border: 1px solid var(--border); padding: 20px; border-radius: 10px; text-align: center;">
<h4>1 месяц</h4>
<div class="price-tag" style="margin: 15px 0;">1 500 ₽</div>
<p style="color: var(--text-muted); font-size: 0.9rem;">Доступ ко всем заявкам</p>
<button class="btn btn-small mt-20" data-plan="month">Выбрать</button>
</div>
<div style="border: 1px solid var(--border); padding: 20px; border-radius: 10px; text-align: center;">
<h4>3 месяца</h4>
<div class="price-tag" style="margin: 15px 0;">3 500 ₽</div>
<p style="color: var(--text-muted); font-size: 0.9rem;">Экономия 1 000 ₽</p>
<button class="btn btn-small mt-20" data-plan="quarter">Выбрать</button>
</div>
<div style="border: 1px solid var(--primary); padding: 20px; border-radius: 10px; text-align: center; position: relative;">
<div style="position: absolute; top: -10px; left: 50%; transform: translateX(-50%); background: var(--primary); color: white; padding: 3px 10px; border-radius: 20px; font-size: 0.8rem;">ВЫГОДА</div>
<h4>12 месяцев</h4>
<div class="price-tag" style="margin: 15px 0;">10 000 ₽</div>
<p style="color: var(--text-muted); font-size: 0.9rem;">Экономия 8 000 ₽</p>
<button class="btn btn-small mt-20" data-plan="year">Выбрать</button>
</div>
</div>
</div>
</div>
<button class="btn btn-secondary" id="closeSubscriptionModalBtn">Закрыть</button>
</div>
</div>
<!-- Модальное окно Boost -->
<div class="modal" id="boostModal">
<div class="modal-content">
<button class="close-modal" id="closeBoostModalBtn">×</button>
<h3 class="modal-title">Поднять заявку</h3>
<div style="text-align: center; margin: 30px 0;">
<i class="fas fa-arrow-up" style="font-size: 3rem; color: var(--gold); margin-bottom: 20px;"></i>
<h4 style="margin-bottom: 10px;">Заявка станет выделенной на 7 дней</h4>
<p style="color: var(--text-muted); margin-bottom: 20px;">
Ваша заявка будет вверху списка владельцев, будет выбрана золотая рамка и вы получите больше просмотров.
</p>
<div style="background: var(--bg-dark); padding: 20px; border-radius: 12px; margin: 20px 0;">
<div class="owner-stat-value" style="color: var(--gold);">100 ₽</div>
<div class="owner-stat-label">за 7 дней продвижения</div>
</div>
</div>
<button class="btn btn-gold" id="confirmBoostBtn">
<i class="fas fa-arrow-up"></i> Поднять заявку за 100₽
</button>
<button class="btn btn-secondary mt-20" id="cancelBoostBtn">Отмена</button>
</div>
</div>
<!-- Модальное окно покупки рекламы -->
<div class="modal" id="buyAdModal">
<div class="modal-content">
<button class="close-modal" id="closeAdModalBtn">×</button>
<h3 class="modal-title">Покупка рекламы</h3>
<div style="text-align: center; margin: 20px 0;">
<i class="fas fa-bullhorn" style="font-size: 3rem; color: var(--warning); margin-bottom: 15px;"></i>
<h4>Реклама заявок</h4>
</div>
<div id="adModalContent">
<!-- Контент будет настроен в движении -->
</div>
<div style="background: var(--bg-dark); padding: 20px; border-radius: 12px; margin: 20px 0;">
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<span>Стоимость рекламы:</span>
<span class="price-tag">199 ₽</span>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<span>Прибыль с контактом:</span>
<span style="color: var(--secondary); font-weight: 600;">250 ₽</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>Ваша заработок:</span>
<span class="roi-badge">+49 ₽</span>
</div>
</div>
<div class="input-group">
<label class="input-label">Количество рекламы (шт.)</label>
<input type="number" class="input-field" id="adQuantity" min="1" value="1" max="100">
<small style="color: var(--text-muted); display: block; Margin-top: 5px;">Вы можете купить любое количество рекламы</small>
</div>
<button class="btn btn-warning" id="confirmAdPurchaseBtn">
<i class="fas fa-shopping-cart"></i> Купить рекламу
</button>
<button class="btn btn-вторичный мт-20" id="cancelAdPurchaseBtn">Отмена</button>
</div>
</div>
<script>
// Инициализация данных
let requests = JSON.parse(localStorage.getItem('stayfinder_requests')) || [];
let owners = JSON.parse(localStorage.getItem('stayfinder_owners')) || [];
let currentOwner = JSON.parse(localStorage.getItem('stayfinder_current_owner')) || null;
let boughtContacts = JSON.parse(localStorage.getItem('stayfinder_bought_contacts')) || [];
let currentGuest = JSON.parse(localStorage.getItem('stayfinder_current_guest')) || null;
let guestRequests = JSON.parse(localStorage.getItem('stayfinder_guest_requests')) || [];
let adCampaigns = JSON.parse(localStorage.getItem('stayfinder_ad_campaigns')) || [];
let referrals = JSON.parse(localStorage.getItem('stayfinder_referrals')) || [];
let transactions = JSON.parse(localStorage.getItem('stayfinder_transactions')) || [];
// Элементы DOM
const guestModeBtn = document.getElementById('guestModeBtn');
const ownerModeBtn = document.getElementById('ownerModeBtn');
const adminModeBtn = document.getElementById('adminModeBtn');
const guestSection = document.getElementById('guestSection');
const ownerSection = document.getElementById('ownerSection');
const ownerAuth = document.getElementById('ownerAuth');
const ownerDashboard = document.getElementById('ownerDashboard');
const guestIdentity = document.getElementById('guestIdentity');
const guestAvatar = document.getElementById('guestAvatar');
const guestName = document.getElementById('guestName');
const guestContact = document.getElementById('guestContact');
const guestLoginBtn = document.getElementById('guestLoginBtn');
const guestLogoutBtn = document.getElementById('guestLogoutBtn');
const guestLoginModal = document.getElementById('guestLoginModal');
const boostModal = document.getElementById('boostModal');
const buyAdModal = document.getElementById('buyAdModal');
const ownerLogoutBtn = document.getElementById('ownerLogoutBtn');
// Функция для создания администратора, если он еще не существует
function createAdminIfNotExists() {
const adminExists = owners.some(owner => owner.email === 'admin@admin.com');
если (!adminExists) {
const admin = {
id: '_admin_' + Math.random().toString(36).substr(2, 9),
email: 'admin@admin.com',
пароль: 'admin123',
имя: 'Администратор',
подписка: {
активный: true,
план: 'ежегодно',
истечение срока действия: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString()
},
createdAt: new Date().toISOString(),
Количество купленных контактов: 0.
refCode: 'ref_admin_' + Math.random().toString(36).substr(2, 8),
isAdmin: true
};
owners.push(admin);
saveData();
console.log('Администратор создан: admin@admin.com / admin123');
}
}
// Инициализация приложения
document.addEventListener('DOMContentLoaded', function() {
// Создать администратора, если он еще не существует
createAdminIfNotExists();
// Установить даты по умолчанию
const today = new Date().toISOString().split('T')[0];
const nextWeek = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
document.getElementById('checkin').value = today;
document.getElementById('checkout').value = nextWeek;
// Проверяем, вошел ли гость в систему
if (currentGuest) {
updateGuestUI();
loadGuestRequests();
guestLogoutBtn.style.display = 'block';
guestLoginBtn.style.display = 'none';
} еще {
// Показать кнопку входа
guestLoginBtn.style.display = 'block';
guestLogoutBtn.style.display = 'none';
}
// Проверяем, вошел ли владелец уже в систему
if (currentOwner) {
ownerAuth.style.display = 'none';
ownerDashboard.classList.remove('hidden');
loadOwnerDashboard();
loadAllRequests();
// Проверить, является ли администратор
if (currentOwner.email === 'admin@admin.com') {
adminModeBtn.style.display = 'block';
document.getElementById('adminTabBtn').style.display = 'block';
}
}
// Добавить пример данных, если пусто
if (requests.length === 0) {
addSampleRequests();
}
// Загрузить запросы от гостей, если они существуют
if (currentGuest) {
loadGuestRequests();
}
// Проверьте реферальную ссылку
setTimeout(checkReferralLink, 1000);
});
// Переключение между режимами гостя, владельца и администратора
guestModeBtn.addEventListener('click', function() {
guestModeBtn.classList.add('active');
ownerModeBtn.classList.remove('active');
adminModeBtn.classList.remove('active');
guestSection.style.display = 'block';
ownerSection.style.display = 'none';
guestIdentity.style.display = 'block';
});
ownerModeBtn.addEventListener('click', function() {
ownerModeBtn.classList.add('active');
guestModeBtn.classList.remove('active');
adminModeBtn.classList.remove('active');
guestSection.style.display = 'none';
ownerSection.style.display = 'block';
guestIdentity.style.display = 'none';
});
adminModeBtn.addEventListener('click', function() {
adminModeBtn.classList.add('active');
guestModeBtn.classList.remove('active');
ownerModeBtn.classList.remove('active');
guestSection.style.display = 'none';
ownerSection.style.display = 'block';
guestIdentity.style.display = 'none';
// Принудительное отображение панели управления владельца, если пользователь не авторизован
if (!currentOwner) {
document.getElementById('ownerEmail').value = 'admin@admin.com';
document.getElementById('ownerPassword').value = 'admin123';
document.getElementById('ownerLoginBtn').click();
} еще {
ownerAuth.style.display = 'none';
ownerDashboard.classList.remove('hidden');
// Переключиться на вкладку "Администратор"
document.querySelector('[data-tab="admin"]').click();
}
});
// Гость: Открыть модальное окно входа
guestLoginBtn.addEventListener('click', function() {
guestLoginModal.style.display = 'flex';
});
// Гость: Выход
guestLogoutBtn.addEventListener('click', function() {
localStorage.removeItem('stayfinder_current_guest');
currentGuest = null;
updateGuestUI();
loadGuestRequests();
guestLogoutBtn.style.display = 'none';
guestLoginBtn.style.display = 'block';
showAlert('guestAlert', 'Вы вышли из системы', 'success');
});
// Владелец: Выход
ownerLogoutBtn.addEventListener('click', function() {
localStorage.removeItem('stayfinder_current_owner');
currentOwner = null;
ownerAuth.style.display = 'block';
ownerDashboard.classList.add('hidden');
showAlert('ownerAuthAlert', 'Вы вышли из системы', 'успех');
});
// Гость: Отправить логин
document.getElementById('guestLoginSubmitBtn').addEventListener('click', function() {
const name = document.getElementById('guestLoginName').value.trim();
const contact = document.getElementById('guestLoginContact').value.trim();
if (!name || !contact) {
showAlert('guestLoginAlert', 'Пожалуйста, заполните все поля', 'ошибка');
возвращаться;
}
currentGuest = {
id: generateId(),
имя,
контакт,
createdAt: new Date().toISOString(),
запросы: []
};
localStorage.setItem('stayfinder_current_guest', JSON.stringify(currentGuest));
// Обновить пользовательский интерфейс
updateGuestUI();
// Закрыть модальное окно
guestLoginModal.style.display = 'none';
// Загрузка запросов гостей
loadGuestRequests();
// Показать кнопку выхода
guestLogoutBtn.style.display = 'block';
guestLoginBtn.style.display = 'none';
showAlert('guestAlert', `Добро пожаловать, ${name}!`, 'успех');
});
// Гость: Отправить запрос
document.getElementById('submitRequestBtn').addEventListener('click', function() {
// Проверяем, вошел ли гость в систему
if (!currentGuest) {
showAlert('guestAlert', 'Пожалуйста, войдите, чтобы сохранить заявки', 'ошибка');
guestLoginModal.style.display = 'flex';
возвращаться;
}
const destination = document.getElementById('destination').value.trim();
const checkin = document.getElementById('checkin').value;
const checkout = document.getElementById('checkout').value;
const guests = parseInt(document.getElementById('guests').value);
const budget = parseInt(document.getElementById('budget').value);
const wishes = document.getElementById('wishes').value.trim();
if (!destination || !checkin || !checkout || !guests || !budget) {
showAlert('guestAlert', 'Пожалуйста, заполните все обязательные поля', 'ошибка');
возвращаться;
}
if (new Date(checkout) <= new Date(checkin)) {
showAlert('guestAlert', 'Дата выезда должна быть позже даты встречи', 'error');
возвращаться;
}
// Проверить, редактируется ли существующий запрос
const editingRequestId = this.dataset.editing;
if (editingRequestId) {
// Обновить существующий запрос
const requestIndex = requests.findIndex(r => r.id === editingRequestId);
if (requestIndex !== -1) {
requests[requestIndex] = {
...requests[requestIndex],
место назначения,
регистрироваться,
проверить,
гости,
бюджет,
пожелания,
updatedAt: new Date().toISOString()
};
saveData();
showAlert('guestAlert', 'Заявка успешно обновлена!', 'успех');
// Сбросить форму
resetForm();
// Обновить пользовательский интерфейс
loadGuestRequests();
}
} еще {
// Создать новый запрос
const newRequest = {
id: generateId(),
место назначения,
регистрироваться,
проверить,
гости,
бюджет,
пожелания,
contactName: currentGuest.name,
contactPhone: currentGuest.contact,
guestId: currentGuest.id,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
статус: 'активный',
просмотров: 0,
бонусов: 0,
boostExpiry: null,
featured: false
};
requests.push(newRequest);
saveData();
// Добавить к запросам гостя
if (!currentGuest.requests) {
currentGuest.requests = [];
}
currentGuest.requests.push(newRequest.id);
localStorage.setItem('stayfinder_current_guest', JSON.stringify(currentGuest));
// Очистить форму
resetForm();
showAlert('guestAlert', 'Заявка успешно создана! Владельцы жилья связываются с вами', 'success');
loadGuestRequests();
}
});
// Отменить редактирование
document.getElementById('cancelEditBtn').addEventListener('click', function() {
resetForm();
});
// Владелец: Вход
document.getElementById('ownerLoginBtn').addEventListener('click', function() {
const email = document.getElementById('ownerEmail').value.trim();
const password = document.getElementById('ownerPassword').value.trim();
if (!email || !password) {
showAlert('ownerAuthAlert', 'Введите адрес электронной почты и пароль', 'ошибка');
возвращаться;
}
const owner = owners.find(o => o.email === email && o.password === password);
если (!владелец) {
showAlert('ownerAuthAlert', 'Неверный адрес электронной почты или пароль', 'ошибка');
возвращаться;
}
currentOwner = owner;
localStorage.setItem('stayfinder_current_owner', JSON.stringify(currentOwner));
ownerAuth.style.display = 'none';
ownerDashboard.classList.remove('hidden');
// Проверить, является ли администратор
if (currentOwner.email === 'admin@admin.com') {
adminModeBtn.style.display = 'block';
document.getElementById('adminTabBtn').style.display = 'block';
}
loadOwnerDashboard();
loadAllRequests();
});
// Владелец: Регистрация
document.getElementById('ownerRegisterBtn').addEventListener('click', function() {
const email = document.getElementById('ownerEmail').value.trim();
const password = document.getElementById('ownerPassword').value.trim();
if (!email || !password) {
showAlert('ownerAuthAlert', 'Введите адрес электронной почты и пароль', 'ошибка');
возвращаться;
}
if (owners.find(o => o.email === email)) {
showAlert('ownerAuthAlert', 'Пользователь с таким электронным письмом уже существует', 'ошибка');
возвращаться;
}
const newOwner = {
id: generateId(),
электронная почта,
пароль,
имя: email.split('@')[0],
подписка: {
активный: true,
план: 'испытание',
истечение срока действия: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
},
createdAt: new Date().toISOString(),
Количество купленных контактов: 0.
refCode: 'ref_' + Math.random().toString(36).substr(2, 8)
};
owners.push(newOwner);
currentOwner = newOwner;
saveData();
localStorage.setItem('stayfinder_current_owner', JSON.stringify(currentOwner));
showAlert('ownerAuthAlert', 'Регистрация успешна! Вам доступен 7-дневный пробный период', 'success');
setTimeout(() => {
ownerAuth.style.display = 'none';
ownerDashboard.classList.remove('hidden');
loadOwnerDashboard();
loadAllRequests();
// Проверить конверсию реферального перехода
checkReferralConversion();
}, 1500);
});
// Вкладки для панели управления владельца
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', function() {
// Обновить активную вкладку
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
this.classList.add('active');
const tabId = this.getAttribute('data-tab');
// Скрыть все содержимое вкладок
document.getElementById('filterSection').style.display = 'none';
document.getElementById('earningsTabContent').classList.add('hidden');
document.getElementById('adminTabContent').classList.add('hidden');
document.getElementById('ownerRequestsContainer').classList.add('hidden');
// Показать/скрыть раздел фильтров
switch(tabId) {
case 'allRequests':
document.getElementById('ownerRequestsContainer').classList.remove('hidden');
loadAllRequests();
document.getElementById('requestsListTitle').textContent = 'Все заявки';
перерыв;
case 'filteredRequests':
document.getElementById('filterSection').style.display = 'block';
document.getElementById('ownerRequestsContainer').classList.remove('hidden');
applyFilter();
document.getElementById('requestsListTitle').textContent = 'Отфильтрованные заявки';
перерыв;
case 'boughtContacts':
document.getElementById('ownerRequestsContainer').classList.remove('hidden');
loadBoughtContacts();
document.getElementById('requestsListTitle').textContent = 'Купленные контакты';
перерыв;
случай 'подписка':
openSubscriptionModal();
перерыв;
case 'earnings':
document.getElementById('earningsTabContent').classList.remove('hidden');
loadEarningsData();
перерыв;
случай 'admin':
if (currentOwner && currentOwner.email === 'admin@admin.com') {
document.getElementById('adminTabContent').classList.remove('hidden');
loadAdminPanel();
} еще {
alert('Доступ запрещен');
document.querySelector('[data-tab="allRequests"]').click();
}
перерыв;
}
});
});
// Применить фильтр
document.getElementById('applyFilterBtn').addEventListener('click', applyFilter);
// Кнопки закрытия модального окна
document.getElementById('closeModalBtn').addEventListener('click', closeContactModal);
document.getElementById('closeContactModalBtn').addEventListener('click', closeContactModal);
document.getElementById('closeSubModalBtn').addEventListener('click', closeSubscriptionModal);
document.getElementById('closeSubscriptionModalBtn').addEventListener('click', closeSubscriptionModal);
document.getElementById('closeGuestLoginModalBtn').addEventListener('click', closeGuestLoginModal);
document.getElementById('closeBoostModalBtn').addEventListener('click', closeBoostModal);
document.getElementById('cancelBoostBtn').addEventListener('click', closeBoostModal);
document.getElementById('closeAdModalBtn').addEventListener('click', closeAdModal);
document.getElementById('cancelAdPurchaseBtn').addEventListener('click', closeAdModal);
// Подтвердить ускорение
document.getElementById('confirmBoostBtn').addEventListener('click', function() {
const requestId = this.dataset.requestId;
boostRequest(requestId);
});
// Подтвердите покупку рекламы
document.getElementById('confirmAdPurchaseBtn').addEventListener('click', function() {
const requestId = this.dataset.requestId;
const quantity = parseInt(document.getElementById('adQuantity').value) || 1;
purchaseAd(requestId, quantity);
});
// Закрытие модальных окон при щелчке вне модального окна
window.addEventListener('click', function(event) {
const contactModal = document.getElementById('contactModal');
const subscriptionModal = document.getElementById('subscriptionModal');
const guestLoginModal = document.getElementById('guestLoginModal');
const boostModal = document.getElementById('boostModal');
const adModal = document.getElementById('buyAdModal');
if (event.target === contactModal) closeContactModal();
if (event.target === subscriptionModal) closeSubscriptionModal();
if (event.target === guestLoginModal) closeGuestLoginModal();
if (event.target === boostModal) closeBoostModal();
if (event.target === adModal) closeAdModal();
});
// Вспомогательные функции
function generateId() {
return '_' + Math.random().toString(36).substr(2, 9);
}
функция saveData() {
localStorage.setItem('stayfinder_requests', JSON.stringify(requests));
localStorage.setItem('stayfinder_owners', JSON.stringify(owners));
localStorage.setItem('stayfinder_bought_contacts', JSON.stringify(boughtContacts));
localStorage.setItem('stayfinder_ad_campaigns', JSON.stringify(adCampaigns));
localStorage.setItem('stayfinder_referrals', JSON.stringify(referrals));
localStorage.setItem('stayfinder_transactions', JSON.stringify(transactions));
}
function showAlert(elementId, message, type) {
const alertEl = document.getElementById(elementId);
alertEl.textContent = сообщение;
alertEl.className = `alert alert-${type}`;
alertEl.style.display = 'block';
setTimeout(() => {
alertEl.style.display = 'none';
}, 5000);
}
function updateGuestUI() {
if (currentGuest) {
guestAvatar.textContent = currentGuest.name.charAt(0).toUpperCase();
GuestName.textContent = currentGuest.name;
GuestContact.textContent = currentGuest.contact;
guestLoginBtn.style.display = 'none';
guestLogoutBtn.style.display = 'block';
} еще {
GuestAvatar.textContent = 'Г';
GuestName.textContent = 'Гость';
GuestContact.textContent = 'Войдите, чтобы сохранить заявки';
guestLoginBtn.style.display = 'block';
guestLogoutBtn.style.display = 'none';
}
}
function loadGuestRequests() {
const guestRequestsList = document.getElementById('guestRequestsList');
const guestStats = document.getElementById('guestStats');
if (!currentGuest) {
GuestRequestsList.innerHTML = `
<div class="text-center" style="color: var(--text-muted); padding: 40px;">
<i class="fas fa-user" style="font-size: 3rem; margin-bottom: 15px;"></i>
<p>Войдите, чтобы увидеть свои заявки</p>
<button class="btn btn-small mt-20" onclick="document.getElementById('guestLoginModal').style.display='flex'">
<i class="fas fa-sign-in-alt"></i> Войти
</button>
</div>
`;
GuestStats.textContent = `Войдите, чтобы увидеть заявки`;
возвращаться;
}
// Фильтрация запросов по guestId
const userRequests = requests.filter(r => r.guestId === currentGuest.id);
if (userRequests.length === 0) {
GuestRequestsList.innerHTML = `
<div class="text-center" style="color: var(--text-muted); padding: 40px;">
<i class="fas fa-inbox" style="font-size: 3rem; margin-bottom: 15px;"></i>
<p>У вас пока нет активных занятий</p>
</div>
`;
GuestStats.textContent = `У вас 0 активных заявок`;
возвращаться;
}
const activeRequests = userRequests.filter(r => r.status === 'active');
GuestStats.textContent = `У вас ${activeRequests.length} активных заявок`;
guestRequestsList.innerHTML = userRequests.map(request => {
const isFeatured = request.featured && new Date(request.boostExpiry) > new Date();
возврат `
<div class="request-card ${isFeatured ? 'featured' : ''}">
${isFeatured ? `<div class="featured-badge"><i class="fas fa-crown"></i> Продвигается</div>` : ''}
<div class="request-header">
<span class="request-id">Заявка #${request.id.substring(0, 8)}</span>
<span class="request-date">${formatDate(request.createdAt)}</span>
</div>
<div class="request-location">
<i class="fas fa-map-marker-alt icon"></i>
<span>${request.destination}</span>
</div>
<div class="request-dates">
<i class="far fa-calendar icon"></i>
<span>${formatDate(request.checkin)} - ${formatDate(request.checkout)}</span>
</div>
<div class="request-guests">
<i class="fas fa-user-friends icon"></i>
<span>${request.guests} концентрация</span>
</div>
<div class="request-budget">
<i class="fas fa-wallet icon"></i>
<span>До ${request.budget} ₽/сутки</span>
</div>
<div class="request-stats">
<div class="stat-item">
<div class="stat-value">${request.views}</div>
<div class="stat-label">Просмотры</div>
</div>
<div class="stat-item">
<div class="stat-value">${request.boosts}</div>
<div class="stat-label">Поднятий</div>
</div>
<div class="stat-item">
<div class="stat-value">${getDaysLeft(request.boostExpiry)}</div>
<div class="stat-label">Дней продвижения</div>
</div>
</div>
${request.wishes ? `<p style="margin-top: 10px;"><strong>Пожелания:</strong> ${request.wishes}</p>` : ''}
<div class="request-actions">
<span style="color: ${request.status === 'active' ? 'var(--secondary)' : 'var(--text-muted)'}">
<i class="fas fa-circle" style="font-size: 0.7rem;"></i> ${request.status === 'active' ? 'Активна' : 'Закрыта'}
</span>
<div class="action-buttons">
${request.status === 'active' ? `
<button class="btn btn-small btn-warning" onclick="editRequest('${request.id}')">
<i class="fas fa-edit"></i> Редактировать
</button>
<button class="btn btn-small btn-gold" onclick="openBoostModal('${request.id}')">
<i class="fas fa-arrow-up"></i> Поднять
</button>
<button class="btn btn-small" onclick="closeRequest('${request.id}')">
<i class="fas fa-times"></i> Закрыть
</button>
` : `
<button class="btn btn-small btn-secondary" onclick="reopenRequest('${request.id}')">
<i class="fas fa-redo"></i> Открыть
</button>
`}
</div>
</div>
</div>
`;
}).присоединиться('');
}
function loadAllRequests() {
const ownerRequestsList = document.getElementById('ownerRequestsList');
const ownerStats = document.getElementById('ownerStats');
// Отфильтровать только активные запросы и отсортировать по приоритету (рекомендуемые)
let activeRequests = requests.filter(r => r.status === 'active');
// Сортировка: сначала избранные, затем по дате создания
activeRequests.sort((a, b) => {
const aFeatured = a.featured && new Date(a.boostExpiry) > new Date();
const bFeatured = b.featured && new Date(b.boostExpiry) > new Date();
if (aFeatured && !bFeatured) return -1;
if (!aFeatured && bFeatured) return 1;
return new Date(b.createdAt) - new Date(a.createdAt);
});
if (activeRequests.length === 0) {
OwnerRequestsList.innerHTML = `
<div class="text-center" style="color: var(--text-muted); padding: 40px;">
<i class="fas fa-inbox" style="font-size: 3rem; margin-bottom: 15px;"></i>
<p>Нет активные заявки</p>
</div>
`;
OwnerStats.textContent = `Показано 0 из 0 заявок`;
возвращаться;
}
OwnerStats.textContent = `Показано ${activeRequests.length} из ${requests.length} заявок`;
ownerRequestsList.innerHTML = activeRequests.map(request => {
const isBought = boughtContacts.some(bc =>
bc.requestId === request.id && bc.ownerId === currentOwner.id
);
const isFeatured = request.featured && new Date(request.boostExpiry) > new Date();
возврат `
<div class="request-card ${isFeatured ? 'featured' : ''}">
${isFeatured ? `<div class="featured-badge"><i class="fas fa-crown"></i> ПРОДВИЖЕНИЕ</div>` : ''}
<div class="request-header">
<span class="request-id">Заявка #${request.id.substring(0, 8)}</span>
<span class="request-date">${formatDate(request.createdAt)}</span>
</div>
<div class="request-location">
<i class="fas fa-map-marker-alt icon"></i>
<span>${request.destination}</span>
</div>
<div class="request-dates">
<i class="far fa-calendar icon"></i>
<span>${formatDate(request.checkin)} - ${formatDate(request.checkout)}</span>
</div>
<div class="request-guests">
<i class="fas fa-user-friends icon"></i>
<span>${request.guests} концентрация</span>
</div>
<div class="request-budget">
<i class="fas fa-wallet icon"></i>
<span class="price-tag">До ${request.budget} ₽/сутки</span>
</div>
<div class="request-stats">
<div class="stat-item">
<div class="stat-value">${request.views}</div>
<div class="stat-label">Просмотры</div>
</div>
<div class="stat-item">
<div class="stat-value">${request.boosts}</div>
<div class="stat-label">Поднятий</div>
</div>
${isFeatured ? `
<div class="stat-item">
<div class="stat-value" style="color: var(--gold);">
<i class="fas fa-crown"></i>
</div>
<div class="stat-label">Выделена</div>
</div>` : ''}
</div>
${request.wishes ? `<p style="margin-top: 10px; color: var(--text-muted);"><i class="far fa-comment"></i> ${request.wishes}</p>` : ''}
<div class="request-actions">
<span style="color: var(--text-muted); font-size: 0.9rem;">
${daysAgo(request.createdAt)}
</span>
${isBought ?
`<button class="btn btn-small btn-success" onclick="showContact('${request.id}')">
<i class="fas fa-eye"></i> Контакт
</button>` :
`<button class="btn btn-small" onclick="buyContact('${request.id}', ${request.budget})">
<i class="fas fa-shopping-cart"></i> Купить контакт
</button>`
}
</div>
</div>
`;
}).присоединиться('');
}
function loadBoughtContacts() {
const ownerRequestsList = document.getElementById('ownerRequestsList');
const ownerStats = document.getElementById('ownerStats');
const myBoughtContacts = boughtContacts.filter(bc => bc.ownerId === currentOwner.id);
if (myBoughtContacts.length === 0) {
OwnerRequestsList.innerHTML = `
<div class="text-center" style="color: var(--text-muted); padding: 40px;">
<i class="fas fa-shopping-cart" style="font-size: 3rem; margin-bottom: 15px;"></i>
<p>Вы еще не купили ни одного контакта</p>
</div>
`;
OwnerStats.textContent = `Показано 0 контактов`;
возвращаться;
}
const boughtRequests = myBoughtContacts.map(bc => {
const request = requests.find(r => r.id === bc.requestId);
return {...request, boughtDate: bc.date};
}).filter(r => r);
OwnerStats.textContent = `Показано ${boughtRequests.length} контактов`;
ownerRequestsList.innerHTML = boughtRequests.map(request => `
<div class="request-card">
<div class="request-header">
<span class="request-id">Куплен ${formatDate(request.boughtDate)}</span>
<span class="request-date">${formatDate(request.createdAt)}</span>
</div>
<div class="request-location">
<i class="fas fa-map-marker-alt icon"></i>
<span>${request.destination}</span>
</div>
<div class="request-dates">
<i class="far fa-calendar icon"></i>
<span>${formatDate(request.checkin)} - ${formatDate(request.checkout)}</span>
</div>
<div class="request-budget">
<i class="fas fa-wallet icon"></i>
<span>До ${request.budget} ₽/сутки</span>
</div>
<div class="contact-info show">
<div class="contact-item">
<i class="fas fa-user"></i>
<span><strong>Имя:</strong> ${request.contactName}</span>
</div>
<div class="contact-item">
<i class="fas fa-phone"></i>
<span><strong>Контакты:</strong> ${request.contactPhone</span>
</div>
</div>
<div class="request-actions">
<span style="color: var(--text-muted); font-size: 0.9rem;">
Куплен ${daysAgo(request.boughtDate)}
</span>
<button class="btn btn-small" onclick="showContact('${request.id}')">
<i class="fas fa-eye"></i> Показать
</button>
</div>
</div>
`).join('');
}
function applyFilter() {
const city = document.getElementById('filterCity').value.toLowerCase().trim();
const guests = parseInt(document.getElementById('filterGuests').value) || 0;
const budget = parseInt(document.getElementById('filterBudget').value) || Infinity;
let filteredRequests = requests.filter(request => {
if (city && !request.destination.toLowerCase().includes(city)) return false;
if (guests && request.guests < guests) return false;
if (budget && request.budget > budget) return false;
return request.status === 'active';
});
// Сортировка: сначала избранные
filteredRequests.sort((a, b) => {
const aFeatured = a.featured && new Date(a.boostExpiry) > new Date();
const bFeatured = b.featured && new Date(b.boostExpiry) > new Date();
if (aFeatured && !bFeatured) return -1;
if (!aFeatured && bFeatured) return 1;
return new Date(b.createdAt) - new Date(a.createdAt);
});
const ownerRequestsList = document.getElementById('ownerRequestsList');
const ownerStats = document.getElementById('ownerStats');
if (filteredRequests.length === 0) {
OwnerRequestsList.innerHTML = `
<div class="text-center" style="color: var(--text-muted); padding: 40px;">
<i class="fas fa-search" style="font-size: 3rem; margin-bottom: 15px;"></i>
<p>По вашему фильтру ничего не найдено</p>
</div>
`;
OwnerStats.textContent = `Найдено 0 заявок`;
возвращаться;
}
OwnerStats.textContent = `Найдены заявки ${filteredRequests.length}`;
ownerRequestsList.innerHTML = filteredRequests.map(request => {
const isBought = boughtContacts.some(bc =>
bc.requestId === request.id && bc.ownerId === currentOwner.id
);
const isFeatured = request.featured && new Date(request.boostExpiry) > new Date();
возврат `
<div class="request-card ${isFeatured ? 'featured' : ''}">
${isFeatured ? `<div class="featured-badge"><i class="fas fa-crown"></i> ПРОДВИЖЕНИЕ</div>` : ''}
<div class="request-header">
<span class="request-id">Заявка #${request.id.substring(0, 8)}</span>
<span class="request-date">${formatDate(request.createdAt)}</span>
</div>
<div class="request-location">
<i class="fas fa-map-marker-alt icon"></i>
<span>${request.destination}</span>
</div>
<div class="request-dates">
<i class="far fa-calendar icon"></i>
<span>${formatDate(request.checkin)} - ${formatDate(request.checkout)}</span>
</div>
<div class="request-guests">
<i class="fas fa-user-friends icon"></i>
<span>${request.guests} концентрация</span>
</div>
<div class="request-budget">
<i class="fas fa-wallet icon"></i>
<span class="price-tag">До ${request.budget} ₽/сутки</span>
</div>
${request.wishes ? `<p style="margin-top: 10px; color: var(--text-muted);"><i class="far fa-comment"></i> ${request.wishes}</p>` : ''}
<div class="request-actions">
<span style="color: var(--text-muted); font-size: 0.9rem;">
${daysAgo(request.createdAt)}
</span>
${isBought ?
`<button class="btn btn-small btn-success" onclick="showContact('${request.id}')">
<i class="fas fa-eye"></i> Контакт
</button>` :
`<button class="btn btn-small" onclick="buyContact('${request.id}', ${request.budget})">
<i class="fas fa-shopping-cart"></i> Купить контакт
</button>`
}
</div>
</div>
`;
}).присоединиться('');
}
function loadOwnerDashboard() {
if (!currentOwner) return;
// Обновить статистику
const activeRequests = requests.filter(r => r.status === 'active').length;
const myBoughtContacts = boughtContacts.filter(bc => bc.ownerId === currentOwner.id).length;
// Рассчитать оставшиеся дни подписки
let subDays = 0;
if (currentOwner.subscription && currentOwner.subscription.active) {
const expiry = new Date(currentOwner.subscription.expiry);
const today = new Date();
subDays = Math.max(0, Math.ceil((expiry - today) / (1000 * 60 * 60 * 24)));
}
document.getElementById('totalRequests').textContent = requests.length;
document.getElementById('availableRequests').textContent = activeRequests;
document.getElementById('subscriptionDays').textContent = subDays;
document.getElementById('contactsBought').textContent = myBoughtContacts;
}
function openSubscriptionModal() {
const modal = document.getElementById('subscriptionModal');
modal.style.display = 'flex';
if (currentOwner && currentOwner.subscription) {
const sub = currentOwner.subscription;
document.getElementById('subStatus').textContent = sub.active ? 'Активна': 'Неактивна';
document.getElementById('subExpiry').textContent = `Истекает: ${formatDate(sub.expiry)}`;
document.getElementById('subPlan').textContent = sub.plan === 'пробная' ? 'БЕСПЛАТНЫЙ ТРИАЛ' : sub.plan.toUpperCase();
}
// Добавить обработчики событий к кнопкам планирования
document.querySelectorAll('[data-plan]').forEach(btn => {
btn.onclick = function() {
const plan = this.getAttribute('data-plan');
купить подписку(план);
};
});
}
// Функции получения прибыли
function loadEarningsData() {
if (!currentOwner) return;
// Сгенерировать реферальную ссылку
if (!currentOwner.refCode) {
currentOwner.refCode = 'ref_' + Math.random().toString(36).substr(2, 8);
saveData();
}
const refLink = window.location.origin + window.location.pathname + '?ref=' + currentOwner.refCode;
document.getElementById('referralLink').value = refLink;
// Вычисление статистики
const myReferrals = referrals.filter(r => r.referrerId === currentOwner.id);
const myAds = adCampaigns.filter(ad => ad.ownerId === currentOwner.id);
const myTransactions = transactions.filter(t => t.ownerId === currentOwner.id && t.type === 'ref_earnings');
// Общая статистика
const totalEarnings = myTransactions.reduce((sum, t) => sum + t.amount, 0);
const totalClicks = myReferrals.length;
const totalConversions = myAds.filter(ad => ad.status === 'completed').length;
const ctrRate = totalClicks > 0 ? Math.round((totalConversions / totalClicks) * 100) : 0;
// Обновить пользовательский интерфейс
document.getElementById('refEarnings').textContent = totalEarnings + '₽';
document.getElementById('refClicks').textContent = totalClicks;
document.getElementById('refConversions').textContent = totalConversions;
document.getElementById('ctrRate').textContent = ctrRate + '%';
// Загрузить доступные объявления
loadAvailableAds();
}
function copyReferralLink() {
const linkInput = document.getElementById('referralLink');
linkInput.select();
document.execCommand('copy');
showAlert('guestAlert', 'Ссылка скопирована в буфер обмена!', 'успех');
}
function loadAvailableAds() {
const container = document.getElementById('adCampaignsList');
const activeRequests = requests.filter(r => r.status === 'active');
if (activeRequests.length === 0) {
container.innerHTML = `
<div class="text-center" style="color: var(--text-muted); padding: 40px;">
<i class="fas fa-bullhorn" style="font-size: 3rem; margin-bottom: 15px;"></i>
<p>Нет доступных приложений для рекламы</p>
</div>
`;
возвращаться;
}
container.innerHTML = activeRequests.map(request => {
const isFeatured = request.featured && new Date(request.boostExpiry) > new Date();
const myAdCampaigns = adCampaigns.filter(ad =>
ad.requestId === request.id && ad.ownerId === currentOwner.id
);
const completedAds = myAdCampaigns.filter(ad => ad.status === 'completed').length;
возврат `
<div class="ad-campaign">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<h4 style="margin-bottom: 5px;">${request.destination}</h4>
<p style="color: var(--text-muted); font-size: 0.9rem;">
${request.guests} гостя · ${formatDate(request.checkin)} - ${formatDate(request.checkout)}
</p>
</div>
${isFeatured ? `<span class="ctr-indicator"><i class="fas fa-crown"></i> Продвигается</span>` : ''}
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 15px;">
<div>
<span class="ad-price-tag">199₽ за рекламу</span>
<span class="roi-badge">ROI 25%</span>
</div>
<div>
${myAdCampaigns.length > 0 ?
`<span style="color: var(--secondary); font-weight: 600;">
<i class="fas fa-check-circle"></i> Куплено: ${myAdCampaigns.length} (${completedAds} завершено)
</span>` :
`<button class="btn btn-small btn-warning" onclick="openBuyAdModal('${request.id}')">
<i class="fas fa-bullhorn"></i> Купить рекламу
</button>`
}
</div>
</div>
${request.wishes ? `
<div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);">
<p style="color: var(--text-muted); font-size: 0.9rem;">
<i class="far fa-comment"></i> ${request.wishes}
</p>
</div>
` : ''}
</div>
`;
}).присоединиться('');
}
function openBuyAdModal(requestId) {
const request = requests.find(r => r.id === requestId);
if (!request) return;
const modal = document.getElementById('buyAdModal');
const content = document.getElementById('adModalContent');
content.innerHTML = `
<div style="background: var(--bg-card); padding: 15px; border-radius: 10px; margin-bottom: 15px;">
<h4>${request.destination}</h4>
<p style="color: var(--text-muted); font-size: 0.9rem;">
${request.guests} гостя · Бюджет: до ${request.budget}₽/сутки<br>
${request.contactName} · ${daysAgo(request.createdAt)}
</p>
</div>
<p style="color: var(--text-muted); font-size: 0.9rem;">
При покупке рекламы этого предложения, когда по ней купят контакт, вы получите 49₽ прибыли.
</p>
`;
document.getElementById('confirmAdPurchaseBtn').dataset.requestId = requestId;
modal.style.display = 'flex';
}
function purchaseAd(requestId, quantity) {
if (!currentOwner) return;
const request = requests.find(r => r.id === requestId);
if (!request) return;
// Создание рекламных кампаний
for (let i = 0; i < quantity; i++) {
const adCampaign = {
id: generateId(),
requestId: requestId,
ownerId: currentOwner.id,
цена: 199,
прибыль: 49
статус: 'активный',
createdAt: new Date().toISOString(),
пункт назначения: request.destination,
бюджет: запрос.бюджет
};
adCampaigns.push(adCampaign);
// Добавить транзакцию
транзакции.push({
id: generateId(),
тип: 'ad_purchase',
ownerId: currentOwner.id,
сумма: -199,
описание: `Покупка рекламы: ${request.destination}`,
дата: new Date().toISOString()
});
}
// Обновить статистику владельца
if (!currentOwner.adCampaigns) {
currentOwner.adCampaigns = [];
}
currentOwner.adCampaigns.push({
requestId,
количество,
дата: new Date().toISOString()
});
saveData();
closeAdModal();
loadEarningsData();
showAlert('guestAlert', `Вы успешно купили ${quantity} рекламы!`, 'success');
}
// Функции администратора
function loadAdminPanel() {
if (!currentOwner || currentOwner.email !== 'admin@admin.com') return;
// Общая статистика
const totalUsers = owners.length + (currentGuest ? 1 : 0);
const totalEarnings = Math.abs(transactions
.filter(t => t.type === 'ad_purchase')
.reduce((sum, t) => sum + t.amount, 0));
const activeAds = adCampaigns.filter(ad => ad.status === 'active').length;
const totalClicks = referrals.length;
const totalConversions = adCampaigns.filter(ad => ad.status === 'completed').length;
const overallCTR = totalClicks > 0 ? Math.round((totalConversions / totalClicks) * 100) : 0;
document.getElementById('adminTotalUsers').textContent = totalUsers;
document.getElementById('adminTotalEarnings').textContent = totalEarnings + '₽';
document.getElementById('adminActiveAds').textContent = activeAds;
document.getElementById('adminCTR').textContent = общийCTR + '%';
// Загрузить список пользователей
loadAdminUsersList();
// Загрузка транзакций
loadAdminTransactions();
}
function loadAdminUsersList() {
const container = document.getElementById('adminUsersList');
const allUsers = [
...владельцы.map(владелец => ({
тип: 'Владелец',
email: owner.email,
баланс: owner.refEarnings || 0,
дата: owner.createdAt,
id: owner.id
}))
];
container.innerHTML = allUsers.map(user => `
<tr>
<td>${user.email}</td>
<td><span class="price-tag">${user.type}</span></td>
<td>${user.balance}₽</td>
<td>${formatDate(user.date)}</td>
<td>
<div class="admin-actions">
<button class="btn btn-small btn-info" onclick="editUser('${user.id}')">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-small btn-danger" onclick="deleteUser('${user.id}')">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
`).join('');
}
function loadAdminTransactions() {
const container = document.getElementById('adminTransactionsList');
const recentTransactions = transactions
.sort((a, b) => new Date(b.date) - new Date(a.date))
.slice(0, 20);
if (recentTransactions.length === 0) {
container.innerHTML = `
<div class="text-center" style="color: var(--text-muted); padding: 20px;">
<i class="fas fa-exchange-alt" style="font-size: 2rem; margin-bottom: 10px;"></i>
<p>Нет транзакций</p>
</div>
`;
возвращаться;
}
container.innerHTML = recentTransactions.map(transaction => {
const owner = owners.find(o => o.id === transaction.ownerId);
const typeColor = transaction.amount > 0 ? 'var(--secondary)' : 'var(--danger)';
возврат `
<div class="history-item">
<div>
<strong>${transaction.description}</strong><br>
<small style="color: var(--text-muted);">${owner ? owner.email : 'Неизвестно'} · ${formatDate(transaction.date)}</small>
</div>
<div style="color: ${typeColor}; font-weight: 600;">
${transaction.amount > 0 ? '+' : ''}${transaction.amount}₽
</div>
</div>
`;
}).присоединиться('');
}
function exportAllData() {
const data = {
запросы,
владельцы,
рекламные кампании,
направления,
транзакции,
метка времени: новый объект Date().toISOString()
};
const dataStr = JSON.stringify(data, null, 2);
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
const exportFileDefaultName = `stayfinder_export_${new Date().toISOString().split('T')[0]}.json`;
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
showAlert('guestAlert', 'Данные экспортированы!', 'успех');
}
function clearAllData() {
if (confirm('Вы уверены? Это удалить ВСЕ данные без возможности восстановления!')) {
localStorage.clear();
location.reload();
}
}
function generateTestData() {
// Сгенерировать тестовые данные
for (let i = 0; i < 5; i++) {
const testOwner = {
id: generateId(),
email: `test_owner${i}@test.com`,
пароль: 'test123',
имя: `Тестовый владелец ${i}`,
подписка: {
активный: true,
план: 'ежемесячно',
истечение срока действия: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()
},
createdAt: new Date(Date.now() - i * 24 * 60 * 60 * 1000).toISOString(),
contactsBought: Math.floor(Math.random() * 10),
refCode: 'ref_' + Math.random().toString(36).substr(2, 8)
};
if (!owners.find(o => o.email === testOwner.email)) {
owners.push(testOwner);
}
}
// Тестовые рефералы
for (let i = 0; i < 10; i++) {
const referral = {
id: generateId(),
referrerId: owners[0]?.id || currentOwner.id,
visitorId: generateId(),
дата: new Date(Date.now() - i * 2 * 60 * 60 * 1000).toISOString(),
преобразовано: Math.random() > 0.7
};
referrals.push(referral);
}
// Тестовые транзакции
for (let i = 0; i < 15; i++) {
const transaction = {
id: generateId(),
type: Math.random() > 0.5 ? 'ad_purchase' : 'ref_earnings',
ownerId: owners[Math.floor(Math.random() * owners.length)]?.id || currentOwner.id,
amount: Math.random() > 0.5 ? -199 : 49,
описание: Math.random() > 0,5 ? «Покупка рекламы»: «Реферальный доход»,
дата: new Date(Date.now() - i * 3 * 60 * 60 * 1000).toISOString()
};
transactions.push(transaction);
}
// Тестирование рекламных кампаний
for (let i = 0; i < 8; i++) {
const request = requests[Math.floor(Math.random() * requests.length)];
если (запрос) {
const adCampaign = {
id: generateId(),
requestId: request.id,
ownerId: owners[Math.floor(Math.random() * owners.length)]?.id || currentOwner.id,
цена: 199,
прибыль: 49
статус: Math.random() > 0.5 ? 'активно' : 'завершено',
createdAt: new Date(Date.now() - i * 6 * 60 * 60 * 1000).toISOString(),
пункт назначения: request.destination,
бюджет: запрос.бюджет
};
adCampaigns.push(adCampaign);
}
}
saveData();
loadAdminPanel();
loadEarningsData();
showAlert('guestAlert', 'Тестовые данные созданы!', 'успех');
}
function editUser(userId) {
alert('Редактирование пользователя: ' + userId);
}
function deleteUser(userId) {
if (confirm('Удалить пользователя?')) {
const index = owners.findIndex(o => o.id === userId);
если (индекс !== -1) {
owners.splice(index, 1);
saveData();
loadAdminPanel();
}
}
}
// Реферальная система
function checkReferralLink() {
const urlParams = new URLSearchParams(window.location.search);
const refCode = urlParams.get('ref');
if (refCode && currentOwner && currentOwner.id) {
const referrer = owners.find(o => o.refCode === refCode);
if (referrer && referrer.id !== currentOwner.id) {
const existingReferral = referrals.find(r =>
r.referrerId === Referrer.id &&
r.visitorId === currentOwner.id
);
if (!existingReferral) {
referrals.push({
id: generateId(),
referrerId: referrer.id,
visitorId: currentOwner.id,
дата: новый объект Date().toISOString(),
преобразовано: ложно
});
saveData();
}
}
}
}
function checkReferralConversion() {
const urlParams = new URLSearchParams(window.location.search);
const refCode = urlParams.get('ref');
if (refCode && currentOwner) {
const referrer = owners.find(o => o.refCode === refCode);
если (referrer) {
const referral = referrals.find(r =>
r.referrerId === Referrer.id &&
r.visitorId === currentOwner.id
);
if (referral && !referral.converted) {
referral.converted = true;
referral.conversionDate = new Date().toISOString();
saveData();
}
}
}
}
// Глобальные функции (доступны из HTML)
window.editRequest = function(requestId) {
const request = requests.find(r => r.id === requestId);
if (!request) return;
// Проверяем, является ли этот запрос собственностью гостя
if (!currentGuest || request.guestId !== currentGuest.id) {
showAlert('guestAlert', 'Вы можете подать только свои заявки', 'error');
возвращаться;
}
// Заполните форму данными запроса
document.getElementById('destination').value = request.destination;
document.getElementById('checkin').value = request.checkin;
document.getElementById('checkout').value = request.checkout;
document.getElementById('guests').value = request.guests;
document.getElementById('budget').value = request.budget;
document.getElementById('wishes').value = request.wishes;
// Обновить заголовок формы и кнопку
document.getElementById('formTitle').textContent = 'Редактировать заявку';
document.getElementById('formSubtitle').textContent = 'Изменить данные ваших проектов';
document.getElementById('submitRequestBtn').innerHTML = '<i class="fas fa-save"></i> Сохранить изменения';
document.getElementById('submitRequestBtn').dataset.editing = requestId;
document.getElementById('cancelEditBtn').style.display = 'block';
// Прокрутите до формы
document.querySelector('.guest-form').scrollIntoView({ behavior: 'smooth' });
};
window.openBoostModal = function(requestId) {
const request = requests.find(r => r.id === requestId);
if (!request) return;
// Проверяем, является ли этот запрос собственностью гостя
if (!currentGuest || request.guestId !== currentGuest.id) {
showAlert('guestAlert', 'Вы можете поднять только свои заявки', 'ошибка');
возвращаться;
}
document.getElementById('confirmBoostBtn').dataset.requestId = requestId;
boostModal.style.display = 'flex';
};
function boostRequest(requestId) {
const request = requests.find(r => r.id === requestId);
if (!request) return;
// Проверить, был ли уже продвинут
if (request.featured && new Date(request.boostExpiry) > new Date()) {
const daysLeft = Math.ceil((new Date(request.boostExpiry) - new Date()) / (1000 * 60 * 60 * 24));
alert(`Заявка уже продвигается! Осталось ${daysLeft} дней.`);
closeBoostModal();
возвращаться;
}
// В реальном приложении оплата обрабатывается здесь
// Для демонстрации просто обновите запрос
// Рассчитать новую дату истечения срока действия (через 7 дней или с момента истечения текущего срока, если срок действия еще не истек)
let newExpiry;
if (request.featured && new Date(request.boostExpiry) > new Date()) {
// Расширить существующий буст
newExpiry = new Date(request.boostExpiry);
newExpiry.setDate(newExpiry.getDate() + 7);
} еще {
// Начать новый буст
newExpiry = new Date();
newExpiry.setDate(newExpiry.getDate() + 7);
}
// Запрос на обновление
request.featured = true;
request.boostExpiry = newExpiry.toISOString();
request.boosts = (request.boosts || 0) + 1;
request.updatedAt = new Date().toISOString();
saveData();
closeBoostModal();
loadGuestRequests();
loadAllRequests();
showAlert('guestAlert', 'Заявка прогрессирует! Она будет выделена на 7 дней.', 'success');
};
window.closeRequest = function(requestId) {
const request = requests.find(r => r.id === requestId);
если (запрос) {
request.status = 'closed';
saveData();
loadGuestRequests();
showAlert('guestAlert', 'Заявка закрыта', 'успех');
}
};
window.reopenRequest = function(requestId) {
const request = requests.find(r => r.id === requestId);
если (запрос) {
request.status = 'active';
saveData();
loadGuestRequests();
showAlert('guestAlert', 'Заявка открыта заново', 'успех');
}
};
window.buyContact = function(requestId, budget) {
if (!currentOwner) return;
// Проверить подписку
if (!currentOwner.subscription || !currentOwner.subscription.active) {
alert('Ваша подписка неактивна. Пожалуйста, продлите подписку.');
openSubscriptionModal();
возвращаться;
}
// Проверить, истек ли срок действия подписки
if (new Date(currentOwner.subscription.expiry) < new Date()) {
alert('Ваша подписка окончательнола. Пожалуйста, продлите подписку.');
openSubscriptionModal();
возвращаться;
}
// Рассчитать цену
const price = Math.min(Math.max(100, Math.floor(budget * 0.1)), 500);
if (confirm(`Купить контакт за ${price}₽?`)) {
// Зарегистрировать покупку
boughtContacts.push({
requestId,
ownerId: currentOwner.id,
дата: новый объект Date().toISOString(),
цена
});
// Обновление представлений запроса
const request = requests.find(r => r.id === requestId);
если (запрос) {
request.views = (request.views || 0) + 1;
}
// Обновить статистику владельца
currentOwner.contactsBought = (currentOwner.contactsBought || 0) + 1;
// Обновить массив владельцев
const ownerIndex = owners.findIndex(o => o.id === currentOwner.id);
if (ownerIndex !== -1) {
owners[ownerIndex] = currentOwner;
}
// Проверьте наличие рекламных кампаний и оплатите реферальные бонусы.
const activeAds = adCampaigns.filter(ad =>
ad.requestId === requestId &&
ad.status === 'active'
);
// Платить владельцам рекламы
activeAds.forEach(ad => {
const adOwner = owners.find(o => o.id === ad.ownerId);
if (adOwner) {
// Добавить прибыль
if (!adOwner.refEarnings) adOwner.refEarnings = 0;
adOwner.refEarnings += ad.profit;
// Обновить статус объявления
ad.status = 'completed';
ad.completedAt = new Date().toISOString();
// Добавить транзакцию
транзакции.push({
id: generateId(),
тип: 'ref_earnings',
ownerId: ad.ownerId,
сумма: рекламная прибыль,
описание: `Прибыль с рекламы: ${ad.destination}`,
дата: new Date().toISOString()
});
// Обновить владельца в массиве
const adOwnerIndex = owners.findIndex(o => o.id === ad.ownerId);
if (adOwnerIndex !== -1) {
owners[adOwnerIndex] = adOwner;
}
}
});
saveData();
localStorage.setItem('stayfinder_current_owner', JSON.stringify(currentOwner));
loadOwnerDashboard();
loadAllRequests();
// Перезагрузить данные о доходах, если вы находитесь на вкладке «Доходы»
if (!document.getElementById('earningsTabContent').classList.contains('hidden')) {
loadEarningsData();
}
// Немедленно показать контакт
showContact(requestId);
}
};
window.showContact = function(requestId) {
const request = requests.find(r => r.id === requestId);
if (!request) return;
const modal = document.getElementById('contactModal');
const modalContent = document.getElementById('modalContactInfo');
modalContent.innerHTML = `
<div class="contact-info" style="display: block; margin: 0;">
<div class="contact-item">
<i class="fas fa-user"></i>
<span><strong>Имя:</strong> ${request.contactName}</span>
</div>
<div class="contact-item">
<i class="fas fa-phone"></i>
<span><strong>Контакты:</strong> ${request.contactPhone</span>
</div>
<div class="contact-item">
<i class="fas fa-map-marker-alt"></i>
<span><strong>Направление:</strong> ${request.destination</span>
</div>
<div class="contact-item">
<i class="far fa-calendar"></i>
<span><strong>Даты:</strong> ${formatDate(request.checkin)} - ${formatDate(request.checkout)}</span>
</div>
<div class="contact-item">
<i class="fas fa-user-friends"></i>
<span><strong>Гости:</strong> ${request.guests} человек</span>
</div>
<div class="contact-item">
<i class="fas fa-wallet"></i>
<span><strong>Бюджет:</strong> до ${request.budget}₽/сутки</span>
</div>
${request.wishes ? `
<div class="contact-item">
<i class="far fa-comment"></i>
<span><strong>Пожелания:</strong> ${request.wishes</span>
</div>` : ''}
</div>
`;
modal.style.display = 'flex';
};
function buySubscription(plan {
if (!currentOwner) return;
let daysToAdd, planName, price;
switch(plan) {
случай 'месяц':
daysToAdd = 30;
planName = 'monthly';
цена = 1500;
перерыв;
случай 'квартал':
daysToAdd = 90;
planName = 'quarterly';
цена = 3500;
перерыв;
год дела:
daysToAdd = 365;
planName = 'yearly';
цена = 10000;
перерыв;
по умолчанию:
возвращаться;
}
if (confirm(`Активировать подписку "${planName}" за ${price}₽?`)) {
// В реальном приложении платеж обрабатывается здесь.
// Обновить подписку
const now = new Date();
let newExpiry;
if (currentOwner.subscription && currentOwner.subscription.active) {
// Продлить текущую подписку
newExpiry = new Date(currentOwner.subscription.expiry);
newExpiry.setDate(newExpiry.getDate() + daysToAdd);
} еще {
// Начать новую подписку
newExpiry = new Date();
newExpiry.setDate(newExpiry.getDate() + daysToAdd);
}
currentOwner.subscription = {
активный: true,
план: planName,
истечение срока действия: newExpiry.toISOString()
};
// Обновить массив владельцев
const ownerIndex = owners.findIndex(o => o.id === currentOwner.id);
if (ownerIndex !== -1) {
owners[ownerIndex] = currentOwner;
}
saveData();
localStorage.setItem('stayfinder_current_owner', JSON.stringify(currentOwner));
closeSubscriptionModal();
loadOwnerDashboard();
alert(`Подписка активирована! Действует до ${formatDate(newExpiry.toISOString())}`);
}
}
function resetForm() {
// Очистить форму
document.getElementById('destination').value = '';
document.getElementById('checkin').value = new Date().toISOString().split('T')[0];
document.getElementById('checkout').value = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
document.getElementById('guests').value = '2';
document.getElementById('budget').value = '3000';
document.getElementById('wishes').value = '';
// Сбросить заголовок формы и кнопку
document.getElementById('formTitle').textContent = 'Найдите идеальное жилье без поиска';
document.getElementById('formSubtitle').textContent = 'Оставьте заявку — держите сами предложат вам лучшие варианты';
document.getElementById('submitRequestBtn').innerHTML = '<i class="fas fa-paper-plane"></i> Оставить заявку';
удалить документ.getElementById('submitRequestBtn').dataset.editing;
document.getElementById('cancelEditBtn').style.display = 'none';
}
function closeContactModal() {
document.getElementById('contactModal').style.display = 'none';
}
function closeSubscriptionModal() {
document.getElementById('subscriptionModal').style.display = 'none';
}
function closeGuestLoginModal() {
document.getElementById('guestLoginModal').style.display = 'none';
}
function closeBoostModal() {
document.getElementById('boostModal').style.display = 'none';
}
function closeAdModal() {
document.getElementById('buyAdModal').style.display = 'none';
}
function formatDate(dateString) {
if (!dateString) return '--';
const date = new Date(dateString);
return date.toLocaleDateString('ru-RU', {
день: 'двузначный',
месяц: 'двузначный',
год: 'числовой'
});
}
function daysAgo(dateString {
const date = new Date(dateString);
const now = new Date();
const diffTime = Math.abs(now - date);
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) вернуть 'сегодня';
if (diffDays === 1) вернуть 'вчера';
if (diffDays < 7) return `${diffDays} дня назад`;
if (diffDays < 30) return `${Math.floor(diffDays/7)} недели назад`;
return `${Math.floor(diffDays/30)} месяца назад`;
}
function getDaysLeft(expiryDate) {
if (!expiryDate) return '0';
const now = new Date();
const expiry = new Date(expiryDate);
const diffTime = expiry - now;
const diffDays = Math.max(0, Math.ceil(diffTime / (1000 * 60 * 60 * 24)));
return diffDays;
}
function addSampleRequests() {
const sampleRequests = [
{
id: generateId(),
пункт назначения: 'Сочи, 1-я линия',
checkin: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
checkout: new Date(Date.now() + 10 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
гостей: 3,
Бюджет: 3200,
пожелания: 'С видом на море, с парковкой',
contactName: 'Анна',
contactPhone: '+7 999 123-45-67',
guestId: '_sample_guest',
createdAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
updatedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
статус: 'активный',
Просмотров: 12,
бонусы: 1,
boostExpiry: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(),
избранное: правда
},
{
id: generateId(),
пункт назначения: 'Адлер',
checkin: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
checkout: new Date(Date.now() + 12 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
гостей: 2,
Бюджет: 2500,
пожелания: 'Без животных, тихий район',
contactName: 'Иче',
contactPhone: '@ivan_telegram',
guestId: '_sample_guest2',
createdAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString(),
updatedAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString(),
статус: 'активный',
Просмотров: 8,
бонусов: 0,
boostExpiry: null,
featured: false
},
{
id: generateId(),
пункт назначения: 'Геленджик',
checkin: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
checkout: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
гостей: 4,
Бюджет: 4000,
пожелания: 'Для большой семьи, 2 спальни',
contactName: 'Мария',
contactPhone: '+7 987 654-32-10',
guestId: '_sample_guest3',
createdAt: new Date(Date.now() - 5 * 60 * 60 * 1000).toISOString(),
updatedAt: new Date(Date.now() - 5 * 60 * 60 * 1000).toISOString(),
статус: 'активный',
Просмотров: 5,
бонусов: 0,
boostExpiry: null,
featured: false
}
];
requests.push(...sampleRequests);
saveData();
}
</script>
</body>
</html>