feat: mobile kanban scroll, OrderModal bottom sheet, table scroll
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -123,7 +123,8 @@ const ClientsPage = () => {
|
||||
{clients.length === 0 ? (
|
||||
<p style={{ padding: '30px', color: 'var(--text-muted)', textAlign: 'center' }}>Нет клиентов</p>
|
||||
) : (
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<div style={{ overflowX: 'auto', WebkitOverflowScrolling: 'touch' }}>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', minWidth: '600px' }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ ...thStyle, width: '32px', padding: '10px 8px 10px 16px' }}></th>
|
||||
@@ -231,6 +232,7 @@ const ClientsPage = () => {
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { DndContext, closestCorners, DragOverlay, PointerSensor, useSensor, useS
|
||||
import { useOutletContext } from 'react-router-dom';
|
||||
import { useOrders } from './useOrders';
|
||||
import { useAuth } from '../auth/useAuth';
|
||||
import { useIsMobile } from '../../hooks/useIsMobile';
|
||||
import KanbanColumn from './KanbanColumn';
|
||||
import OrderCard from './OrderCard';
|
||||
import OrderModal from './OrderModal';
|
||||
@@ -13,6 +14,7 @@ const KanbanBoard = () => {
|
||||
const { ordersByStatus, loading, updateStatus } = useOrders();
|
||||
const { session } = useAuth();
|
||||
const { showNewOrder, setShowNewOrder } = useOutletContext() || {};
|
||||
const isMobile = useIsMobile();
|
||||
const [activeOrder, setActiveOrder] = useState(null);
|
||||
const [selectedOrder, setSelectedOrder] = useState(null);
|
||||
|
||||
@@ -85,13 +87,22 @@ const KanbanBoard = () => {
|
||||
return (
|
||||
<>
|
||||
<DndContext sensors={sensors} collisionDetection={closestCorners} onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||
<div style={{ display: 'flex', gap: '14px', overflowX: 'auto', paddingBottom: '20px', alignItems: 'flex-start' }}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
gap: '14px',
|
||||
alignItems: 'flex-start',
|
||||
overflowX: isMobile ? 'auto' : 'visible',
|
||||
WebkitOverflowScrolling: 'touch',
|
||||
paddingBottom: isMobile ? '12px' : '20px',
|
||||
minWidth: 'min-content',
|
||||
}}>
|
||||
{STATUSES.map((status) => (
|
||||
<KanbanColumn
|
||||
key={status}
|
||||
status={status}
|
||||
orders={effectiveOrdersByStatus[status] || []}
|
||||
onCardClick={setSelectedOrder}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -10,11 +10,16 @@ const STATUS_COLORS = {
|
||||
'закрыт': '#22c55e',
|
||||
};
|
||||
|
||||
const KanbanColumn = ({ status, orders, onCardClick }) => {
|
||||
const KanbanColumn = ({ status, orders, onCardClick, isMobile }) => {
|
||||
const { setNodeRef, isOver } = useDroppable({ id: status });
|
||||
|
||||
return (
|
||||
<div style={{ flex: '1 1 200px', minWidth: '215px', maxWidth: '270px', display: 'flex', flexDirection: 'column', gap: '10px' }}>
|
||||
<div style={{
|
||||
flex: isMobile ? '0 0 260px' : '1 1 200px',
|
||||
minWidth: isMobile ? '260px' : '215px',
|
||||
maxWidth: isMobile ? '260px' : '270px',
|
||||
display: 'flex', flexDirection: 'column', gap: '10px',
|
||||
}}>
|
||||
{/* Header */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: '8px',
|
||||
|
||||
@@ -4,6 +4,7 @@ import { pdf } from '@react-pdf/renderer';
|
||||
import { supabase } from '../../lib/supabase';
|
||||
import { useAuth } from '../auth/useAuth';
|
||||
import { usePricing } from '../../hooks/usePricing';
|
||||
import { useIsMobile } from '../../hooks/useIsMobile';
|
||||
import { InvoicePDF } from './InvoicePDF';
|
||||
|
||||
const STATUSES = ['новая', 'замер', 'согласование', 'монтаж', 'закрыт'];
|
||||
@@ -34,6 +35,7 @@ const labelStyle = {
|
||||
const OrderModal = ({ order, onClose, onSaved, mode = 'edit' }) => {
|
||||
const { session } = useAuth();
|
||||
const { prices } = usePricing();
|
||||
const isMobile = useIsMobile();
|
||||
const [form, setForm] = useState({
|
||||
name: order?.name || '',
|
||||
phone: order?.phone || '',
|
||||
@@ -178,7 +180,7 @@ const OrderModal = ({ order, onClose, onSaved, mode = 'edit' }) => {
|
||||
style={{
|
||||
position: 'fixed', inset: 0,
|
||||
background: 'rgba(15, 23, 42, 0.55)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
display: 'flex', alignItems: isMobile ? 'flex-end' : 'center', justifyContent: 'center',
|
||||
zIndex: 1000, backdropFilter: 'blur(4px)',
|
||||
}}
|
||||
onClick={onClose}
|
||||
@@ -187,7 +189,16 @@ const OrderModal = ({ order, onClose, onSaved, mode = 'edit' }) => {
|
||||
ref={modalRef}
|
||||
tabIndex={-1}
|
||||
className="glass-panel"
|
||||
style={{ width: '100%', maxWidth: '620px', maxHeight: '90vh', overflow: 'auto', padding: '30px', margin: '20px' }}
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: isMobile ? '100%' : '620px',
|
||||
maxHeight: isMobile ? '92vh' : '90vh',
|
||||
overflow: 'auto',
|
||||
padding: isMobile ? '20px 16px' : '30px',
|
||||
margin: isMobile ? '0' : '20px',
|
||||
borderRadius: isMobile ? '20px 20px 0 0' : '16px',
|
||||
alignSelf: isMobile ? 'flex-end' : 'center',
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Header */}
|
||||
|
||||
@@ -177,8 +177,8 @@ const UserManagementPage = () => {
|
||||
{loading ? (
|
||||
<div style={{ color: 'var(--text-muted)', padding: '10px 0' }}>Загрузка...</div>
|
||||
) : (
|
||||
<div style={{ overflowX: 'auto' }}>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<div style={{ overflowX: 'auto', WebkitOverflowScrolling: 'touch' }}>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', minWidth: '600px' }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={thStyle}>Email</th>
|
||||
|
||||
Reference in New Issue
Block a user