Compare commits

...

3 Commits

5 changed files with 130 additions and 24 deletions

View File

@ -2,6 +2,7 @@ import { Table } from "@tanstack/react-table";
import { ButtonGroup } from "@/components"; import { ButtonGroup } from "@/components";
import { DataTableFilterField, useDataTableContext } from "@/lib/hooks"; import { DataTableFilterField, useDataTableContext } from "@/lib/hooks";
import { cn } from "@/lib/utils";
import { Badge, Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/ui"; import { Badge, Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/ui";
import { t } from "i18next"; import { t } from "i18next";
import { PlusIcon, SearchIcon, XIcon } from "lucide-react"; import { PlusIcon, SearchIcon, XIcon } from "lucide-react";
@ -53,8 +54,13 @@ export function CatalogDataTableFilter<TData>({
return ( return (
<TooltipProvider> <TooltipProvider>
<div className='w-full space-y-2' {...props}> <div className='w-full space-y-2' {...props}>
<div className='relative flex items-center flex-1 p-2 space-x-2 border rounded-md border-input'> <div
<SearchIcon className='w-4 h-4 text-gray-500' /> className={cn(
"relative flex items-center flex-1 p-2 space-x-2 border rounded-md",
isFiltered ? "border-primary" : "border-input"
)}
>
<SearchIcon className={cn("w-4 h-4 text-gray-500")} />
<div className='flex flex-wrap items-center flex-1 gap-2'> <div className='flex flex-wrap items-center flex-1 gap-2'>
{globalFilter && {globalFilter &&
globalFilter.map((filterTerm) => ( globalFilter.map((filterTerm) => (
@ -95,9 +101,9 @@ export function CatalogDataTableFilter<TData>({
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant='outline' variant='ghost'
onClick={() => resetGlobalFilter()} onClick={() => resetGlobalFilter()}
className='h-8 px-2 transition-all lg:px-3' className='h-8 px-2 transition-all lg:px-3 text-primary'
> >
<XIcon className='w-4 h-4 mr-2' /> <XIcon className='w-4 h-4 mr-2' />
{t("common.filter.reset_filter")} {t("common.filter.reset_filter")}

View File

@ -4,12 +4,12 @@ import { CatalogDataTableFilter } from "@/app/catalog/components/CatalogDataTabl
import { useCatalogList } from "@/app/catalog/hooks"; import { useCatalogList } from "@/app/catalog/hooks";
import { DataTable } from "@/components"; import { DataTable } from "@/components";
import { useDataTable, useDataTableContext } from "@/lib/hooks"; import { useDataTable, useDataTableContext } from "@/lib/hooks";
import { Button } from "@/ui"; import { Button, Input } from "@/ui";
import { IListArticles_Response_DTO, MoneyValue } from "@shared/contexts"; import { IListArticles_Response_DTO, MoneyValue } from "@shared/contexts";
import { ColumnDef, Row } from "@tanstack/react-table"; import { createColumnHelper, Row } from "@tanstack/react-table";
import { t } from "i18next"; import { t } from "i18next";
import { PackagePlusIcon } from "lucide-react"; import { PackagePlusIcon } from "lucide-react";
import { useMemo, useState } from "react"; import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
export const CatalogPickerDataTable = ({ export const CatalogPickerDataTable = ({
@ -23,12 +23,19 @@ export const CatalogPickerDataTable = ({
const [quantities, setQuantities] = useState<number[]>([]); const [quantities, setQuantities] = useState<number[]>([]);
const handleQuantity = (index: number, quantity: number) => { const handleQuantity = (index: number, quantity: number) => {
console.log("handleQuantity", index, quantity);
setQuantities((prev) => { setQuantities((prev) => {
prev[index] = quantity; const newQuantities = [...prev];
return prev; newQuantities[index] = quantity;
return newQuantities;
}); });
}; };
const resetQuantities = () => {
console.log("reset");
setQuantities([]);
};
const { data, isPending, isError, error } = useCatalogList({ const { data, isPending, isError, error } = useCatalogList({
pagination: { pagination: {
pageIndex: pagination.pageIndex, pageIndex: pagination.pageIndex,
@ -37,7 +44,79 @@ export const CatalogPickerDataTable = ({
searchTerm: globalFilter, searchTerm: globalFilter,
}); });
const columns = useMemo<ColumnDef<IListArticles_Response_DTO>[]>(() => { useEffect(() => {
resetQuantities();
}, [pagination, globalFilter, isFiltered]);
console.log(quantities);
const columnHelper = createColumnHelper<IListArticles_Response_DTO>();
const columns = [
columnHelper.accessor("description", {
id: "description",
header: () => <>{t("catalog.list.columns.description")}</>,
}),
columnHelper.accessor("points", {
id: "points",
header: () => <div className='text-right'>{t("catalog.list.columns.points")}</div>,
cell: ({ renderValue }: { renderValue: () => any }) => (
<div className='text-right'>{renderValue()}</div>
),
}),
columnHelper.accessor("retail_price", {
id: "retail_price",
header: () => <div className='text-right'>{t("catalog.list.columns.retail_price")}</div>,
cell: ({ row }: { row: Row<IListArticles_Response_DTO> }) => {
const price = MoneyValue.create(row.original.retail_price).object;
return <div className='text-right'>{price.toFormat()}</div>;
},
}),
columnHelper.display({
id: "quantity",
header: () => (
<div className='font-medium text-right text-foreground'>
{t("catalog.list.columns.quantity")}
</div>
),
cell: ({ row: { index } }) => {
return (
<Input
type='number'
name='quantity'
defaultValue={1}
min={1}
className='w-24'
value={quantities[index]}
onChange={(event) => {
event.preventDefault();
handleQuantity(index, parseInt(event.target.value));
}}
/>
);
},
}),
columnHelper.display({
id: "row-actions",
header: () => null,
cell: ({ row }: { row: Row<IListArticles_Response_DTO> }) => (
<Button
size='sm'
variant='outline'
className='h-8 gap-1'
onClick={(event) => {
event.preventDefault();
onSelect && onSelect(row.original, quantities[row.index]);
}}
>
<PackagePlusIcon className='h-3.5 w-3.5' />
<span className='sr-only xl:not-sr-only xl:whitespace-nowrap'>{t("common.add")}</span>
</Button>
),
}),
];
/*const columns2 = useMemo<ColumnDef<IListArticles_Response_DTO>[]>(() => {
return [ return [
{ {
id: "description" as const, id: "description" as const,
@ -64,18 +143,23 @@ export const CatalogPickerDataTable = ({
{ {
id: "quantity" as const, id: "quantity" as const,
accessorKey: "quantity", accessorKey: "quantity",
header: () => <div className='text-right'>{t("catalog.list.columns.quantity")}</div>, header: () => (
<div className='font-medium text-right text-foreground'>
{t("catalog.list.columns.quantity")}
</div>
),
cell: ({ row: { index } }) => { cell: ({ row: { index } }) => {
return ( return (
<input <Input
type='number' type='number'
name='quantity' name='quantity'
defaultValue={1} defaultValue={1}
min={1} min={1}
className='w-24 text-right'
value={quantities[index]} value={quantities[index]}
onChange={(event) => { onChange={(event) => {
event.preventDefault(); event.preventDefault();
handleQuantity(index, +event.target.value); handleQuantity(index, parseInt(event.target.value));
}} }}
/> />
); );
@ -100,10 +184,10 @@ export const CatalogPickerDataTable = ({
), ),
}, },
]; ];
}, []); }, []);*/
const { table } = useDataTable({ const { table } = useDataTable<IListArticles_Response_DTO, any>({
data: data?.items ?? [], data: [...(data?.items || [])],
columns: columns, columns: columns,
pageCount: data?.total_pages ?? -1, pageCount: data?.total_pages ?? -1,
}); });

View File

@ -9,7 +9,7 @@ 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 { useToast } from "@/ui/use-toast"; import { useToast } from "@/ui/use-toast";
import { CurrencyData, Language, Quantity } from "@shared/contexts"; import { CurrencyData, Language, Quantity, UnitPrice } from "@shared/contexts";
import { ColumnDef } from "@tanstack/react-table"; import { ColumnDef } from "@tanstack/react-table";
import { t } from "i18next"; import { t } from "i18next";
import { ChevronDownIcon, ChevronUpIcon, CopyIcon, Trash2Icon } from "lucide-react"; import { ChevronDownIcon, ChevronUpIcon, CopyIcon, Trash2Icon } from "lucide-react";
@ -241,6 +241,18 @@ export const QuoteDetailsCardEditor = ({
(block: any) => { (block: any) => {
fieldActions.append({ fieldActions.append({
description: `${block.title}\n${block.body}`, 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({ toast({
title: t("quotes.blocks_picker_dialog.toast_article_added"), title: t("quotes.blocks_picker_dialog.toast_article_added"),

View File

@ -91,7 +91,13 @@ export const useQuotes = () => {
value: status, value: status,
}, },
] ]
: undefined, : [
{
field: "status",
operator: "ne",
value: "archived",
},
],
pagination, pagination,
}); });
}, },

View File

@ -11,6 +11,7 @@ export enum CONNECTING_OPERATORS {
export enum OPERATORS { export enum OPERATORS {
EQ = "EQ", EQ = "EQ",
NE = "NE",
NOT = "NOT", NOT = "NOT",
IN = "IN", IN = "IN",
NOTIN = "NOTIN", NOTIN = "NOTIN",
@ -35,6 +36,7 @@ const SEQUELIZE_OP_MAP: {
[CONNECTING_OPERATORS.OR]: Op.or, [CONNECTING_OPERATORS.OR]: Op.or,
[CONNECTING_OPERATORS.AND]: Op.and, [CONNECTING_OPERATORS.AND]: Op.and,
[OPERATORS.EQ]: Op.eq, [OPERATORS.EQ]: Op.eq,
[OPERATORS.NE]: Op.ne,
[OPERATORS.NOT]: Op.not, [OPERATORS.NOT]: Op.not,
[OPERATORS.IN]: Op.in, [OPERATORS.IN]: Op.in,
[OPERATORS.NOTIN]: Op.notIn, [OPERATORS.NOTIN]: Op.notIn,
@ -54,13 +56,9 @@ const SEQUELIZE_FN_MAP: {
} = { } = {
[FUNCTIONS.INCLUDES]: (field: string, val: string) => [FUNCTIONS.INCLUDES]: (field: string, val: string) =>
Sequelize.where( Sequelize.where(
Sequelize.fn( Sequelize.fn("FIND_IN_SET", Sequelize.literal(`'${val}'`), Sequelize.col(field)),
"FIND_IN_SET",
Sequelize.literal(`'${val}'`),
Sequelize.col(field),
),
SEQUELIZE_OP_MAP[OPERATORS.GT], SEQUELIZE_OP_MAP[OPERATORS.GT],
0, 0
), ),
}; };