From 4d3430cc91c559332af87debb73f2d58732970bf Mon Sep 17 00:00:00 2001 From: david Date: Tue, 16 Sep 2025 19:59:58 +0200 Subject: [PATCH] Clientes y facturas de cliente --- .../templates/customer-invoice/template.hbs | 306 +++++++------- .../rdx-ui/src/components/form/TextField.tsx | 7 +- .../rdx-ui/src/components/layout/nav-main.tsx | 16 +- packages/shadcn-ui/src/components/sidebar.tsx | 389 ++++++++---------- 4 files changed, 355 insertions(+), 363 deletions(-) diff --git a/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template.hbs b/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template.hbs index c28ecd34..c2e62555 100644 --- a/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template.hbs +++ b/modules/customer-invoices/src/api/application/use-cases/report/reporter/templates/customer-invoice/template.hbs @@ -103,12 +103,12 @@
Logo Rodax -
+

Factura nº:xxxxxxxx

Fecha:12/12/2024

-
+

{{customer.name}}

AAAA

BBBBBBsdfsfsdf sfsdf sf sdfs fsdfsd fsdf sdfsd fds

@@ -132,158 +132,164 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ConceptoCantidadPrecio unidadImporte total
Mantenimiento de sistemas informáticos - Agosto (1 Equipo Servidor, 30 Ordenadores, 2 Impresoras, Disco - copias de seguridad)10,14 €0,14 €
Mantenimiento del programa FactuGES140,00 €40,00 €
Control VPN para portátil Rubén16,00 €6,00 €
Control VPN para portátil Míriam16,00 €6,00 €
Control VPN para portátil Fernando16,00 €6,00 €
Control VPN para portátil Elena16,00 €6,00 €
Control VPN para portátil Miguel16,00 €6,00 €
Control VPN para portátil Adrian16,00 €6,00 €
Control VPN para portátil David Lablanca16,00 €6,00 €
Control VPN para portátil Noemí16,00 €6,00 €
Control VPN para portátil John16,00 €6,00 €
Control VPN para portátil Eva16,00 €6,00 €
Control VPN para portátil Alberto16,00 €6,00 €
Mantenimiento mensual copia de seguridad remota y VPN (Uecko Madrid)140,00 €40,00 €
50% dto fidelización servicios contratados-120,00 €-20,00 €
Mantenimiento de presupuestador web para distribuidores (Agosto)1375,00 €375,00 €
Informe de compras de artículos (Presupuesto 22/04/25)1260,00 €260,00 €
Informe presupuestos cliente (Gunni Tentrino) – modificación funcionalidad visible. Sin cargo.10,00 €0,00 €
+
+ +
+
+ TOTAL: 960,56 € + + +
+
- - - - - - - - - - - - - -
Base imponible:761,14 €
IVA (21%):159,84 €
Total factura:960,56 €
+ + + + + + + + + + -
-

Insc. en el Reg. Merc. de Madrid, Tomo 20.073, Libro 0, Folio 141, Sección 8, Hoja M-354212 | CIF: B83999441 - - Rodax Software S.L.

-

Forma de pago: Domiciliación bancaria

-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ConceptoCantidadPrecio unidadImporte total
Mantenimiento de sistemas informáticos - Agosto (1 Equipo Servidor, 30 Ordenadores, 2 Impresoras, Disco + copias de seguridad)10,14 €0,14 €
Mantenimiento del programa FactuGES140,00 €40,00 €
Control VPN para portátil Rubén16,00 €6,00 €
Control VPN para portátil Míriam16,00 €6,00 €
Control VPN para portátil Fernando16,00 €6,00 €
Control VPN para portátil Elena16,00 €6,00 €
Control VPN para portátil Miguel16,00 €6,00 €
Control VPN para portátil Adrian16,00 €6,00 €
Control VPN para portátil David Lablanca16,00 €6,00 €
Control VPN para portátil Noemí16,00 €6,00 €
Control VPN para portátil John16,00 €6,00 €
Control VPN para portátil Eva16,00 €6,00 €
Control VPN para portátil Alberto16,00 €6,00 €
Mantenimiento mensual copia de seguridad remota y VPN (Uecko Madrid)140,00 €40,00 €
50% dto fidelización servicios contratados-120,00 €-20,00 €
Mantenimiento de presupuestador web para distribuidores (Agosto)1375,00 €375,00 €
Informe de compras de artículos (Presupuesto 22/04/25)1260,00 €260,00 €
Informe presupuestos cliente (Gunni Tentrino) – modificación funcionalidad visible. Sin cargo.10,00 €0,00 €
+ + + + + + + + + + + + + + +
Base imponible:761,14 €
IVA (21%):159,84 €
Total factura:960,56 €
+ +
+

Insc. en el Reg. Merc. de Madrid, Tomo 20.073, Libro 0, Folio 141, Sección 8, Hoja M-354212 | CIF: B83999441 - + Rodax Software S.L.

+

Forma de pago: Domiciliación bancaria

+
diff --git a/packages/rdx-ui/src/components/form/TextField.tsx b/packages/rdx-ui/src/components/form/TextField.tsx index 155c4cb3..f4cd2cc2 100644 --- a/packages/rdx-ui/src/components/form/TextField.tsx +++ b/packages/rdx-ui/src/components/form/TextField.tsx @@ -50,7 +50,12 @@ export function TextField({
)} - +

diff --git a/packages/rdx-ui/src/components/layout/nav-main.tsx b/packages/rdx-ui/src/components/layout/nav-main.tsx index 463f3f7c..13867891 100644 --- a/packages/rdx-ui/src/components/layout/nav-main.tsx +++ b/packages/rdx-ui/src/components/layout/nav-main.tsx @@ -1,13 +1,14 @@ import { type LucideIcon, MailIcon, PlusCircleIcon } from "lucide-react"; -import { Button } from "@repo/shadcn-ui/components/button"; import { + Button, SidebarGroup, SidebarGroupContent, SidebarMenu, SidebarMenuButton, SidebarMenuItem, -} from "@repo/shadcn-ui/components/sidebar"; +} from "@repo/shadcn-ui/components"; +import { useNavigate } from "react-router"; export function NavMain({ items, @@ -18,6 +19,10 @@ export function NavMain({ icon?: LucideIcon; }[]; }) { + const navigate = useNavigate(); + + console.log(window.location.href); + return ( @@ -43,7 +48,12 @@ export function NavMain({ {items.map((item) => ( - + navigate(item.url)} + className='data-[active=true]:bg-accent data-[active=true]:text-accent-foreground cursor-pointer' + > {item.icon && } {item.title} diff --git a/packages/shadcn-ui/src/components/sidebar.tsx b/packages/shadcn-ui/src/components/sidebar.tsx index 22537abb..3131f042 100644 --- a/packages/shadcn-ui/src/components/sidebar.tsx +++ b/packages/shadcn-ui/src/components/sidebar.tsx @@ -1,56 +1,54 @@ -"use client" +"use client"; -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { VariantProps, cva } from "class-variance-authority" -import { PanelLeftIcon } from "lucide-react" +import { Slot } from "@radix-ui/react-slot"; +import { VariantProps, cva } from "class-variance-authority"; +import { PanelLeftIcon } from "lucide-react"; +import * as React from "react"; -import { useIsMobile } from "@repo/shadcn-ui/hooks/use-mobile" -import { cn } from "@repo/shadcn-ui/lib/utils" -import { Button } from "@repo/shadcn-ui/components/button" -import { Input } from "@repo/shadcn-ui/components/input" -import { Separator } from "@repo/shadcn-ui/components/separator" import { + Button, + Input, + Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, -} from "@repo/shadcn-ui/components/sheet" -import { Skeleton } from "@repo/shadcn-ui/components/skeleton" -import { + Skeleton, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, -} from "@repo/shadcn-ui/components/tooltip" +} from "@repo/shadcn-ui/components"; +import { useIsMobile } from "../hooks/use-mobile.ts"; +import { cn } from "../lib/utils.ts"; -const SIDEBAR_COOKIE_NAME = "sidebar_state" -const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 -const SIDEBAR_WIDTH = "16rem" -const SIDEBAR_WIDTH_MOBILE = "18rem" -const SIDEBAR_WIDTH_ICON = "3rem" -const SIDEBAR_KEYBOARD_SHORTCUT = "b" +const SIDEBAR_COOKIE_NAME = "sidebar_state"; +const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; +const SIDEBAR_WIDTH = "16rem"; +const SIDEBAR_WIDTH_MOBILE = "18rem"; +const SIDEBAR_WIDTH_ICON = "3rem"; +const SIDEBAR_KEYBOARD_SHORTCUT = "b"; type SidebarContextProps = { - state: "expanded" | "collapsed" - open: boolean - setOpen: (open: boolean) => void - openMobile: boolean - setOpenMobile: (open: boolean) => void - isMobile: boolean - toggleSidebar: () => void -} + state: "expanded" | "collapsed"; + open: boolean; + setOpen: (open: boolean) => void; + openMobile: boolean; + setOpenMobile: (open: boolean) => void; + isMobile: boolean; + toggleSidebar: () => void; +}; -const SidebarContext = React.createContext(null) +const SidebarContext = React.createContext(null); function useSidebar() { - const context = React.useContext(SidebarContext) + const context = React.useContext(SidebarContext); if (!context) { - throw new Error("useSidebar must be used within a SidebarProvider.") + throw new Error("useSidebar must be used within a SidebarProvider."); } - return context + return context; } function SidebarProvider({ @@ -62,56 +60,53 @@ function SidebarProvider({ children, ...props }: React.ComponentProps<"div"> & { - defaultOpen?: boolean - open?: boolean - onOpenChange?: (open: boolean) => void + defaultOpen?: boolean; + open?: boolean; + onOpenChange?: (open: boolean) => void; }) { - const isMobile = useIsMobile() - const [openMobile, setOpenMobile] = React.useState(false) + const isMobile = useIsMobile(); + const [openMobile, setOpenMobile] = React.useState(false); // This is the internal state of the sidebar. // We use openProp and setOpenProp for control from outside the component. - const [_open, _setOpen] = React.useState(defaultOpen) - const open = openProp ?? _open + const [_open, _setOpen] = React.useState(defaultOpen); + const open = openProp ?? _open; const setOpen = React.useCallback( (value: boolean | ((value: boolean) => boolean)) => { - const openState = typeof value === "function" ? value(open) : value + const openState = typeof value === "function" ? value(open) : value; if (setOpenProp) { - setOpenProp(openState) + setOpenProp(openState); } else { - _setOpen(openState) + _setOpen(openState); } // This sets the cookie to keep the sidebar state. - document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; }, [setOpenProp, open] - ) + ); // Helper to toggle the sidebar. const toggleSidebar = React.useCallback(() => { - return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open) - }, [isMobile, setOpen, setOpenMobile]) + return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open); + }, [isMobile, setOpen, setOpenMobile]); // Adds a keyboard shortcut to toggle the sidebar. React.useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if ( - event.key === SIDEBAR_KEYBOARD_SHORTCUT && - (event.metaKey || event.ctrlKey) - ) { - event.preventDefault() - toggleSidebar() + if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) { + event.preventDefault(); + toggleSidebar(); } - } + }; - window.addEventListener("keydown", handleKeyDown) - return () => window.removeEventListener("keydown", handleKeyDown) - }, [toggleSidebar]) + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [toggleSidebar]); // We add a state so that we can do data-state="expanded" or "collapsed". // This makes it easier to style the sidebar with Tailwind classes. - const state = open ? "expanded" : "collapsed" + const state = open ? "expanded" : "collapsed"; const contextValue = React.useMemo( () => ({ @@ -124,13 +119,13 @@ function SidebarProvider({ toggleSidebar, }), [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] - ) + ); return (

- ) + ); } function Sidebar({ @@ -159,16 +154,16 @@ function Sidebar({ children, ...props }: React.ComponentProps<"div"> & { - side?: "left" | "right" - variant?: "sidebar" | "floating" | "inset" - collapsible?: "offcanvas" | "icon" | "none" + side?: "left" | "right"; + variant?: "sidebar" | "floating" | "inset"; + collapsible?: "offcanvas" | "icon" | "none"; }) { - const { isMobile, state, openMobile, setOpenMobile } = useSidebar() + const { isMobile, state, openMobile, setOpenMobile } = useSidebar(); if (collapsible === "none") { return (
{children}
- ) + ); } if (isMobile) { return ( - + Sidebar Displays the mobile sidebar. -
{children}
+
{children}
- ) + ); } return (
{/* This is what handles the sidebar gap on desktop */}
- ) + ); } -function SidebarTrigger({ - className, - onClick, - ...props -}: React.ComponentProps) { - const { toggleSidebar } = useSidebar() +function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps) { + const { toggleSidebar } = useSidebar(); return ( - ) + ); } function SidebarRail({ className, ...props }: React.ComponentProps<"button">) { - const { toggleSidebar } = useSidebar() + const { toggleSidebar } = useSidebar(); return (