288 lines
8.7 KiB
TypeScript
288 lines
8.7 KiB
TypeScript
import { FormControl, FormField, FormItem, FormMessage, Input } from "@repo/shadcn-ui/components";
|
|
|
|
import { TextAreaField } from "@repo/rdx-ui/components";
|
|
import { ColumnDef } from "@tanstack/react-table";
|
|
import { ChevronDownIcon, ChevronUpIcon, CopyIcon, Trash2Icon } from "lucide-react";
|
|
import { useState } from "react";
|
|
import { useFieldArray, useFormContext } from "react-hook-form";
|
|
import { useDetailColumns } from "../../hooks";
|
|
import { useTranslation } from "../../i18n";
|
|
import { formatCurrency } from "../../pages/create/utils";
|
|
import { CustomerInvoiceTaxesMultiSelect } from "../customer-invoice-taxes-multi-select";
|
|
import {
|
|
CustomerInvoiceItemsSortableDataTable,
|
|
RowIdData,
|
|
} from "./customer-invoice-items-sortable-datatable";
|
|
|
|
export const CustomerInvoiceItemsCardEditor = ({
|
|
//currency,
|
|
//language,
|
|
defaultValues,
|
|
}: {
|
|
//currency: CurrencyData;
|
|
//language: Language;
|
|
defaultValues: Readonly<{ [x: string]: any }> | undefined;
|
|
}) => {
|
|
const { t } = useTranslation();
|
|
|
|
const { control, watch, getValues } = useFormContext();
|
|
|
|
const watchedItems = watch("items");
|
|
|
|
//const [pickerMode] = useState<"dialog" | "panel">("dialog");
|
|
|
|
//const [articlePickerDialogOpen, setArticlePickerDialogOpen] = useState<boolean>(false);
|
|
//const [blockPickerDialogOpen, setBlockPickerDialogOpen] = useState<boolean>(false);
|
|
|
|
const { fields, ...fieldActions } = useFieldArray({
|
|
control,
|
|
name: "items",
|
|
});
|
|
|
|
const columns: ColumnDef<RowIdData, unknown>[] = useDetailColumns(
|
|
[
|
|
/*{
|
|
id: "row_id" as const,
|
|
header: () => (
|
|
<HashIcon aria-label='Orden de fila' className='items-center justify-center w-4 h-4' />
|
|
),
|
|
accessorFn: (_: unknown, index: number) => index + 1,
|
|
size: 5,
|
|
enableHiding: false,
|
|
enableSorting: false,
|
|
enableResizing: false,
|
|
},*/
|
|
/*{
|
|
id: "id_article" as const,
|
|
accessorKey: "id_article",
|
|
header: "artículo",
|
|
cell: ({ row: { index, original } }) => {
|
|
return (
|
|
<FormTextAreaField
|
|
readOnly={original?.id_article}
|
|
autoSize
|
|
{...register(`items.${index}.id_article`)}
|
|
/>
|
|
);
|
|
},
|
|
size: 500,
|
|
},*/
|
|
{
|
|
id: "description" as const,
|
|
accessorKey: "description",
|
|
header: t("form_fields.items.description.label"),
|
|
cell: ({ row: { index, original } }) => (
|
|
<TextAreaField
|
|
control={control}
|
|
name={`items.${index}.description`}
|
|
placeholder={t("form_fields.items.description.placeholder")}
|
|
className='md:col-span-2'
|
|
/>
|
|
),
|
|
minSize: 200,
|
|
size: 400,
|
|
},
|
|
{
|
|
id: "quantity" as const,
|
|
accessorKey: "quantity",
|
|
header: () => <div className='text-right'>{t("form_fields.items.quantity.label")}</div>,
|
|
cell: ({ row: { index } }) => (
|
|
<FormField
|
|
control={control}
|
|
name={`items.${index}.quantity.amount`}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Input
|
|
type='number'
|
|
step='0.01'
|
|
min='0'
|
|
{...field}
|
|
onChange={(e) => field.onChange(Number(e.target.value) * 100)}
|
|
value={field.value / 100}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
),
|
|
size: 75,
|
|
},
|
|
{
|
|
id: "unit_price" as const,
|
|
accessorKey: "unit_price",
|
|
header: () => <div className='text-right'>{t("form_fields.items.unit_price.label")}</div>,
|
|
cell: ({ row: { index } }) => (
|
|
<FormField
|
|
control={control}
|
|
name={`items.${index}.unit_price.amount`}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Input
|
|
type='number'
|
|
step='0.01'
|
|
min='0'
|
|
{...field}
|
|
onChange={(e) => field.onChange(Number(e.target.value) * 100)}
|
|
value={field.value / 100}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
),
|
|
size: 125,
|
|
},
|
|
{
|
|
id: "subtotal_price" as const,
|
|
accessorKey: "subtotal_price",
|
|
header: () => (
|
|
<div className='text-right'>{t("form_fields.items.subtotal_price.label")}</div>
|
|
),
|
|
cell: ({ row: { index } }) => {
|
|
/*return (
|
|
<FormCurrencyField
|
|
variant='ghost'
|
|
currency={currency}
|
|
language={language}
|
|
scale={2}
|
|
readOnly
|
|
className='text-right'
|
|
{...register(`items.${index}.subtotal_price`)}
|
|
/>
|
|
);*/
|
|
return null;
|
|
},
|
|
size: 150,
|
|
},
|
|
{
|
|
id: "discount" as const,
|
|
accessorKey: "discount",
|
|
header: () => <div className='text-right'>{t("form_fields.items.discount.label")}</div>,
|
|
cell: ({ row: { index } }) => (
|
|
<FormField
|
|
control={control}
|
|
name={`items.${index}.discount.amount`}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<Input
|
|
type='number'
|
|
step='0.01'
|
|
min='0'
|
|
max='100'
|
|
{...field}
|
|
onChange={(e) => field.onChange(Number(e.target.value) * 100)}
|
|
value={field.value / 100}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
),
|
|
size: 100,
|
|
},
|
|
{
|
|
id: "taxes" as const,
|
|
accessorKey: "taxes",
|
|
header: () => <div className='text-right'>{t("form_fields.items.taxes.label")}</div>,
|
|
cell: ({ row: { index } }) => (
|
|
<FormField
|
|
control={control}
|
|
name={`items.${index}.taxes.amount`}
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<CustomerInvoiceTaxesMultiSelect
|
|
{...field}
|
|
//onChange={(e) => field.onChange(Number(e.target.value) * 100)}
|
|
//value={field.value / 100}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
),
|
|
size: 150,
|
|
},
|
|
{
|
|
id: "total_price" as const,
|
|
accessorKey: "total_price",
|
|
header: () => <div className='text-right'>{t("form_fields.items.total_price.label")}</div>,
|
|
cell: ({ row: { index } }) => (
|
|
<>
|
|
{formatCurrency(
|
|
watchedItems[index]?.total_price?.amount || 0,
|
|
2,
|
|
getValues("currency")
|
|
)}
|
|
</>
|
|
),
|
|
size: 150,
|
|
},
|
|
],
|
|
{
|
|
enableDragHandleColumn: true,
|
|
enableSelectionColumn: true,
|
|
enableActionsColumn: true,
|
|
rowActionFn: (props) => {
|
|
const { table, row } = props;
|
|
return [
|
|
{
|
|
label: t("common.duplicate_row"),
|
|
icon: <CopyIcon className='w-4 h-4 mr-2' />,
|
|
onClick: () => table.options.meta?.duplicateItems(row.index),
|
|
},
|
|
{
|
|
label: t("common.insert_row_above"),
|
|
icon: <ChevronUpIcon className='w-4 h-4 mr-2' />,
|
|
onClick: () => table.options.meta?.insertItem(row.index),
|
|
},
|
|
{
|
|
label: t("common.insert_row_below"),
|
|
icon: <ChevronDownIcon className='w-4 h-4 mr-2' />,
|
|
onClick: () => table.options.meta?.insertItem(row.index + 1),
|
|
},
|
|
|
|
{
|
|
label: "-",
|
|
},
|
|
{
|
|
label: t("common.remove_row"),
|
|
//shortcut: "⌘⌫",
|
|
icon: <Trash2Icon className='w-4 h-4 mr-2' />,
|
|
onClick: () => {
|
|
table.options.meta?.deleteItems(row.index);
|
|
},
|
|
},
|
|
];
|
|
},
|
|
}
|
|
);
|
|
|
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
|
|
const defaultLayout = [265, 440, 655];
|
|
const navCollapsedSize = 4;
|
|
|
|
return (
|
|
<div className='relative'>
|
|
<CustomerInvoiceItemsSortableDataTable
|
|
actions={{
|
|
...fieldActions,
|
|
//pickCatalogArticle: () => setArticlePickerDialogOpen(true),
|
|
//pickBlock: () => setBlockPickerDialogOpen(true),
|
|
}}
|
|
columns={columns}
|
|
data={fields}
|
|
defaultValues={defaultValues}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|