Compare commits

..

No commits in common. "d062c4b5fe719e82c234f07a20afba909f8771b5" and "77ccdf4f13a940d50a9c9ba73d270d43a02766de" have entirely different histories.

9 changed files with 159 additions and 141 deletions

View File

@ -1,4 +1,4 @@
FROM node:24-alpine AS build FROM node:22.13.1 AS build
WORKDIR /app WORKDIR /app
COPY package*.json ./ COPY package*.json ./
@ -7,7 +7,7 @@ COPY . .
RUN npm run build RUN npm run build
# Servir con Vite (modo dev) o un servidor web # Servir con Vite (modo dev) o un servidor web
FROM node:24-alpine FROM node:22.13.1
WORKDIR /app WORKDIR /app
COPY --from=build /app . COPY --from=build /app .
EXPOSE 5173 EXPOSE 5173

View File

@ -20,6 +20,7 @@
"dinero.js": "^1.9.1", "dinero.js": "^1.9.1",
"express": "^4.18.2", "express": "^4.18.2",
"i18next": "^25.1.1", "i18next": "^25.1.1",
"sequelize": "^6.37.5",
"zod": "^3.25.67" "zod": "^3.25.67"
}, },
"devDependencies": { "devDependencies": {

View File

@ -39,6 +39,7 @@
"@repo/rdx-utils": "workspace:*", "@repo/rdx-utils": "workspace:*",
"@repo/shadcn-ui": "workspace:*", "@repo/shadcn-ui": "workspace:*",
"@tanstack/react-query": "^5.75.4", "@tanstack/react-query": "^5.75.4",
"ag-grid-community": "^33.3.0",
"axios": "^1.9.0", "axios": "^1.9.0",
"express": "^4.18.2", "express": "^4.18.2",
"http-status": "^2.1.0", "http-status": "^2.1.0",
@ -47,6 +48,7 @@
"react-hook-form": "^7.58.1", "react-hook-form": "^7.58.1",
"react-i18next": "^15.5.1", "react-i18next": "^15.5.1",
"react-router-dom": "^6.26.0", "react-router-dom": "^6.26.0",
"sequelize": "^6.37.5",
"zod": "^4.1.11" "zod": "^4.1.11"
} }
} }

View File

@ -1,4 +1,5 @@
export * from "./json-tax-catalog.provider"; export * from "./json-tax-catalog.provider";
export * from "./spain-tax-catalog.provider"; export * from "./spain-tax-catalog.provider";
export * from "./tax-catalog.provider";
export * from "./tax-catalog-types"; export * from "./tax-catalog-types";
export * from "./tax-catalog.provider";

View File

@ -1,8 +1,8 @@
// --- Adaptador que carga el catálogo JSON en memoria e indexa por code --- // --- Adaptador que carga el catálogo JSON en memoria e indexa por code ---
import { Maybe } from "@repo/rdx-utils"; import { Maybe } from "@repo/rdx-utils";
import { TaxCatalogProvider } from "./tax-catalog.provider";
import { TaxCatalogType, TaxItemType, TaxLookupItems } from "./tax-catalog-types"; import { TaxCatalogType, TaxItemType, TaxLookupItems } from "./tax-catalog-types";
import { TaxCatalogProvider } from "./tax-catalog.provider";
export class JsonTaxCatalogProvider implements TaxCatalogProvider { export class JsonTaxCatalogProvider implements TaxCatalogProvider {
// Índice por código normalizado // Índice por código normalizado

View File

@ -1,9 +1,9 @@
import { useDebounce } from "@repo/rdx-ui/components"; import { useDebounce } from '@repo/rdx-ui/components';
import { import {
InputGroup, InputGroup,
InputGroupAddon, InputGroupAddon,
InputGroupButton, InputGroupButton,
InputGroupInput, InputGroupInput
} from "@repo/shadcn-ui/components"; } from "@repo/shadcn-ui/components";
import { Spinner } from "@repo/shadcn-ui/components/spinner"; import { Spinner } from "@repo/shadcn-ui/components/spinner";
import { SearchIcon, XIcon } from "lucide-react"; import { SearchIcon, XIcon } from "lucide-react";
@ -47,7 +47,10 @@ export const SimpleSearchInput = ({
const saveHistory = (term: string) => { const saveHistory = (term: string) => {
if (!term.trim()) return; if (!term.trim()) return;
const cleaned = term.trim(); const cleaned = term.trim();
const newHistory = [cleaned, ...history.filter((h) => h !== cleaned)].slice(0, maxHistory); const newHistory = [cleaned, ...history.filter((h) => h !== cleaned)].slice(
0,
maxHistory
);
setHistory(newHistory); setHistory(newHistory);
localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(newHistory)); localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(newHistory));
}; };
@ -103,16 +106,20 @@ export const SimpleSearchInput = ({
}; };
return ( return (
<div className='relative flex-1 max-w-xl'> <div
<InputGroup className='bg-background' data-disabled={loading}> className="relative flex-1 max-w-xl"
aria-label={t("pages.list.searchPlaceholder", "Search input")}
>
<InputGroup className="bg-background" data-disabled={loading}>
<InputGroupInput <InputGroupInput
ref={inputRef} ref={inputRef}
placeholder={t("common.search_placeholder", "Search...")} placeholder={t("common.search_placeholder", "Search...")}
value={searchValue} value={searchValue}
onChange={handleInputChange} onChange={handleInputChange}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
inputMode='search' inputMode="search"
autoComplete='off' autoComplete="off"
spellCheck={false} spellCheck={false}
disabled={loading} disabled={loading}
onFocus={() => history.length > 0 && setOpen(true)} onFocus={() => history.length > 0 && setOpen(true)}
@ -121,12 +128,14 @@ export const SimpleSearchInput = ({
<SearchIcon aria-hidden /> <SearchIcon aria-hidden />
</InputGroupAddon> </InputGroupAddon>
<InputGroupAddon align='inline-end'> <InputGroupAddon align="inline-end">
{loading && <Spinner aria-label={t("common.loading", "Loading")} />} {loading && (
<Spinner aria-label={t("common.loading", "Loading")} />
)}
{!searchValue && !loading && ( {!searchValue && !loading && (
<InputGroupButton <InputGroupButton
variant='secondary' variant="secondary"
className='cursor-pointer' className="cursor-pointer"
onClick={() => onSearchChange(searchValue)} onClick={() => onSearchChange(searchValue)}
> >
{t("common.search", "Search")} {t("common.search", "Search")}
@ -134,17 +143,21 @@ export const SimpleSearchInput = ({
)} )}
{searchValue && !loading && ( {searchValue && !loading && (
<InputGroupButton <InputGroupButton
variant='secondary' variant="secondary"
className='cursor-pointer' className="cursor-pointer"
aria-label={t("common.clear", "Clear search")} aria-label={t("common.clear", "Clear search")}
onClick={handleClear} onClick={handleClear}
> >
<XIcon className='size-4' aria-hidden /> <XIcon className="size-4" aria-hidden />
<span className='sr-only'>{t("common.clear", "Clear")}</span> <span className="sr-only">{t("common.clear", "Clear")}</span>
</InputGroupButton> </InputGroupButton>
)} )}
</InputGroupAddon> </InputGroupAddon>
</InputGroup> </InputGroup>
</div> </div>
); );
}; };

View File

@ -1,2 +1,6 @@
import { AllCommunityModule, ModuleRegistry } from "ag-grid-community";
ModuleRegistry.registerModules([AllCommunityModule]);
export * from "./form"; export * from "./form";
export * from "./page-header"; export * from "./page-header";

View File

@ -1,8 +1,10 @@
import { Button } from "@repo/shadcn-ui/components"; import { Button } from '@repo/shadcn-ui/components';
import { cn } from "@repo/shadcn-ui/lib/utils"; import { cn } from '@repo/shadcn-ui/lib/utils';
import { ChevronLeftIcon } from "lucide-react"; import { ChevronLeftIcon } from 'lucide-react';
// features/common/components/page-header.tsx
import type { ReactNode } from "react"; import type { ReactNode } from "react";
interface PageHeaderProps { interface PageHeaderProps {
backIcon?: ReactNode; backIcon?: ReactNode;
title: ReactNode; title: ReactNode;
@ -13,13 +15,8 @@ interface PageHeaderProps {
className?: string; className?: string;
} }
export function PageHeader({
backIcon, export function PageHeader({ backIcon, title, description, rightSlot, className }: PageHeaderProps) {
title,
description,
rightSlot,
className,
}: PageHeaderProps) {
return ( return (
<div className={cn("pt-6 pb-6 lg:flex lg:items-center lg:justify-between", className)}> <div className={cn("pt-6 pb-6 lg:flex lg:items-center lg:justify-between", className)}>
{/* Lado izquierdo */} {/* Lado izquierdo */}
@ -37,16 +34,16 @@ export function PageHeader({
)} )}
<div> <div>
<h2 className='h-8 text-xl font-semibold text-foreground sm:truncate sm:tracking-tight'> <h2 className='h-8 text-xl font-semibold text-foreground sm:truncate sm:tracking-tight'>{title}</h2>
{title}
</h2>
{description && <p className='text-sm text-muted-foreground'>{description}</p>} {description && <p className='text-sm text-muted-foreground'>{description}</p>}
</div> </div>
</div> </div>
</div> </div>
{/* Lado derecho parametrizable */} {/* Lado derecho parametrizable */}
<div className='mt-4 flex lg:mt-0 lg:ml-4'>{rightSlot}</div> <div className="mt-4 flex lg:mt-0 lg:ml-4">
{rightSlot}
</div>
</div> </div>
); );
} }

View File

@ -6,7 +6,7 @@ const MODULE_VERSION = "1.0.0";
export const CoreModuleManifiest: IModuleClient = { export const CoreModuleManifiest: IModuleClient = {
name: MODULE_NAME, name: MODULE_NAME,
version: MODULE_VERSION, version: MODULE_VERSION,
dependencies: [], dependencies: ["core"],
protected: true, protected: true,
layout: "app", layout: "app",