diff --git a/src/pages/AvailabilityPage.tsx b/src/pages/AvailabilityPage.tsx index aa970fd..c8b2f5c 100644 --- a/src/pages/AvailabilityPage.tsx +++ b/src/pages/AvailabilityPage.tsx @@ -3,7 +3,7 @@ import { addDays, format, parseISO, isWithinInterval, startOfDay, getDay, isSame import { ru } from 'date-fns/locale' import { ChevronLeft, ChevronRight, X, Check, CalendarDays, - ListFilter, Plus, Pencil, Trash2, AlertCircle, RefreshCw, + Plus, Pencil, Trash2, AlertCircle, RefreshCw, MousePointer2, Rows3, } from 'lucide-react' import { cn } from '../lib/utils' import { @@ -55,25 +55,30 @@ interface Selection { start: string; end: string } function PriceGrid({ dates, prices, selection, dragging, onCellDown, onCellEnter, activeChannel, + cellEditMode, selectedCat, }: { dates: string[] prices: Record> selection: Selection | null dragging: boolean - onCellDown: (date: string) => void + onCellDown: (date: string, catId: string) => void onCellEnter: (date: string) => void activeChannel: string + cellEditMode: boolean + selectedCat: string | null }) { const today = format(new Date(), DATE_FMT) - const isSelected = useCallback((date: string) => { + const isSelected = useCallback((date: string, catId: string) => { if (!selection) return false const [s, e] = normRange(selection.start, selection.end) - return date >= s && date <= e - }, [selection]) + const inRange = date >= s && date <= e + if (cellEditMode && selectedCat) return inRange && catId === selectedCat + return inRange + }, [selection, cellEditMode, selectedCat]) return ( -
+
{/* Date header */} @@ -118,12 +123,20 @@ function PriceGrid({
{/* Category rows */} - {ROOM_CATEGORIES.map(cat => ( -
+ {ROOM_CATEGORIES.map(cat => { + const isActiveCat = cellEditMode && selectedCat === cat.id + return ( +
{/* Label */}
@@ -141,7 +154,7 @@ function PriceGrid({ const dow = getDay(dt) const isWe = dow === 0 || dow === 6 const isTd = d === today - const isSel = isSelected(d) + const isSel = isSelected(d, cat.id) const price = activeChannel === 'direct' ? cell?.price : cell?.channelPrices[activeChannel] ?? cell?.price @@ -150,7 +163,7 @@ function PriceGrid({
onCellDown(d)} + onMouseDown={() => onCellDown(d, cat.id)} onMouseEnter={() => onCellEnter(d)} className={cn( 'shrink-0 flex flex-col items-center justify-center select-none cursor-pointer', @@ -185,7 +198,8 @@ function PriceGrid({ ) })}
- ))} + ) + })}
) @@ -198,6 +212,7 @@ function EditPanel({ prices, onApply, onClose, + onlyCategoryId, }: { selection: Selection prices: Record> @@ -209,13 +224,19 @@ function EditPanel({ minNights: number channelMarkup: Record closed: boolean + onlyCategoryId?: string }) => void onClose: () => void + onlyCategoryId?: string }) { const [s, e] = normRange(selection.start, selection.end) + const activeCat = onlyCategoryId + ? ROOM_CATEGORIES.find(c => c.id === onlyCategoryId) ?? ROOM_CATEGORIES[0] + : ROOM_CATEGORIES[0] + // Initial values from first cell of selection - const firstCell = prices[ROOM_CATEGORIES[0].id]?.[s] + const firstCell = prices[activeCat.id]?.[s] const [startDate, setStartDate] = useState(s) const [endDate, setEndDate] = useState(e) @@ -228,7 +249,10 @@ function EditPanel({ const [catPrices, setCatPrices] = useState>(() => { const r: Record = {} - for (const cat of ROOM_CATEGORIES) { + const catsToInit = onlyCategoryId + ? ROOM_CATEGORIES.filter(c => c.id === onlyCategoryId) + : ROOM_CATEGORIES + for (const cat of catsToInit) { r[cat.id] = prices[cat.id]?.[s]?.price ?? cat.basePrice } return r @@ -245,8 +269,18 @@ function EditPanel({ {/* Header */}
-

Редактировать цены

-

{nightCount} {nightCount === 1 ? 'день' : 'дней'}

+

+ {onlyCategoryId ? ( + + + {activeCat.name} + + ) : 'Редактировать цены'} +

+

+ {nightCount} {nightCount === 1 ? 'день' : 'дней'} + {onlyCategoryId && ' • только эта категория'} +

@@ -299,10 +333,10 @@ function EditPanel({ {!closed && (
- {ROOM_CATEGORIES.map(cat => ( + {(onlyCategoryId ? [activeCat] : ROOM_CATEGORIES).map(cat => (
@@ -406,6 +440,7 @@ function EditPanel({ onClick={() => onApply({ startDate, endDate, categoryPrices: catPrices, extraPerson, minNights, channelMarkup, closed, + onlyCategoryId, })} className="btn-primary flex-1 justify-center gap-2 text-sm py-2" > @@ -603,6 +638,8 @@ export function AvailabilityPage() { const [dragStart, setDragStart] = useState(null) const [showEditPanel, setShowEditPanel] = useState(false) const [periodModal, setPeriodModal] = useState(null) + const [cellEditMode, setCellEditMode] = useState(false) + const [selectedCat, setSelectedCat] = useState(null) const dates = useMemo(() => Array.from({ length: DAYS }, (_, i) => @@ -610,11 +647,12 @@ export function AvailabilityPage() { ), [today, offset]) // ── Mouse handlers ── - const handleCellDown = (date: string) => { + const handleCellDown = (date: string, catId: string) => { setDragging(true) setDragStart(date) setSelection({ start: date, end: date }) setShowEditPanel(false) + if (cellEditMode) setSelectedCat(catId) } const handleCellEnter = (date: string) => { @@ -633,17 +671,22 @@ export function AvailabilityPage() { // ── Apply price changes ── const applyPrices = ({ startDate, endDate, categoryPrices, extraPerson, minNights, channelMarkup, closed, + onlyCategoryId, }: { startDate: string; endDate: string categoryPrices: Record extraPerson: number; minNights: number channelMarkup: Record closed: boolean + onlyCategoryId?: string }) => { const days = datesInRange(startDate, endDate) + const catsToUpdate = onlyCategoryId + ? ROOM_CATEGORIES.filter(c => c.id === onlyCategoryId) + : ROOM_CATEGORIES setPrices(prev => { const next = { ...prev } - for (const cat of ROOM_CATEGORIES) { + for (const cat of catsToUpdate) { next[cat.id] = { ...prev[cat.id] } const basePrice = categoryPrices[cat.id] ?? cat.basePrice for (const d of days) { @@ -660,6 +703,7 @@ export function AvailabilityPage() { }) setShowEditPanel(false) setSelection(null) + if (onlyCategoryId) setSelectedCat(null) } // ── Apply period ── @@ -754,6 +798,27 @@ export function AvailabilityPage() {
)} + {tab === 'grid' && ( + + )} + {tab === 'periods' && (
@@ -799,6 +866,8 @@ export function AvailabilityPage() { onCellDown={handleCellDown} onCellEnter={handleCellEnter} activeChannel={activeChannel} + cellEditMode={cellEditMode} + selectedCat={selectedCat} />
@@ -808,7 +877,8 @@ export function AvailabilityPage() { selection={selection} prices={prices} onApply={applyPrices} - onClose={() => { setShowEditPanel(false); setSelection(null) }} + onClose={() => { setShowEditPanel(false); setSelection(null); setSelectedCat(null) }} + onlyCategoryId={cellEditMode && selectedCat ? selectedCat : undefined} /> )}