diff --git a/client/src/app/quotes/components/QuotesDataTable.tsx b/client/src/app/quotes/components/QuotesDataTable.tsx index 82f339c..ea48f7f 100644 --- a/client/src/app/quotes/components/QuotesDataTable.tsx +++ b/client/src/app/quotes/components/QuotesDataTable.tsx @@ -38,9 +38,10 @@ export const QuotesDataTable = ({ const [activeRow, setActiveRow] = useState | undefined>(undefined); - const { useList, useDownloader, useSetStatus, getQuotePDFFilename } = useQuotes(); + const { useList, useDownloader, useSetStatus, useDuplicate, getQuotePDFFilename } = useQuotes(); const { mutate: setStatusMutation } = useSetStatus(); + const { mutate: duplicate } = useDuplicate(); const { data, isPending, isError, error } = useList({ pagination: { @@ -70,6 +71,24 @@ export const QuotesDataTable = ({ [navigate, toast] ); + const handleDuplicateQuote = (id: string) => { + duplicate( + { + id, + }, + { + onSuccess(data) { + toast({ + description: t("quotes.duplicate_action.toast_success"), + variant: "success", + }); + + navigate(`/quotes/edit/${data.id}`, { relative: "path" }); + }, + } + ); + }; + const handleOnChangeStatus = (id: string, newStatus: string) => { setStatusMutation( { id, newStatus }, @@ -77,7 +96,10 @@ export const QuotesDataTable = ({ { onSuccess: () => { toast({ - description: t("quotes.quote_status_editor.toast_status_changed"), + description: t("quotes.quote_status_editor.toast_status_changed", { + newStatus: t(`quotes.status.${newStatus}`), + }), + variant: "success", }); }, } @@ -248,7 +270,7 @@ export const QuotesDataTable = ({ size='icon' onClick={(e) => { e.preventDefault(); - //handleDuplicateQuote(original); + handleDuplicateQuote(original.id); }} > diff --git a/client/src/app/quotes/hooks/useQuotes.tsx b/client/src/app/quotes/hooks/useQuotes.tsx index 8762cc3..07845ac 100644 --- a/client/src/app/quotes/hooks/useQuotes.tsx +++ b/client/src/app/quotes/hooks/useQuotes.tsx @@ -7,6 +7,7 @@ import { useQueryKey } from "@/lib/hooks/useQueryKey"; import { ICreateQuote_Request_DTO, ICreateQuote_Response_DTO, + IDuplicateQuote_Response_DTO, IGetQuote_Response_DTO, IListQuotes_Response_DTO, IListResponse_DTO, @@ -170,6 +171,27 @@ export const useQuotes = () => { }); }, + useDuplicate: () => { + const queryClient = useQueryClient(); + + return useMutation({ + //mutationKey: keys().data().resource("quotes").action("one").id("").params().get(), + mutationFn: (data) => { + const { id } = data; + + return dataSource.custom({ + url: `${dataSource.getApiUrl()}/quotes/${id}/duplicate`, + method: "post", + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["data", "default", "quotes"], + }); + }, + }); + }, + useSentTo: (id?: string) => { const queryClient = useQueryClient(); diff --git a/client/src/locales/en.json b/client/src/locales/en.json index edb4417..b37d8aa 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -207,6 +207,9 @@ "save_quote": "Save quote" } }, + "duplicate_action": { + "toast_success": "Quote duplicated" + }, "downloading_dialog": { "title": "Downloading quote", "description": "Please wait while your quotation is generated and downloaded in PDF format...", @@ -254,7 +257,7 @@ } }, "submit_button": "Change status", - "toast_status_changed": "Quote status changed to:" + "toast_status_changed": "Quote status changed to: {{newStatus}}" }, "status": { "draft": "Draft", diff --git a/client/src/locales/es.json b/client/src/locales/es.json index bb5a609..1d180fe 100644 --- a/client/src/locales/es.json +++ b/client/src/locales/es.json @@ -207,6 +207,9 @@ "save_quote": "Guardar cotización" } }, + "duplicate_action": { + "toast_success": "Cotización duplicada" + }, "downloading_dialog": { "title": "Descargando cotización", "description": "Espere mientras se genera la cotización y se descarga en formato PDF...", @@ -217,7 +220,7 @@ "value_label": "Elapsed time in seconds {{elapsed}}" }, "cancel_button": "Cancelar la descarga", - "toast_success": "Quote downloaded" + "toast_success": "Cotización descargada" }, "catalog_picker_dialog": { "title": "Seleccionar artículos del catálogo", @@ -262,7 +265,7 @@ } }, "submit_button": "Cambiar estado", - "toast_status_changed": "Estado de la cotización cambiado a:" + "toast_status_changed": "Estado de la cotización cambiado a: {{newStatus}}" }, "status": { "draft": "Borrador", diff --git a/server/src/contexts/sales/infrastructure/sequelize/quote.model.ts b/server/src/contexts/sales/infrastructure/sequelize/quote.model.ts index e898ed8..f30224f 100644 --- a/server/src/contexts/sales/infrastructure/sequelize/quote.model.ts +++ b/server/src/contexts/sales/infrastructure/sequelize/quote.model.ts @@ -191,6 +191,13 @@ export default (sequelize: Sequelize) => { whereMergeStrategy: "and", // <- cómo tratar el merge de un scope + defaultScope: { + order: [ + ["reference", "DESC"], + ["date", "DESC"], + ], + }, + scopes: { quickSearch(value) { return { @@ -207,6 +214,10 @@ export default (sequelize: Sequelize) => { }, }, }, + order: [ + ["reference", "DESC"], + ["date", "DESC"], + ], }; }, },