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)}
+ />
+ )}
);
};