From 132408ac236b523a31df0c9ae489de9dd870a4d8 Mon Sep 17 00:00:00 2001 From: david Date: Mon, 23 Mar 2026 12:13:33 +0100 Subject: [PATCH] . --- .../components/form/simple-search-input.tsx | 4 +- .../src/web/lib/helpers/http-url-utils.ts | 5 + modules/core/src/web/lib/helpers/index.ts | 1 + .../src/common/locales/es.json | 9 +- .../src/web/customer-invoice-routes.tsx | 10 +- .../download-pdf/hooks/index.ts | 1 - .../web/issued-invoices/list/hooks/index.ts | 1 - .../web/issued-invoices/shared/hooks/index.ts | 2 + .../hooks/use-download-invoice-pdf-query.ts | 2 +- .../hooks/use-issued-invoice-list-query.ts | 2 +- .../src/web/issued-invoices/shared/index.ts | 1 + .../create-customer-invoice-edit-form.tsx | 4 +- .../web/proformas/change-status/api/index.ts | 1 - .../change-status/controllers/index.ts | 2 +- ...hange-proforma-status-dialog-controller.ts | 160 ++++++++++++++ .../use-change-status-dialog-controller.ts | 64 ------ .../proformas/change-status/hooks/index.ts | 1 - .../change-status/hooks/use-change-status.ts | 51 ----- .../change-status/ui/change-status-dialog.tsx | 8 +- .../src/web/proformas/delete/api/index.ts | 1 - .../use-delete-proforma-dialog-controller.ts | 198 ++++++++++++++---- .../src/web/proformas/delete/hooks/index.ts | 1 - .../delete/hooks/use-delete-proforma.ts | 45 ---- .../src/web/proformas/delete/index.ts | 1 + .../delete-proforma-dialog.tsx | 81 ++++--- .../proformas/delete/ui/components/index.ts | 1 + .../src/web/proformas/delete/ui/index.ts | 2 +- .../src/web/proformas/hooks/index.ts | 2 - ...ts => use-delete-proforma-mutation.ts.bak} | 9 +- .../web/proformas/hooks/use-proforma-query.ts | 49 ----- .../hooks/use-proforma-update-mutation.ts | 66 ------ .../src/web/proformas/index.ts | 1 + .../src/web/proformas/list/adapters/index.ts | 1 - .../adapters/proforma-summary-dto.adapter.ts | 71 ------- .../src/web/proformas/list/api/index.ts | 1 - .../web/proformas/list/controllers/index.ts | 4 +- .../use-list-proformas-page.controller.ts | 62 ++++++ ...er.ts => use-list-proformas.controller.ts} | 46 ++-- .../use-proforma-list-page.controller.ts.ts | 63 ------ .../src/web/proformas/list/hooks/index.ts | 1 - .../list/hooks/use-proforma-list-query.ts | 38 ---- .../list/ui/blocks/proformas-grid/index.ts | 1 + .../list/ui/pages/proforma-list-page.tsx | 10 +- .../pages/update/proforma-update-comp.tsx | 2 +- .../src/web/proformas/schemas/index.ts | 0 .../web/proformas/shared/adapters/index.ts | 1 + .../shared/adapters/list-proformas.adapter.ts | 99 +++++++++ .../api/change-proforma-status-by-id.api.ts} | 2 +- .../api/delete-proforma-by-ip.api.ts} | 2 +- .../shared/api/get-proforma-by-ip.api.ts | 9 + .../src/web/proformas/shared/api/index.ts | 4 + .../api/list-proformas.api.ts} | 7 +- .../web/proformas/shared/entities/index.ts | 3 + .../entities/proforma-list-row.entity.ts | 49 +++++ .../shared/entities/proforma-list.entity.ts | 9 + .../shared/entities/proforma-status.entity.ts | 18 ++ .../src/web/proformas/shared/hooks/index.ts | 3 + .../use-change-proforma-status-mutation.ts | 33 +++ .../hooks/use-delete-proforma-mutation.ts | 31 +++ .../shared/hooks/use-list-proformas-query.tsx | 114 ++++++++++ .../shared/hooks/use-proforma-get-query.ts | 47 +++++ .../hooks/use-proforma-update-mutation.ts | 58 +++++ .../src/web/proformas/shared/index.ts | 4 + .../web/proformas/shared/ui/blocks/index.ts | 1 + .../ui/blocks/proforma-layout.tsx | 0 .../src/web/proformas/shared/ui/index.ts | 1 + .../src/web/proformas/types/index.ts | 4 - .../web/proformas/types/proforma-status.ts | 19 -- .../proformas/types/proforma.api.schema.ts | 27 --- .../src/web/proformas/ui/blocks/index.ts | 1 - .../web/proformas/update/controllers/index.ts | 1 + .../use-proforma-update-page.controller.ts | 142 +++++++++++++ .../src/web/proformas/update/index.ts | 1 + .../src/web/proformas/update}/types/index.ts | 0 .../types/types.ts} | 0 .../src/web/proformas/update/ui/index.ts | 1 + .../web/proformas/update/ui/pages/index.ts | 1 + .../ui/pages}/proforma-update-page.tsx | 47 +++-- .../components/client-selector-modal.tsx | 2 +- .../customer-modal-selector/customer-card.tsx | 0 .../customer-create-modal.tsx | 2 +- .../customer-empty-card.tsx | 0 .../customer-modal-selector-field.tsx | 0 .../customer-modal-selector.tsx | 0 .../customer-search-dialog.tsx | 0 .../customer-view-dialog.tsx | 0 .../customer-modal-selector/index.ts | 0 .../web/{ => _archived}/components/index.ts | 2 +- .../constants/customer.constants.ts | 0 .../web/{ => _archived}/constants/index.ts | 0 .../context/customers-context.tsx | 0 .../src/web/{ => _archived}/context/index.ts | 0 .../src/web/{ => _archived}/hooks/index.ts | 0 .../hooks/use-create-customer-mutation.ts | 4 +- .../hooks/use-customer-query.ts | 10 +- .../hooks/use-customers-context.tsx | 3 +- .../hooks/use-delete-customer-mutation.ts | 11 +- .../hooks/use-update-customer-mutation.ts | 4 +- .../pages/create/customer-create-page.tsx | 2 +- .../web/{ => _archived}/pages/create/index.ts | 0 .../create/use-customer-create-controller.ts | 2 +- .../web/{ => _archived}/pages/create/utils.ts | 0 .../src/web/{ => _archived}/pages/index.ts | 0 .../pages/list/customers-list-grid.tsx | 2 +- .../src/web/_archived/pages/list/index.ts | 1 + .../pages/list/use-customers-list-columns.tsx | 4 +- .../pages/update/customer-update-modal.tsx | 2 +- .../pages/update/customer-update-page.tsx | 4 +- .../web/{ => _archived}/pages/update/index.ts | 0 .../schemas/customer-resume.form.schema.ts | 0 .../schemas/customer.api.schema.ts | 2 +- .../schemas/customer.form.schema.ts | 0 .../src/web/{ => _archived}/schemas/index.ts | 0 .../customers/src/web/common/api/api-types.ts | 12 -- modules/customers/src/web/common/api/index.ts | 3 - .../common/hooks/use-customer-list-query.tsx | 98 --------- modules/customers/src/web/common/index.ts | 3 - modules/customers/src/web/common/types.ts | 3 - modules/customers/src/web/customer-routes.tsx | 6 +- .../adapters/customer-summary-dto.adapter.ts | 13 -- .../customers/src/web/list/adapters/index.ts | 1 - .../src/web/list/controllers/index.ts | 2 +- .../use-customer-list-page.controller.ts | 9 - .../use-list-customers-page.controller.ts | 9 + ...er.ts => use-list-customers.controller.ts} | 37 ++-- modules/customers/src/web/list/types/types.ts | 7 - .../blocks/customers-grid/customers-grid.tsx | 12 +- .../use-customer-grid-columns.tsx | 153 ++++++-------- .../web/list/ui/components/address-cell.tsx | 9 +- .../web/list/ui/components/contact-cell.tsx | 15 +- .../customers/src/web/list/ui/pages/index.ts | 2 +- ...-list-page.tsx => list-customers-page.tsx} | 16 +- modules/customers/src/web/manifest.ts | 3 +- modules/customers/src/web/pages/list/index.ts | 1 - .../adapters/get-customer-by-id.adapter.ts | 39 ++++ .../src/web/shared/adapters/index.ts | 2 + .../shared/adapters/list-customers.adapter.ts | 57 +++++ .../api/get-customer-by-id.api.ts} | 2 +- modules/customers/src/web/shared/api/index.ts | 2 + .../api/list-customers.api.ts} | 6 +- .../entities/customer-list-row.entity.ts | 30 +++ .../shared/entities/customer-list.entity.ts | 9 + .../web/shared/entities/customer.entity.ts | 34 +++ .../src/web/shared/entities/index.ts | 3 + .../src/web/{common => shared}/hooks/index.ts | 2 +- .../hooks/toValidationErrors.ts | 0 .../hooks/use-customer-get-query.ts | 0 .../hooks/use-customer-update-mutation.ts | 0 .../shared/hooks/use-list-customers-query.ts | 114 ++++++++++ modules/customers/src/web/shared/index.ts | 4 + .../ui/blocks/customer-layout.tsx | 0 .../src/web/{ => shared}/ui/blocks/index.ts | 0 .../ui/components/customer-status-badge.tsx | 0 .../ui/components/error-alert.tsx | 0 .../web/{ => shared}/ui/components/index.ts | 0 .../src/web/{ => shared}/ui/index.ts | 0 .../use-customer-update-page.controller.ts | 8 +- .../update/hooks/use-customer-get-query.ts | 35 ---- .../hooks/use-customer-update-mutation.ts | 46 ---- .../customer-additional-config-fields.tsx | 2 +- .../ui}/editor/customer-address-fields.tsx | 2 +- .../ui}/editor/customer-basic-info-fields.tsx | 2 +- .../ui}/editor/customer-contact-fields.tsx | 2 +- .../ui}/editor/customer-edit-form.tsx | 0 .../editor/customer-taxes-multi-select.tsx | 2 +- .../{components => update/ui}/editor/index.ts | 0 .../update/ui/pages/customer-update-page.tsx | 6 +- .../web/view/adapters/customer-dto.adapter.ts | 2 +- .../use-customer-view.controller.ts | 2 +- modules/customers/src/web/view/types/types.ts | 2 +- 170 files changed, 1650 insertions(+), 1061 deletions(-) create mode 100644 modules/core/src/web/lib/helpers/http-url-utils.ts create mode 100644 modules/customer-invoices/src/web/issued-invoices/shared/hooks/index.ts rename modules/customer-invoices/src/web/issued-invoices/{download-pdf => shared}/hooks/use-download-invoice-pdf-query.ts (96%) rename modules/customer-invoices/src/web/issued-invoices/{list => shared}/hooks/use-issued-invoice-list-query.ts (96%) create mode 100644 modules/customer-invoices/src/web/issued-invoices/shared/index.ts delete mode 100644 modules/customer-invoices/src/web/proformas/change-status/api/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts delete mode 100644 modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-status-dialog-controller.ts delete mode 100644 modules/customer-invoices/src/web/proformas/change-status/hooks/index.ts delete mode 100644 modules/customer-invoices/src/web/proformas/change-status/hooks/use-change-status.ts delete mode 100644 modules/customer-invoices/src/web/proformas/delete/api/index.ts delete mode 100644 modules/customer-invoices/src/web/proformas/delete/hooks/index.ts delete mode 100644 modules/customer-invoices/src/web/proformas/delete/hooks/use-delete-proforma.ts rename modules/customer-invoices/src/web/proformas/delete/ui/{ => components}/delete-proforma-dialog.tsx (50%) create mode 100644 modules/customer-invoices/src/web/proformas/delete/ui/components/index.ts rename modules/customer-invoices/src/web/proformas/hooks/{use-delete-proforma-mutation.ts => use-delete-proforma-mutation.ts.bak} (96%) delete mode 100644 modules/customer-invoices/src/web/proformas/hooks/use-proforma-query.ts delete mode 100644 modules/customer-invoices/src/web/proformas/hooks/use-proforma-update-mutation.ts delete mode 100644 modules/customer-invoices/src/web/proformas/list/adapters/index.ts delete mode 100644 modules/customer-invoices/src/web/proformas/list/adapters/proforma-summary-dto.adapter.ts delete mode 100644 modules/customer-invoices/src/web/proformas/list/api/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts rename modules/customer-invoices/src/web/proformas/list/controllers/{use-proforma-list.controller.ts => use-list-proformas.controller.ts} (50%) delete mode 100644 modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list-page.controller.ts.ts delete mode 100644 modules/customer-invoices/src/web/proformas/list/hooks/index.ts delete mode 100644 modules/customer-invoices/src/web/proformas/list/hooks/use-proforma-list-query.ts create mode 100644 modules/customer-invoices/src/web/proformas/schemas/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/adapters/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/adapters/list-proformas.adapter.ts rename modules/customer-invoices/src/web/proformas/{change-status/api/change-proforma-status.api.ts => shared/api/change-proforma-status-by-id.api.ts} (88%) rename modules/customer-invoices/src/web/proformas/{delete/api/delete-proforma.api.ts => shared/api/delete-proforma-by-ip.api.ts} (80%) create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/get-proforma-by-ip.api.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/api/index.ts rename modules/customer-invoices/src/web/proformas/{list/api/get-proforma-list.api.ts => shared/api/list-proformas.api.ts} (53%) create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/proforma-list-row.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/proforma-list.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/entities/proforma-status.entity.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/use-change-proforma-status-mutation.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/use-delete-proforma-mutation.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/use-list-proformas-query.tsx create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-get-query.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/hooks/use-proforma-update-mutation.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/shared/ui/blocks/index.ts rename modules/customer-invoices/src/web/proformas/{ => shared}/ui/blocks/proforma-layout.tsx (100%) create mode 100644 modules/customer-invoices/src/web/proformas/shared/ui/index.ts delete mode 100644 modules/customer-invoices/src/web/proformas/types/proforma.api.schema.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/controllers/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/controllers/use-proforma-update-page.controller.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/index.ts rename modules/{customers/src/web/list => customer-invoices/src/web/proformas/update}/types/index.ts (100%) rename modules/customer-invoices/src/web/proformas/{types/proforma.form.schema.ts => update/types/types.ts} (100%) create mode 100644 modules/customer-invoices/src/web/proformas/update/ui/index.ts create mode 100644 modules/customer-invoices/src/web/proformas/update/ui/pages/index.ts rename modules/customer-invoices/src/web/proformas/{pages/update => update/ui/pages}/proforma-update-page.tsx (57%) rename modules/customers/src/web/{ => _archived}/components/client-selector-modal.tsx (99%) rename modules/customers/src/web/{ => _archived}/components/customer-modal-selector/customer-card.tsx (100%) rename modules/customers/src/web/{ => _archived}/components/customer-modal-selector/customer-create-modal.tsx (98%) rename modules/customers/src/web/{ => _archived}/components/customer-modal-selector/customer-empty-card.tsx (100%) rename modules/customers/src/web/{ => _archived}/components/customer-modal-selector/customer-modal-selector-field.tsx (100%) rename modules/customers/src/web/{ => _archived}/components/customer-modal-selector/customer-modal-selector.tsx (100%) rename modules/customers/src/web/{ => _archived}/components/customer-modal-selector/customer-search-dialog.tsx (100%) rename modules/customers/src/web/{ => _archived}/components/customer-modal-selector/customer-view-dialog.tsx (100%) rename modules/customers/src/web/{ => _archived}/components/customer-modal-selector/index.ts (100%) rename modules/customers/src/web/{ => _archived}/components/index.ts (75%) rename modules/customers/src/web/{ => _archived}/constants/customer.constants.ts (100%) rename modules/customers/src/web/{ => _archived}/constants/index.ts (100%) rename modules/customers/src/web/{ => _archived}/context/customers-context.tsx (100%) rename modules/customers/src/web/{ => _archived}/context/index.ts (100%) rename modules/customers/src/web/{ => _archived}/hooks/index.ts (100%) rename modules/customers/src/web/{ => _archived}/hooks/use-create-customer-mutation.ts (94%) rename modules/customers/src/web/{ => _archived}/hooks/use-customer-query.ts (90%) rename modules/customers/src/web/{ => _archived}/hooks/use-customers-context.tsx (78%) rename modules/customers/src/web/{ => _archived}/hooks/use-delete-customer-mutation.ts (90%) rename modules/customers/src/web/{ => _archived}/hooks/use-update-customer-mutation.ts (93%) rename modules/customers/src/web/{ => _archived}/pages/create/customer-create-page.tsx (97%) rename modules/customers/src/web/{ => _archived}/pages/create/index.ts (100%) rename modules/customers/src/web/{ => _archived}/pages/create/use-customer-create-controller.ts (98%) rename modules/customers/src/web/{ => _archived}/pages/create/utils.ts (100%) rename modules/customers/src/web/{ => _archived}/pages/index.ts (100%) rename modules/customers/src/web/{ => _archived}/pages/list/customers-list-grid.tsx (98%) create mode 100644 modules/customers/src/web/_archived/pages/list/index.ts rename modules/customers/src/web/{ => _archived}/pages/list/use-customers-list-columns.tsx (98%) rename modules/customers/src/web/{ => _archived}/pages/update/customer-update-modal.tsx (99%) rename modules/customers/src/web/{ => _archived}/pages/update/customer-update-page.tsx (95%) rename modules/customers/src/web/{ => _archived}/pages/update/index.ts (100%) rename modules/customers/src/web/{ => _archived}/schemas/customer-resume.form.schema.ts (100%) rename modules/customers/src/web/{ => _archived}/schemas/customer.api.schema.ts (98%) rename modules/customers/src/web/{ => _archived}/schemas/customer.form.schema.ts (100%) rename modules/customers/src/web/{ => _archived}/schemas/index.ts (100%) delete mode 100644 modules/customers/src/web/common/api/api-types.ts delete mode 100644 modules/customers/src/web/common/api/index.ts delete mode 100644 modules/customers/src/web/common/hooks/use-customer-list-query.tsx delete mode 100644 modules/customers/src/web/common/index.ts delete mode 100644 modules/customers/src/web/common/types.ts delete mode 100644 modules/customers/src/web/list/adapters/customer-summary-dto.adapter.ts delete mode 100644 modules/customers/src/web/list/adapters/index.ts delete mode 100644 modules/customers/src/web/list/controllers/use-customer-list-page.controller.ts create mode 100644 modules/customers/src/web/list/controllers/use-list-customers-page.controller.ts rename modules/customers/src/web/list/controllers/{use-customer-list.controller.ts => use-list-customers.controller.ts} (53%) delete mode 100644 modules/customers/src/web/list/types/types.ts rename modules/customers/src/web/list/ui/pages/{customer-list-page.tsx => list-customers-page.tsx} (80%) delete mode 100644 modules/customers/src/web/pages/list/index.ts create mode 100644 modules/customers/src/web/shared/adapters/get-customer-by-id.adapter.ts create mode 100644 modules/customers/src/web/shared/adapters/index.ts create mode 100644 modules/customers/src/web/shared/adapters/list-customers.adapter.ts rename modules/customers/src/web/{common/api/get-customer-by-ip.api.ts => shared/api/get-customer-by-id.api.ts} (87%) create mode 100644 modules/customers/src/web/shared/api/index.ts rename modules/customers/src/web/{common/api/get-customer-list.api.ts => shared/api/list-customers.api.ts} (56%) create mode 100644 modules/customers/src/web/shared/entities/customer-list-row.entity.ts create mode 100644 modules/customers/src/web/shared/entities/customer-list.entity.ts create mode 100644 modules/customers/src/web/shared/entities/customer.entity.ts create mode 100644 modules/customers/src/web/shared/entities/index.ts rename modules/customers/src/web/{common => shared}/hooks/index.ts (67%) rename modules/customers/src/web/{common => shared}/hooks/toValidationErrors.ts (100%) rename modules/customers/src/web/{common => shared}/hooks/use-customer-get-query.ts (100%) rename modules/customers/src/web/{common => shared}/hooks/use-customer-update-mutation.ts (100%) create mode 100644 modules/customers/src/web/shared/hooks/use-list-customers-query.ts create mode 100644 modules/customers/src/web/shared/index.ts rename modules/customers/src/web/{ => shared}/ui/blocks/customer-layout.tsx (100%) rename modules/customers/src/web/{ => shared}/ui/blocks/index.ts (100%) rename modules/customers/src/web/{ => shared}/ui/components/customer-status-badge.tsx (100%) rename modules/customers/src/web/{ => shared}/ui/components/error-alert.tsx (100%) rename modules/customers/src/web/{ => shared}/ui/components/index.ts (100%) rename modules/customers/src/web/{ => shared}/ui/index.ts (100%) delete mode 100644 modules/customers/src/web/update/hooks/use-customer-get-query.ts delete mode 100644 modules/customers/src/web/update/hooks/use-customer-update-mutation.ts rename modules/customers/src/web/{components => update/ui}/editor/customer-additional-config-fields.tsx (97%) rename modules/customers/src/web/{components => update/ui}/editor/customer-address-fields.tsx (98%) rename modules/customers/src/web/{components => update/ui}/editor/customer-basic-info-fields.tsx (99%) rename modules/customers/src/web/{components => update/ui}/editor/customer-contact-fields.tsx (99%) rename modules/customers/src/web/{components => update/ui}/editor/customer-edit-form.tsx (100%) rename modules/customers/src/web/{components => update/ui}/editor/customer-taxes-multi-select.tsx (97%) rename modules/customers/src/web/{components => update/ui}/editor/index.ts (100%) diff --git a/modules/core/src/web/components/form/simple-search-input.tsx b/modules/core/src/web/components/form/simple-search-input.tsx index a91afda1..c9646da3 100644 --- a/modules/core/src/web/components/form/simple-search-input.tsx +++ b/modules/core/src/web/components/form/simple-search-input.tsx @@ -12,6 +12,7 @@ import { useEffect, useRef, useState } from "react"; import { useTranslation } from "../../i18n"; type SimpleSearchInputProps = { + value: string; onSearchChange: (value: string) => void; loading?: boolean; maxHistory?: number; @@ -20,12 +21,13 @@ type SimpleSearchInputProps = { const SEARCH_HISTORY_KEY = "search_history"; export const SimpleSearchInput = ({ + value, onSearchChange, loading = false, maxHistory = 8, }: SimpleSearchInputProps) => { const { t } = useTranslation(); - const [searchValue, setSearchValue] = useState(""); + const [searchValue, setSearchValue] = useState(value); const [lastSearch, setLastSearch] = useState(""); const [history, setHistory] = useState([]); const [open, setOpen] = useState(false); diff --git a/modules/core/src/web/lib/helpers/http-url-utils.ts b/modules/core/src/web/lib/helpers/http-url-utils.ts new file mode 100644 index 00000000..3b7426b8 --- /dev/null +++ b/modules/core/src/web/lib/helpers/http-url-utils.ts @@ -0,0 +1,5 @@ +export function safeHTTPUrl(url: string) { + if (!url) return "#"; + if (/^https?:\/\//i.test(url)) return url; + return `https://${url}`; +} diff --git a/modules/core/src/web/lib/helpers/index.ts b/modules/core/src/web/lib/helpers/index.ts index 68621538..3634925d 100644 --- a/modules/core/src/web/lib/helpers/index.ts +++ b/modules/core/src/web/lib/helpers/index.ts @@ -1,2 +1,3 @@ export * from "./date-func"; export * from "./form-utils"; +export * from "./http-url-utils"; diff --git a/modules/customer-invoices/src/common/locales/es.json b/modules/customer-invoices/src/common/locales/es.json index c9c12770..43bf102f 100644 --- a/modules/customer-invoices/src/common/locales/es.json +++ b/modules/customer-invoices/src/common/locales/es.json @@ -103,7 +103,14 @@ "delete": "Eliminar", "deleting": "Eliminando...", "success_title": "Proforma eliminada", - "error_title": "Error al eliminar la proforma" + "error_title": "Error al eliminar la proforma", + "single_title": "Eliminar proforma {{reference}}", + "second_confirm_title": "Confirmación adicional", + "multiple_title": "Eliminar {{count}} proformas", + "second_confirm_description": "Estás a punto de borrar {{total}} proformas. Esta acción masiva no se puede deshacer. ¿Seguro que quieres continuar?", + "single_description": "¿Seguro que deseas eliminar esta proforma? Esta acción no se puede deshacer.", + "multiple_description": "¿Seguro que deseas eliminar las {{total}} proformas seleccionadas? Esta acción no se puede deshacer.", + "list_item": "Proforma {{reference}}" } }, "pages": { diff --git a/modules/customer-invoices/src/web/customer-invoice-routes.tsx b/modules/customer-invoices/src/web/customer-invoice-routes.tsx index b6da56d9..a5358ec0 100644 --- a/modules/customer-invoices/src/web/customer-invoice-routes.tsx +++ b/modules/customer-invoices/src/web/customer-invoice-routes.tsx @@ -2,19 +2,17 @@ import type { ModuleClientParams } from "@erp/core/client"; import { lazy } from "react"; import { Outlet, type RouteObject } from "react-router-dom"; -import { ProformaCreatePage } from "./proformas/create"; - const ProformaLayout = lazy(() => - import("./proformas/ui").then((m) => ({ default: m.ProformaLayout })) + import("./proformas/shared").then((m) => ({ default: m.ProformaLayout })) ); const ProformasListPage = lazy(() => import("./proformas/list").then((m) => ({ default: m.ProformaListPage })) ); -const ProformasCreatePage = lazy(() => +/*const ProformasCreatePage = lazy(() => import("./proformas/create").then((m) => ({ default: m.ProformaCreatePage })) -); +);*/ /*const InvoiceUpdatePage = lazy(() => import("./pages").then((m) => ({ default: m.InvoiceUpdatePage })) @@ -40,7 +38,7 @@ export const CustomerInvoiceRoutes = (params: ModuleClientParams): RouteObject[] children: [ { path: "", index: true, element: }, // index { path: "list", element: }, - { path: "create", element: }, + //{ path: "create", element: }, //{ path: ":id/edit", element: }, ], }, diff --git a/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/index.ts b/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/index.ts index fba7aefc..e69de29b 100644 --- a/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/index.ts +++ b/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/index.ts @@ -1 +0,0 @@ -export * from "./use-download-invoice-pdf-query"; diff --git a/modules/customer-invoices/src/web/issued-invoices/list/hooks/index.ts b/modules/customer-invoices/src/web/issued-invoices/list/hooks/index.ts index 6903c330..e69de29b 100644 --- a/modules/customer-invoices/src/web/issued-invoices/list/hooks/index.ts +++ b/modules/customer-invoices/src/web/issued-invoices/list/hooks/index.ts @@ -1 +0,0 @@ -export * from "./use-issued-invoice-list-query"; diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/hooks/index.ts b/modules/customer-invoices/src/web/issued-invoices/shared/hooks/index.ts new file mode 100644 index 00000000..780a37a8 --- /dev/null +++ b/modules/customer-invoices/src/web/issued-invoices/shared/hooks/index.ts @@ -0,0 +1,2 @@ +export * from "./use-download-invoice-pdf-query"; +export * from "./use-issued-invoice-list-query"; diff --git a/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/use-download-invoice-pdf-query.ts b/modules/customer-invoices/src/web/issued-invoices/shared/hooks/use-download-invoice-pdf-query.ts similarity index 96% rename from modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/use-download-invoice-pdf-query.ts rename to modules/customer-invoices/src/web/issued-invoices/shared/hooks/use-download-invoice-pdf-query.ts index 0bef7f2d..10c7be4f 100644 --- a/modules/customer-invoices/src/web/issued-invoices/download-pdf/hooks/use-download-invoice-pdf-query.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/hooks/use-download-invoice-pdf-query.ts @@ -3,7 +3,7 @@ import { useDataSource } from "@erp/core/hooks"; import { type QueryKey, useQuery } from "@tanstack/react-query"; -import { downloadInvoicePDFApi } from "../api"; +import { downloadInvoicePDFApi } from "../../download-pdf/api"; export const ISSUED_INVOICE_QUERY_KEY = (id: string): QueryKey => ["issued_invoice", id] as const; diff --git a/modules/customer-invoices/src/web/issued-invoices/list/hooks/use-issued-invoice-list-query.ts b/modules/customer-invoices/src/web/issued-invoices/shared/hooks/use-issued-invoice-list-query.ts similarity index 96% rename from modules/customer-invoices/src/web/issued-invoices/list/hooks/use-issued-invoice-list-query.ts rename to modules/customer-invoices/src/web/issued-invoices/shared/hooks/use-issued-invoice-list-query.ts index 1e3f34f0..f788632f 100644 --- a/modules/customer-invoices/src/web/issued-invoices/list/hooks/use-issued-invoice-list-query.ts +++ b/modules/customer-invoices/src/web/issued-invoices/shared/hooks/use-issued-invoice-list-query.ts @@ -3,8 +3,8 @@ import { useDataSource } from "@erp/core/hooks"; import { INITIAL_PAGE_INDEX, INITIAL_PAGE_SIZE } from "@repo/rdx-criteria"; import { type DefaultError, type QueryKey, useQuery } from "@tanstack/react-query"; +import { getIssuedInvoiceListApi } from "../../list/api"; import type { IssuedInvoiceSummaryPage } from "../../types"; -import { getIssuedInvoiceListApi } from "../api"; export const ISSUED_INVOICES_QUERY_KEY = (criteria?: CriteriaDTO): QueryKey => [ "issued_invoices", diff --git a/modules/customer-invoices/src/web/issued-invoices/shared/index.ts b/modules/customer-invoices/src/web/issued-invoices/shared/index.ts new file mode 100644 index 00000000..007f69d0 --- /dev/null +++ b/modules/customer-invoices/src/web/issued-invoices/shared/index.ts @@ -0,0 +1 @@ +export * from "./hooks"; diff --git a/modules/customer-invoices/src/web/pages/create/create-customer-invoice-edit-form.tsx b/modules/customer-invoices/src/web/pages/create/create-customer-invoice-edit-form.tsx index dbbc1657..cb3f8326 100644 --- a/modules/customer-invoices/src/web/pages/create/create-customer-invoice-edit-form.tsx +++ b/modules/customer-invoices/src/web/pages/create/create-customer-invoice-edit-form.tsx @@ -38,8 +38,8 @@ import { useFieldArray, useForm } from "react-hook-form"; import * as z from "zod"; import { useTranslation } from "../../i18n"; -import { CustomerInvoicePricesCard } from "../../shared/ui/components"; -import { CustomerInvoiceItemsCardEditor } from "../../shared/ui/components/items"; +import { CustomerInvoicePricesCard } from "../../proformas/shared/ui/components"; +import { CustomerInvoiceItemsCardEditor } from "../../proformas/shared/ui/components/items"; import type { CustomerInvoiceData } from "./customer-invoice.schema"; import { formatCurrency } from "./utils"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/api/index.ts b/modules/customer-invoices/src/web/proformas/change-status/api/index.ts deleted file mode 100644 index 0d8419a6..00000000 --- a/modules/customer-invoices/src/web/proformas/change-status/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./change-proforma-status.api"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/controllers/index.ts b/modules/customer-invoices/src/web/proformas/change-status/controllers/index.ts index 44081230..e2ac21d5 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/controllers/index.ts +++ b/modules/customer-invoices/src/web/proformas/change-status/controllers/index.ts @@ -1 +1 @@ -export * from "./use-change-status-dialog-controller"; +export * from "./use-change-proforma-status-dialog-controller"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts new file mode 100644 index 00000000..74d06518 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-proforma-status-dialog-controller.ts @@ -0,0 +1,160 @@ +import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers"; +import * as React from "react"; + +import { useTranslation } from "../../../i18n"; +import type { ProformaListRow } from "../../shared"; +import type { PROFORMA_STATUS } from "../../shared/entities"; +import { useChangeProformaStatusMutation } from "../../shared/hooks"; + +interface ChangeStatusDialogState { + open: boolean; + proformas: ProformaListRow[]; + isSubmitting: boolean; +} + +const INITIAL_STATE: ChangeStatusDialogState = { + open: false, + proformas: [], + isSubmitting: false, +}; + +function canChangeStatus(isSubmitting: boolean, proformas: ProformaListRow[]): boolean { + return !isSubmitting && proformas.length > 0; +} + +export function useChangeProformaStatusDialogController() { + const { t } = useTranslation(); + const { changeProformaStatus } = useChangeProformaStatusMutation(); + + const [state, setState] = React.useState(INITIAL_STATE); + const { isSubmitting, proformas } = state; + + const openDialog = React.useCallback((proformas: ProformaListRow[]) => { + setState({ + open: true, + proformas, + isSubmitting: false, + }); + }, []); + + const closeDialog = React.useCallback(() => { + setState(INITIAL_STATE); + }, []); + + const changeStatusSelectedProformas = React.useCallback( + async (proformas: ProformaListRow[], newStatus: PROFORMA_STATUS) => { + const results = await Promise.allSettled( + proformas.map((proforma) => + changeProformaStatus({ + proformaId: proforma.id, + newStatus, + }) + ) + ); + + const successCount = results.filter((result) => result.status === "fulfilled").length; + const errorCount = results.length - successCount; + + return { + successCount, + errorCount, + }; + }, + [changeProformaStatus] + ); + + const notifyChangeResult = React.useCallback( + (proformas: ProformaListRow[], successCount: number, errorCount: number) => { + if (proformas.length === 1 && successCount === 1) { + const proforma = proformas[0]; + + showSuccessToast( + t("pages.proformas.change_status.successTitle"), + t("pages.proformas.change_status.successSingleMessage", { + reference: proforma.reference || `#${proforma.id}`, + }) + ); + } else if (successCount > 0) { + showSuccessToast( + t("pages.proformas.change_status.successTitle"), + t("pages.proformas.change_status.successMultipleMessage", { + count: successCount, + }) + ); + } + + if (errorCount > 0) { + showErrorToast( + t("pages.proformas.change_status.errorTitle"), + proformas.length === 1 + ? t("pages.proformas.change_status.errorSingleMessage") + : t("pages.proformas.change_status.errorMultipleMessage", { + count: errorCount, + }) + ); + } + }, + [t] + ); + + const changeStatus = React.useCallback( + async (newStatus: PROFORMA_STATUS) => { + setState((current) => ({ + ...current, + isSubmitting: true, + })); + + try { + const { successCount, errorCount } = await changeStatusSelectedProformas( + proformas, + newStatus + ); + + notifyChangeResult(proformas, successCount, errorCount); + + if (errorCount === 0) { + closeDialog(); + return; + } + + setState((current) => ({ + ...current, + isSubmitting: false, + })); + } catch { + showErrorToast( + t("pages.proformas.change_status.errorTitle"), + t("pages.proformas.change_status.errorUnexpectedMessage") + ); + + setState((current) => ({ + ...current, + isSubmitting: false, + })); + } + }, + [closeDialog, changeStatusSelectedProformas, notifyChangeResult, proformas, t] + ); + + const confirmChangeStatus = React.useCallback( + async (newStatus: PROFORMA_STATUS) => { + if (!canChangeStatus(isSubmitting, proformas)) { + return; + } + + await changeStatus(newStatus); + }, + [changeStatus, isSubmitting, proformas] + ); + + return { + open: state.open, + proformas: state.proformas, + isSubmitting: state.isSubmitting, + isBulkDelete: state.proformas.length > 1, + + openDialog, + closeDialog, + confirmChangeStatus, + }; +} diff --git a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-status-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-status-dialog-controller.ts deleted file mode 100644 index 2f4b1b17..00000000 --- a/modules/customer-invoices/src/web/proformas/change-status/controllers/use-change-status-dialog-controller.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers"; -import * as React from "react"; - -import type { PROFORMA_STATUS, ProformaSummaryData } from "../../types"; -import { useChangeProformaStatus } from "../hooks/use-change-status"; - -interface ChangeStatusDialogState { - open: boolean; - proformas: ProformaSummaryData[]; - loading: boolean; -} - -export function useChangeStatusDialogController() { - const { changeStatus, isPending } = useChangeProformaStatus(); - - const [state, setState] = React.useState({ - open: false, - proformas: [] as ProformaSummaryData[], - loading: false, - }); - - const openDialog = (proformas: ProformaSummaryData[]) => { - setState({ - open: true, - proformas, - loading: false, - }); - }; - - const closeDialog = () => { - setState((s) => ({ ...s, open: false })); - }; - - const confirmChangeStatus = async (status: PROFORMA_STATUS) => { - if (!state.proformas.length) return; - - setState((s) => ({ ...s, loading: true })); - - for (const proforma of state.proformas) { - await changeStatus(proforma.id, status, { - onSuccess: () => { - showSuccessToast("Estado cambiado"); - }, - onError: (err: unknown) => { - const error = err as Error; - showErrorToast("Error cambiando estado", error.message); - }, - }); - } - - setState((s) => ({ ...s, loading: false })); - closeDialog(); - }; - - return { - open: state.open, - proformas: state.proformas, - isSubmitting: state.loading, - - openDialog, - closeDialog, - confirmChangeStatus, - }; -} diff --git a/modules/customer-invoices/src/web/proformas/change-status/hooks/index.ts b/modules/customer-invoices/src/web/proformas/change-status/hooks/index.ts deleted file mode 100644 index 5f4e2a47..00000000 --- a/modules/customer-invoices/src/web/proformas/change-status/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./use-change-status"; diff --git a/modules/customer-invoices/src/web/proformas/change-status/hooks/use-change-status.ts b/modules/customer-invoices/src/web/proformas/change-status/hooks/use-change-status.ts deleted file mode 100644 index 5e81d971..00000000 --- a/modules/customer-invoices/src/web/proformas/change-status/hooks/use-change-status.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { useDataSource } from "@erp/core/hooks"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; - -import type { PROFORMA_STATUS } from "../../types"; -import { changeProformaStatusApi } from "../api/change-proforma-status.api"; - -interface ChangeProformaStatusOptions { - onSuccess?: () => void; - onError?: (err: unknown) => void; - onLoadingChange?: (loading: boolean) => void; -} - -interface ChangeProformaStatusPayload { - proformaId: string; - newStatus: PROFORMA_STATUS; -} - -export function useChangeProformaStatus() { - const dataSource = useDataSource(); - const queryClient = useQueryClient(); - - const mutation = useMutation({ - mutationFn: ({ proformaId, newStatus }: ChangeProformaStatusPayload) => - changeProformaStatusApi(dataSource, proformaId, newStatus), - - onSuccess() { - queryClient.invalidateQueries({ queryKey: ["proformas"] }); - }, - }); - - async function changeStatus( - proformaId: string, - newStatus: string, - opts?: ChangeProformaStatusOptions - ) { - try { - opts?.onLoadingChange?.(true); - await mutation.mutateAsync({ proformaId, newStatus: newStatus as PROFORMA_STATUS }); - opts?.onSuccess?.(); - } catch (err) { - opts?.onError?.(err); - } finally { - opts?.onLoadingChange?.(false); - } - } - - return { - changeStatus, - isPending: mutation.isPending, - }; -} diff --git a/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx b/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx index eed5bfd3..dce99f59 100644 --- a/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/change-status/ui/change-status-dialog.tsx @@ -119,7 +119,13 @@ export function ChangeStatusDialog({ }; return ( - + { + if (isSubmitting) return; + onOpenChange(nextOpen); + }} + open={open} + > Cambiar estado de la proforma diff --git a/modules/customer-invoices/src/web/proformas/delete/api/index.ts b/modules/customer-invoices/src/web/proformas/delete/api/index.ts deleted file mode 100644 index 63e61c01..00000000 --- a/modules/customer-invoices/src/web/proformas/delete/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./delete-proforma.api"; diff --git a/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts b/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts index e12a6710..9b699ac2 100644 --- a/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts +++ b/modules/customer-invoices/src/web/proformas/delete/controllers/use-delete-proforma-dialog-controller.ts @@ -2,76 +2,182 @@ import { showErrorToast, showSuccessToast } from "@repo/rdx-ui/helpers"; import React from "react"; import { useTranslation } from "../../../i18n"; -import type { ProformaSummaryData } from "../../types"; -import { useDeleteProforma } from "../hooks"; +import { type ProformaListRow, useDeleteProformaMutation } from "../../shared"; interface DeleteProformaDialogState { open: boolean; - proformas: ProformaSummaryData[]; - loading: boolean; - requireSecondConfirm: boolean; + proformas: ProformaListRow[]; + isSubmitting: boolean; + requiresSecondConfirm: boolean; + confirmStep: "initial" | "second"; +} + +const INITIAL_STATE: DeleteProformaDialogState = { + open: false, + proformas: [], + isSubmitting: false, + requiresSecondConfirm: false, + confirmStep: "initial", +}; + +const SECOND_CONFIRM_THRESHOLD = 5; + +function canSubmitDelete(isSubmitting: boolean, proformas: ProformaListRow[]): boolean { + return !isSubmitting && proformas.length > 0; +} + +function shouldMoveToSecondConfirmStep( + requiresSecondConfirm: boolean, + confirmStep: "initial" | "second" +): boolean { + return requiresSecondConfirm && confirmStep === "initial"; } export function useDeleteProformaDialogController() { const { t } = useTranslation(); - const { deleteProforma } = useDeleteProforma(); + const { deleteProforma } = useDeleteProformaMutation(); - const [state, setState] = React.useState({ - open: false, - proformas: [], - loading: false, - requireSecondConfirm: false, - }); + const [state, setState] = React.useState(INITIAL_STATE); + const { isSubmitting, proformas, requiresSecondConfirm, confirmStep } = state; + + const openDialog = React.useCallback((proformas: ProformaListRow[]) => { + const requiresSecondConfirm = proformas.length > SECOND_CONFIRM_THRESHOLD; - const openDialog = (proformas: ProformaSummaryData[]) => { - const needDoubleCheck = proformas.length > 5; setState({ open: true, proformas, - loading: false, - requireSecondConfirm: needDoubleCheck, + isSubmitting: false, + requiresSecondConfirm, + confirmStep: "initial", }); - }; + }, []); - const closeDialog = () => { - setState((s) => ({ ...s, open: false })); - }; + const closeDialog = React.useCallback(() => { + setState(INITIAL_STATE); + }, []); - const confirmDelete = async () => { - if (state.proformas.length === 0) return; + const moveToSecondConfirmStep = React.useCallback(() => { + setState((current) => ({ + ...current, + confirmStep: "second", + })); + }, []); - if (state.requireSecondConfirm) { - setState((s) => ({ ...s, requireSecondConfirm: false })); - return; // ahora el UI mostrará un segundo mensaje de confirmación + const deleteSelectedProformas = React.useCallback( + async (proformas: ProformaListRow[]) => { + const results = await Promise.allSettled( + proformas.map((proforma) => + deleteProforma({ + proformaId: proforma.id, + }) + ) + ); + + const successCount = results.filter((result) => result.status === "fulfilled").length; + const errorCount = results.length - successCount; + + return { + successCount, + errorCount, + }; + }, + [deleteProforma] + ); + + const notifyDeleteResult = React.useCallback( + (proformas: ProformaListRow[], successCount: number, errorCount: number) => { + if (proformas.length === 1 && successCount === 1) { + const proforma = proformas[0]; + + showSuccessToast( + t("pages.proformas.delete.successTitle"), + t("pages.proformas.delete.successSingleMessage", { + reference: proforma.reference || `#${proforma.id}`, + }) + ); + } else if (successCount > 0) { + showSuccessToast( + t("pages.proformas.delete.successTitle"), + t("pages.proformas.delete.successMultipleMessage", { + count: successCount, + }) + ); + } + + if (errorCount > 0) { + showErrorToast( + t("pages.proformas.delete.errorTitle"), + proformas.length === 1 + ? t("pages.proformas.delete.errorSingleMessage") + : t("pages.proformas.delete.errorMultipleMessage", { + count: errorCount, + }) + ); + } + }, + [t] + ); + + const submitDelete = React.useCallback(async () => { + setState((current) => ({ + ...current, + isSubmitting: true, + })); + + try { + const { successCount, errorCount } = await deleteSelectedProformas(proformas); + + notifyDeleteResult(proformas, successCount, errorCount); + + if (errorCount === 0) { + closeDialog(); + return; + } + + setState((current) => ({ + ...current, + isSubmitting: false, + })); + } catch { + showErrorToast( + t("pages.proformas.delete.errorTitle"), + t("pages.proformas.delete.errorUnexpectedMessage") + ); + + setState((current) => ({ + ...current, + isSubmitting: false, + })); + } + }, [closeDialog, deleteSelectedProformas, notifyDeleteResult, proformas, t]); + + const confirmDelete = React.useCallback(async () => { + if (!canSubmitDelete(isSubmitting, proformas)) { + return; } - setState((s) => ({ ...s, loading: true })); - - for (const p of state.proformas) { - await deleteProforma(p.id, { - onSuccess: () => { - showSuccessToast( - "Proforma eliminada", - `La proforma ${p.reference ?? `#${p.id}`} ha sido eliminada.` - ); - }, - onError: (err) => { - showErrorToast( - "Error al eliminar", - err instanceof Error ? err.message : "Ocurrió un error al eliminar la proforma" - ); - }, - }); + if (shouldMoveToSecondConfirmStep(requiresSecondConfirm, confirmStep)) { + moveToSecondConfirmStep(); + return; } - setState((s) => ({ ...s, loading: false })); - closeDialog(); - }; + await submitDelete(); + }, [ + moveToSecondConfirmStep, + submitDelete, + isSubmitting, + proformas, + requiresSecondConfirm, + confirmStep, + ]); return { open: state.open, proformas: state.proformas, - isSubmitting: state.loading, + isSubmitting: state.isSubmitting, + requiresSecondConfirm: state.requiresSecondConfirm, + isSecondConfirmStep: state.confirmStep === "second", + isBulkDelete: state.proformas.length > 1, openDialog, closeDialog, diff --git a/modules/customer-invoices/src/web/proformas/delete/hooks/index.ts b/modules/customer-invoices/src/web/proformas/delete/hooks/index.ts deleted file mode 100644 index feacd21f..00000000 --- a/modules/customer-invoices/src/web/proformas/delete/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./use-delete-proforma"; diff --git a/modules/customer-invoices/src/web/proformas/delete/hooks/use-delete-proforma.ts b/modules/customer-invoices/src/web/proformas/delete/hooks/use-delete-proforma.ts deleted file mode 100644 index 6ce74b1e..00000000 --- a/modules/customer-invoices/src/web/proformas/delete/hooks/use-delete-proforma.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { useDataSource } from "@erp/core/hooks"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; - -import { deleteProformaApi } from "../api"; - -interface DeleteProformaOptions { - onSuccess?: () => void; - onError?: (err: unknown) => void; - onLoadingChange?: (loading: boolean) => void; -} - -interface DeleteProformaPayload { - proformaId: string; -} - -export function useDeleteProforma() { - const dataSource = useDataSource(); - const queryClient = useQueryClient(); - - const mutation = useMutation({ - mutationFn: ({ proformaId }: DeleteProformaPayload) => - deleteProformaApi(dataSource, proformaId), - - onSuccess() { - queryClient.invalidateQueries({ queryKey: ["proformas"] }); - }, - }); - - async function deleteProforma(proformaId: string, opts?: DeleteProformaOptions) { - try { - opts?.onLoadingChange?.(true); - await mutation.mutateAsync({ proformaId }); - opts?.onSuccess?.(); - } catch (err) { - opts?.onError?.(err); - } finally { - opts?.onLoadingChange?.(false); - } - } - - return { - deleteProforma, - isPending: mutation.isPending, - }; -} diff --git a/modules/customer-invoices/src/web/proformas/delete/index.ts b/modules/customer-invoices/src/web/proformas/delete/index.ts index 6b67c80e..0d88782e 100644 --- a/modules/customer-invoices/src/web/proformas/delete/index.ts +++ b/modules/customer-invoices/src/web/proformas/delete/index.ts @@ -1 +1,2 @@ export * from "./controllers"; +export * from "./ui"; diff --git a/modules/customer-invoices/src/web/proformas/delete/ui/delete-proforma-dialog.tsx b/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx similarity index 50% rename from modules/customer-invoices/src/web/proformas/delete/ui/delete-proforma-dialog.tsx rename to modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx index 920b46a1..b3005a60 100644 --- a/modules/customer-invoices/src/web/proformas/delete/ui/delete-proforma-dialog.tsx +++ b/modules/customer-invoices/src/web/proformas/delete/ui/components/delete-proforma-dialog.tsx @@ -8,18 +8,18 @@ import { Button, Spinner, } from "@repo/shadcn-ui/components"; -import { useEffect } from "react"; -import { useTranslation } from "../../../i18n"; -import type { ProformaSummaryData } from "../../types"; +import { useTranslation } from "../../../../i18n"; +import type { ProformaListRow } from "../../../shared"; interface DeleteProformaDialogProps { open: boolean; onOpenChange: (open: boolean) => void; - proformas: ProformaSummaryData[]; + proformas: ProformaListRow[]; isSubmitting: boolean; onConfirm: () => void; - requireSecondConfirm: boolean; + requiresSecondConfirm: boolean; + isSecondConfirmStep: boolean; } export function DeleteProformaDialog({ @@ -28,56 +28,53 @@ export function DeleteProformaDialog({ proformas, isSubmitting, onConfirm, - requireSecondConfirm, + requiresSecondConfirm, + isSecondConfirmStep, }: DeleteProformaDialogProps) { const { t } = useTranslation(); + const total = proformas.length; const isSingle = total === 1; + const firstProforma = proformas[0]; - const title = requireSecondConfirm - ? "Confirmación adicional" + const title = isSecondConfirmStep + ? t("proformas.delete_proforma_dialog.second_confirm_title", { count: total }) : isSingle - ? `Eliminar proforma ${proformas[0].reference ?? `#${proformas[0].id}`}` - : `Eliminar ${total} proformas`; + ? t("proformas.delete_proforma_dialog.single_title", { + reference: firstProforma?.reference ?? `#${firstProforma?.id}`, + }) + : t("proformas.delete_proforma_dialog.multiple_title", { count: total }); - const description = requireSecondConfirm - ? `Estás a punto de borrar ${total} proformas. Esta acción masiva no se puede deshacer. ¿Seguro que quieres continuar?` + const description = isSecondConfirmStep + ? t("proformas.delete_proforma_dialog.second_confirm_description", { count: total }) : isSingle - ? "¿Seguro que deseas eliminar esta proforma? Esta acción no se puede deshacer." - : `¿Seguro que deseas eliminar las ${total} proformas seleccionadas? Esta acción no se puede deshacer.`; - - // Usar teclado para confirmar (Enter / Escape) - useEffect(() => { - if (!open) return; - - const handleKey = (e: KeyboardEvent) => { - if (e.key === "Enter") { - e.preventDefault(); - onConfirm(); - } - if (e.key === "Escape") { - onOpenChange(false); - } - }; - - window.addEventListener("keydown", handleKey); - return () => window.removeEventListener("keydown", handleKey); - }, [open, onConfirm, onOpenChange]); + ? t("proformas.delete_proforma_dialog.single_description") + : t("proformas.delete_proforma_dialog.multiple_description", { count: total }); return ( - + { + if (isSubmitting) return; + onOpenChange(nextOpen); + }} + open={open} + > {title} {description} - {!requireSecondConfirm && total > 1 && ( + {!isSecondConfirmStep && total > 1 && (
    - {proformas.map((p) => ( -
  • - Proforma {p.reference ?? `#${p.id}`} + {proformas.map((proforma) => ( +
  • + + {t("proformas.delete_proforma_dialog.list_item", { + reference: proforma.reference ?? `#${proforma.id}`, + })} +
  • ))}
@@ -95,12 +92,14 @@ export function DeleteProformaDialog({ {t("proformas.delete_proforma_dialog.deleting")} - ) : requireSecondConfirm ? ( - <>{t("proformas.delete_proforma_dialog.mass_delete")} + ) : isSecondConfirmStep ? ( + t("proformas.delete_proforma_dialog.confirm_mass_delete") ) : isSingle ? ( - <>{t("proformas.delete_proforma_dialog.delete")} + t("proformas.delete_proforma_dialog.delete") + ) : requiresSecondConfirm ? ( + t("proformas.delete_proforma_dialog.continue") ) : ( - <>{t("proformas.delete_proforma_dialog.delete_plural")} + t("proformas.delete_proforma_dialog.delete_plural") )} diff --git a/modules/customer-invoices/src/web/proformas/delete/ui/components/index.ts b/modules/customer-invoices/src/web/proformas/delete/ui/components/index.ts new file mode 100644 index 00000000..2b81d3b9 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/delete/ui/components/index.ts @@ -0,0 +1 @@ +export * from "./delete-proforma-dialog"; diff --git a/modules/customer-invoices/src/web/proformas/delete/ui/index.ts b/modules/customer-invoices/src/web/proformas/delete/ui/index.ts index 2b81d3b9..40b494c5 100644 --- a/modules/customer-invoices/src/web/proformas/delete/ui/index.ts +++ b/modules/customer-invoices/src/web/proformas/delete/ui/index.ts @@ -1 +1 @@ -export * from "./delete-proforma-dialog"; +export * from "./components"; diff --git a/modules/customer-invoices/src/web/proformas/hooks/index.ts b/modules/customer-invoices/src/web/proformas/hooks/index.ts index 635178f6..1f1631c0 100644 --- a/modules/customer-invoices/src/web/proformas/hooks/index.ts +++ b/modules/customer-invoices/src/web/proformas/hooks/index.ts @@ -1,4 +1,2 @@ export * from "./use-issue-proforma-invoice"; -export * from "./use-proforma-query"; -export * from "./use-proforma-update-mutation"; export * from "./use-proformas-query"; diff --git a/modules/customer-invoices/src/web/proformas/hooks/use-delete-proforma-mutation.ts b/modules/customer-invoices/src/web/proformas/hooks/use-delete-proforma-mutation.ts.bak similarity index 96% rename from modules/customer-invoices/src/web/proformas/hooks/use-delete-proforma-mutation.ts rename to modules/customer-invoices/src/web/proformas/hooks/use-delete-proforma-mutation.ts.bak index 63a07c14..0b135939 100644 --- a/modules/customer-invoices/src/web/proformas/hooks/use-delete-proforma-mutation.ts +++ b/modules/customer-invoices/src/web/proformas/hooks/use-delete-proforma-mutation.ts.bak @@ -1,9 +1,6 @@ -11111import -{ - useDataSource; -} -from; -("@erp/core/hooks"); +import { + useDataSource +} from ("@erp/core/hooks"); import { useMutation, useQueryClient } from "@tanstack/react-query"; diff --git a/modules/customer-invoices/src/web/proformas/hooks/use-proforma-query.ts b/modules/customer-invoices/src/web/proformas/hooks/use-proforma-query.ts deleted file mode 100644 index 33f732fc..00000000 --- a/modules/customer-invoices/src/web/proformas/hooks/use-proforma-query.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { useDataSource } from "@erp/core/hooks"; -import { type DefaultError, type QueryKey, useQuery } from "@tanstack/react-query"; - -import type { Proforma } from "../types/proforma.api.schema"; - -export const PROFORMA_QUERY_KEY = (id: string): QueryKey => ["proforma", id] as const; - -type InvoiceQueryOptions = { - enabled?: boolean; -}; - -export const useProformaQuery = (proformaId?: string, options?: InvoiceQueryOptions) => { - const dataSource = useDataSource(); - const enabled = (options?.enabled ?? true) && !!proformaId; - - return useQuery({ - queryKey: PROFORMA_QUERY_KEY(proformaId ?? "unknown"), - queryFn: async (context) => { - const { signal } = context; - if (!proformaId) { - if (!proformaId) throw new Error("proformaId is required"); - } - return await dataSource.getOne("proformas", proformaId, { - signal, - }); - }, - enabled, - }); -}; - -/* - export function useQuery< - TQueryFnData = unknown, - TError = unknown, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey - > - - TQueryFnData: the type returned from the queryFn. - TError: the type of Errors to expect from the queryFn. - TData: the type our data property will eventually have. - Only relevant if you use the select option, - because then the data property can be different - from what the queryFn returns. - Otherwise, it will default to whatever the queryFn returns. - TQueryKey: the type of our queryKey, only relevant - if you use the queryKey that is passed to your queryFn. - -*/ diff --git a/modules/customer-invoices/src/web/proformas/hooks/use-proforma-update-mutation.ts b/modules/customer-invoices/src/web/proformas/hooks/use-proforma-update-mutation.ts deleted file mode 100644 index dcdf1155..00000000 --- a/modules/customer-invoices/src/web/proformas/hooks/use-proforma-update-mutation.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { useDataSource } from "@erp/core/hooks"; -import { ValidationErrorCollection } from "@repo/rdx-ddd"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; - -import { - type UpdateProformaByIdRequestDTO, - UpdateProformaByIdRequestSchema, -} from "../../../common"; -import type { InvoiceFormData } from "../../schemas"; - -import { PROFORMA_QUERY_KEY } from "./use-proforma-query"; -import { PROFORMAS_QUERY_KEY } from "./use-proformas-query"; - -type UpdateProformaContext = unknown; - -type UpdateProformaPayload = { - id: string; - data: Partial; -}; - -export function useUpdateProforma() { - const queryClient = useQueryClient(); - const dataSource = useDataSource(); - const schema = UpdateProformaByIdRequestSchema; - - return useMutation({ - mutationKey: ["proforma:update"], //, customerId], - - mutationFn: async (payload) => { - const { id: invoiceId, data } = payload; - - if (!invoiceId) { - throw new Error("customerInvoiceId is required"); - } - - const result = schema.safeParse(data); - if (!result.success) { - // Construye errores detallados - const validationErrors = result.error.issues.map((err) => ({ - field: err.path.join("."), - message: err.message, - })); - - throw new ValidationErrorCollection("Validation failed", validationErrors); - } - - const updated = await dataSource.updateOne("customer-invoices", invoiceId, data); - return updated as InvoiceFormData; - }, - onSuccess: (updated: InvoiceFormData, variables) => { - const { id: invoiceId } = variables; - - // Refresca inmediatamente el detalle - queryClient.setQueryData( - PROFORMA_QUERY_KEY(invoiceId), - updated - ); - - // Otra opción es invalidar el detalle para forzar refetch: - // queryClient.invalidateQueries({ queryKey: CUSTOMER_QUERY_KEY(customerId) }); - - // Invalida el listado para refrescar desde servidor - queryClient.invalidateQueries({ queryKey: PROFORMAS_QUERY_KEY() }); - }, - }); -} diff --git a/modules/customer-invoices/src/web/proformas/index.ts b/modules/customer-invoices/src/web/proformas/index.ts index 491ccf0c..eee8384f 100644 --- a/modules/customer-invoices/src/web/proformas/index.ts +++ b/modules/customer-invoices/src/web/proformas/index.ts @@ -1 +1,2 @@ export * from "./list"; +export * from "./update"; diff --git a/modules/customer-invoices/src/web/proformas/list/adapters/index.ts b/modules/customer-invoices/src/web/proformas/list/adapters/index.ts deleted file mode 100644 index e2a4d99c..00000000 --- a/modules/customer-invoices/src/web/proformas/list/adapters/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./proforma-summary-dto.adapter"; diff --git a/modules/customer-invoices/src/web/proformas/list/adapters/proforma-summary-dto.adapter.ts b/modules/customer-invoices/src/web/proformas/list/adapters/proforma-summary-dto.adapter.ts deleted file mode 100644 index 913005c8..00000000 --- a/modules/customer-invoices/src/web/proformas/list/adapters/proforma-summary-dto.adapter.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { MoneyDTOHelper, PercentageDTOHelper, formatCurrency } from "@erp/core"; - -import type { - ProformaSummaryData, - ProformaSummaryPage, - ProformaSummaryPageData, -} from "../../types"; - -/** - * Convierte el DTO completo de API a datos numéricos para el formulario. - */ -export const ProformaSummaryDtoAdapter = { - fromDto(pageDto: ProformaSummaryPage, context?: unknown): ProformaSummaryPageData { - return { - ...pageDto, - items: pageDto.items.map( - (summaryDto) => - ({ - ...summaryDto, - - subtotal_amount: MoneyDTOHelper.toNumber(summaryDto.subtotal_amount), - subtotal_amount_fmt: formatCurrency( - MoneyDTOHelper.toNumber(summaryDto.subtotal_amount), - Number(summaryDto.total_amount.scale || 2), - summaryDto.currency_code, - summaryDto.language_code - ), - - discount_percentage: PercentageDTOHelper.toNumber(summaryDto.discount_percentage), - discount_percentage_fmt: PercentageDTOHelper.toNumericString( - summaryDto.discount_percentage - ), - - discount_amount: MoneyDTOHelper.toNumber(summaryDto.discount_amount), - discount_amount_fmt: formatCurrency( - MoneyDTOHelper.toNumber(summaryDto.discount_amount), - Number(summaryDto.total_amount.scale || 2), - summaryDto.currency_code, - summaryDto.language_code - ), - - taxable_amount: MoneyDTOHelper.toNumber(summaryDto.taxable_amount), - taxable_amount_fmt: formatCurrency( - MoneyDTOHelper.toNumber(summaryDto.taxable_amount), - Number(summaryDto.total_amount.scale || 2), - summaryDto.currency_code, - summaryDto.language_code - ), - - taxes_amount: MoneyDTOHelper.toNumber(summaryDto.taxes_amount), - taxes_amount_fmt: formatCurrency( - MoneyDTOHelper.toNumber(summaryDto.taxes_amount), - Number(summaryDto.total_amount.scale || 2), - summaryDto.currency_code, - summaryDto.language_code - ), - - total_amount: MoneyDTOHelper.toNumber(summaryDto.total_amount), - total_amount_fmt: formatCurrency( - MoneyDTOHelper.toNumber(summaryDto.total_amount), - Number(summaryDto.total_amount.scale || 2), - summaryDto.currency_code, - summaryDto.language_code - ), - - //taxes: dto.taxes, - }) as unknown as ProformaSummaryData - ), - }; - }, -}; diff --git a/modules/customer-invoices/src/web/proformas/list/api/index.ts b/modules/customer-invoices/src/web/proformas/list/api/index.ts deleted file mode 100644 index 7bce9fda..00000000 --- a/modules/customer-invoices/src/web/proformas/list/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./get-proforma-list.api"; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/index.ts b/modules/customer-invoices/src/web/proformas/list/controllers/index.ts index a2a5ee16..efe38e55 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/index.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/index.ts @@ -1,2 +1,2 @@ -export * from "./use-proforma-list.controller.ts"; -export * from "./use-proforma-list-page.controller.ts"; +export * from "./use-list-proformas.controller.ts"; +export * from "./use-list-proformas-page.controller.ts"; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts new file mode 100644 index 00000000..29770d54 --- /dev/null +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas-page.controller.ts @@ -0,0 +1,62 @@ +import React from "react"; + +import { useChangeProformaStatusDialogController } from "../../change-status"; +import { useDeleteProformaDialogController } from "../../delete"; +import { useProformaIssueDialogController } from "../../issue-proforma"; +import type { ProformaListRow } from "../../shared"; +import { + type PROFORMA_STATUS, + PROFORMA_STATUS_TRANSITIONS, +} from "../../shared/entities/proforma-status.entity"; + +import { useListProformasController } from "./use-list-proformas.controller"; + +export function useListProformasPageController() { + const listCtrl = useListProformasController(); + + // Controlador de diálogos + const issueDialogCtrl = useProformaIssueDialogController(); + const changeStatusDialogCtrl = useChangeProformaStatusDialogController(); + const deleteDialogCtrl = useDeleteProformaDialogController(); + + const handleOpenIssueProformaDialog = React.useCallback( + (proforma: ProformaListRow) => { + issueDialogCtrl.openDialog(proforma); + }, + [issueDialogCtrl] + ); + + const handleOpenChangeProformaStatusDialog = React.useCallback( + (proforma: ProformaListRow, nextStatus: string) => { + const proforma_status = proforma.status as PROFORMA_STATUS; + const transitions = PROFORMA_STATUS_TRANSITIONS[proforma_status] ?? []; + + if (!transitions.includes(nextStatus as PROFORMA_STATUS)) { + console.warn(`Transición inválida: ${proforma.status} → ${nextStatus}`); + return; + } + + changeStatusDialogCtrl.openDialog([proforma], nextStatus as PROFORMA_STATUS); + }, + [changeStatusDialogCtrl] + ); + + const handleOpenDeleteProformaDialog = React.useCallback( + (proforma: ProformaListRow) => { + deleteDialogCtrl.openDialog([proforma]); + }, + [deleteDialogCtrl] + ); + + return { + listCtrl, + + issueDialogCtrl, + changeStatusDialogCtrl, + deleteDialogCtrl, + + handleIssueProforma: handleOpenIssueProformaDialog, + handleChangeStatusProforma: handleOpenChangeProformaStatusDialog, + handleDeleteProforma: handleOpenDeleteProformaDialog, + }; +} diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list.controller.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts similarity index 50% rename from modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list.controller.ts rename to modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts index 91ba60e2..2d070b57 100644 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list.controller.ts +++ b/modules/customer-invoices/src/web/proformas/list/controllers/use-list-proformas.controller.ts @@ -2,10 +2,9 @@ import type { CriteriaDTO } from "@erp/core"; import { useDebounce } from "@repo/rdx-ui/components"; import { useMemo, useState } from "react"; -import { ProformaSummaryDtoAdapter } from "../adapters"; -import { useProformaListQuery } from "../hooks"; +import { useListProformasQuery } from "../../shared"; -export const useProformaListController = () => { +export const useListProformasController = () => { const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); const [search, setSearch] = useState(""); @@ -15,7 +14,7 @@ export const useProformaListController = () => { const criteria = useMemo(() => { const baseFilters = - status !== "all" ? [{ field: "status", operator: "CONTAINS", value: status }] : []; + status === "all" ? [] : [{ field: "status", operator: "EQUALS", value: status }]; return { q: debouncedQ || "", @@ -27,25 +26,42 @@ export const useProformaListController = () => { }; }, [pageSize, pageIndex, debouncedQ, status]); - const query = useProformaListQuery({ criteria }); - const data = useMemo( - () => (query.data ? ProformaSummaryDtoAdapter.fromDto(query.data) : undefined), - [query.data] - ); + const query = useListProformasQuery({ criteria }); - const setSearchValue = (value: string) => setSearch(value.trim().replace(/\s+/g, " ")); + const setSearchValue = (value: string) => { + setSearch(value.trim().replace(/\s+/g, " ")); + setPageIndex(0); + }; - const setStatusFilter = (newStatus: string) => setStatus(newStatus); + const setPageSizeValue = (value: number) => { + setPageSize(value); + setPageIndex(0); + }; + + const setStatusFilter = (newStatus: string) => { + setStatus(newStatus); + setPageIndex(0); + }; return { - ...query, - data, + data: query.data, + isLoading: query.isLoading, + isFetching: query.isFetching, + + isError: query.isError, + error: query.error, + + refetch: query.refetch, + pageIndex, pageSize, - search, setPageIndex, - setPageSize, + setPageSize: setPageSizeValue, + + search, setSearchValue, + + status, setStatusFilter, }; }; diff --git a/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list-page.controller.ts.ts b/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list-page.controller.ts.ts deleted file mode 100644 index eba7c503..00000000 --- a/modules/customer-invoices/src/web/proformas/list/controllers/use-proforma-list-page.controller.ts.ts +++ /dev/null @@ -1,63 +0,0 @@ -import React from "react"; - -import { useChangeStatusDialogController } from "../../change-status"; -import { useDeleteProformaDialogController } from "../../delete"; -import { useProformaIssueDialogController } from "../../issue-proforma"; -import { - type PROFORMA_STATUS, - PROFORMA_STATUS_TRANSITIONS, - type ProformaSummaryData, -} from "../../types"; - -import { useProformaListController } from "./use-proforma-list.controller"; - -export function useProformaListPageController() { - const listCtrl = useProformaListController(); - - // Controlador de diálogos - const issueDialogCtrl = useProformaIssueDialogController(); - const changeStatusDialogCtrl = useChangeStatusDialogController(); - const deleteDialogCtrl = useDeleteProformaDialogController(); - - const handleIssueProforma = React.useCallback( - (proforma: ProformaSummaryData) => { - // Solo si approved → issued - issueDialogCtrl.openDialog(proforma); - }, - [issueDialogCtrl] - ); - - const handleChangeStatusProforma = React.useCallback( - (proforma: ProformaSummaryData, nextStatus: string) => { - const proforma_status = proforma.status as PROFORMA_STATUS; - const transitions = PROFORMA_STATUS_TRANSITIONS[proforma_status] ?? []; - - if (!transitions.includes(nextStatus as PROFORMA_STATUS)) { - console.warn(`Transición inválida: ${proforma.status} → ${nextStatus}`); - return; - } - - changeStatusDialogCtrl.openDialog([proforma]); - }, - [changeStatusDialogCtrl] - ); - - const handleDeleteProforma = React.useCallback( - (proforma: ProformaSummaryData) => { - deleteDialogCtrl.openDialog([proforma]); - }, - [deleteDialogCtrl] - ); - - return { - listCtrl, - - issueDialogCtrl, - changeStatusDialogCtrl, - deleteDialogCtrl, - - handleIssueProforma, - handleChangeStatusProforma, - handleDeleteProforma, - }; -} diff --git a/modules/customer-invoices/src/web/proformas/list/hooks/index.ts b/modules/customer-invoices/src/web/proformas/list/hooks/index.ts deleted file mode 100644 index 6342d288..00000000 --- a/modules/customer-invoices/src/web/proformas/list/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./use-proforma-list-query"; diff --git a/modules/customer-invoices/src/web/proformas/list/hooks/use-proforma-list-query.ts b/modules/customer-invoices/src/web/proformas/list/hooks/use-proforma-list-query.ts deleted file mode 100644 index f1d0e922..00000000 --- a/modules/customer-invoices/src/web/proformas/list/hooks/use-proforma-list-query.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { CriteriaDTO } from "@erp/core"; -import { useDataSource } from "@erp/core/hooks"; -import { INITIAL_PAGE_INDEX, INITIAL_PAGE_SIZE } from "@repo/rdx-criteria"; -import { type DefaultError, type QueryKey, useQuery } from "@tanstack/react-query"; - -import type { ProformaSummaryPage } from "../../types"; -import { getProformaListApi } from "../api"; - -export const PROFORMAS_QUERY_KEY = (criteria?: CriteriaDTO): QueryKey => [ - "proforma", - { - pageNumber: criteria?.pageNumber ?? INITIAL_PAGE_INDEX, - pageSize: criteria?.pageSize ?? INITIAL_PAGE_SIZE, - q: criteria?.q ?? "", - filters: criteria?.filters ?? [], - orderBy: criteria?.orderBy ?? "", - order: criteria?.order ?? "", - }, -]; - -type ProformasQueryOptions = { - enabled?: boolean; - criteria?: CriteriaDTO; -}; - -// Obtener todas las facturas -export const useProformaListQuery = (options?: ProformasQueryOptions) => { - const dataSource = useDataSource(); - const enabled = options?.enabled ?? true; - const criteria = options?.criteria ?? {}; - - return useQuery({ - queryKey: PROFORMAS_QUERY_KEY(criteria), - queryFn: async ({ signal }) => getProformaListApi(dataSource, signal, criteria), - enabled, - placeholderData: (previousData, _previousQuery) => previousData, // Mantener datos previos mientras se carga nueva datos (antiguo `keepPreviousData`) - }); -}; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/index.ts b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/index.ts index cf0040bf..842b9277 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/index.ts +++ b/modules/customer-invoices/src/web/proformas/list/ui/blocks/proformas-grid/index.ts @@ -1 +1,2 @@ export * from "./proformas-grid"; +export * from "./use-proforma-grid-columns"; diff --git a/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx b/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx index 359ffe13..47c688f9 100644 --- a/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx +++ b/modules/customer-invoices/src/web/proformas/list/ui/pages/proforma-list-page.tsx @@ -13,11 +13,10 @@ import { useNavigate } from "react-router-dom"; import { useTranslation } from "../../../../i18n"; import { ChangeStatusDialog } from "../../../change-status"; -import { DeleteProformaDialog } from "../../../delete/ui"; +import { DeleteProformaDialog } from "../../../delete/ui/components"; import { ProformaIssueDialog } from "../../../issue-proforma"; -import { useProformaListPageController } from "../../controllers"; -import { ProformasGrid } from "../blocks/proformas-grid"; -import { useProformasGridColumns } from "../blocks/proformas-grid/use-proforma-grid-columns"; +import { useListProformasPageController } from "../../controllers"; +import { ProformasGrid, useProformasGridColumns } from "../blocks"; export const ProformaListPage = () => { const { t } = useTranslation(); @@ -33,7 +32,7 @@ export const ProformaListPage = () => { handleChangeStatusProforma, handleDeleteProforma, handleIssueProforma, - } = useProformaListPageController(); + } = useListProformasPageController(); const columns = useProformasGridColumns({ onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`), @@ -77,6 +76,7 @@ export const ProformaListPage = () => {