diff --git a/modules/customer-invoices/src/web/components/editor/items/item-row.tsx b/modules/customer-invoices/src/web/components/editor/items/item-row.tsx new file mode 100644 index 00000000..327e9384 --- /dev/null +++ b/modules/customer-invoices/src/web/components/editor/items/item-row.tsx @@ -0,0 +1,230 @@ +import { Button, Checkbox, TableCell, TableRow, Tooltip, TooltipContent, TooltipTrigger } from "@repo/shadcn-ui/components"; +import { ArrowDownIcon, ArrowUpIcon, CopyIcon, Trash2Icon } from "lucide-react"; +import { useEffect } from 'react'; +import { Controller, useFormContext } from "react-hook-form"; +import { useCalcInvoiceItemTotals } from '../../../hooks'; +import { useTranslation } from '../../../i18n'; +import { CustomerInvoiceItemFormData } from '../../../schemas'; +import { CustomerInvoiceTaxesMultiSelect } from '../../customer-invoice-taxes-multi-select'; +import { AmountDTOInputField } from './amount-dto-input-field'; +import { HoverCardTotalsSummary } from './hover-card-total-summary'; +import { PercentageDTOInputField } from './percentage-dto-input-field'; +import { QuantityDTOInputField } from './quantity-dto-input-field'; + +export type ItemRowProps = { + + item: CustomerInvoiceItemFormData; + rowIndex: number; + isSelected: boolean; + isFirst: boolean; + isLast: boolean; + readOnly: boolean; + onToggleSelect: () => void; + onDuplicate: () => void; + onMoveUp: () => void; + onMoveDown: () => void; + onRemove: () => void; +} + + +export const ItemRow = ({ item, + rowIndex, + isSelected, + isFirst, + isLast, + readOnly, + onToggleSelect, + onDuplicate, + onMoveUp, + onMoveDown, + onRemove, }: ItemRowProps) => { + const { t } = useTranslation(); + + const { control, setValue } = useFormContext(); + const totals = useCalcInvoiceItemTotals(item); + + console.log(totals); + + // sincroniza el total con el form + useEffect(() => { + if (totals?.totalDTO) { + setValue?.(`items.${rowIndex}.total_amount`, totals.totalDTO); + } + }, [totals.totalDTO, control, rowIndex]); + + + return ( + + {/* selección */} + + + + + + + {/* # */} + + + {rowIndex + 1} + + + + {/* description */} + + ( + { + const el = e.currentTarget; + el.style.height = "auto"; + el.style.height = `${el.scrollHeight}px`; + }} + /> + )} + /> + + + {/* qty */} + + + + + {/* unit */} + + + + + {/* discount */} + + + + + {/* taxes */} + + ( + + )} + /> + + + + {/* total (solo lectura) */} + + + + + + + {/* actions */} + + + {onDuplicate && ( + + + e.preventDefault()} + onClick={onDuplicate} + disabled={readOnly} + aria-label='Duplicar fila' + className='h-8 w-8 self-start -translate-y-[1px]' + > + + + + Duplicar + + )} + + {onMoveUp && ( + e.preventDefault()} + onClick={onMoveUp} + disabled={readOnly || isFirst} + aria-label='Mover arriba' + className='h-8 w-8 self-start -translate-y-[1px]' + > + + + )} + + {onMoveDown && ( + e.preventDefault()} + onClick={onMoveDown} + disabled={readOnly || isLast} + aria-label='Mover abajo' + className='h-8 w-8 self-start -translate-y-[1px]' + > + + + )} + + {onRemove && ( + e.preventDefault()} + onClick={onRemove} + disabled={readOnly} + aria-label='Eliminar fila' + className='h-8 w-8 self-start -translate-y-[1px]' + > + + + )} + + + + ); +} \ No newline at end of file diff --git a/modules/customer-invoices/src/web/components/editor/items/items-editor.tsx b/modules/customer-invoices/src/web/components/editor/items/items-editor.tsx index 23231c17..b4c938f1 100644 --- a/modules/customer-invoices/src/web/components/editor/items/items-editor.tsx +++ b/modules/customer-invoices/src/web/components/editor/items/items-editor.tsx @@ -1,18 +1,13 @@ import { useRowSelection } from '@repo/rdx-ui/hooks'; -import { Button, Checkbox, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tooltip, TooltipContent, TooltipTrigger } from "@repo/shadcn-ui/components"; -import { ArrowDown, ArrowUp, CopyIcon, Trash2 } from "lucide-react"; +import { Checkbox, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@repo/shadcn-ui/components"; import * as React from "react"; -import { Controller, useFormContext } from "react-hook-form"; -import { useCalcInvoiceItemTotals, useItemsTableNavigation } from '../../../hooks'; +import { useFormContext } from "react-hook-form"; +import { useItemsTableNavigation } from '../../../hooks'; import { useTranslation } from '../../../i18n'; import { CustomerInvoiceItemFormData, defaultCustomerInvoiceItemFormData } from '../../../schemas'; -import { CustomerInvoiceTaxesMultiSelect } from '../../customer-invoice-taxes-multi-select'; -import { AmountDTOInputField } from './amount-dto-input-field'; -import { HoverCardTotalsSummary } from './hover-card-total-summary'; +import { ItemRow } from './item-row'; import { ItemsEditorToolbar } from './items-editor-toolbar'; import { LastCellTabHook } from './last-cell-tab-hook'; -import { PercentageDTOInputField } from './percentage-dto-input-field'; -import { QuantityDTOInputField } from './quantity-dto-input-field'; interface ItemsEditorProps { value?: CustomerInvoiceItemFormData[]; @@ -105,183 +100,24 @@ export const ItemsEditor = ({ value = [], onChange, readOnly = false }: ItemsEdi - {tableNav.fa.fields.map((f, rowIndex) => { - const isFirst = rowIndex === 0; - const isLast = rowIndex === tableNav.fa.fields.length - 1; + {tableNav.fa.fields.map((f, rowIndex) => ( + toggleRow(rowIndex)} + onDuplicate={() => tableNav.duplicate(rowIndex)} + onMoveUp={() => tableNav.moveUp(rowIndex)} + onMoveDown={() => tableNav.moveDown(rowIndex)} + onRemove={() => tableNav.remove(rowIndex)} + /> + ))} - const item = form.watch(`items.${rowIndex}`); - const totals = useCalcInvoiceItemTotals(item); - - // sincronizar con react-hook-form - React.useEffect(() => { - form.setValue(`items.${rowIndex}.total_amount`, totals.totalDTO, { shouldDirty: true }); - }, [totals.totalDTO, form, rowIndex]); - - - return ( - - {/* selección */} - - - toggleRow(rowIndex)} - /> - - - - {/* # */} - - - {rowIndex + 1} - - - - {/* description */} - - ( - { - const el = e.currentTarget; - el.style.height = "auto"; - el.style.height = `${el.scrollHeight}px`; - }} - /> - )} - /> - - - {/* qty */} - - - - - {/* unit */} - - - - - {/* discount */} - - - - - {/* taxes */} - - ( - - )} - /> - - - - {/* total (solo lectura) */} - - - - - - - {/* actions */} - - - - - e.preventDefault()} - onClick={() => tableNav.duplicate(rowIndex)} - disabled={readOnly} - aria-label='Duplicar fila' - className='h-8 w-8 self-start translate-y-[-1px]' - > - - - - Duplicar - - e.preventDefault()} - onClick={() => tableNav.moveUp(rowIndex)} - disabled={readOnly || isFirst} - aria-label='Mover arriba' - className='h-8 w-8 self-start translate-y-[-1px]' - > - - - e.preventDefault()} - onClick={() => tableNav.moveDown(rowIndex)} - disabled={readOnly || isLast} - aria-label='Mover abajo' - className='h-8 w-8 self-start translate-y-[-1px]' - > - - - e.preventDefault()} - onClick={() => tableNav.remove(rowIndex)} - disabled={readOnly} - aria-label='Eliminar fila' - className='h-8 w-8 self-start translate-y-[-1px]' - > - - - - - - ); - })}