{c.title}
{c.desc}
// Editorial Luxury — pages (real backend via window.EH_API) const D2 = window.EH_DATA; const API = window.EH_API; // --- helpers --- const isoDay = (d) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; // «5 июля» из ISO-даты (короткое отображение для диапазонов) const fmtRuDate = (iso) => { const d = new Date(String(iso) + 'T00:00:00'); return isNaN(d) ? iso : d.toLocaleDateString('ru', { day: 'numeric', month: 'short' }); }; // Форматирует время брони: часовая — «13:00–14:00, 15:00–17:00», // дневная — «Весь день», месячная — «30 дней · по 5 авг» function fmtHours(b) { if (b.kind === 'day') return `Весь день (${b.start}–${b.end})`; if (b.kind === 'month') return `30 дней · по ${fmtRuDate(b.dateEnd || b.date)}`; const hrs = Array.isArray(b.hours) && b.hours.length ? b.hours.slice().sort((a, c) => a - c) : null; if (!hrs) return `${b.start} — ${b.end}`; const runs = []; let s = hrs[0], p = hrs[0]; for (let i = 1; i < hrs.length; i++) { if (hrs[i] === p + 1) { p = hrs[i]; } else { runs.push([s, p]); s = hrs[i]; p = hrs[i]; } } runs.push([s, p]); const f = (h) => String(h).padStart(2, '0') + ':00'; return runs.map(([a, z]) => `${f(a)}–${f(z + 1)}`).join(', '); } function maskPhone(v) { let d = (v || '').replace(/\D/g, ''); if (d[0] === '8') d = '7' + d.slice(1); if (d[0] !== '7') d = '7' + d; d = d.slice(0, 11); let out = '+7'; if (d.length > 1) out += ' (' + d.slice(1, 4); if (d.length >= 4) out += ') ' + d.slice(4, 7); if (d.length >= 7) out += '-' + d.slice(7, 9); if (d.length >= 9) out += '-' + d.slice(9, 11); return out; } async function completePendingBooking() { const raw = sessionStorage.getItem('eh_pending_booking'); if (!raw) return; sessionStorage.removeItem('eh_pending_booking'); try { await API.createBooking(JSON.parse(raw)); } catch (e) { /* конфликт/ошибка — пропускаем */ } } const DashLoading = ({ label }) => (
Нажмите на место на плане — увидите фотографии, цены и свободные часы. Оплата онлайн, бронь подтверждается мгновенно.
Оборудованное рабочее место мастера красоты в центре Тамбова — без переплат и бюрократии.
Мы построили пространство, в котором сами хотели бы работать. Спокойный интерьер, профессиональное оборудование уровня дорогой клиники, никаких очередей за раковину и никакой текучки клиентов.
Вы платите только за то время, что используете. Полная свобода планировать день, как удобно — приходите хоть в шесть утра, хоть в полночь.
Свободный доступ круглосуточно для арендаторов от 1 месяца.
Профессиональные кресла, мойки, лампы, ринг-лайты и инструмент.
От 1 часа до месяца. Бронируйте онлайн в один клик.
Шкафчик с замком — оставляйте инструменты и приходите налегке.
Зерновой кофе-аппарат в зоне лаунжа для вас и ваших клиентов.
ул. Октябрьская, 11, 3-й этаж — рядом остановки общественного транспорта.
Мастер-классы, фотосъёмки для контента, обучения, дни красоты и закрытые мероприятия — арендуйте весь зал или его часть в нерабочие часы.
Выберите зону и нажмите «Подробнее» — вы увидите схему зала, цены и сможете забронировать конкретное место за минуту.
{c.desc}
Не нашли подходящий вариант? Позвоните или напишите — расскажем какие места и кабинеты свободны прямо сейчас и когда освобождаются занятые.
Нажмите на место на плане или в списке — увидите фотографии, тарифы и свободные часы.
Покупайте часы заранее по выгодной цене и оплачивайте ими брони в общем зале и кабинете барберов — прямо в личном кабинете.
Абонемент на {D2.brand.abonementHours || 30} часов аренды. Тратится на места в общем зале и кабинет барберов — 1 час брони = 1 час с абонемента. Покупка и оплата прямо на сайте.
Купить абонемент →Работайте в удобном для Вас графике, а мы позаботимся о комфорте для Вас и Ваших клиентов.
Доброжелательные администраторы встретят вас и ваших клиентов, предложат чай/кофе и безлимитный Wi-Fi.
Свежий стильный и аккуратный ремонт, профессиональное оборудование для работы, дополнительные материалы по себестоимости прямо на месте.
Приходите работать налегке — ваши инструменты и личные вещи можно оставить в локере, который закрывается на ключ.
Центр Тамбова, 3-й этаж. Остановка общественного транспорта в шаговой доступности.
Открыть в Яндекс.Картах →{cfg.desc}
Управляйте бронированиями и расписанием в одном месте.
Зарегистрируйтесь, чтобы бронировать рабочие места и оплачивать на сайте.
Личные данные и безопасность аккаунта. Зарегистрирован {new Date(user.created_at).toLocaleDateString('ru')}.
Покупайте часы или целые дни заранее и оплачивайте ими брони в общем зале и кабинете барберов.
У вас ещё есть {balanceHours} ч. Новый пакет можно купить, когда часы закончатся.
) : (1 час брони = 1 час с абонемента. Оплата по QR, зачисление после подтверждения администратором.
)}У вас ещё есть {balanceDays} дн. Новый пакет можно купить, когда дни закончатся.
) : (1 день с абонемента = бронь места на весь рабочий день. Выберите количество дней — от 1 до 60.
)}Пока нет покупок и начислений.
) : (| # | Дата | Объём | Использовано | Тип | Сумма | Статус |
|---|---|---|---|---|---|---|
| {String(i + 1).padStart(2, '0')} | {new Date(h.createdAt).toLocaleDateString('ru')} | {h.hoursTotal < 0 ? `− ${-h.hoursTotal} ${unit(h)}` : `${h.hoursTotal} ${unit(h)}`} | {h.hoursTotal < 0 ? '—' : `${h.hoursUsed} ${unit(h)}`} | {srcLabel(h.source)} | {h.price ? h.price.toLocaleString() + ' ₽' : '—'} | {stLabel(h.status) ? {stLabel(h.status)} : '✓'} |
У вас {upcoming} активных бронирований. Всего за всё время — {bookings.length}.
{tab === 'active' ? 'Ещё нет активных бронирований.' : tab === 'history' ? 'История появится после первых сеансов.' : 'Отменённых бронирований нет.'}
Выбрать место →Сводка по реальным данным базы. Обновляется автоматически при возврате на вкладку и каждые 30 секунд.
Активных бронирований пока нет.
) : (| # | Клиент | Место | Дата | Время | Сумма | Оплата |
|---|---|---|---|---|---|---|
| {String(i+1).padStart(2,'0')} | {b.clientName} {b.spec} |
{r.name} | {new Date(b.date).toLocaleDateString('ru')} | {fmtHours(b)} | {b.price.toLocaleString()} ₽ | {b.paid ? 'Оплачено' : 'Ожидает'} |
Клиенты переводят деньги по QR на реквизиты. Проверьте поступление в банке и подтвердите — бронь/абонемент активируются. Список обновляется автоматически.
Как только клиент оформит бронь или абонемент с оплатой переводом, заявка появится здесь.
Здесь появятся подтверждённые и отклонённые оплаты.
) : (| Тип | Клиент | Детали | Сумма | Решение | Когда |
|---|---|---|---|---|---|
| {p.kind === 'booking' ? `Бронь №${p.id}` : `Абонемент №${p.id}`} | {p.clientName || '—'} | {p.details} | {p.amount.toLocaleString()} ₽ | {decisionLabel(p.decision)} | {p.decidedAt || '—'} |
Реальная база зарегистрированных мастеров из БД.
Здесь появятся пользователи, как только зарегистрируются на сайте.
По фильтру ничего не найдено.
) : (| # | Имя | Телефон | Специализация | Регистрация | Броней | Сумма | |
|---|---|---|---|---|---|---|---|
| {String(i+1).padStart(2,'0')} | {c.name} | {c.phone} | {c.email} | {c.spec} | {new Date(c.createdAt).toLocaleDateString('ru')} | {c.bookingsCount} | {c.totalSpent.toLocaleString()} ₽ |
Все брони из базы — текущие, завершённые, отменённые.
Появятся здесь, как только мастера начнут бронировать места.
По фильтру ничего не найдено.
) : (| # | Клиент | Место | Дата | Время | Сумма | Оплата | Статус | |
|---|---|---|---|---|---|---|---|---|
| {String(i+1).padStart(2,'0')} | {b.clientName} {b.spec} |
{r.name} · {r.zoneRu} | {new Date(b.date).toLocaleDateString('ru')} | {fmtHours(b)} | {b.price.toLocaleString()} ₽ | {b.paid ? 'Оплачено' : (b.status === 'pending' ? 'Ждёт перевода' : 'Ожидает')} | {b.status === 'active' ? 'Активна' : b.status === 'pending' ? 'Не подтверждена' : b.status === 'completed' ? 'Завершена' : 'Отменена'} | {(b.status === 'active' || b.status === 'pending') && } |
Редактируйте цены, описание, фотографии и статус. Внутри зоны можно применить ко всем местам сразу. Изменения сразу видны на сайте.
Замените любую фотографию на главной странице и странице отзывов. Изменения видны сразу после обновления страницы.
{g.hint}
}Загрузка настроек…
; const upd = (k, v) => setForm(f => ({ ...f, [k]: v })); const save = async () => { const e = {}; if (form.email && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(form.email)) e.email = 'Некорректный email'; if (!/^\d{1,2}:\d{2}$/.test(form.open)) e.open = 'Формат ЧЧ:ММ'; if (!/^\d{1,2}:\d{2}$/.test(form.close)) e.close = 'Формат ЧЧ:ММ'; setErr(e); if (Object.keys(e).length) return; setSaving(true); setOk(false); try { const r = await API.saveSettings(form); const st = r.settings; const open = st.open || '09:00', close = st.close || '21:00'; window.EH_DATA.brand = Object.assign({}, window.EH_DATA.brand, { address: st.address, phone: st.phone, email: st.email, open, close, weekend: st.weekend || '—', hours: (st.weekend && st.weekend !== '—') ? `Пн–Сб · ${open}–${close}` : `Пн–Вс · ${open}–${close}`, abonementPrice: parseInt(st.abonement_price, 10) || 6000, abonementHours: parseInt(st.abonement_hours, 10) || 30, abonementDayPrice: parseInt(st.abonement_day_price, 10) || 1000, vk: st.vk || window.EH_DATA.brand.vk, instagram: st.instagram || '', maxGroup: st.max_group || '', maxDm: st.max_dm || window.EH_DATA.brand.maxDm, }); window.EH_DATA.pay = { name: st.pay_name || '', inn: st.pay_inn || '', account: st.pay_account || '', bank: st.pay_bank || '', bic: st.pay_bic || '', corr: st.pay_corr || '', }; setOk(true); setTimeout(() => setOk(false), 3000); } catch (ex) { setErr(ex.fields || { address: ex.message || 'Ошибка сохранения' }); } finally { setSaving(false); } }; const hLabel = { fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 20, fontWeight: 400, marginBottom: 16 }; return ( <>Контакты и рабочее время. Меняются сразу на сайте — футер, блок контактов, карточки.
Клиенты переводят оплату на эти реквизиты. По ним же строится QR-код (стандарт банков РФ).
Настоящие отзывы мастеров, которые работают в Estetic Hall каждый день.
«{r.text}»
Забронируйте место на час — почувствуйте пространство, а потом решайте.
Выбрать место →Редакция: {data.updated} · г. {D2.brand.city || 'Тамбов'}
{data.intro}
{data.sections.map((s, i) => ({b}
)}