.
This commit is contained in:
parent
0b5f180b6f
commit
8e80bfe31e
@ -148,6 +148,11 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
|
||||
getRowId: (originalRow: unknown) => originalRow?.id,
|
||||
debugHeaders: false,
|
||||
debugColumns: false,
|
||||
defaultColumn: {
|
||||
size: 8, //starting column size
|
||||
minSize: 1, //enforced during column resizing
|
||||
maxSize: 96, //enforced during column resizing
|
||||
},
|
||||
meta: {
|
||||
insertItem: (rowIndex: number, data: object = {}) => {
|
||||
actions.insert(rowIndex, data, { shouldFocus: true });
|
||||
@ -326,13 +331,14 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
|
||||
collisionDetection={closestCenter}
|
||||
>
|
||||
<SortableDataTableToolbar table={table} />
|
||||
<Table className='table-auto'>
|
||||
<Table className='table-fixed'>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id} className='hover:bg-transparent'>
|
||||
{headerGroup.headers.map((header) => {
|
||||
console.log(header.getSize());
|
||||
return (
|
||||
<TableHead key={header.id} className='px-1'>
|
||||
<TableHead key={header.id} className={`px-1 w-${header.getSize()}`}>
|
||||
{header.isPlaceholder ? null : (
|
||||
<DataTableColumnHeader table={table} header={header} />
|
||||
)}
|
||||
@ -350,11 +356,7 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
|
||||
{filterItems(table.getRowModel().rows).map((row) => (
|
||||
<SortableTableRow key={row.id} id={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
className='p-1 align-top'
|
||||
key={cell.id}
|
||||
style={{ width: cell.column.getSize() }}
|
||||
>
|
||||
<TableCell key={cell.id} className='px-2 py-1 align-top'>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
|
||||
@ -58,7 +58,10 @@ export function SortableTableRow({ id, children }: PropsWithChildren<SortablePro
|
||||
<TableRow
|
||||
key={id}
|
||||
id={String(id)}
|
||||
className={cn(isDragging ? "opacity-40" : "opacity-100", "hover:bg-muted/30 m-0")}
|
||||
className={cn(
|
||||
isDragging ? "opacity-40" : "opacity-100",
|
||||
"m-0 hover:bg-muted hover:focus-within:bg-accent focus-within:bg-accent"
|
||||
)}
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
>
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import {
|
||||
FormCurrencyField,
|
||||
FormQuantityField,
|
||||
FormTextAreaField,
|
||||
FormTextField,
|
||||
} from "@/components";
|
||||
import { FormQuantityField, FormTextField } from "@/components";
|
||||
import { DataTableProvider } from "@/lib/hooks";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/ui";
|
||||
import { CurrencyData, Quantity } from "@shared/contexts";
|
||||
import { t } from "i18next";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useFieldArray, useFormContext } from "react-hook-form";
|
||||
import { useDetailColumns } from "../../hooks";
|
||||
@ -38,32 +34,42 @@ export const QuoteDetailsCardEditor = ({ currency }: { currency: CurrencyData })
|
||||
{
|
||||
id: "quantity" as const,
|
||||
accessorKey: "quantity",
|
||||
header: "quantity",
|
||||
header: () => (
|
||||
<div className='text-right'>{t("quotes.form_fields.items.quantity.label")}</div>
|
||||
),
|
||||
size: 5,
|
||||
cell: ({ row: { index } }) => {
|
||||
return <FormQuantityField {...register(`items.${index}.quantity`)} />;
|
||||
return (
|
||||
<FormQuantityField
|
||||
variant='outline'
|
||||
precision={2}
|
||||
{...register(`items.${index}.quantity`)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "description" as const,
|
||||
accessorKey: "description",
|
||||
header: "description",
|
||||
header: t("quotes.form_fields.items.description.label"),
|
||||
size: 24,
|
||||
cell: ({ row: { index } }) => {
|
||||
return <FormTextAreaField autoSize {...register(`items.${index}.description`)} />;
|
||||
return <FormTextField autoSize {...register(`items.${index}.description`)} />;
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: "unit_price" as const,
|
||||
accessorKey: "unit_price",
|
||||
header: "unit_price",
|
||||
size: 10,
|
||||
header: () => (
|
||||
<div className='text-right'>{t("quotes.form_fields.items.unit_price.label")}</div>
|
||||
),
|
||||
cell: ({ row: { index }, column: { id } }) => {
|
||||
return (
|
||||
<FormCurrencyField
|
||||
<FormTextField
|
||||
variant='outline'
|
||||
currency={currency}
|
||||
precision={4}
|
||||
className='text-right'
|
||||
{...register(`items.${index}.unit_price`)}
|
||||
/>
|
||||
);
|
||||
@ -72,8 +78,9 @@ export const QuoteDetailsCardEditor = ({ currency }: { currency: CurrencyData })
|
||||
{
|
||||
id: "subtotal_price" as const,
|
||||
accessorKey: "subtotal_price",
|
||||
header: "subtotal_price",
|
||||
size: 10,
|
||||
header: () => (
|
||||
<div className='text-right'>{t("quotes.form_fields.items.subtotal_price.label")}</div>
|
||||
),
|
||||
cell: ({ row: { index }, column: { id } }) => {
|
||||
return <FormTextField {...register(`items.${index}.subtotal_price`)} />;
|
||||
},
|
||||
@ -81,17 +88,20 @@ export const QuoteDetailsCardEditor = ({ currency }: { currency: CurrencyData })
|
||||
{
|
||||
id: "discount" as const,
|
||||
accessorKey: "discount",
|
||||
header: "discount",
|
||||
size: 5,
|
||||
header: () => (
|
||||
<div className='text-right'>{t("quotes.form_fields.items.discount.label")}</div>
|
||||
),
|
||||
cell: ({ row: { index }, column: { id } }) => {
|
||||
return <FormTextField {...register(`items.${index}.discount`)} />;
|
||||
return <FormTextField className='text-right' {...register(`items.${index}.discount`)} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "total_price" as const,
|
||||
accessorKey: "total_price",
|
||||
header: "total_price",
|
||||
size: 10,
|
||||
header: () => (
|
||||
<div className='text-right'>{t("quotes.form_fields.items.total_price.label")}</div>
|
||||
),
|
||||
cell: ({ row: { index }, column: { id } }) => {
|
||||
return <FormTextField {...register(`items.${index}.total_price`)} />;
|
||||
},
|
||||
@ -151,6 +161,8 @@ export const QuoteDetailsCardEditor = ({ currency }: { currency: CurrencyData })
|
||||
const defaultLayout = [265, 440, 655];
|
||||
const navCollapsedSize = 4;
|
||||
|
||||
return <SortableDataTable actions={fieldActions} columns={columns} data={fields} />;
|
||||
|
||||
return (
|
||||
<ResizablePanelGroup
|
||||
direction='horizontal'
|
||||
|
||||
@ -5,27 +5,17 @@ import {
|
||||
} from "@/components";
|
||||
import { Checkbox } from "@/ui";
|
||||
import { ColumnDef, Row, Table } from "@tanstack/react-table";
|
||||
import { MoreHorizontalIcon } from "lucide-react";
|
||||
|
||||
import { useMemo } from "react";
|
||||
|
||||
/*function getSelectedRowRange<T>(
|
||||
rows: Row<T>[],
|
||||
currentID: number,
|
||||
selectedID: number,
|
||||
): Row<T>[] {
|
||||
const rangeStart = selectedID > currentID ? currentID : selectedID;
|
||||
const rangeEnd = rangeStart === currentID ? selectedID : currentID;
|
||||
return rows.slice(rangeStart, rangeEnd + 1);
|
||||
}*/
|
||||
|
||||
export function useDetailColumns<TData, TValue>(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function useDetailColumns<TData, TValue = unknown>(
|
||||
columns: ColumnDef<TData>[],
|
||||
options: {
|
||||
enableDragHandleColumn?: boolean;
|
||||
enableSelectionColumn?: boolean;
|
||||
enableActionsColumn?: boolean;
|
||||
rowActionFn?: DataTablaRowActionFunction<TData, TValue>;
|
||||
rowActionFn?: DataTablaRowActionFunction<TData>;
|
||||
} = {}
|
||||
): ColumnDef<TData>[] {
|
||||
const {
|
||||
@ -67,7 +57,9 @@ export function useDetailColumns<TData, TValue>(
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
size: 30,
|
||||
size: 2,
|
||||
minSize: 2,
|
||||
maxSize: 2,
|
||||
});
|
||||
}
|
||||
|
||||
@ -79,7 +71,10 @@ export function useDetailColumns<TData, TValue>(
|
||||
),*/
|
||||
header: () => null,
|
||||
cell: (info) => <DataTableRowDragHandleCell rowId={info.row.id} />,
|
||||
size: 16,
|
||||
size: 2,
|
||||
minSize: 2,
|
||||
maxSize: 2,
|
||||
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
});
|
||||
@ -88,16 +83,23 @@ export function useDetailColumns<TData, TValue>(
|
||||
if (enableActionsColumn) {
|
||||
columns.push({
|
||||
id: "row_actions",
|
||||
header: () => (
|
||||
/*header: () => (
|
||||
<MoreHorizontalIcon
|
||||
aria-label='Acciones'
|
||||
className='items-center justify-center w-4 h-4'
|
||||
/>
|
||||
),
|
||||
),*/
|
||||
cell: (props) => {
|
||||
return <DataTableRowActions {...props} actions={rowActionFn} />;
|
||||
return (
|
||||
<div className='w-full mx-auto text-center'>
|
||||
<DataTableRowActions rowContext={props} actions={rowActionFn} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
size: 16,
|
||||
size: 4,
|
||||
minSize: 4,
|
||||
maxSize: 4,
|
||||
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
});
|
||||
|
||||
@ -28,7 +28,7 @@ export function DataTableColumnHeader<TData, TValue>({
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
"data-[state=open]:bg-accent font-bold text-muted-foreground uppercase text-xs tracking-wide",
|
||||
"data-[state=open]:bg-accent font-bold text-muted-foreground uppercase text-xs tracking-wide text-ellipsis",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
@ -10,32 +11,31 @@ import {
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/ui";
|
||||
import { CellContext, Row } from "@tanstack/react-table";
|
||||
import { CellContext } from "@tanstack/react-table";
|
||||
import { t } from "i18next";
|
||||
import { MoreHorizontalIcon } from "lucide-react";
|
||||
import { MoreVerticalIcon } from "lucide-react";
|
||||
|
||||
type DataTableRowActionContext<TData, TValue> = CellContext<TData, TValue> & {
|
||||
row: Row<TData>;
|
||||
};
|
||||
export type DataTablaRowActionFunction<TData> = (
|
||||
props: CellContext<TData, unknown>
|
||||
) => DataTableRowActionDefinition<TData>[];
|
||||
|
||||
export type DataTablaRowActionFunction<TData, TValue> = (
|
||||
props: DataTableRowActionContext<TData, TValue>
|
||||
) => DataTableRowActionDefinition<TData, TValue>[];
|
||||
|
||||
export type DataTableRowActionDefinition<TData, TValue> = {
|
||||
export type DataTableRowActionDefinition<TData> = {
|
||||
label: string | "-";
|
||||
shortcut?: string;
|
||||
onClick?: (props: DataTableRowActionContext<TData, TValue>, e: React.BaseSyntheticEvent) => void;
|
||||
onClick?: (props: CellContext<TData, unknown>, e: React.BaseSyntheticEvent) => void;
|
||||
};
|
||||
|
||||
export type DataTableRowActionsProps<TData, TValue> = {
|
||||
actions?: DataTablaRowActionFunction<TData, TValue>;
|
||||
row?: DataTableRowActionContext<TData, TValue>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export type DataTableRowActionsProps<TData, TValue = unknown> = {
|
||||
className?: string;
|
||||
actions?: DataTablaRowActionFunction<TData>;
|
||||
rowContext: CellContext<TData, unknown>;
|
||||
};
|
||||
|
||||
export function DataTableRowActions<TData = any, TValue = any>({
|
||||
export function DataTableRowActions<TData = any, TValue = unknown>({
|
||||
actions,
|
||||
...props
|
||||
rowContext,
|
||||
className,
|
||||
}: DataTableRowActionsProps<TData, TValue>) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@ -44,9 +44,9 @@ export function DataTableRowActions<TData = any, TValue = any>({
|
||||
aria-haspopup='true'
|
||||
size='icon'
|
||||
variant='link'
|
||||
className='w-4 h-4 translate-y-[2px]'
|
||||
className={cn("w-4 h-4 mt-2 text-ring hover:text-muted-foreground", className)}
|
||||
>
|
||||
<MoreHorizontalIcon className='w-4 h-4' />
|
||||
<MoreVerticalIcon className='w-4 h-4' />
|
||||
<span className='sr-only'>{t("common.open_menu")}</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
@ -54,13 +54,13 @@ export function DataTableRowActions<TData = any, TValue = any>({
|
||||
<DropdownMenuContent align='end'>
|
||||
<DropdownMenuLabel>{t("common.actions")} </DropdownMenuLabel>
|
||||
{actions &&
|
||||
actions(props).map((action, index) =>
|
||||
actions(rowContext).map((action, index) =>
|
||||
action.label === "-" ? (
|
||||
<DropdownMenuSeparator key={index} />
|
||||
) : (
|
||||
<DropdownMenuItem
|
||||
key={index}
|
||||
onClick={(event) => (action.onClick ? action.onClick(props, event) : null)}
|
||||
onClick={(event) => (action.onClick ? action.onClick(rowContext, event) : null)}
|
||||
>
|
||||
{action.label}
|
||||
<DropdownMenuShortcut>{action.shortcut}</DropdownMenuShortcut>
|
||||
|
||||
@ -9,15 +9,14 @@ import { FormErrorMessage } from "./FormErrorMessage";
|
||||
import { FormLabel, FormLabelProps } from "./FormLabel";
|
||||
import { FormInputProps, FormInputWithIconProps } from "./FormProps";
|
||||
|
||||
export const formCurrencyFieldVariants = cva(
|
||||
const formCurrencyFieldVariants = cva(
|
||||
"flex h-10 w-full rounded-md bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border border-input ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ",
|
||||
outline:
|
||||
"ring-offset-background focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 ",
|
||||
outline: "focus-visible:border focus-visible:border-input",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@ -31,6 +30,7 @@ export type FormCurrencyFieldProps<
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
> = {
|
||||
button?: (props?: React.PropsWithChildren) => React.ReactNode;
|
||||
defaultValue?: any;
|
||||
} & InputProps &
|
||||
FormInputProps &
|
||||
Partial<FormLabelProps> &
|
||||
@ -48,6 +48,7 @@ export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrency
|
||||
label,
|
||||
hint,
|
||||
description,
|
||||
placeholder,
|
||||
className,
|
||||
disabled,
|
||||
defaultValue,
|
||||
@ -97,8 +98,7 @@ export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrency
|
||||
suffix={` ${currency?.symbol}`}
|
||||
groupSeparator='.'
|
||||
decimalSeparator=','
|
||||
//placeholder={`0 ${fieldCurrenty.value?.symbol}`}
|
||||
|
||||
placeholder={placeholder}
|
||||
//fixedDecimalLength={precision} <- no activar para que sea más cómodo escribir las cantidades
|
||||
decimalsLimit={precision}
|
||||
decimalScale={precision}
|
||||
|
||||
@ -1,19 +1,30 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { FormControl, FormDescription, FormItem, InputProps } from "@/ui";
|
||||
import * as React from "react";
|
||||
|
||||
import { Quantity, QuantityObject } from "@shared/contexts";
|
||||
import { createElement, forwardRef, useState } from "react";
|
||||
import {
|
||||
Controller,
|
||||
FieldPath,
|
||||
FieldValues,
|
||||
UseControllerProps,
|
||||
useFormContext,
|
||||
} from "react-hook-form";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { FormControl, FormDescription, FormField, FormItem, Input, InputProps } from "@/ui";
|
||||
import { Quantity } from "@shared/contexts";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { FieldPath, FieldValues, UseControllerProps, useFormContext } from "react-hook-form";
|
||||
import { FormErrorMessage } from "./FormErrorMessage";
|
||||
import { FormLabel, FormLabelProps } from "./FormLabel";
|
||||
import { FormInputProps, FormInputWithIconProps } from "./FormProps";
|
||||
|
||||
const formQuantityFieldVariants = cva(
|
||||
"text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "",
|
||||
outline:
|
||||
"border-0 focus-visible:border focus-visible:border-input focus-visible:ring-0 focus-visible:ring-offset-0 ",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export type FormQuantityFieldProps<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
@ -24,139 +35,82 @@ export type FormQuantityFieldProps<
|
||||
FormInputProps &
|
||||
Partial<FormLabelProps> &
|
||||
FormInputWithIconProps &
|
||||
UseControllerProps<TFieldValues, TName>;
|
||||
|
||||
export const FormQuantityField = forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & FormQuantityFieldProps
|
||||
>((props, ref) => {
|
||||
const {
|
||||
name,
|
||||
label,
|
||||
hint,
|
||||
placeholder,
|
||||
description,
|
||||
|
||||
required,
|
||||
className,
|
||||
leadIcon,
|
||||
trailIcon,
|
||||
button,
|
||||
defaultValue,
|
||||
} = props;
|
||||
|
||||
const { control } = useFormContext();
|
||||
|
||||
const [precision, setPrecision] = useState<number>(Quantity.DEFAULT_PRECISION);
|
||||
|
||||
const transform = {
|
||||
input: (value: QuantityObject) => {
|
||||
const quantityOrError = Quantity.create(value);
|
||||
|
||||
if (quantityOrError.isFailure) {
|
||||
throw quantityOrError.error;
|
||||
}
|
||||
|
||||
const quantityValue = quantityOrError.object;
|
||||
setPrecision(quantityValue.getPrecision());
|
||||
return quantityValue.toString();
|
||||
},
|
||||
output: (event: React.ChangeEvent<HTMLInputElement>): QuantityObject => {
|
||||
const value = parseFloat(event.target.value);
|
||||
const output = !isNaN(value) ? value : 0;
|
||||
|
||||
const quantityOrError = Quantity.create({
|
||||
amount: output * Math.pow(10, precision),
|
||||
precision,
|
||||
});
|
||||
|
||||
if (quantityOrError.isFailure) {
|
||||
throw quantityOrError.error;
|
||||
}
|
||||
|
||||
return quantityOrError.object.toObject();
|
||||
},
|
||||
UseControllerProps<TFieldValues, TName> &
|
||||
VariantProps<typeof formQuantityFieldVariants> & {
|
||||
precision: number;
|
||||
};
|
||||
|
||||
return (
|
||||
<Controller
|
||||
defaultValue={defaultValue}
|
||||
control={control}
|
||||
name={name}
|
||||
rules={{
|
||||
required,
|
||||
}}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
render={({ field, fieldState, formState }) => {
|
||||
return (
|
||||
<input
|
||||
type='number'
|
||||
{...field}
|
||||
className='text-right'
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => field.onChange(transform.output(e))}
|
||||
value={transform.input(field.value)}
|
||||
/>
|
||||
);
|
||||
export const FormQuantityField = React.forwardRef<HTMLInputElement, FormQuantityFieldProps>(
|
||||
(props, ref) => {
|
||||
const {
|
||||
name,
|
||||
label,
|
||||
hint,
|
||||
description,
|
||||
placeholder,
|
||||
className,
|
||||
disabled,
|
||||
defaultValue,
|
||||
rules,
|
||||
precision,
|
||||
variant,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<FormItem ref={ref} className={cn(className, "space-y-3")}>
|
||||
{label && <FormLabel label={label} hint={hint} required={required} />}
|
||||
<div className={cn(button ? "flex" : null)}>
|
||||
<div
|
||||
className={cn(
|
||||
leadIcon ? "relative flex items-stretch flex-grow focus-within:z-10" : ""
|
||||
)}
|
||||
>
|
||||
{leadIcon && (
|
||||
<div className='absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none'>
|
||||
{createElement(
|
||||
leadIcon,
|
||||
{
|
||||
className: "h-5 w-5 text-muted-foreground",
|
||||
"aria-hidden": true,
|
||||
},
|
||||
null
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
const { control } = useFormContext();
|
||||
|
||||
<FormControl
|
||||
className={cn("block", leadIcon ? "pl-10" : "", trailIcon ? "pr-10" : "")}
|
||||
>
|
||||
<input
|
||||
type='number'
|
||||
placeholder={placeholder}
|
||||
className={cn(
|
||||
fieldState.error ? "border-destructive focus-visible:ring-destructive" : ""
|
||||
)}
|
||||
{...field}
|
||||
onInput={(e) => field.onChange(transform.output(e))}
|
||||
value={transform.input(field.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
const transformToInput = (value: any) => {
|
||||
if (typeof value !== "object") {
|
||||
return value;
|
||||
}
|
||||
|
||||
{trailIcon && (
|
||||
<div className='absolute inset-y-0 right-0 flex items-center pl-3 pointer-events-none'>
|
||||
{createElement(
|
||||
trailIcon,
|
||||
{
|
||||
className: "h-5 w-5 text-muted-foreground",
|
||||
"aria-hidden": true,
|
||||
},
|
||||
null
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{button && <>{createElement(button)}</>}
|
||||
</div>
|
||||
const quantityOrError = Quantity.create(value);
|
||||
if (quantityOrError.isFailure) {
|
||||
throw quantityOrError.error;
|
||||
}
|
||||
|
||||
{description && <FormDescription>{description}</FormDescription>}
|
||||
<FormErrorMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return (
|
||||
quantityOrError.object
|
||||
.toNumber()
|
||||
//.toPrecision(precision ?? value.precision)
|
||||
.toString()
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormField
|
||||
defaultValue={defaultValue}
|
||||
control={control}
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
rules={rules}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem ref={ref} className={cn(className, "space-y-3")}>
|
||||
{label && <FormLabel label={label} hint={hint} required={rules?.required ?? false} />}
|
||||
<FormControl>
|
||||
<Input
|
||||
type='number'
|
||||
name={field.name}
|
||||
//ref={field.ref} <-- no activar que hace cosas raras
|
||||
onBlur={field.onBlur}
|
||||
disabled={field.disabled}
|
||||
className={cn(formQuantityFieldVariants({ variant, className }))}
|
||||
placeholder={placeholder}
|
||||
value={transformToInput(field.value)}
|
||||
onChange={(value) => {
|
||||
// "value" ya viene con los "0" de la precisión
|
||||
console.log(value);
|
||||
field.onChange(value ?? "");
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
{description && <FormDescription>{description}</FormDescription>}
|
||||
<FormErrorMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@ -190,6 +190,12 @@ export function useDataTable<TData, TValue>({
|
||||
debugTable: false,
|
||||
debugHeaders: false,
|
||||
debugColumns: false,
|
||||
|
||||
defaultColumn: {
|
||||
size: 5, //starting column size
|
||||
minSize: 0, //enforced during column resizing
|
||||
maxSize: 96, //enforced during column resizing
|
||||
},
|
||||
});
|
||||
|
||||
return { table };
|
||||
|
||||
@ -86,9 +86,6 @@
|
||||
"total_price": "Imp. total"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"draft": "Borrador"
|
||||
},
|
||||
"create": {
|
||||
"title": "Nueva cotización",
|
||||
"buttons": {
|
||||
@ -123,41 +120,76 @@
|
||||
"desc": ""
|
||||
}
|
||||
},
|
||||
"form_fields": {
|
||||
"date": {
|
||||
"label": "Fecha",
|
||||
"desc": "Fecha de esta cotización",
|
||||
"placeholder": ""
|
||||
},
|
||||
"reference": {
|
||||
"label": "Referencia",
|
||||
"desc": "Referencia para esta cotización",
|
||||
"placeholder": ""
|
||||
},
|
||||
"customer_information": {
|
||||
"label": "Datos del cliente",
|
||||
"desc": "Escriba el nombre del cliente en la primera línea, la direccion en la segunda y el código postal y ciudad en la tercera.",
|
||||
"placeholder": "Nombre y apellidos\nCalle y número\nCódigo postal y ciudad..."
|
||||
},
|
||||
"payment_method": {
|
||||
"label": "Forma de pago",
|
||||
"placeholder": "placeholder",
|
||||
"desc": "desc"
|
||||
},
|
||||
"notes": {
|
||||
"label": "Notas",
|
||||
"placeholder": "",
|
||||
"desc": "desc"
|
||||
},
|
||||
"validity": {
|
||||
"label": "Validez de la cotización",
|
||||
"placeholder": "",
|
||||
"desc": "desc"
|
||||
}
|
||||
"edit": {
|
||||
"title": "Cotización"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"title": "Cotización"
|
||||
"status": {
|
||||
"draft": "Borrador"
|
||||
},
|
||||
"form_fields": {
|
||||
"date": {
|
||||
"label": "Fecha",
|
||||
"desc": "Fecha de esta cotización",
|
||||
"placeholder": ""
|
||||
},
|
||||
"reference": {
|
||||
"label": "Referencia",
|
||||
"desc": "Referencia para esta cotización",
|
||||
"placeholder": ""
|
||||
},
|
||||
"customer_information": {
|
||||
"label": "Datos del cliente",
|
||||
"desc": "Escriba el nombre del cliente en la primera línea, la direccion en la segunda y el código postal y ciudad en la tercera.",
|
||||
"placeholder": "Nombre y apellidos\nCalle y número\nCódigo postal y ciudad..."
|
||||
},
|
||||
"payment_method": {
|
||||
"label": "Forma de pago",
|
||||
"placeholder": "placeholder",
|
||||
"desc": "desc"
|
||||
},
|
||||
"notes": {
|
||||
"label": "Notas",
|
||||
"placeholder": "",
|
||||
"desc": "desc"
|
||||
},
|
||||
"validity": {
|
||||
"label": "Validez de la cotización",
|
||||
"placeholder": "",
|
||||
"desc": "desc"
|
||||
},
|
||||
"items": {
|
||||
"quantity": {
|
||||
"label": "Cantidad",
|
||||
"placeholder": "",
|
||||
"desc": ""
|
||||
},
|
||||
"description": {
|
||||
"label": "Descripción",
|
||||
"placeholder": "",
|
||||
"desc": ""
|
||||
},
|
||||
"unit_price": {
|
||||
"label": "Imp. unitario",
|
||||
"placeholder": "",
|
||||
"desc": "Importe unitario del artículo"
|
||||
},
|
||||
"subtotal_price": {
|
||||
"label": "Subtotal",
|
||||
"placeholder": "",
|
||||
"desc": ""
|
||||
},
|
||||
"discount": {
|
||||
"label": "Dto (%)",
|
||||
"placeholder": "",
|
||||
"desc": "Porcentaje de descuento"
|
||||
},
|
||||
"total_price": {
|
||||
"label": "Imp. total",
|
||||
"placeholder": "",
|
||||
"desc": "Importe total con el descuento ya aplicado"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user