Connect ChannelsPage, FloorMapPage, SettingsPage, UsersPage to real API
- ChannelsPage: load from API, toggle/sync call real endpoints; normalize enabled→isEnabled, lastSyncedAt→lastSyncAt, add displayName/mappings defaults - FloorMapPage: load rooms+bookings from API; create booking via API - SettingsPage: load hotel via GET /api/hotels/:slug; save general section via PATCH (name, address, timezone, currency, check_in/out times) - UsersPage: load users from API; create/update/delete via API; map backend User (name/role) → StaffUser (firstName/lastName/StaffRole) - api.ts: add hotels.get/update, users.delete, channel normalization, HotelPayload/toHotelPayload - types/index.ts: extend Hotel with phone/checkInTime/checkOutTime/optional fields; add User.createdAt/updatedAt - backend/routes/hotels.ts: extend PATCH to allow address/phone/check_in_time/check_out_time - backend/migrations/004_hotels_contacts.sql: add address/phone/check_in/out_time to hotels Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { RefreshCw, CheckCircle2, XCircle, Clock, Globe, AlertTriangle } from 'lucide-react'
|
||||
import { MOCK_CHANNELS } from '../data/mockData'
|
||||
import { api } from '../lib/api'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import type { Channel, SyncStatus } from '../types'
|
||||
import { cn } from '../lib/utils'
|
||||
import { Badge } from '../components/ui/Badge'
|
||||
@@ -35,25 +36,40 @@ function SyncStatusBadge({ status }: { status: SyncStatus }) {
|
||||
}
|
||||
|
||||
export function ChannelsPage() {
|
||||
const [channels, setChannels] = useState<Channel[]>(MOCK_CHANNELS)
|
||||
const { user } = useAuth()
|
||||
const slug = user?.hotelSlug ?? ''
|
||||
const [channels, setChannels] = useState<Channel[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const triggerSync = (id: string) => {
|
||||
setChannels(prev => prev.map(c =>
|
||||
c.id === id ? { ...c, lastSyncStatus: 'syncing' } : c,
|
||||
))
|
||||
setTimeout(() => {
|
||||
setChannels(prev => prev.map(c =>
|
||||
c.id === id
|
||||
? { ...c, lastSyncStatus: 'success', lastSyncAt: new Date().toISOString(), bookingsImported: c.bookingsImported + Math.floor(Math.random() * 3) }
|
||||
: c,
|
||||
))
|
||||
}, 2000)
|
||||
useEffect(() => {
|
||||
if (!slug) return
|
||||
api.channels.list(slug)
|
||||
.then(setChannels)
|
||||
.catch(console.error)
|
||||
.finally(() => setLoading(false))
|
||||
}, [slug])
|
||||
|
||||
const triggerSync = async (id: string) => {
|
||||
setChannels(prev => prev.map(c => c.id === id ? { ...c, lastSyncStatus: 'syncing' } : c))
|
||||
try {
|
||||
const { channel } = await api.channels.sync(slug, id)
|
||||
setChannels(prev => prev.map(c => c.id === id ? { ...c, ...channel, lastSyncStatus: 'success' } : c))
|
||||
} catch {
|
||||
setChannels(prev => prev.map(c => c.id === id ? { ...c, lastSyncStatus: 'error' } : c))
|
||||
}
|
||||
}
|
||||
|
||||
const toggleChannel = (id: string) => {
|
||||
setChannels(prev => prev.map(c =>
|
||||
c.id === id ? { ...c, isEnabled: !c.isEnabled } : c,
|
||||
))
|
||||
const toggleChannel = async (id: string) => {
|
||||
const ch = channels.find(c => c.id === id)
|
||||
if (!ch) return
|
||||
const next = !ch.isEnabled
|
||||
setChannels(prev => prev.map(c => c.id === id ? { ...c, isEnabled: next } : c))
|
||||
try {
|
||||
const updated = await api.channels.update(slug, id, { enabled: next })
|
||||
setChannels(prev => prev.map(c => c.id === id ? updated : c))
|
||||
} catch {
|
||||
setChannels(prev => prev.map(c => c.id === id ? { ...c, isEnabled: !next } : c))
|
||||
}
|
||||
}
|
||||
|
||||
const totalImported = channels.reduce((s, c) => s + c.bookingsImported, 0)
|
||||
@@ -62,6 +78,10 @@ export function ChannelsPage() {
|
||||
const internationalChannels = channels.filter(c => !RUSSIAN_CHANNELS.has(c.name))
|
||||
const russianChannels = channels.filter(c => RUSSIAN_CHANNELS.has(c.name))
|
||||
|
||||
if (loading) return (
|
||||
<div className="p-6 flex items-center justify-center text-slate-400 text-sm">Загрузка...</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-6 space-y-5">
|
||||
{/* Header */}
|
||||
|
||||
Reference in New Issue
Block a user