This commit is contained in:
David Arranz 2024-07-11 13:08:53 +02:00
parent 0b5f180b6f
commit 8e80bfe31e
10 changed files with 261 additions and 250 deletions

View File

@ -148,6 +148,11 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
getRowId: (originalRow: unknown) => originalRow?.id, getRowId: (originalRow: unknown) => originalRow?.id,
debugHeaders: false, debugHeaders: false,
debugColumns: false, debugColumns: false,
defaultColumn: {
size: 8, //starting column size
minSize: 1, //enforced during column resizing
maxSize: 96, //enforced during column resizing
},
meta: { meta: {
insertItem: (rowIndex: number, data: object = {}) => { insertItem: (rowIndex: number, data: object = {}) => {
actions.insert(rowIndex, data, { shouldFocus: true }); actions.insert(rowIndex, data, { shouldFocus: true });
@ -326,13 +331,14 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
collisionDetection={closestCenter} collisionDetection={closestCenter}
> >
<SortableDataTableToolbar table={table} /> <SortableDataTableToolbar table={table} />
<Table className='table-auto'> <Table className='table-fixed'>
<TableHeader> <TableHeader>
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className='hover:bg-transparent'> <TableRow key={headerGroup.id} className='hover:bg-transparent'>
{headerGroup.headers.map((header) => { {headerGroup.headers.map((header) => {
console.log(header.getSize());
return ( return (
<TableHead key={header.id} className='px-1'> <TableHead key={header.id} className={`px-1 w-${header.getSize()}`}>
{header.isPlaceholder ? null : ( {header.isPlaceholder ? null : (
<DataTableColumnHeader table={table} header={header} /> <DataTableColumnHeader table={table} header={header} />
)} )}
@ -350,11 +356,7 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
{filterItems(table.getRowModel().rows).map((row) => ( {filterItems(table.getRowModel().rows).map((row) => (
<SortableTableRow key={row.id} id={row.id}> <SortableTableRow key={row.id} id={row.id}>
{row.getVisibleCells().map((cell) => ( {row.getVisibleCells().map((cell) => (
<TableCell <TableCell key={cell.id} className='px-2 py-1 align-top'>
className='p-1 align-top'
key={cell.id}
style={{ width: cell.column.getSize() }}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())} {flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell> </TableCell>
))} ))}

View File

@ -58,7 +58,10 @@ export function SortableTableRow({ id, children }: PropsWithChildren<SortablePro
<TableRow <TableRow
key={id} key={id}
id={String(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} ref={setNodeRef}
style={style} style={style}
> >

View File

@ -1,13 +1,9 @@
import { import { FormQuantityField, FormTextField } from "@/components";
FormCurrencyField,
FormQuantityField,
FormTextAreaField,
FormTextField,
} from "@/components";
import { DataTableProvider } from "@/lib/hooks"; import { DataTableProvider } from "@/lib/hooks";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/ui"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/ui";
import { CurrencyData, Quantity } from "@shared/contexts"; import { CurrencyData, Quantity } from "@shared/contexts";
import { t } from "i18next";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { useFieldArray, useFormContext } from "react-hook-form"; import { useFieldArray, useFormContext } from "react-hook-form";
import { useDetailColumns } from "../../hooks"; import { useDetailColumns } from "../../hooks";
@ -38,32 +34,42 @@ export const QuoteDetailsCardEditor = ({ currency }: { currency: CurrencyData })
{ {
id: "quantity" as const, id: "quantity" as const,
accessorKey: "quantity", accessorKey: "quantity",
header: "quantity", header: () => (
<div className='text-right'>{t("quotes.form_fields.items.quantity.label")}</div>
),
size: 5, size: 5,
cell: ({ row: { index } }) => { cell: ({ row: { index } }) => {
return <FormQuantityField {...register(`items.${index}.quantity`)} />; return (
<FormQuantityField
variant='outline'
precision={2}
{...register(`items.${index}.quantity`)}
/>
);
}, },
}, },
{ {
id: "description" as const, id: "description" as const,
accessorKey: "description", accessorKey: "description",
header: "description", header: t("quotes.form_fields.items.description.label"),
size: 24,
cell: ({ row: { index } }) => { cell: ({ row: { index } }) => {
return <FormTextAreaField autoSize {...register(`items.${index}.description`)} />; return <FormTextField autoSize {...register(`items.${index}.description`)} />;
}, },
}, },
{ {
id: "unit_price" as const, id: "unit_price" as const,
accessorKey: "unit_price", accessorKey: "unit_price",
header: "unit_price", header: () => (
size: 10, <div className='text-right'>{t("quotes.form_fields.items.unit_price.label")}</div>
),
cell: ({ row: { index }, column: { id } }) => { cell: ({ row: { index }, column: { id } }) => {
return ( return (
<FormCurrencyField <FormTextField
variant='outline' variant='outline'
currency={currency} currency={currency}
precision={4} precision={4}
className='text-right'
{...register(`items.${index}.unit_price`)} {...register(`items.${index}.unit_price`)}
/> />
); );
@ -72,8 +78,9 @@ export const QuoteDetailsCardEditor = ({ currency }: { currency: CurrencyData })
{ {
id: "subtotal_price" as const, id: "subtotal_price" as const,
accessorKey: "subtotal_price", accessorKey: "subtotal_price",
header: "subtotal_price", header: () => (
size: 10, <div className='text-right'>{t("quotes.form_fields.items.subtotal_price.label")}</div>
),
cell: ({ row: { index }, column: { id } }) => { cell: ({ row: { index }, column: { id } }) => {
return <FormTextField {...register(`items.${index}.subtotal_price`)} />; return <FormTextField {...register(`items.${index}.subtotal_price`)} />;
}, },
@ -81,17 +88,20 @@ export const QuoteDetailsCardEditor = ({ currency }: { currency: CurrencyData })
{ {
id: "discount" as const, id: "discount" as const,
accessorKey: "discount", accessorKey: "discount",
header: "discount",
size: 5, size: 5,
header: () => (
<div className='text-right'>{t("quotes.form_fields.items.discount.label")}</div>
),
cell: ({ row: { index }, column: { id } }) => { 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, id: "total_price" as const,
accessorKey: "total_price", accessorKey: "total_price",
header: "total_price", header: () => (
size: 10, <div className='text-right'>{t("quotes.form_fields.items.total_price.label")}</div>
),
cell: ({ row: { index }, column: { id } }) => { cell: ({ row: { index }, column: { id } }) => {
return <FormTextField {...register(`items.${index}.total_price`)} />; return <FormTextField {...register(`items.${index}.total_price`)} />;
}, },
@ -151,6 +161,8 @@ export const QuoteDetailsCardEditor = ({ currency }: { currency: CurrencyData })
const defaultLayout = [265, 440, 655]; const defaultLayout = [265, 440, 655];
const navCollapsedSize = 4; const navCollapsedSize = 4;
return <SortableDataTable actions={fieldActions} columns={columns} data={fields} />;
return ( return (
<ResizablePanelGroup <ResizablePanelGroup
direction='horizontal' direction='horizontal'

View File

@ -5,27 +5,17 @@ import {
} from "@/components"; } from "@/components";
import { Checkbox } from "@/ui"; import { Checkbox } from "@/ui";
import { ColumnDef, Row, Table } from "@tanstack/react-table"; import { ColumnDef, Row, Table } from "@tanstack/react-table";
import { MoreHorizontalIcon } from "lucide-react";
import { useMemo } from "react"; import { useMemo } from "react";
/*function getSelectedRowRange<T>( // eslint-disable-next-line @typescript-eslint/no-unused-vars
rows: Row<T>[], export function useDetailColumns<TData, TValue = unknown>(
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>(
columns: ColumnDef<TData>[], columns: ColumnDef<TData>[],
options: { options: {
enableDragHandleColumn?: boolean; enableDragHandleColumn?: boolean;
enableSelectionColumn?: boolean; enableSelectionColumn?: boolean;
enableActionsColumn?: boolean; enableActionsColumn?: boolean;
rowActionFn?: DataTablaRowActionFunction<TData, TValue>; rowActionFn?: DataTablaRowActionFunction<TData>;
} = {} } = {}
): ColumnDef<TData>[] { ): ColumnDef<TData>[] {
const { const {
@ -67,7 +57,9 @@ export function useDetailColumns<TData, TValue>(
), ),
enableSorting: false, enableSorting: false,
enableHiding: false, enableHiding: false,
size: 30, size: 2,
minSize: 2,
maxSize: 2,
}); });
} }
@ -79,7 +71,10 @@ export function useDetailColumns<TData, TValue>(
),*/ ),*/
header: () => null, header: () => null,
cell: (info) => <DataTableRowDragHandleCell rowId={info.row.id} />, cell: (info) => <DataTableRowDragHandleCell rowId={info.row.id} />,
size: 16, size: 2,
minSize: 2,
maxSize: 2,
enableSorting: false, enableSorting: false,
enableHiding: false, enableHiding: false,
}); });
@ -88,16 +83,23 @@ export function useDetailColumns<TData, TValue>(
if (enableActionsColumn) { if (enableActionsColumn) {
columns.push({ columns.push({
id: "row_actions", id: "row_actions",
header: () => ( /*header: () => (
<MoreHorizontalIcon <MoreHorizontalIcon
aria-label='Acciones' aria-label='Acciones'
className='items-center justify-center w-4 h-4' className='items-center justify-center w-4 h-4'
/> />
), ),*/
cell: (props) => { 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, enableSorting: false,
enableHiding: false, enableHiding: false,
}); });

View File

@ -28,7 +28,7 @@ export function DataTableColumnHeader<TData, TValue>({
<> <>
<div <div
className={cn( 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 className
)} )}
> >

View File

@ -1,5 +1,6 @@
"use client"; "use client";
import { cn } from "@/lib/utils";
import { import {
Button, Button,
DropdownMenu, DropdownMenu,
@ -10,32 +11,31 @@ import {
DropdownMenuShortcut, DropdownMenuShortcut,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/ui"; } from "@/ui";
import { CellContext, Row } from "@tanstack/react-table"; import { CellContext } from "@tanstack/react-table";
import { t } from "i18next"; import { t } from "i18next";
import { MoreHorizontalIcon } from "lucide-react"; import { MoreVerticalIcon } from "lucide-react";
type DataTableRowActionContext<TData, TValue> = CellContext<TData, TValue> & { export type DataTablaRowActionFunction<TData> = (
row: Row<TData>; props: CellContext<TData, unknown>
}; ) => DataTableRowActionDefinition<TData>[];
export type DataTablaRowActionFunction<TData, TValue> = ( export type DataTableRowActionDefinition<TData> = {
props: DataTableRowActionContext<TData, TValue>
) => DataTableRowActionDefinition<TData, TValue>[];
export type DataTableRowActionDefinition<TData, TValue> = {
label: string | "-"; label: string | "-";
shortcut?: 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> = { // eslint-disable-next-line @typescript-eslint/no-unused-vars
actions?: DataTablaRowActionFunction<TData, TValue>; export type DataTableRowActionsProps<TData, TValue = unknown> = {
row?: DataTableRowActionContext<TData, TValue>; className?: string;
actions?: DataTablaRowActionFunction<TData>;
rowContext: CellContext<TData, unknown>;
}; };
export function DataTableRowActions<TData = any, TValue = any>({ export function DataTableRowActions<TData = any, TValue = unknown>({
actions, actions,
...props rowContext,
className,
}: DataTableRowActionsProps<TData, TValue>) { }: DataTableRowActionsProps<TData, TValue>) {
return ( return (
<DropdownMenu> <DropdownMenu>
@ -44,9 +44,9 @@ export function DataTableRowActions<TData = any, TValue = any>({
aria-haspopup='true' aria-haspopup='true'
size='icon' size='icon'
variant='link' 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> <span className='sr-only'>{t("common.open_menu")}</span>
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
@ -54,13 +54,13 @@ export function DataTableRowActions<TData = any, TValue = any>({
<DropdownMenuContent align='end'> <DropdownMenuContent align='end'>
<DropdownMenuLabel>{t("common.actions")} </DropdownMenuLabel> <DropdownMenuLabel>{t("common.actions")} </DropdownMenuLabel>
{actions && {actions &&
actions(props).map((action, index) => actions(rowContext).map((action, index) =>
action.label === "-" ? ( action.label === "-" ? (
<DropdownMenuSeparator key={index} /> <DropdownMenuSeparator key={index} />
) : ( ) : (
<DropdownMenuItem <DropdownMenuItem
key={index} key={index}
onClick={(event) => (action.onClick ? action.onClick(props, event) : null)} onClick={(event) => (action.onClick ? action.onClick(rowContext, event) : null)}
> >
{action.label} {action.label}
<DropdownMenuShortcut>{action.shortcut}</DropdownMenuShortcut> <DropdownMenuShortcut>{action.shortcut}</DropdownMenuShortcut>

View File

@ -9,15 +9,14 @@ import { FormErrorMessage } from "./FormErrorMessage";
import { FormLabel, FormLabelProps } from "./FormLabel"; import { FormLabel, FormLabelProps } from "./FormLabel";
import { FormInputProps, FormInputWithIconProps } from "./FormProps"; 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", "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: { variants: {
variant: { variant: {
default: default:
"border border-input ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ", "border border-input ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ",
outline: outline: "focus-visible:border focus-visible:border-input",
"ring-offset-background focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 ",
}, },
}, },
defaultVariants: { defaultVariants: {
@ -31,6 +30,7 @@ export type FormCurrencyFieldProps<
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = { > = {
button?: (props?: React.PropsWithChildren) => React.ReactNode; button?: (props?: React.PropsWithChildren) => React.ReactNode;
defaultValue?: any;
} & InputProps & } & InputProps &
FormInputProps & FormInputProps &
Partial<FormLabelProps> & Partial<FormLabelProps> &
@ -48,6 +48,7 @@ export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrency
label, label,
hint, hint,
description, description,
placeholder,
className, className,
disabled, disabled,
defaultValue, defaultValue,
@ -97,8 +98,7 @@ export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrency
suffix={` ${currency?.symbol}`} suffix={` ${currency?.symbol}`}
groupSeparator='.' groupSeparator='.'
decimalSeparator=',' decimalSeparator=','
//placeholder={`0 ${fieldCurrenty.value?.symbol}`} placeholder={placeholder}
//fixedDecimalLength={precision} <- no activar para que sea más cómodo escribir las cantidades //fixedDecimalLength={precision} <- no activar para que sea más cómodo escribir las cantidades
decimalsLimit={precision} decimalsLimit={precision}
decimalScale={precision} decimalScale={precision}

View File

@ -1,19 +1,30 @@
import { cn } from "@/lib/utils"; import * as React from "react";
import { FormControl, FormDescription, FormItem, InputProps } from "@/ui";
import { Quantity, QuantityObject } from "@shared/contexts"; import { cn } from "@/lib/utils";
import { createElement, forwardRef, useState } from "react"; import { FormControl, FormDescription, FormField, FormItem, Input, InputProps } from "@/ui";
import { import { Quantity } from "@shared/contexts";
Controller, import { cva, type VariantProps } from "class-variance-authority";
FieldPath, import { FieldPath, FieldValues, UseControllerProps, useFormContext } from "react-hook-form";
FieldValues,
UseControllerProps,
useFormContext,
} from "react-hook-form";
import { FormErrorMessage } from "./FormErrorMessage"; import { FormErrorMessage } from "./FormErrorMessage";
import { FormLabel, FormLabelProps } from "./FormLabel"; import { FormLabel, FormLabelProps } from "./FormLabel";
import { FormInputProps, FormInputWithIconProps } from "./FormProps"; 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< export type FormQuantityFieldProps<
TFieldValues extends FieldValues = FieldValues, TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
@ -24,139 +35,82 @@ export type FormQuantityFieldProps<
FormInputProps & FormInputProps &
Partial<FormLabelProps> & Partial<FormLabelProps> &
FormInputWithIconProps & FormInputWithIconProps &
UseControllerProps<TFieldValues, TName>; UseControllerProps<TFieldValues, TName> &
VariantProps<typeof formQuantityFieldVariants> & {
export const FormQuantityField = forwardRef< precision: number;
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();
},
}; };
return ( export const FormQuantityField = React.forwardRef<HTMLInputElement, FormQuantityFieldProps>(
<Controller (props, ref) => {
defaultValue={defaultValue} const {
control={control} name,
name={name} label,
rules={{ hint,
required, description,
}} placeholder,
// eslint-disable-next-line @typescript-eslint/no-unused-vars className,
render={({ field, fieldState, formState }) => { disabled,
return ( defaultValue,
<input rules,
type='number' precision,
{...field} variant,
className='text-right' } = props;
placeholder={placeholder}
onChange={(e) => field.onChange(transform.output(e))}
value={transform.input(field.value)}
/>
);
return ( const { control } = useFormContext();
<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>
)}
<FormControl const transformToInput = (value: any) => {
className={cn("block", leadIcon ? "pl-10" : "", trailIcon ? "pr-10" : "")} if (typeof value !== "object") {
> return value;
<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>
{trailIcon && ( const quantityOrError = Quantity.create(value);
<div className='absolute inset-y-0 right-0 flex items-center pl-3 pointer-events-none'> if (quantityOrError.isFailure) {
{createElement( throw quantityOrError.error;
trailIcon, }
{
className: "h-5 w-5 text-muted-foreground",
"aria-hidden": true,
},
null
)}
</div>
)}
</div>
{button && <>{createElement(button)}</>}
</div>
{description && <FormDescription>{description}</FormDescription>} return (
<FormErrorMessage /> quantityOrError.object
</FormItem> .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>
);
}}
/>
);
}
);

View File

@ -190,6 +190,12 @@ export function useDataTable<TData, TValue>({
debugTable: false, debugTable: false,
debugHeaders: false, debugHeaders: false,
debugColumns: false, debugColumns: false,
defaultColumn: {
size: 5, //starting column size
minSize: 0, //enforced during column resizing
maxSize: 96, //enforced during column resizing
},
}); });
return { table }; return { table };

View File

@ -86,9 +86,6 @@
"total_price": "Imp. total" "total_price": "Imp. total"
} }
}, },
"status": {
"draft": "Borrador"
},
"create": { "create": {
"title": "Nueva cotización", "title": "Nueva cotización",
"buttons": { "buttons": {
@ -123,41 +120,76 @@
"desc": "" "desc": ""
} }
}, },
"form_fields": { "edit": {
"date": { "title": "Cotización"
"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": { "status": {
"title": "Cotización" "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": { "settings": {