import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useNavigate, useSearchParams } from 'react-router-dom'; import { FaArrowLeft, FaSave, FaEdit, FaTimes, FaPlus, FaTrash, FaSpinner, FaShoppingCart, FaPaperPlane, FaTruck, FaChevronDown, FaChevronRight, FaPencilAlt, FaFileInvoiceDollar, FaBoxes, FaFolderOpen, } from 'react-icons/fa'; import { toast, ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import salesOrderService, { SalesOrder, SalesOrderItem, SalesTaxCharge } from '../services/salesOrderService'; import LinkField from '../components/LinkField'; import ActivityLog from '../components/ActivityLog'; import { formatFrappeApiError } from '../utils/frappeErrorMessage'; import { DEFAULT_COMPANY, DEFAULT_CURRENCY, DEFAULT_SALES_TAXES_TEMPLATE, taxRatePercent, displayTxnCurrency, } from '../constants/orgDefaults'; // ── Shared helpers ──────────────────────────────────────────────────────────── const FL: React.FC<{ children: React.ReactNode; required?: boolean }> = ({ children, required }) => ( ); const RV: React.FC<{ children?: React.ReactNode }> = ({ children }) => (
{children || -}
); const inputCls = 'w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-1 focus:ring-blue-400'; const numCls = inputCls + ' text-right'; const inlineNum = 'w-full px-2 py-1 text-sm text-right border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 focus:outline-none focus:ring-1 focus:ring-blue-400'; const inlineTxt = 'w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 focus:outline-none focus:ring-1 focus:ring-blue-400'; // ── Create Dropdown ─────────────────────────────────────────────────────────── const CreateDropdown: React.FC<{ items: { label: string; icon: React.ReactNode; onClick: () => void }[]; }> = ({ items }) => { const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { const handler = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, []); return (
{open && (
Create from this order
{items.map(({ label, icon, onClick }) => ( ))}
)}
); }; // ── Collapsible group inside row editor ─────────────────────────────────────── const RGroup: React.FC<{ title: string; children: React.ReactNode; defaultOpen?: boolean }> = ({ title, children, defaultOpen = false }) => { const [open, setOpen] = useState(defaultOpen); return (
{open &&
{children}
}
); }; // ── Item Row Editor ─────────────────────────────────────────────────────────── const SOItemRowEditor: React.FC<{ item: Partial; rowNo: number; onChange: (k: keyof SalesOrderItem, v: any) => void; onClose: () => void; onDelete: () => void; onInsertBelow: () => void; }> = ({ item, rowNo, onChange, onClose, onDelete, onInsertBelow }) => (
{/* Editor header */}
Editing Row #{rowNo}
{/* Row 1: Item Code + Delivery Date */}
Item Code onChange('item_code', v)} placeholder="Select item…" />
Delivery Date onChange('delivery_date', e.target.value)} className={inputCls} />
{/* Ensure delivery checkbox + Item Name */}
onChange('ensure_delivery_based_on_produced_serial_no', e.target.checked ? 1 : 0)} className="rounded" /> Ensure Delivery Based on Produced Serial No
Item Name onChange('item_name', e.target.value)} className={inputCls} placeholder="Item name…" />
{/* Description */}