Uecko_ERP/modules/customer-invoices/src/web/components/items/customer-invoice-items-card-editor.tsx

344 lines
9.6 KiB
TypeScript
Raw Normal View History

2025-07-07 18:25:13 +00:00
import {
FormControl,
FormField,
FormItem,
FormMessage,
Input,
Textarea,
} from "@repo/shadcn-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 { useTranslation } from "react-i18next";
import { useDetailColumns } from "../../hooks";
import { MODULE_NAME } from "../../manifest";
import { formatCurrency } from "../../pages/create/utils";
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(MODULE_NAME);
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("customer_invoices.form_fields.description.label"),
cell: ({ row: { index, original } }) => (
<FormField
control={control}
name={`items.${index}.description`}
render={({ field }) => (
<FormItem className='md:col-span-2'>
<FormControl>
<Textarea
placeholder={t("customer_invoices.form_fields.description.placeholder")}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
),
minSize: 200,
size: 400,
},
{
id: "quantity" as const,
accessorKey: "quantity",
header: () => (
<div className='text-right'>{t("customer_invoices.form_fields.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("customer_invoices.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("customer_invoices.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("customer_invoices.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: "total_price" as const,
accessorKey: "total_price",
header: () => (
<div className='text-right'>
{t("customer_invoices.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 handleAppendCatalogArticle = useCallback(
(article: any, quantity = 1) => {
fieldActions.append({
...article,
quantity: {
amount: 100 * quantity,
scale: Quantity.DEFAULT_SCALE,
},
unit_price: article.retail_price,
discount: {
amount: null,
scale: 2,
},
});
toast({
title: t("quotes.catalog_picker_dialog.toast_article_added"),
description: article.description,
});
},
[fieldActions, toast]
);
const handleAppendBlock = useCallback(
(block: any) => {
fieldActions.append({
description: `${block.title}\n${block.body}`,
quantity: {
amount: null,
scale: Quantity.DEFAULT_SCALE,
},
unit_price: {
amount: null,
scale: UnitPrice.DEFAULT_SCALE,
},
discount: {
amount: null,
scale: 2,
},
});
toast({
title: t("quotes.blocks_picker_dialog.toast_article_added"),
description: block.title,
});
},
[fieldActions]
);*/
const [isCollapsed, setIsCollapsed] = useState(false);
const defaultLayout = [265, 440, 655];
const navCollapsedSize = 4;
console.log(columns);
return (
<div className='relative'>
<CustomerInvoiceItemsSortableDataTable
actions={{
...fieldActions,
//pickCatalogArticle: () => setArticlePickerDialogOpen(true),
//pickBlock: () => setBlockPickerDialogOpen(true),
}}
columns={columns}
data={fields}
defaultValues={defaultValues}
/>
</div>
);
};