diff --git a/src/admin/calendar/CalendarPage.jsx b/src/admin/calendar/CalendarPage.jsx index 8e03657..154bb1a 100644 --- a/src/admin/calendar/CalendarPage.jsx +++ b/src/admin/calendar/CalendarPage.jsx @@ -1,8 +1,9 @@ -import { useState } from 'react'; -import { ChevronLeft, ChevronRight, Plus } from 'lucide-react'; +import { useState, useEffect, useRef } from 'react'; +import { ChevronLeft, ChevronRight, Plus, Link2, Search, X } from 'lucide-react'; import { useCalendar } from './useCalendar'; import { useIsMobile } from '../../hooks/useIsMobile'; import OrderModal from '../orders/OrderModal'; +import { supabase } from '../../lib/supabase'; const MONTH_NAMES = [ 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', @@ -140,7 +141,156 @@ function DayCell({ date, orders, isCurrentMonth, isToday, isSelected, onSelect, ); } -function DayDetail({ date, orders, onOrderClick, onAdd }) { +function OrderPicker({ date, onAssign, onClose }) { + const [query, setQuery] = useState(''); + const [allOrders, setAllOrders] = useState([]); + const [loading, setLoading] = useState(true); + const [assigning, setAssigning] = useState(null); + const inputRef = useRef(null); + + useEffect(() => { + let isMounted = true; + supabase.from('orders') + .select('id, name, phone, status, scheduled_at') + .order('created_at', { ascending: false }) + .then(({ data }) => { if (isMounted) { setAllOrders(data ?? []); setLoading(false); } }); + return () => { isMounted = false; }; + }, []); + + useEffect(() => { inputRef.current?.focus(); }, []); + + const q = query.trim().toLowerCase(); + const filtered = q + ? allOrders.filter((o) => + o.name?.toLowerCase().includes(q) || o.phone?.toLowerCase().includes(q) + ) + : allOrders; + + const handleAssign = async (order) => { + setAssigning(order.id); + // Set time to noon on the selected date + const d = new Date(date); + d.setHours(12, 0, 0, 0); + await supabase.from('orders').update({ scheduled_at: d.toISOString() }).eq('id', order.id); + onAssign(); + }; + + const dateLabel = date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long' }); + + return ( +
+
e.stopPropagation()} + style={{ + background: '#fff', borderRadius: '16px', width: '100%', maxWidth: '460px', + boxShadow: '0 8px 40px rgba(0,0,0,0.18)', overflow: 'hidden', + display: 'flex', flexDirection: 'column', maxHeight: '80vh', + }} + > + {/* Header */} +
+
+
+
+ Привязать заявку +
+
+ к {dateLabel} +
+
+ +
+
+ + setQuery(e.target.value)} + placeholder="Имя или телефон..." + style={{ + width: '100%', padding: '9px 12px 9px 34px', + border: '1.5px solid #E9ECEF', borderRadius: '8px', + fontSize: '0.88rem', outline: 'none', boxSizing: 'border-box', + fontFamily: 'var(--font-body)', + }} + onFocus={(e) => e.target.style.borderColor = '#4361EE'} + onBlur={(e) => e.target.style.borderColor = '#E9ECEF'} + /> +
+
+ + {/* List */} +
+ {loading && ( +
+ Загрузка... +
+ )} + {!loading && filtered.length === 0 && ( +
+ Ничего не найдено +
+ )} + {filtered.map((o) => { + const color = getStatusColor(o.status); + const busy = assigning === o.id; + return ( +
!assigning && handleAssign(o)} + style={{ + padding: '12px 20px', + display: 'flex', alignItems: 'center', gap: '12px', + borderBottom: '1px solid #F3F4F6', + cursor: assigning ? 'wait' : 'pointer', + opacity: assigning && !busy ? 0.5 : 1, + transition: 'background 0.1s', + }} + onMouseEnter={(e) => { if (!assigning) e.currentTarget.style.background = '#F5F7FA'; }} + onMouseLeave={(e) => e.currentTarget.style.background = ''} + > +
+
{o.name}
+
+ {o.phone} + {o.scheduled_at && ( + + · сейчас: {new Date(o.scheduled_at).toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' })} + + )} +
+
+ + {busy ? '...' : o.status} + +
+ ); + })} +
+
+
+ ); +} + +function DayDetail({ date, orders, onOrderClick, onAdd, onAssign }) { const dayOrders = orders.filter((o) => o.scheduled_at && isSameDay(new Date(o.scheduled_at), date)); return ( @@ -199,17 +349,31 @@ function DayDetail({ date, orders, onOrderClick, onAdd }) { ); })} - +
+ + +
); } @@ -221,6 +385,7 @@ const CalendarPage = () => { const [selectedDate, setSelectedDate] = useState(today); const [editOrder, setEditOrder] = useState(null); const [createDate, setCreateDate] = useState(null); + const [showPicker, setShowPicker] = useState(false); const isMobile = useIsMobile(); const { orders, loading, refetch } = useCalendar(year, month); @@ -361,6 +526,7 @@ const CalendarPage = () => { orders={orders} onOrderClick={setEditOrder} onAdd={() => setCreateDate(selectedDate)} + onAssign={() => setShowPicker(true)} /> )} @@ -382,6 +548,14 @@ const CalendarPage = () => { onSaved={handleModalSaved} /> )} + + {showPicker && ( + { setShowPicker(false); refetch(); }} + onClose={() => setShowPicker(false)} + /> + )} ); };