Dashboard.jsx (1346 -> 378 LOC): - DashKpiCards, DashQuickActions, DashActivityFeed, DashAttendanceToday, DashProfile, DashSessions - dashboardHelpers.js (konstanty + helper funkce) OfferDetail.jsx (1061 -> ~530 LOC): - useOfferForm hook (form state, draft, items/sections, submit) - OfferCustomerPicker (customer search/select dropdown) AttendanceAdmin.jsx (1036 -> ~275 LOC): - useAttendanceAdmin hook (data fetching, filters, CRUD, print) - AttendanceShiftTable (shift records table) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
91 lines
3.0 KiB
JavaScript
91 lines
3.0 KiB
JavaScript
import { useState, useEffect, useMemo } from 'react'
|
|
|
|
export default function OfferCustomerPicker({
|
|
customers,
|
|
customerId,
|
|
customerName,
|
|
onSelect,
|
|
onClear,
|
|
error,
|
|
readOnly
|
|
}) {
|
|
const [customerSearch, setCustomerSearch] = useState('')
|
|
const [showDropdown, setShowDropdown] = useState(false)
|
|
|
|
// Close dropdown on outside click
|
|
useEffect(() => {
|
|
const handleClickOutside = () => setShowDropdown(false)
|
|
if (showDropdown) {
|
|
document.addEventListener('click', handleClickOutside)
|
|
return () => document.removeEventListener('click', handleClickOutside)
|
|
}
|
|
}, [showDropdown])
|
|
|
|
const filteredCustomers = useMemo(() => {
|
|
if (!customerSearch) return customers
|
|
const q = customerSearch.toLowerCase()
|
|
return customers.filter(c =>
|
|
(c.name || '').toLowerCase().includes(q) ||
|
|
(c.company_id || '').includes(customerSearch) ||
|
|
(c.city || '').toLowerCase().includes(q)
|
|
)
|
|
}, [customers, customerSearch])
|
|
|
|
const handleSelect = (customer) => {
|
|
onSelect(customer)
|
|
setCustomerSearch('')
|
|
setShowDropdown(false)
|
|
}
|
|
|
|
return (
|
|
<div className={`admin-form-group${error ? ' has-error' : ''}`}>
|
|
<label className="admin-form-label required">Zákazník</label>
|
|
{customerId && (
|
|
<div className="offers-customer-selected">
|
|
<span>{customerName}</span>
|
|
{!readOnly && (
|
|
<button type="button" onClick={onClear} className="admin-btn-icon" title="Odebrat zákazníka" aria-label="Odebrat zákazníka">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
|
</svg>
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
{!customerId && !readOnly && (
|
|
<div className="offers-customer-select" onClick={(e) => e.stopPropagation()}>
|
|
<input
|
|
type="text"
|
|
value={customerSearch}
|
|
onChange={(e) => { setCustomerSearch(e.target.value); setShowDropdown(true) }}
|
|
onFocus={() => setShowDropdown(true)}
|
|
className="admin-form-input"
|
|
placeholder="Hledat zákazníka..."
|
|
/>
|
|
{showDropdown && (
|
|
<div className="offers-customer-dropdown">
|
|
{filteredCustomers.length === 0 ? (
|
|
<div className="offers-customer-dropdown-empty">
|
|
Žádní zákazníci
|
|
</div>
|
|
) : (
|
|
filteredCustomers.slice(0, 10).map(c => (
|
|
<div
|
|
key={c.id}
|
|
className="offers-customer-dropdown-item"
|
|
onMouseDown={() => handleSelect(c)}
|
|
>
|
|
<div>{c.name}</div>
|
|
{c.city && <div>{c.city}</div>}
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
{error && <span className="admin-form-error">{error}</span>}
|
|
</div>
|
|
)
|
|
}
|