Wide BookingModal (2-col), RoomModal, RU channels, migration module, all modules active
- BookingModal: 2-column layout (size=2xl/max-w-3xl), no scroll needed; hourly mode toggle for rooms with allowHourly=true - RoomModal: full add/edit form with hourly rate toggle and amenities checkboxes; RoomsPage wired up - Channel manager: Яндекс Путешествия, Островок, Суточно.ру, OneTwoTrip added; split into RU/International sections - Migration module: 3-step wizard (source select → file upload → progress/results) - All modules set to active by default (version bump to reset localStorage) - BookingCalendar booking blocks: guest count badge (Xг), unpaid red dot indicator Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,12 +6,18 @@ import { cn } from '../lib/utils'
|
||||
import { Badge } from '../components/ui/Badge'
|
||||
|
||||
const CHANNEL_ICONS: Record<string, string> = {
|
||||
booking_com: '🔵',
|
||||
airbnb: '🔴',
|
||||
expedia: '🟡',
|
||||
vrbo: '🟢',
|
||||
booking_com: '🔵',
|
||||
airbnb: '🔴',
|
||||
expedia: '🟡',
|
||||
vrbo: '🟢',
|
||||
yandex_travel: '🔴',
|
||||
ostrovok: '🟣',
|
||||
sutochno: '🟠',
|
||||
onetwotrip: '🔵',
|
||||
}
|
||||
|
||||
const RUSSIAN_CHANNELS = new Set(['yandex_travel', 'ostrovok', 'sutochno', 'onetwotrip'])
|
||||
|
||||
function SyncStatusBadge({ status }: { status: SyncStatus }) {
|
||||
const map: Record<SyncStatus, { icon: React.ElementType; label: string; cls: string }> = {
|
||||
idle: { icon: Clock, label: 'Не настроен', cls: 'bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-300' },
|
||||
@@ -53,6 +59,9 @@ export function ChannelsPage() {
|
||||
const totalImported = channels.reduce((s, c) => s + c.bookingsImported, 0)
|
||||
const activeCount = channels.filter(c => c.isEnabled).length
|
||||
|
||||
const internationalChannels = channels.filter(c => !RUSSIAN_CHANNELS.has(c.name))
|
||||
const russianChannels = channels.filter(c => RUSSIAN_CHANNELS.has(c.name))
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-6 space-y-5">
|
||||
{/* Header */}
|
||||
@@ -78,16 +87,38 @@ export function ChannelsPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Channels */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{channels.map(channel => (
|
||||
<ChannelCard
|
||||
key={channel.id}
|
||||
channel={channel}
|
||||
onSync={() => triggerSync(channel.id)}
|
||||
onToggle={() => toggleChannel(channel.id)}
|
||||
/>
|
||||
))}
|
||||
{/* International Channels */}
|
||||
<div>
|
||||
<h2 className="text-base font-semibold text-slate-700 dark:text-slate-300 mb-3">
|
||||
🌍 Международные площадки
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{internationalChannels.map(channel => (
|
||||
<ChannelCard
|
||||
key={channel.id}
|
||||
channel={channel}
|
||||
onSync={() => triggerSync(channel.id)}
|
||||
onToggle={() => toggleChannel(channel.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Russian Channels */}
|
||||
<div>
|
||||
<h2 className="text-base font-semibold text-slate-700 dark:text-slate-300 mb-3">
|
||||
🇷🇺 Российские площадки
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{russianChannels.map(channel => (
|
||||
<ChannelCard
|
||||
key={channel.id}
|
||||
channel={channel}
|
||||
onSync={() => triggerSync(channel.id)}
|
||||
onToggle={() => toggleChannel(channel.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
@@ -167,7 +198,6 @@ function ChannelCard({ channel, onSync, onToggle }: {
|
||||
{channel.mappings.slice(0, 2).map(m => (
|
||||
<div key={m.localRoomId} className="flex items-center justify-between text-xs">
|
||||
<span className="text-slate-600 dark:text-slate-400">№{
|
||||
// get room number from localRoomId
|
||||
m.localRoomId.replace('r', '')
|
||||
}</span>
|
||||
<span className="text-slate-400">→</span>
|
||||
|
||||
Reference in New Issue
Block a user