import { useState } from 'react' import { Copy, CheckCheck, ChevronDown, ChevronRight, Lock, Globe } from 'lucide-react' import { cn } from '../lib/utils' import type { ApiEndpoint } from '../types' const METHOD_COLORS: Record = { GET: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300', POST: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300', PATCH: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', PUT: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300', DELETE: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300', } const API_ENDPOINTS: ApiEndpoint[] = [ // Auth { method: 'POST', path: '/auth/login', summary: 'Авторизация', tags: ['Auth'], auth: false, requestBody: '{ "email": "string", "password": "string" }', response: '{ "token": "string", "user": {...} }' }, { method: 'POST', path: '/auth/refresh', summary: 'Обновить токен', tags: ['Auth'], auth: true }, { method: 'POST', path: '/auth/logout', summary: 'Выход', tags: ['Auth'], auth: true }, // Hotels { method: 'GET', path: '/hotels', summary: 'Список отелей', tags: ['Hotels'], auth: true, roles: ['super_admin'], response: '{ "data": Hotel[], "total": number }' }, { method: 'POST', path: '/hotels', summary: 'Создать отель', tags: ['Hotels'], auth: true, roles: ['super_admin'] }, { method: 'GET', path: '/hotels/:hotelId', summary: 'Получить отель', tags: ['Hotels'], auth: true }, { method: 'PATCH', path: '/hotels/:hotelId', summary: 'Обновить отель', tags: ['Hotels'], auth: true, roles: ['super_admin', 'hotel_manager'] }, { method: 'DELETE', path: '/hotels/:hotelId', summary: 'Удалить отель', tags: ['Hotels'], auth: true, roles: ['super_admin'] }, // Rooms { method: 'GET', path: '/hotels/:hotelId/rooms', summary: 'Список номеров', tags: ['Rooms'], auth: true }, { method: 'POST', path: '/hotels/:hotelId/rooms', summary: 'Создать номер', tags: ['Rooms'], auth: true, roles: ['hotel_manager'] }, { method: 'PATCH', path: '/hotels/:hotelId/rooms/:roomId', summary: 'Обновить номер', tags: ['Rooms'], auth: true, roles: ['hotel_manager'] }, { method: 'DELETE', path: '/hotels/:hotelId/rooms/:roomId', summary: 'Удалить номер', tags: ['Rooms'], auth: true, roles: ['hotel_manager'] }, // Bookings { method: 'GET', path: '/hotels/:hotelId/bookings', summary: 'Список бронирований (с фильтрацией по датам)', tags: ['Bookings'], auth: true, response: '{ "data": Booking[], "total": number }' }, { method: 'POST', path: '/hotels/:hotelId/bookings', summary: 'Создать бронирование', tags: ['Bookings'], auth: true, requestBody: '{ "roomId": "string", "guestName": "string", "checkIn": "YYYY-MM-DD", "checkOut": "YYYY-MM-DD" }' }, { method: 'GET', path: '/hotels/:hotelId/bookings/:bookingId', summary: 'Получить бронирование', tags: ['Bookings'], auth: true }, { method: 'PATCH', path: '/hotels/:hotelId/bookings/:bookingId', summary: 'Обновить бронирование', tags: ['Bookings'], auth: true }, { method: 'DELETE', path: '/hotels/:hotelId/bookings/:bookingId', summary: 'Отменить бронирование', tags: ['Bookings'], auth: true }, // Channels { method: 'GET', path: '/hotels/:hotelId/channels', summary: 'Список каналов', tags: ['Channels'], auth: true }, { method: 'PATCH', path: '/hotels/:hotelId/channels/:channelId', summary: 'Настроить канал', tags: ['Channels'], auth: true, roles: ['hotel_manager'] }, { method: 'POST', path: '/hotels/:hotelId/channels/:channelId/sync', summary: 'Запустить синхронизацию', tags: ['Channels'], auth: true, roles: ['hotel_manager'] }, // Housekeeping { method: 'GET', path: '/hotels/:hotelId/housekeeping/tasks', summary: 'Список задач', tags: ['Housekeeping'], auth: true }, { method: 'POST', path: '/hotels/:hotelId/housekeeping/tasks', summary: 'Создать задачу', tags: ['Housekeeping'], auth: true }, { method: 'PATCH', path: '/hotels/:hotelId/housekeeping/tasks/:taskId', summary: 'Обновить задачу', tags: ['Housekeeping'], auth: true }, // Webhooks { method: 'POST', path: '/webhooks/booking-com', summary: 'Webhook Booking.com', tags: ['Webhooks'], auth: false, description: 'Endpoint для получения событий от Booking.com' }, { method: 'POST', path: '/webhooks/airbnb', summary: 'Webhook Airbnb', tags: ['Webhooks'], auth: false }, ] const ALL_TAGS = [...new Set(API_ENDPOINTS.flatMap(e => e.tags))] export function ApiDocsPage() { const [activeTag, setActiveTag] = useState('Bookings') const [expandedEndpoint, setExpandedEndpoint] = useState(null) const [copied, setCopied] = useState(null) const filtered = API_ENDPOINTS.filter(e => e.tags.includes(activeTag)) const copy = (text: string, key: string) => { navigator.clipboard.writeText(text) setCopied(key) setTimeout(() => setCopied(null), 1500) } return (
{/* Header */}

HotelSync API

REST API v1.0 · Base URL: https://api.hotelsync.io/v1

{/* Auth info */}

Аутентификация

Все запросы (кроме /auth/login и webhooks) требуют заголовка:

Authorization: Bearer <access_token>

Также требуется: X-Hotel-Id: <hotelId> для отельных запросов

{/* Tag nav */}
{ALL_TAGS.map(tag => ( ))}
{/* Endpoints */}
{filtered.map((endpoint, i) => { const key = `${endpoint.method}-${endpoint.path}` const isOpen = expandedEndpoint === key return (
{isOpen && (

{endpoint.description ?? endpoint.summary}

{endpoint.roles && (

Доступные роли

{endpoint.roles.map(r => ( {r} ))}
)} {endpoint.requestBody && (

Request Body

                        {endpoint.requestBody}
                      
)} {endpoint.response && (

Response 200

                        {endpoint.response}
                      
)} {/* Try it */}

cURL пример

{`curl -X ${endpoint.method} \\
  https://api.hotelsync.io/v1${endpoint.path} \\${endpoint.auth ? `
  -H "Authorization: Bearer " \\
  -H "X-Hotel-Id: " \\` : ''}
  -H "Content-Type: application/json"`}
                      
)}
) })}
{/* Rate limits */}

Лимиты запросов

{[ { plan: 'Starter', limit: '1 000 / час', color: 'text-slate-600 dark:text-slate-400' }, { plan: 'Pro', limit: '10 000 / час', color: 'text-brand-600 dark:text-brand-400' }, { plan: 'Enterprise', limit: 'Неограничено', color: 'text-amber-600 dark:text-amber-400' }, ].map(r => (

{r.plan}

{r.limit}

))}
) }