.
This commit is contained in:
parent
e1ae090c9f
commit
6a3e42acbb
@ -9,6 +9,7 @@ import {
|
||||
} from "@/ui";
|
||||
|
||||
import { DataTableProvider } from "@/lib/hooks";
|
||||
import { t } from "i18next";
|
||||
import { CatalogPickerDataTable } from "../CatalogPickerDataTable";
|
||||
|
||||
export const CatalogPickerDialog = ({
|
||||
@ -24,18 +25,15 @@ export const CatalogPickerDialog = ({
|
||||
<Dialog modal open={isOpen} onOpenChange={onOpenChange}>
|
||||
<DialogContent className='w-11/12 max-w-full'>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone. This will permanently delete your account and remove your
|
||||
data from our servers.
|
||||
</DialogDescription>
|
||||
<DialogTitle>{t("quotes.catalog_picker_dialog.title")}</DialogTitle>
|
||||
<DialogDescription>{t("quotes.catalog_picker_dialog.subtitle")}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DataTableProvider syncWithLocation={false} initialPageSize={5}>
|
||||
<CatalogPickerDataTable onSelect={onSelect} />
|
||||
</DataTableProvider>
|
||||
|
||||
<DialogFooter>
|
||||
<Button type='submit'>Choose</Button>
|
||||
<Button type='submit'>{t("common.close")}</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -11,6 +11,7 @@ import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/ui";
|
||||
import { CurrencyData, Language, Quantity } from "@shared/contexts";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { t } from "i18next";
|
||||
import { ChevronDownIcon, ChevronUpIcon, CopyIcon, Trash2Icon } from "lucide-react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useFieldArray, useFormContext } from "react-hook-form";
|
||||
import { useDetailColumns } from "../../hooks";
|
||||
@ -51,6 +52,16 @@ export const QuoteDetailsCardEditor = ({
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
},*/
|
||||
|
||||
{
|
||||
id: "description" as const,
|
||||
accessorKey: "description",
|
||||
header: t("quotes.form_fields.items.description.label"),
|
||||
size: 24,
|
||||
cell: ({ row: { index } }) => {
|
||||
return <FormTextAreaField autoSize {...register(`items.${index}.description`)} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "quantity" as const,
|
||||
accessorKey: "quantity",
|
||||
@ -61,7 +72,6 @@ export const QuoteDetailsCardEditor = ({
|
||||
cell: ({ row: { index } }) => {
|
||||
return (
|
||||
<FormQuantityField
|
||||
variant='outline'
|
||||
scale={0}
|
||||
className='text-right'
|
||||
{...register(`items.${index}.quantity`)}
|
||||
@ -69,21 +79,6 @@ export const QuoteDetailsCardEditor = ({
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "description" as const,
|
||||
accessorKey: "description",
|
||||
header: t("quotes.form_fields.items.description.label"),
|
||||
size: 24,
|
||||
cell: ({ row: { index } }) => {
|
||||
return (
|
||||
<FormTextAreaField
|
||||
variant='outline'
|
||||
autoSize
|
||||
{...register(`items.${index}.description`)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "unit_price" as const,
|
||||
accessorKey: "unit_price",
|
||||
@ -93,7 +88,6 @@ export const QuoteDetailsCardEditor = ({
|
||||
cell: ({ row: { index } }) => {
|
||||
return (
|
||||
<FormCurrencyField
|
||||
variant='outline'
|
||||
currency={currency}
|
||||
language={language}
|
||||
scale={4}
|
||||
@ -113,7 +107,6 @@ export const QuoteDetailsCardEditor = ({
|
||||
cell: ({ row: { index } }) => {
|
||||
return (
|
||||
<FormCurrencyField
|
||||
variant='outline'
|
||||
currency={currency}
|
||||
language={language}
|
||||
scale={4}
|
||||
@ -134,7 +127,6 @@ export const QuoteDetailsCardEditor = ({
|
||||
cell: ({ row: { index } }) => {
|
||||
return (
|
||||
<FormPercentageField
|
||||
variant='outline'
|
||||
scale={2}
|
||||
className='text-right'
|
||||
{...register(`items.${index}.discount`)}
|
||||
@ -152,7 +144,7 @@ export const QuoteDetailsCardEditor = ({
|
||||
cell: ({ row: { index } }) => {
|
||||
return (
|
||||
<FormCurrencyField
|
||||
variant='outline'
|
||||
variant='ghost'
|
||||
currency={currency}
|
||||
language={language}
|
||||
scale={4}
|
||||
@ -172,15 +164,18 @@ export const QuoteDetailsCardEditor = ({
|
||||
const { table, row } = props;
|
||||
return [
|
||||
{
|
||||
label: t("common.duplicate_rows"),
|
||||
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),
|
||||
},
|
||||
|
||||
@ -189,7 +184,8 @@ export const QuoteDetailsCardEditor = ({
|
||||
},
|
||||
{
|
||||
label: t("common.remove_row"),
|
||||
shortcut: "⌘⌫",
|
||||
//shortcut: "⌘⌫",
|
||||
icon: <Trash2Icon className='w-4 h-4 mr-2' />,
|
||||
onClick: () => {
|
||||
table.options.meta?.deleteItems(row.index);
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {
|
||||
BackHistoryButton,
|
||||
FormDatePickerField,
|
||||
FormGroup,
|
||||
FormTextAreaField,
|
||||
FormTextField,
|
||||
LoadingOverlay,
|
||||
@ -9,7 +10,7 @@ import { t } from "i18next";
|
||||
|
||||
import { SubmitButton } from "@/components";
|
||||
import { useUnsavedChangesNotifier } from "@/lib/hooks";
|
||||
import { Button, Form } from "@/ui";
|
||||
import { Button, Form, Separator } from "@/ui";
|
||||
import { joiResolver } from "@hookform/resolvers/joi";
|
||||
import { ICreateQuote_Request_DTO } from "@shared/contexts";
|
||||
import Joi from "joi";
|
||||
@ -17,6 +18,7 @@ import { useMemo } from "react";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import SpanishJoiMessages from "../../spanish-joi-messages.json";
|
||||
import { useQuotes } from "./hooks";
|
||||
|
||||
interface QuoteDataForm extends ICreateQuote_Request_DTO {}
|
||||
@ -41,13 +43,14 @@ export const QuoteCreate = () => {
|
||||
defaultValues,
|
||||
resolver: joiResolver(
|
||||
Joi.object({
|
||||
//reference: Joi.string().required(),
|
||||
customer_information: Joi.string().required(),
|
||||
date: Joi.date().required(),
|
||||
customer_reference: Joi.string(),
|
||||
date: Joi.date().required(),
|
||||
customer_information: Joi.string().required(),
|
||||
}),
|
||||
{
|
||||
//messages: SpanishJoiMessages,
|
||||
messages: {
|
||||
es: SpanishJoiMessages,
|
||||
},
|
||||
}
|
||||
),
|
||||
});
|
||||
@ -101,49 +104,47 @@ export const QuoteCreate = () => {
|
||||
</div>
|
||||
|
||||
<div className='grid w-6/12 gap-6 mx-auto'>
|
||||
{/*<FormTextField
|
||||
className='row-span-2'
|
||||
name='reference'
|
||||
required
|
||||
label={t("quotes.form_fields.reference.label")}
|
||||
description={t("quotes.form_fields.reference.desc")}
|
||||
placeholder={t("quotes.form_fields.reference.placeholder")}
|
||||
/>*/}
|
||||
<FormGroup
|
||||
className='md:col-span-4'
|
||||
title={t("quotes.create.form_groups.general.title")}
|
||||
description={t("quotes.create.form_groups.general.desc")}
|
||||
footerActions={
|
||||
<div className='flex items-stretch justify-between flex-1'>
|
||||
<Button size='sm' variant={"ghost"} onClick={() => navigate("/quotes")}>
|
||||
{t("common.discard")}
|
||||
</Button>
|
||||
<SubmitButton size='sm' label={t("common.continue")}></SubmitButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<FormTextField
|
||||
required
|
||||
name='customer_reference'
|
||||
label={t("quotes.form_fields.customer_reference.label")}
|
||||
description={t("quotes.form_fields.customer_reference.desc")}
|
||||
placeholder={t("quotes.form_fields.customer_reference.placeholder")}
|
||||
/>
|
||||
|
||||
<FormTextField
|
||||
className='row-span-2'
|
||||
name='customer_reference'
|
||||
required
|
||||
label={t("quotes.form_fields.customer_reference.label")}
|
||||
description={t("quotes.form_fields.customer_reference.desc")}
|
||||
placeholder={t("quotes.form_fields.customer_reference.placeholder")}
|
||||
/>
|
||||
<FormDatePickerField
|
||||
required
|
||||
label={t("quotes.form_fields.date.label")}
|
||||
description={t("quotes.form_fields.date.desc")}
|
||||
placeholder={t("quotes.form_fields.date.placeholder")}
|
||||
name='date'
|
||||
/>
|
||||
|
||||
<FormDatePickerField
|
||||
required
|
||||
label={t("quotes.form_fields.date.label")}
|
||||
description={t("quotes.form_fields.date.desc")}
|
||||
placeholder={t("quotes.form_fields.date.placeholder")}
|
||||
name='date'
|
||||
/>
|
||||
<Separator />
|
||||
|
||||
<FormTextAreaField
|
||||
rows={4}
|
||||
className='row-span-2'
|
||||
name='customer_information'
|
||||
required
|
||||
label={t("quotes.form_fields.customer_information.label")}
|
||||
description={t("quotes.form_fields.customer_information.desc")}
|
||||
placeholder={t("quotes.form_fields.customer_information.placeholder")}
|
||||
/>
|
||||
|
||||
<div className='flex items-center justify-around gap-2'>
|
||||
<Button size='sm' variant={"outline"} onClick={() => navigate("/quotes")}>
|
||||
{t("common.discard")}
|
||||
</Button>
|
||||
|
||||
<SubmitButton size='sm' label={t("common.continue")}></SubmitButton>
|
||||
</div>
|
||||
<FormTextAreaField
|
||||
rows={4}
|
||||
className='row-span-2'
|
||||
name='customer_information'
|
||||
required
|
||||
label={t("quotes.form_fields.customer_information.label")}
|
||||
description={t("quotes.form_fields.customer_information.desc")}
|
||||
placeholder={t("quotes.form_fields.customer_information.placeholder")}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@ -28,6 +28,23 @@ export function useDetailColumns<TData = unknown, TValue = unknown>(
|
||||
// const lastSelectedId = "";
|
||||
|
||||
return useMemo(() => {
|
||||
if (enableDragHandleColumn) {
|
||||
columns.unshift({
|
||||
id: "row_drag_handle",
|
||||
/*header: () => (
|
||||
<UnfoldVertical aria-label='Mover fila' className='items-center justify-center w-4 h-4' />
|
||||
),*/
|
||||
header: () => null,
|
||||
cell: (info) => <DataTableRowDragHandleCell rowId={info.row.id} />,
|
||||
size: 2,
|
||||
minSize: 2,
|
||||
maxSize: 2,
|
||||
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (enableSelectionColumn) {
|
||||
columns.unshift({
|
||||
id: "select",
|
||||
@ -63,23 +80,6 @@ export function useDetailColumns<TData = unknown, TValue = unknown>(
|
||||
});
|
||||
}
|
||||
|
||||
if (enableDragHandleColumn) {
|
||||
columns.unshift({
|
||||
id: "row_drag_handle",
|
||||
/*header: () => (
|
||||
<UnfoldVertical aria-label='Mover fila' className='items-center justify-center w-4 h-4' />
|
||||
),*/
|
||||
header: () => null,
|
||||
cell: (info) => <DataTableRowDragHandleCell rowId={info.row.id} />,
|
||||
size: 2,
|
||||
minSize: 2,
|
||||
maxSize: 2,
|
||||
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (enableActionsColumn) {
|
||||
columns.push({
|
||||
id: "row_actions",
|
||||
|
||||
@ -13,7 +13,8 @@ import {
|
||||
} from "@/ui";
|
||||
import { CellContext } from "@tanstack/react-table";
|
||||
import { t } from "i18next";
|
||||
import { MoreHorizontalIcon } from "lucide-react";
|
||||
import { MoreVerticalIcon } from "lucide-react";
|
||||
import { ReactElement } from "react";
|
||||
|
||||
export type DataTablaRowActionFunction<TData> = (
|
||||
props: CellContext<TData, unknown>
|
||||
@ -21,6 +22,7 @@ export type DataTablaRowActionFunction<TData> = (
|
||||
|
||||
export type DataTableRowActionDefinition<TData> = {
|
||||
label: string | "-";
|
||||
icon?: ReactElement<any, any>;
|
||||
shortcut?: string;
|
||||
onClick?: (props: CellContext<TData, unknown>, e: React.BaseSyntheticEvent) => void;
|
||||
};
|
||||
@ -46,7 +48,7 @@ export function DataTableRowActions<TData = any, TValue = unknown>({
|
||||
variant='link'
|
||||
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>
|
||||
@ -62,8 +64,9 @@ export function DataTableRowActions<TData = any, TValue = unknown>({
|
||||
key={index}
|
||||
onClick={(event) => (action.onClick ? action.onClick(rowContext, event) : null)}
|
||||
>
|
||||
{action.icon && <>{action.icon}</>}
|
||||
{action.label}
|
||||
<DropdownMenuShortcut>{action.shortcut}</DropdownMenuShortcut>
|
||||
{action.shortcut && <DropdownMenuShortcut>{action.shortcut}</DropdownMenuShortcut>}
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
)}
|
||||
|
||||
@ -5,7 +5,7 @@ import { DataTableFilterField, useDataTableContext } from "@/lib/hooks";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button, Input } from "@/ui";
|
||||
import { t } from "i18next";
|
||||
import { XIcon } from "lucide-react";
|
||||
import { SearchIcon, XIcon } from "lucide-react";
|
||||
import { DataTableColumnOptions } from "./DataTableColumnOptions";
|
||||
|
||||
interface DataTableToolbarProps<TData> extends React.HTMLAttributes<HTMLDivElement> {
|
||||
@ -32,6 +32,7 @@ export function DataTableToolbar<TData>({
|
||||
{...props}
|
||||
>
|
||||
<div className='flex items-center flex-1 space-x-2'>
|
||||
<SearchIcon className='w-4 h-4 text-gray-500' />
|
||||
<Input
|
||||
key='global-filter'
|
||||
placeholder={t("common.filter_placeholder")}
|
||||
|
||||
@ -17,6 +17,7 @@ const formCurrencyFieldVariants = cva(
|
||||
default:
|
||||
"border border-input ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ",
|
||||
outline: "focus-visible:border focus-visible:border-input",
|
||||
ghost: "bg-transparent",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@ -66,7 +66,6 @@ export const FormDatePickerField = React.forwardRef<
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"pl-3 text-left font-normal",
|
||||
!field.value && "text-muted-foreground"
|
||||
@ -79,7 +78,7 @@ export const FormDatePickerField = React.forwardRef<
|
||||
) : (
|
||||
<span>{t("common.pick_date")}</span>
|
||||
)}
|
||||
<CalendarIcon className='w-4 h-4 ml-auto text-input' />
|
||||
<CalendarIcon className='w-4 h-4 ml-auto text-' />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
|
||||
@ -13,7 +13,7 @@ const formTextFieldVariants = cva("", {
|
||||
variants: {
|
||||
variant: {
|
||||
default: "",
|
||||
outline:
|
||||
ghost:
|
||||
"border-0 focus-visible:border focus-visible:border-input focus-visible:ring-0 focus-visible:ring-offset-0 ",
|
||||
},
|
||||
},
|
||||
@ -48,6 +48,7 @@ export const FormTextField = React.forwardRef<HTMLInputElement, FormTextFieldPro
|
||||
rules,
|
||||
type,
|
||||
variant,
|
||||
required,
|
||||
|
||||
button,
|
||||
leadIcon,
|
||||
@ -62,12 +63,19 @@ export const FormTextField = React.forwardRef<HTMLInputElement, FormTextFieldPro
|
||||
control={control}
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
rules={rules}
|
||||
rules={{
|
||||
required,
|
||||
...rules,
|
||||
}}
|
||||
render={({ field, fieldState }) => {
|
||||
return (
|
||||
<FormItem ref={ref} className={cn(className, "space-y-3")}>
|
||||
{label && (
|
||||
<FormLabel label={label} hint={hint} required={Boolean(rules?.required ?? false)} />
|
||||
<FormLabel
|
||||
label={label}
|
||||
hint={hint}
|
||||
required={Boolean(rules?.required ?? required)}
|
||||
/>
|
||||
)}
|
||||
<div className={cn(button ? "flex" : null)}>
|
||||
<div
|
||||
|
||||
@ -62,7 +62,7 @@
|
||||
--card: 334 62% 100%;
|
||||
--card-foreground: 334 55% 1%;
|
||||
--border: 334 5% 95%;
|
||||
--input: 334 5% 95%;
|
||||
--input: 214.29 31.82% 91.37%;
|
||||
--primary: 242.93 100% 67.84%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 213.75 20.25% 69.02%;
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
"back": "Back",
|
||||
"upload": "Upload",
|
||||
"continue": "Continue",
|
||||
"close": "Close",
|
||||
"sort_asc": "Asc",
|
||||
"sort_asc_description": "In ascending order. Click to sort descending order.",
|
||||
"sort_desc": "Desc",
|
||||
@ -30,6 +31,7 @@
|
||||
"error": "Error",
|
||||
"actions": "Actions",
|
||||
"open_menu": "Open menu",
|
||||
"duplicate_row": "Duplicate",
|
||||
"duplicate_selected_rows": "Duplicate",
|
||||
"duplicate_selected_rows_tooltip": "Duplicate selected row(s)",
|
||||
"append_empty_row": "Append row",
|
||||
@ -183,6 +185,10 @@
|
||||
"cancel_button": "Cancel the download",
|
||||
"toast_success": "Quote downloaded"
|
||||
},
|
||||
"catalog_picker_dialog": {
|
||||
"title": "Select catalog items",
|
||||
"description": "To complete your quote, you can add items from the catalog."
|
||||
},
|
||||
"status": {
|
||||
"draft": "Draft"
|
||||
},
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
"back": "Volver",
|
||||
"upload": "Cargar",
|
||||
"continue": "Continuar",
|
||||
"close": "Cerrar",
|
||||
"sort_asc": "Asc",
|
||||
"sort_asc_description": "En order ascendente. Click para ordenar descendentemente.",
|
||||
"sort_desc": "Desc",
|
||||
@ -30,6 +31,7 @@
|
||||
"error": "Error",
|
||||
"actions": "Acciones",
|
||||
"open_menu": "Abrir el menú",
|
||||
"duplicate_row": "Duplicar",
|
||||
"duplicate_selected_rows": "Duplicar",
|
||||
"duplicate_selected_rows_tooltip": "Duplica las fila(s) seleccionadas(s)",
|
||||
"append_empty_row": "Añadir fila",
|
||||
@ -179,6 +181,10 @@
|
||||
"cancel_button": "Cancelar la descarga",
|
||||
"toast_success": "Quote downloaded"
|
||||
},
|
||||
"catalog_picker_dialog": {
|
||||
"title": "Seleccionar artículos del catálogo",
|
||||
"description": "Para rellenar su cotización, puede añadir artículos del catálogo."
|
||||
},
|
||||
"status": {
|
||||
"draft": "Borrador"
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user