Skip to content

Commit

Permalink
Merge pull request #14 from alnavarrop99/fix/styles&rols
Browse files Browse the repository at this point in the history
Create print view
alnavarrop99 authored Apr 7, 2024
2 parents f8f8343 + 9287d60 commit a0f75ef
Showing 41 changed files with 671 additions and 1,093 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -50,6 +50,7 @@
"react": "^18.2.0",
"react-day-picker": "^8.10.0",
"react-dom": "^18.2.0",
"react-to-print": "^2.15.1",
"tailwind-merge": "^2.2.1",
"tailwindcss-animate": "^1.0.7",
"zustand": "^4.4.7"
Empty file removed src/assets/.gitkeep
Empty file.
Binary file added src/assets/menu-off-brand.avif
Binary file not shown.
Binary file added src/assets/menu-off-brand.webp
Binary file not shown.
16 changes: 10 additions & 6 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -10,16 +10,18 @@
--card-foreground: 222.2 84% 4.9%;
--popover: 60 100% 99%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 257 70% 58%;
--primary: 207 100% 43%;
--primary-foreground: 210 40% 98%;
--secondary: 240 30% 87%;
--secondary-foreground: 222.2 47.4% 11.2%;
--secondary: 40 100% 50%;
--secondary-foreground: 60 100% 80%;
--muted: 240 30% 87%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 240 30% 87%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--success: 140 120% 45%;
--success-foreground: 120 100% 80%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
@@ -33,16 +35,18 @@
--card-foreground: 210 40% 98%;
--popover: 233 24% 20%;
--popover-foreground: 210 40% 98%;
--primary: 241 94% 74%;
--primary: 207 55% 43%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 257 18% 20%;
--secondary-foreground: 210 40% 98%;
--secondary: 40 100% 35%;
--secondary-foreground: 40 100% 25%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 235 26% 67%;
--accent-foreground: 210 40% 98%;
--destructive: 0 73% 61%;
--destructive-foreground: 210 40% 98%;
--success: 140 120% 45%;
--success-foreground: 120 100% 80%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
50 changes: 0 additions & 50 deletions src/mocks/__mock__/CLIENTS.json

This file was deleted.

20 changes: 0 additions & 20 deletions src/mocks/__mock__/CREDITS.json

This file was deleted.

50 changes: 0 additions & 50 deletions src/mocks/__mock__/CUOTES.json

This file was deleted.

10 changes: 0 additions & 10 deletions src/mocks/__mock__/MORA.json

This file was deleted.

200 changes: 0 additions & 200 deletions src/mocks/__mock__/PAYMENTS.json

This file was deleted.

144 changes: 0 additions & 144 deletions src/mocks/__mock__/REPORT.json

This file was deleted.

10 changes: 2 additions & 8 deletions src/mocks/data.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
import _payments from '@/mocks/__mock__/PAYMENTS.json'
import _credits from "@/mocks/__mock__/CREDITS.json";
import _clients from '@/mocks/__mock__/CLIENTS.json'
import _cuotes from "@/mocks/__mock__/CUOTES.json"
import _mora from '@/mocks/__mock__/MORA.json'
import _reports from '@/mocks/__mock__/REPORT.json'
import type { TUSER_GET } from '@/api/users';
import type { TCLIENT_GET_BASE } from '@/api/clients';
import type { TCREDIT_GET, TCUOTE } from '@/api/credit';
@@ -17,9 +11,9 @@ import { formatISO } from 'date-fns';
import { faker } from "@faker-js/faker/locale/en"
import { TPAYMENT_GET_BASE } from '@/api/payment';

const CLIENTS_LENGTH = 100
const CLIENTS_LENGTH = 20
const USERS_LENGTH = 10
const CREDITS_LENGTH = 200
const CREDITS_LENGTH = 20
const REPORTS_LENGTH = 5

export const users = new Map<number, TUSER_GET>( Array.from({ length: USERS_LENGTH })?.map<[number, TUSER_GET]>( (_, index) => {
104 changes: 0 additions & 104 deletions src/pages/__stories__/-client.stories.tsx

This file was deleted.

182 changes: 0 additions & 182 deletions src/pages/__stories__/-credit.stories.tsx

This file was deleted.

53 changes: 0 additions & 53 deletions src/pages/__stories__/-notifications.stories.tsx

This file was deleted.

34 changes: 0 additions & 34 deletions src/pages/__stories__/-report.stories.tsx

This file was deleted.

34 changes: 0 additions & 34 deletions src/pages/__stories__/-root.stories.tsx

This file was deleted.

108 changes: 0 additions & 108 deletions src/pages/__stories__/-user.stories.tsx

This file was deleted.

19 changes: 11 additions & 8 deletions src/pages/_layout.tsx
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@ import { useToken } from '@/lib/context/login'
import { useIsFetching, useIsMutating, useQuery } from '@tanstack/react-query'
import { SpinLoader } from '@/components/ui/loader'
import brand from "@/assets/menu-brand.avif"
import brandOff from "@/assets/menu-off-brand.avif"

export const Route = createFileRoute('/_layout')({
component: Layout,
@@ -78,7 +79,7 @@ interface TStatus {

/* eslint-disable-next-line */
interface TNavigationProps {
clients?: TCLIENT_GET_ALL[]
clients?: TCLIENT_GET_ALL
user?: TUSER_GET
theme?: Theme
open?: boolean
@@ -92,7 +93,7 @@ const reducer: React.Reducer<TStatus, TStatus> = (prev, state) => {
export function Layout({
children,
theme: _theme,
clients: _clients = [] as TCLIENT_GET_ALL[],
clients: _clients = [] as TCLIENT_GET_ALL,
open: _open = false,
user: _user = {} as TUSER_GET,
}: React.PropsWithChildren<TNavigationProps>) {
@@ -215,7 +216,9 @@ export function Layout({
}
)}
>
<img alt='brand' src={brand} className='my-4' />
<Link to={"/"}>
<img alt='brand' src={ !open ? brand : brandOff} className='aspect-contain min-h-24' />
</Link>
<Separator className="my-4" />
<div className="p-4 px-6 text-xl">
<ul className="space-y-3 [&_button]:w-full">
@@ -244,7 +247,7 @@ export function Layout({
<Separator className="my-4" />
<div className="grid place-items-center">
{!open ? (
<Calendar key={'calendar'} className="rounded-xl bg-secondary" />
<Calendar key={'calendar'} className="rounded-xl bg-secondary-foreground text-muted-foreground ring-1 ring-secondary [&_*]:font-bold" />
) : (
<Popover onOpenChange={onclick(setStatus, { calendar: !calendar })}>
<PopoverTrigger>
@@ -255,8 +258,8 @@ export function Layout({
<CalendarIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-76 rounded-xl bg-secondary">
<Calendar key={'calendar'} />
<PopoverContent className="w-76 rounded-xl">
<Calendar key={'calendar'} className='rounded-xl bg-secondary-foreground text-muted-foreground ring-1 ring-secondary [&_*]:font-bold' />
</PopoverContent>
</Popover>
)}
@@ -401,8 +404,8 @@ export function Layout({
{!offline && (
<Network
className={clsx('ms-auto animate-bounce', {
'stroke-green-500': offline,
'stroke-red-500': !offline,
'stroke-success': offline,
'stroke-destructive': !offline,
})}
/>
)}
2 changes: 1 addition & 1 deletion src/pages/_layout/client.tsx
Original file line number Diff line number Diff line change
@@ -109,7 +109,7 @@ export function Clients({
return clients?.filter(
({ id: userId }) => userId && search?.clients?.includes(userId)
)
}, [JSON.stringify(clients)])
}, [clients])

useEffect( () => {
document.title = import.meta.env.VITE_NAME + " | " + text.browser
2 changes: 1 addition & 1 deletion src/pages/_layout/client/$clientId/delete.tsx
Original file line number Diff line number Diff line change
@@ -140,7 +140,7 @@ export function DeleteClientById({ client: _client = {} as TCLIENT_GET }: TDelet
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
2 changes: 1 addition & 1 deletion src/pages/_layout/client/$clientId/update.tsx
Original file line number Diff line number Diff line change
@@ -303,7 +303,7 @@ export function UpdateClientById({ client: _client = {} as TCLIENT_GET }: TUpdat
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
2 changes: 1 addition & 1 deletion src/pages/_layout/client/delete.tsx
Original file line number Diff line number Diff line change
@@ -140,7 +140,7 @@ export function DeleteSelectedClients({ clients: _clients = [] as TClientTable[]
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
2 changes: 1 addition & 1 deletion src/pages/_layout/client/new.tsx
Original file line number Diff line number Diff line change
@@ -244,7 +244,7 @@ export function NewClient() {
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>

81 changes: 72 additions & 9 deletions src/pages/_layout/credit.tsx
Original file line number Diff line number Diff line change
@@ -18,29 +18,31 @@ import {
Printer,
CircleDollarSign as Pay,
} from 'lucide-react'
import { createContext, useEffect, useState } from 'react'
import { getCreditById, getCreditsList, type TCREDIT_GET_FILTER, type TCREDIT_GET_FILTER_ALL } from '@/api/credit'
import { createContext, forwardRef, useEffect, useState } from 'react'
import { getCreditById, getCreditsFilter, getCreditsList, type TCREDIT_GET_FILTER, type TCREDIT_GET_FILTER_ALL } from '@/api/credit'
import { useStatus } from '@/lib/context/layout'
import { format, isValid } from 'date-fns'
import styles from "@/styles/global.module.css";
import { getFrecuencyById } from '@/lib/type/frecuency'
import { getClientById } from '@/api/clients'
import brand from '@/assets/menu-brand.avif'

export const Route = createFileRoute('/_layout/credit')({
component: Credits,
loader: async () => {
// TODO: this is a temporal function to getFilter
if(+import.meta.env.VITE_MSW) return (await getCreditsFilter()());
const list = await getCreditsList()
const data: TCREDIT_GET_FILTER_ALL = await Promise.all( list?.map<Promise<TCREDIT_GET_FILTER>>( async ({ id: creditId, owner_id, frecuencia_del_credito_id }) => {
const { nombres, apellidos } = await getClientById({ params: { clientId: "" + owner_id } })
const { cuotas } = await getCreditById({ params: { creditId: "" + creditId } })
const { cuotas, pagos } = await getCreditById({ params: { creditId: "" + creditId } })
return ({
clientId: owner_id,
id: creditId,
frecuencia: getFrecuencyById({ frecuencyId: frecuencia_del_credito_id ?? 1 }),
fecha_de_cuota: cuotas?.at(-1)?.fecha_de_pago ,
valor_de_cuota: cuotas?.at(-1)?.valor_de_cuota,
numero_de_cuota: cuotas?.at(-1)?.numero_de_cuota,
numero_de_cuota: pagos?.length,
valor_de_la_mora: cuotas?.at(-1)?.valor_de_mora,
nombre_del_cliente: nombres + " " + apellidos,
}) as TCREDIT_GET_FILTER
@@ -190,28 +192,31 @@ export function Credits({
{ isValid(fecha_de_cuota) && <Badge> {format(fecha_de_cuota , "dd-MM-yyyy")} </Badge>}
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogTrigger asChild className="ms-auto" >
<Link
{<Link
to={'./print'}
params={{ creditId }}
search={{ creditId }}
disabled={numero_de_cuota <= 0}
>
<Button
variant="ghost"
onClick={onClick(index)}
disabled={numero_de_cuota <= 0}
className={clsx(
'invisible px-3 opacity-0 hover:ring hover:ring-primary group-hover:visible group-hover:opacity-100'
'invisible px-3 opacity-0 hover:ring hover:ring-primary group-hover:opacity-100',
{ "group-hover:visible": numero_de_cuota > 0 }
)}
>
<Printer />
</Button>
</Link>
</Link>}
</DialogTrigger>
<DialogTrigger asChild>
<Link to={'./pay'} params={{ creditId }}>
<Button
onClick={onClick(index)}
variant="default"
className={clsx(
'invisible bg-green-400 px-3 opacity-0 hover:bg-green-700 group-hover:visible group-hover:opacity-100'
'invisible opacity-0 group-hover:visible group-hover:opacity-100 bg-success hover:bg-success hover:ring-4 ring-success-foreground'
)}
>
<Pay />
@@ -233,6 +238,50 @@ export function Credits({

Credits.dispalyname = 'CreditsList'

interface TPrintCredit extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
client: string
ssn: string
telephone: string
phone: string
date: string
pay: number
mora?: number
cuoteNumber: number
pending: number
comment?: string
}

export const PrintCredit = forwardRef<HTMLDivElement, TPrintCredit>( function ({ client, cuoteNumber, mora, pay, date, comment, pending, telephone, ssn, phone }, ref) {
return <main ref={ref} className='p-4 py-6 text-sm [&>section>p]:font-bold [&>section>p>span]:italic [&>section>p>span]:font-normal space-y-3 divide-y-2 divide-gray-900 dark:divide-gray-300'>
<header>
<img alt='brand' src={brand} className='filter grayscale light:brightness-50 dark:brightness-200 mx-auto' width={160} />
<h4 className='text-sm font-bold'>{text.print.title + ":"}</h4>
</header>
<section>
<p>{text.print.client + ":"}<span>{client + "."}</span> </p>
<p >{text.print.ssn + ":"}<span>{ssn + "."}</span> </p>
<p>{text.print.telephone + ":"}<span>{telephone + "."}</span> </p>
<p>{text.print.phone + ":"}<span>{phone + "."}</span> </p>
<p>{text.print.date + ":"}<span>{date + "."}</span></p>
</section>
<section>
<p>{text.print.cuoteNumber + ":"}<span>{cuoteNumber + "."}</span></p>
<p>{text.print.pay + ":"}<span> $ {pay + "."} </span></p>
{ mora && <p>{text.print.mora + ":"}<span>{mora + "."}</span></p> }
</section>
<section>
<p>{text.print.pending + ""} <span>$ {pending + "."}</span></p>
{ comment && <>
<p>{text.print.comment + ":"}</p>
<p className='italic !font-normal line-clamp-3'>{comment}</p>
</>}
</section>
<footer>
<p className='my-4 ms-auto w-fit italic underline'> {text.print.services} <span className='font-bold not-italic'>{import.meta.env.VITE_NAME}</span></p>
</footer>
</main>
} )

const text = {
title: 'Prestamos:',
browser: 'Prestamos',
@@ -259,4 +308,18 @@ const text = {
frecuency: 'Frecuencia',
history: 'Historial de pagos',
},
print: {
title: "Comprobante de pago",
client: "Cliente",
ssn: "Cédula",
telephone: "Teléfono",
phone: "Celular",
date: "Fecha",
pay: "Pago cuota",
mora: "Mora",
cuoteNumber: "Número de cuota",
pending: "Pendiente",
comment: "Comentario",
services: "Servicios",
}
}
7 changes: 5 additions & 2 deletions src/pages/_layout/credit/new.tsx
Original file line number Diff line number Diff line change
@@ -347,13 +347,16 @@ export function NewCredit( { clients: _clients = [] as TCLIENT_GET[] }: TNewCred
<li><span>Monto por cuota</span>: {"$" + getAmountCuote({ interest, amount, coute })}. </li>
</ul>
<div className='space-x-2'>
<Button variant="default" form="new-credit" type="submit" className='self-end'>
<Button
variant="default"
form="new-credit"
type="submit" className='self-end'>
{text.button.update}
</Button>
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
4 changes: 2 additions & 2 deletions src/pages/_layout/credit/pay.tsx
Original file line number Diff line number Diff line change
@@ -176,13 +176,13 @@ export function PaySelectedCredit( { credit: _credit = {} as TCREDIT_GET }: TPay
form="pay-credit"
type="submit"
disabled={!checked}
className={clsx({ "bg-green-500 hover:bg-green-700": checked, })}>
className={clsx({ "bg-success hover:bg-success hover:ring-4 ring-success-foreground": checked, })}>
{text.button.pay}
</Button>
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className={clsx("font-bold hover:ring hover:ring-primary")}
>
{text.button.close}
119 changes: 97 additions & 22 deletions src/pages/_layout/credit/print.tsx
Original file line number Diff line number Diff line change
@@ -10,53 +10,87 @@ import { Label } from '@/components/ui/label'
import { Separator } from '@/components/ui/separator'
import { DialogDescription } from '@radix-ui/react-dialog'
import { Navigate, createFileRoute } from '@tanstack/react-router'
import { useContext, useRef, useState } from 'react'
import { useMemo, useRef, useState } from 'react'
import clsx from 'clsx'
import styles from "@/styles/global.module.css"
import { type TCREDIT_GET } from '@/api/credit'
import { TCREDIT_GET_FILTER, getCreditById } from '@/api/credit'
import { Select, SelectContent, SelectTrigger, SelectValue, SelectItem } from '@/components/ui/select'
import { _creditSelected } from "@/pages/_layout/credit";
import { PrintCredit, _creditSelected } from "@/pages/_layout/credit";
import { useStatus } from '@/lib/context/layout'
import { useReactToPrint } from "react-to-print";
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
import { getClientById } from '@/api/clients'
import { Input } from '@/components/ui/input'

type TSearch = {
creditId: number
}
export const Route = createFileRoute('/_layout/credit/print')({
component: PrintSelectedCredit,
validateSearch: ( searh: TSearch ) => ( searh as TSearch ),
loader: async ({ location: { search } }) => {
const credit = await getCreditById({ params: { creditId: "" + (search as TSearch)?.creditId } })
const client = await getClientById({ params: { clientId: "" + credit?.owner_id } })
return ({ credit, client })
}
})

const options = { last: "Ultimo pago", especific: "Pago especifico" }

/* eslint-disable-next-line */
interface TPrintSelectedCreditProps {
credit?: TCREDIT_GET
credit?: TCREDIT_GET_FILTER
}

/* eslint-disable-next-line */
type TOptState = "last" | "especific"
type TOptState = keyof typeof options

/* eslint-disable-next-line */
export function PrintSelectedCredit( { credit: _credit = {} as TCREDIT_GET }: TPrintSelectedCreditProps ) {
export function PrintSelectedCredit( { credit: _credit = {} as TCREDIT_GET_FILTER }: TPrintSelectedCreditProps ) {
const form = useRef<HTMLFormElement>(null)
const [ opt, setOpt ] = useState<TOptState | undefined>(undefined)
const credit = useContext(_creditSelected) ?? _credit
const [ { opt, payIndex }, setOpt ] = useState<{ payIndex?: number, opt?: TOptState }>({})
const { client, credit: creditDB } = Route.useLoaderData()
const { open, setOpen } = useStatus()
const ref = useRef< React.ComponentRef< typeof PrintCredit > >(null)

const onValueChange = ( value: string ) => {
setOpt(value as TOptState)
setOpt({opt: value as TOptState })
}

const onChange: React.ChangeEventHandler< React.ComponentRef< typeof Input > > = ( ev ) => {
const value = +ev.target.value - 1
if( value < 0 && value >= creditDB?.pagos?.length) return;
setOpt( { opt, payIndex: value })
}

const handlePrint = useReactToPrint({
content: () => ref?.current,
documentTitle: "Pago-" + new Date(),
})

const onSubmit: React.FormEventHandler<HTMLFormElement> = (ev) => {
if (!form.current || !opt) return
if (!form.current || !opt || !ref?.current) return

console.table(credit)
console.table(creditDB)
setOpen({ open: !open })

handlePrint()

form.current.reset()
ev.preventDefault()
}

const pay = useMemo( () => creditDB?.pagos?.at( payIndex ?? -1 ), [payIndex] )
const mora = useMemo( () => creditDB?.cuotas?.at( payIndex ?? -1 )?.valor_de_mora, [payIndex] )

return (
<>
{!open && <Navigate to={"../"} replace />}
<DialogContent className="max-w-lg">
<DialogHeader>
<DialogTitle className="text-2xl">{text.title}</DialogTitle>
<div className='flex gap-2 center'>
<DialogTitle className="text-2xl">{text.title}</DialogTitle>
</div>
<Separator />
<DialogDescription className='text-muted-foreground'>{text.description}</DialogDescription>
</DialogHeader>
@@ -77,27 +111,65 @@ export function PrintSelectedCredit( { credit: _credit = {} as TCREDIT_GET }: TP
<SelectValue placeholder={text.form.options.placeholder} />
</SelectTrigger>
<SelectContent className='[&_*]:cursor-pointer'>
{ text.form.options.items.map( ( item ) => <SelectItem key={item} value={item}>{item}</SelectItem> ) }
{ Object.entries(options).map( ( [ key, value ], i ) => <SelectItem key={i} value={key}>{value}</SelectItem> ) }
</SelectContent>
</Select>
</Label>
{ opt === "especific" &&
<Label>
<span>{text.form.pay.label}</span>
<Input
required
onChange={onChange}
type='number'
min={1}
max={creditDB?.pagos?.length }
placeholder={text.form.pay.placeholder}
/>
</Label> }
</form>
<DialogFooter >
<div className={clsx("flex gap-2",
{
'!flex-row-reverse': opt,
'[&>*:last-child]:animate-pulse': !opt,
'!flex-row-reverse': opt === "last" || ( opt === "especific" && typeof payIndex !== "undefined" ),
'[&>*:last-child]:animate-pulse': !opt || (opt === "especific" && typeof payIndex === "undefined"),
}
)}>
<Button form="print-credit" type="submit"
disabled={!opt}
>
{text.button.print}
</Button>
<HoverCard openDelay={0} closeDelay={0.5 * 1000}>
<HoverCardTrigger asChild className={clsx('[&>svg]:stroke-primary [&>svg]:cursor-pointer', {
})}>
<Button form="print-credit" type="submit"
disabled={!opt || ( opt === "especific" && typeof payIndex === "undefined")}
>
{text.button.print}
</Button>
</HoverCardTrigger>
{ opt && <HoverCardContent side='right' className='bg-secondary-foreground rounded-md'>
<PrintCredit
{...{
client: client?.nombres + " " + client?.apellidos,
ssn: client?.numero_de_identificacion,
telephone: client?.telefono,
phone: client?.celular,
// TODO: date: format( pay?.fecha_de_pago ?? "", "dd-MM-yyyy / hh:mm aaaa" ),
date: pay?.fecha_de_pago?.slice(0,10) ?? "",
pay: +(pay?.valor_del_pago ?? 0)?.toFixed(2),
mora: mora ? +mora.toFixed(2) : undefined,
cuoteNumber: (payIndex ?? creditDB?.pagos?.length - 1) + 1,
pending: +(creditDB?.monto - creditDB?.pagos?.slice( 0, payIndex ? payIndex + 1 : -1)?.reduce( (prev, acc) => {
const res: typeof acc = { ...acc }
res.valor_del_pago += prev?.valor_del_pago
return res
}, { valor_del_pago: 0 } )?.valor_del_pago)?.toFixed(2),
comment: pay?.comentario === "" ? pay?.comentario : undefined,
}}
ref={ref} />
</HoverCardContent>}
</HoverCard>
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
@@ -120,10 +192,13 @@ const text = {
print: 'Imprimir',
},
form: {
pay: {
label: 'Numero del pago:',
placeholder: 'Escriba el numero del pago',
},
options: {
label: 'Opciones:',
placeholder: 'Seleccione la opcion de impresion',
items: [ "Ultimo pago","Pago especifico" ]
},
}
}
13 changes: 8 additions & 5 deletions src/pages/_layout/credit_/$creditId.tsx
Original file line number Diff line number Diff line change
@@ -85,8 +85,11 @@ export function CreditById({
<h1 className="text-3xl font-bold">{text.title}</h1>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogTrigger className="ms-auto" asChild>
<Link to={'./print'}>
<Button variant="ghost" className='hover:ring hover:ring-primary'>
<Link to={'./print'}
disabled={credit?.pagos?.length <= 0}>
<Button variant="ghost"
className='hover:ring hover:ring-primary'
disabled={credit?.pagos?.length <= 0}>
<Printer />
</Button>
</Link>
@@ -95,7 +98,7 @@ export function CreditById({
<Link to={'./pay'}>
<Button
variant="default"
className={clsx('bg-green-500 hover:bg-green-700')}
className={clsx('bg-success hover:bg-success hover:ring-4 ring-success-foreground')}
>
<Pay />
</Button>
@@ -146,7 +149,7 @@ export function CreditById({
<li>
<b>{text.details.frecuency + ":"}</b> <span>{getFrecuencyById({ frecuencyId: credit?.frecuencia_del_credito_id })?.nombre + '.'}</span>
</li>
<li className={clsx({ "[&>b]:text-green-700": !moraStatus, "[&>b]:line-through": moraStatus })}>
<li className={clsx({ "[&>b]:text-success": !moraStatus, "[&>b]:line-through": moraStatus })}>
<b>{text.details.interest + ":"}</b> <span>{credit?.tasa_de_interes + '% de $' + cuoteValue + "."}</span>
</li>
<li className={clsx({ "[&>b]:text-destructive": moraStatus, "[&>b]:line-through": !moraStatus })}>
@@ -189,7 +192,7 @@ export function CreditById({
{/* fix this date because returt a invalid date time */}
<ul>
<li className='before:content-["_-_"] before:font-bold before:text-destructive'>{credit?.pagos?.[index]?.fecha_de_pago?.slice(0,10)}</li>
<li className='before:content-["_+_"] before:font-bold before:text-green-700'>{format(fecha_de_pago, 'yyyy-MM-dd')}</li>
<li className='before:content-["_+_"] before:font-bold before:text-success'>{format(fecha_de_pago, 'yyyy-MM-dd')}</li>
</ul>
</TableCell>
<TableCell>
2 changes: 1 addition & 1 deletion src/pages/_layout/credit_/$creditId/delete.tsx
Original file line number Diff line number Diff line change
@@ -140,7 +140,7 @@ export function DeleteCreditById({ credit: _credit = {} as TCREDIT_GET }: TDelet
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
4 changes: 2 additions & 2 deletions src/pages/_layout/credit_/$creditId/pay.tsx
Original file line number Diff line number Diff line change
@@ -176,14 +176,14 @@ export function PayCreditById( { credit: _credit = {} as TCREDIT_GET }: TPayment
)}
>
<Button variant="default" form="pay-credit" type="submit" disabled={!checked} className={clsx({
"bg-green-500 hover:bg-green-700": checked,
"bg-success hover:bg-success hover:ring-4 ring-success-foreground": checked,
})}>
{text.button.pay}
</Button>
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className={clsx("font-bold hover:ring hover:ring-primary")}
>
{text.button.close}
108 changes: 88 additions & 20 deletions src/pages/_layout/credit_/$creditId/print.tsx
Original file line number Diff line number Diff line change
@@ -10,17 +10,22 @@ import { Label } from '@/components/ui/label'
import { Separator } from '@/components/ui/separator'
import { DialogDescription } from '@radix-ui/react-dialog'
import { createFileRoute } from '@tanstack/react-router'
import { useRef, useState } from 'react'
import { useContext, useMemo, useRef, useState } from 'react'
import clsx from 'clsx'
import styles from "@/styles/global.module.css"
import { type TCREDIT_GET, getCreditById } from '@/api/credit'
import { type TCREDIT_GET } from '@/api/credit'
import { Select, SelectContent, SelectTrigger, SelectValue, SelectItem } from '@/components/ui/select'
import { useStatus } from '@/lib/context/layout'
import { Navigate } from '@tanstack/react-router'
import { PrintCredit, _creditSelected } from '../../credit'
import { useReactToPrint } from 'react-to-print'
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
import { _clientContext, _selectedCredit } from '../$creditId'
import { TCLIENT_GET } from '@/api/clients'
import { Input } from '@/components/ui/input'

export const Route = createFileRoute('/_layout/credit/$creditId/print')({
component: PrintCreditById,
loader: getCreditById
})

/* eslint-disable-next-line */
@@ -29,18 +34,33 @@ interface TPaymentCreditByIdProps {
}

/* eslint-disable-next-line */
type TOptState = "last" | "especific"
const options = { last: "Ultimo pago", especific: "Pago especifico" }
type TOptState = keyof typeof options


/* eslint-disable-next-line */
export function PrintCreditById( { credit: _credit = {} as TCREDIT_GET }: TPaymentCreditByIdProps ) {
const form = useRef<HTMLFormElement>(null)
const [ opt, setOpt ] = useState<TOptState | undefined>(undefined)
const credit = Route.useLoaderData() ?? _credit
const [ { opt, payIndex }, setOpt ] = useState<{ payIndex?: number, opt?: TOptState }>({})
const [ credit ] = useContext(_selectedCredit) ?? [ {} as TCREDIT_GET ]
const [ client ] = useContext(_clientContext) ?? [ {} as TCLIENT_GET ]
const { open, setOpen } = useStatus()
const ref = useRef< React.ComponentRef< typeof PrintCredit > >(null)

const handlePrint = useReactToPrint({
content: () => ref?.current,
documentTitle: "Pago-" + new Date(),
})

const onChange: React.ChangeEventHandler< React.ComponentRef< typeof Input > > = ( ev ) => {
const value = +ev.target.value - 1
if( value < 0 && value >= credit?.pagos?.length) return;
setOpt( { opt, payIndex: value })

}

const onValueChange = ( value: string ) => {
setOpt(value as TOptState)
setOpt({opt: value as TOptState })
}

const onSubmit: React.FormEventHandler< HTMLFormElement > = (ev) => {
@@ -49,10 +69,15 @@ export function PrintCreditById( { credit: _credit = {} as TCREDIT_GET }: TPayme
console.table(credit)
setOpen({ open: !open })

handlePrint()

form.current.reset()
ev.preventDefault()
}

const pay = useMemo( () => credit?.pagos?.at( payIndex ?? -1 ), [payIndex] )
const mora = useMemo( () => credit?.cuotas?.at( payIndex ?? -1 )?.valor_de_mora, [payIndex] )

return (
<>
{ !open && <Navigate to={"../"} replace /> }
@@ -74,35 +99,74 @@ export function PrintCreditById( { credit: _credit = {} as TCREDIT_GET }: TPayme
>
<Label className='[&>span]:after:content-["_*_"] [&>span]:after:text-red-500'>
<span>{text.form.options.label} </span>
<Select required name={'options' as keyof typeof text.form} value={opt} onValueChange={onValueChange}>
<Select required name={'options'} value={opt} onValueChange={onValueChange}>
<SelectTrigger className="w-full">
<SelectValue placeholder={text.form.options.placeholder} />
</SelectTrigger>
<SelectContent className='[&_*]:cursor-pointer'>
{ text.form.options.items.map( ( item ) => <SelectItem key={item} value={item}>{item}</SelectItem> ) }
{ Object.entries(options).map( ( [ key, value ], i ) => <SelectItem key={i} value={key}>{value}</SelectItem> ) }
</SelectContent>
</Select>
</Label>
{ opt === "especific" &&
<Label>
<span>{text.form.pay.label}</span>
<Input
required
onChange={onChange}
type='number'
min={1}
max={credit?.pagos?.length}
placeholder={text.form.pay.placeholder}
/>
</Label> }
</form>
<DialogFooter >
<div className={clsx("flex gap-2",
{
'!flex-row-reverse': opt,
'[&>*:last-child]:animate-pulse': !opt,
'!flex-row-reverse': opt === "last" || ( opt === "especific" && typeof payIndex !== "undefined" ),
'[&>*:last-child]:animate-pulse': !opt || (opt === "especific" && typeof payIndex === "undefined"),

}
)}>
<Button
variant="default"
form="print-credit"
type="submit"
disabled={!opt}
>
{text.button.print}
</Button>
<HoverCard openDelay={0} closeDelay={0.5 * 1000}>
<HoverCardTrigger asChild className={clsx('[&>svg]:stroke-primary [&>svg]:cursor-pointer', {
})}>
<Button
variant="default"
form="print-credit"
type="submit"
disabled={!opt || ( opt === "especific" && typeof payIndex === "undefined")}
>
{text.button.print}
</Button>
</HoverCardTrigger>
{ opt && <HoverCardContent side='right' className='bg-secondary-foreground rounded-md'>
<PrintCredit
{...{
client: client?.nombres + " " + client?.apellidos,
ssn: client?.numero_de_identificacion,
telephone: client?.telefono,
phone: client?.celular,
// TODO: date: format( pay?.fecha_de_pago ?? "", "dd-MM-yyyy / hh:mm aaaa" ),
date: pay?.fecha_de_pago ?? "",
pay: +(pay?.valor_del_pago ?? 0)?.toFixed(2),
mora: mora ? +mora.toFixed(2) : undefined,
cuoteNumber: (payIndex ?? credit?.pagos?.length - 1) + 1,
pending: +(credit?.monto - credit?.pagos?.slice( 0, payIndex ? payIndex + 1 : -1)?.reduce( (prev, acc) => {
const res: typeof acc = { ...acc }
res.valor_del_pago += prev?.valor_del_pago
return res
}, { valor_del_pago: 0 } )?.valor_del_pago)?.toFixed(2),
comment: pay?.comentario === "" ? pay?.comentario : undefined,
}}
ref={ref} />
</HoverCardContent>}
</HoverCard>
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
@@ -125,6 +189,10 @@ const text = {
print: 'Imprimir',
},
form: {
pay: {
label: 'Numero del pago:',
placeholder: 'Escriba el numero del pago',
},
options: {
label: 'Opciones:',
placeholder: 'Seleccione la opcion de impresion',
6 changes: 3 additions & 3 deletions src/pages/_layout/credit_/$creditId_/update.confirm.tsx
Original file line number Diff line number Diff line change
@@ -79,7 +79,7 @@ export function UpdateConfirmationCredit({ credit: _credit = {} as TCREDIT_GET }

const deleteItems = Object.values( deletePayment )

const action = (credit: Record< keyof TCREDIT_GET, string> & { id: number }, payment: (Record< keyof TPAYMENT_GET_BASE, string> & { id: number })[], deletePay: number[]) => () => {
const action = (credit: Record< keyof TCREDIT_GET, string> & { id: number }, payment: (Record< keyof TPAYMENT_GET_BASE, string> & { id: number })[], deletePay: (number | undefined)[]) => () => {
if(credit?.id && Object?.values( credit )?.length > 1) {
updateCredit({
creditId: credit.id,
@@ -100,7 +100,7 @@ export function UpdateConfirmationCredit({ credit: _credit = {} as TCREDIT_GET }
})
}
for ( const pay of payment ){
if( !pay?.id ) continue;
if( !pay?.id || ( pay?.id && deletePay?.includes(pay?.id)) ) continue;;
updatePayment({
paymentId: pay?.id,
updatePayment: {
@@ -203,7 +203,7 @@ export function UpdateConfirmationCredit({ credit: _credit = {} as TCREDIT_GET }
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
10 changes: 6 additions & 4 deletions src/pages/_layout/credit_/$creditId_/update.tsx
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ export function UpdateCreditById( { children, open: _open, credit: _credit = {}

const active = useMemo(() =>
Object.values(credit).flat().every( ( value, i ) => value === Object.values(creditChange).flat()?.[i]
) || !Object.values( paymentDelete ?? {} )?.length, [ creditChange, paymentDelete ])
) && !Object.values( paymentDelete ?? {} )?.length, [ creditChange, paymentDelete ])

const { client, user, ref } = useMemo( () => {
const client = clients?.find( ({ id }) => ( id === credit?.owner_id ) )
@@ -168,8 +168,9 @@ export function UpdateCreditById( { children, open: _open, credit: _credit = {}
const onDeletePaymentById: ( index: number ) => React.MouseEventHandler< React.ComponentRef< typeof Button > > = (index) => (ev) => {
ev.stopPropagation()

if( !!paymentDelete?.[index] ){
if( !!paymentDelete?.[index] && form?.[index] ){
setPaymentDelete( { ...paymentDelete , [ index ]: undefined } )
form?.[index]?.current?.reset()
return;
}

@@ -391,11 +392,11 @@ export function UpdateCreditById( { children, open: _open, credit: _credit = {}
className={clsx('ms-auto invisible opacity-0 group-hover/item:visible group-hover/item:opacity-100 transition delay-150 duration-300 p-1 w-6 h-6 rounded-full group/button',
{
"hover:bg-destructive": !paymentDelete?.[index],
"hover:bg-green-500": paymentDelete?.[index]
"hover:bg-success": paymentDelete?.[index]
} )}>
<Cross className={clsx('group-hover/button:stroke-white transition delay-150 duration-500', {
"stroke-destructive stroke-destructive rotate-45": !paymentDelete?.[index],
"stroke-green-500": paymentDelete?.[index]
"stroke-success": paymentDelete?.[index]
})} />
</Button>
</AccordionTrigger>
@@ -415,6 +416,7 @@ export function UpdateCreditById( { children, open: _open, credit: _credit = {}
date={new Date(payment?.fecha_de_pago)}
label={text.form.pay.payDate.placeholder}
defaultValue={payment?.fecha_de_pago}
value={!!paymentDelete?.[index] ? "" : undefined}
/>
</Label>
<Label>
2 changes: 1 addition & 1 deletion src/pages/_layout/report.tsx
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ export function Report({ reports: _reports = [] as TREPORT_GET_ALL }: TReportPro
{reports.map(({ nombre, parametros, id, comentario}, index) => (
<AccordionItem
key={id}
className={clsx('rounded-m px-4 py-2 shadow-lg hover:shadow-xl')}
className={clsx('rounded-m px-4 py-2 shadow-lg hover:shadow-xl bg-card rounded-md')}
value={'item' + id}
>
<AccordionTrigger className="group !no-underline">
2 changes: 1 addition & 1 deletion src/pages/_layout/user/$userId/delete.tsx
Original file line number Diff line number Diff line change
@@ -134,7 +134,7 @@ export function DeleteUserById({ user: _user={} as TUsersState }: TDeleteByUser)
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
325 changes: 322 additions & 3 deletions src/pages/_layout/user/$userId/update.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,324 @@
import { createFileRoute } from '@tanstack/react-router'
import { Button } from '@/components/ui/button'
import { DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Separator } from '@/components/ui/separator'
import { ToastAction } from '@/components/ui/toast'
import { toast } from '@/components/ui/use-toast'
import { Navigate, createFileRoute } from '@tanstack/react-router'
import clsx from 'clsx'
import { ComponentRef, useContext, useMemo, useRef, useState } from 'react'
import { Eye, EyeOff } from 'lucide-react'
import { getUserById, pathUserById } from '@/api/users'
import { useStatus } from '@/lib/context/layout'
import { useNotifications } from '@/lib/context/notification'
import { useMutation } from '@tanstack/react-query'
import { type TROLES, getRolById, getRolByName, listRols } from '@/lib/type/rol'
import { type TUsersState, _usersContext } from '@/pages/_layout/user'

export const Route = createFileRoute('/_layout/user/$userId/update')({
component: () => <div>Hello /_layout/user/$userId/update!</div>
})
component: UpdateUserById,
loader: getUserById
})

/* eslint-disable-next-line */
interface TPassowordVisibilityState {
password?: boolean
confirmation?: boolean
}

interface TPassowordValueState{
password?: string
confirmation?: string
}

/* eslint-disable-next-line */
interface TUpdateUserById {
user?: TUsersState
}

type TFormName = "firstName" | "lastName" | "rol" | "password" | "newPassword"

/* eslint-disable-next-line */
export function UpdateUserById({ user: _user = {} as TUsersState }: TUpdateUserById) {
const [ visibility, setVisibility ] = useState<TPassowordVisibilityState>({})
const [ password, setPassword ] = useState<TPassowordValueState | undefined>(undefined)
const form = useRef<HTMLFormElement>(null)
const _userDB = (Route.useLoaderData() ?? _user)
const userDB = { ..._userDB, password: "" }
const [ user, setUser ] = useState(userDB)
const [ users, setUsers ] = useContext(_usersContext) ?? [ [], () => {} ]
const { pushNotification } = useNotifications()
const { open, setOpen } = useStatus()
const {mutate: updateUser} = useMutation( {
mutationKey: ["update-user"],
mutationFn: pathUserById,
})
const active = useMemo( () => Object.values(userDB)?.every( ( value, i ) => ( value === Object.values(user)?.[i] ) ), [ JSON.stringify(user) ] )

const onClick: ( prop: keyof TPassowordVisibilityState ) => React.MouseEventHandler< ComponentRef< typeof Button > > = ( prop ) => () => {
setVisibility( { ...visibility, [ prop ]: !visibility?.[prop] } )
}

const onChangePassword: React.ChangeEventHandler< ComponentRef< typeof Input > > = (ev) => {
const { name, value } = ev?.target
setPassword( { ...password, [ name ]: value } )
}

const onChange: React.ChangeEventHandler< HTMLFormElement > = (ev) => {
const { name, value } = ev.target

if( name === "firstName" as TFormName || name === "lastName" as TFormName ){
return;
}

setUser( { ...user, [ name ]: value } )
}

const onChangeName: ( filed: "firstName" | "lastName" ) => React.ChangeEventHandler< React.ComponentRef< typeof Input > > = (field) => (ev) => {
const { value } = ev.target
const { nombre } = user
if( field === "firstName" ){
setUser( { ...user, nombre: value + nombre.split(" ")?.[1] } )
}
else if ( field === "lastName" ){
setUser( { ...user, nombre: nombre.split(" ")?.[0] + value } )
}
ev.stopPropagation()
}

const onSubmit: React.FormEventHandler<HTMLFormElement> = (ev) => {
if (!form.current) return;

const items = Object.fromEntries( Array.from( new FormData(form.current)?.entries() )?.map( ([ key, value ], i, list) => {
if( value === "" ) return [ key, undefined ]
return list?.[i]
})) as Record<TFormName, string>

const description = text.notification.decription({
username: userDB?.nombre
})

const action =
({ ...items }: Record<TFormName, string>) =>
() => {
updateUser({
userId: userDB?.id,
params: {
rol_id: +items.rol,
password: items?.newPassword,
nombre: items?.firstName + " " + items?.lastName
} })
pushNotification({
date: new Date(),
action: "PATH",
description,
})
}

const timer = setTimeout(action(items), 6 * 1000)
setOpen({ open: !open })
setUsers( users?.map( ({ id: userId }, i, list) => {
if( user.id !== userId ) return list[i];
return ({
...list?.[i],
nombre: items?.firstName + " " + items?.lastName,
rol: getRolById({ rolId: +items?.rol })?.nombre
})
} ) )

const onClick = () => {
setUsers(users)
clearTimeout(timer)
}

toast({
title: text.notification.titile,
description,
variant: 'default',
action: (
<ToastAction altText="action from new user">
<Button variant="default" onClick={onClick}>
{text.notification.undo}
</Button>
</ToastAction>
),
})

form.current.reset()
ev.preventDefault()
}

return (
<>
{!open && <Navigate to={"../../"} />}
<DialogContent className="max-w-lg">
<DialogHeader>
<DialogTitle className="text-2xl">{text.title}</DialogTitle>
<Separator />
<DialogDescription>{text.descriiption}</DialogDescription>
</DialogHeader>
<form
autoComplete="off"
ref={form}
onSubmit={onSubmit}
onChange={onChange}
id="update-user"
className={clsx(
'grid-rows-subgrid grid grid-cols-2 gap-3 gap-y-4 [&>:is(label,div)]:space-y-2 [&>*]:col-span-full [&_label>span]:font-bold',
)}
>
<Label className='!col-span-1' >
<span>{text.form.firstName.label}</span>
<Input
required
name={'firstName' as TFormName}
type="text"
placeholder={text.form.firstName.placeholder}
defaultValue={userDB?.nombre.split(" ")?.at(0)}
onChange={onChangeName("firstName")}
/>
</Label>
<Label className='!col-span-1' >
<span>{text.form.lastName.label} </span>
<Input
required
name={'lastName' as TFormName}
type="text"
placeholder={text.form.lastName.placeholder}
defaultValue={userDB?.nombre.split(" ")?.at(1)}
onChange={onChangeName("lastName")}
/>
</Label>
<Label>
<span>{text.form.rol.label} </span>
<Select required name={'rol' as TFormName} defaultValue={ ""+getRolByName({ rolName: userDB?.rol as TROLES })?.id }>
<SelectTrigger className="w-full">
<SelectValue placeholder={text.form.rol.placeholder} />
</SelectTrigger>
<SelectContent className='[&_*]:cursor-pointer'>
{ listRols()?.map( ({ id, nombre }) =>
<SelectItem key={id} value={""+id}>{nombre}</SelectItem>
) }
</SelectContent>
</Select>
</Label>
<div>
<Label htmlFor='user-password'>
<span>{text.form.password.current.label} </span>
</Label>
<div className="flex flex-row-reverse items-center gap-x-2">
<Button
type="button"
className="w-fit p-1.5"
onClick={onClick("password")}
variant={!visibility.password ? 'outline' : 'default'}
>
{!visibility.password ? <Eye /> : <EyeOff />}
</Button>
<Input
id='user-password'
name={'password' as TFormName}
type={!visibility.password ? "password" : "text"}
placeholder={text.form.password.current.placeholder}
value={password?.password}
onChange={onChangePassword}
/>
</div>
</div>
<div>
<Label htmlFor='user-new'>
<span>{text.form.password.new.label} </span>
</Label>
<div className="flex flex-row-reverse items-center gap-x-2">
<Button
type="button"
className="w-fit p-1.5"
onClick={onClick( "confirmation" )}
variant={!visibility.confirmation ? 'outline' : 'default'}
>
{!visibility.confirmation ? <Eye /> : <EyeOff />}
</Button>
<Input
id='user-new'
name={'newPassword' as TFormName}
required={!!password?.password}
type={!visibility.confirmation ? "password" : "text"}
placeholder={text.form.password.new.placeholder}
value={password?.confirmation}
onChange={onChangePassword}
/>
</div>
</div>
</form>
<DialogFooter className="justify-end">
<Button
disabled={ active }
variant="default"
form="update-user"
type="submit">
{text.button.update}
</Button>
<DialogClose asChild>
<Button
type="button"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</>
)
}

UpdateUserById.dispalyname = 'UpdateUserById'

const text = {
title: 'Actualizar Usuario:',
descriiption:
'Modifique los campos para actualizar los datos del usuario en la plataforma.',
button: {
close: 'Cerrar',
update: 'Actualizar',
},
notification: {
titile: 'Actualizacion de usuario',
decription: ({ username }: { username: string }) =>
'Se ha actualizacion el usuario ' + username + ' con exito.',
error: 'Error: la actualizacion del usuario ha fallado',
undo: 'Deshacer',
},
form: {
firstName: {
label: 'Nombre:',
placeholder: 'Escriba el nombre del usuario',
},
lastName: {
label: 'Apellidos:',
placeholder: 'Escriba el apellido del usuario',
},
password: {
current: {
label: 'Contraseña actual:',
placeholder: 'Escriba la cantraseña actual del usuario',
},
new: {
label: 'Nueva contraseña:',
placeholder: 'Escriba la nuva cantraseña del usuario',
}
},
rol: {
label: 'Tipo de rol:',
placeholder: 'Seleccione el rol del usuario',
items: {
user: "Usuario",
admin: "Administrador",
client: "Cliente",
}
},
},
}
2 changes: 1 addition & 1 deletion src/pages/_layout/user/delete.tsx
Original file line number Diff line number Diff line change
@@ -129,7 +129,7 @@ export function DeleteSelectedUsers({users: _users=[] as TUsersState[]}: TDelete
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
2 changes: 1 addition & 1 deletion src/pages/_layout/user/new.tsx
Original file line number Diff line number Diff line change
@@ -225,7 +225,7 @@ export function NewUser({}: TNewUserProps) {
<DialogClose asChild>
<Button
type="button"
variant="secondary"
variant="outline"
className="font-bold hover:ring hover:ring-primary"
>
{text.button.close}
4 changes: 4 additions & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
@@ -30,6 +30,10 @@ module.exports = {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
success: {
DEFAULT: 'hsl(var(--success))',
foreground: 'hsl(var(--success-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
28 changes: 28 additions & 0 deletions vite.config.ts.timestamp-1712419199346-55c64d0d0698d.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// vite.config.ts
import { defineConfig } from "file:///home/mob/mob99/projects/prestamos/node_modules/.pnpm/vite@5.0.11_@types+node@20.8.10_terser@5.24.0/node_modules/vite/dist/node/index.js";
import react from "file:///home/mob/mob99/projects/prestamos/node_modules/.pnpm/@vitejs+plugin-react-swc@3.5.0_vite@5.0.11/node_modules/@vitejs/plugin-react-swc/index.mjs";
import legacy from "file:///home/mob/mob99/projects/prestamos/node_modules/.pnpm/@vitejs+plugin-legacy@4.1.1_terser@5.24.0_vite@5.0.11/node_modules/@vitejs/plugin-legacy/dist/index.mjs";
import progress from "file:///home/mob/mob99/projects/prestamos/node_modules/.pnpm/vite-plugin-progress@0.0.7_vite@5.0.11/node_modules/vite-plugin-progress/dist/index.mjs";
import svgr from "file:///home/mob/mob99/projects/prestamos/node_modules/.pnpm/vite-plugin-svgr@4.2.0_typescript@5.2.2_vite@5.0.11/node_modules/vite-plugin-svgr/dist/index.js";
import { TanStackRouterVite } from "file:///home/mob/mob99/projects/prestamos/node_modules/.pnpm/@tanstack+router-vite-plugin@1.16.3/node_modules/@tanstack/router-vite-plugin/dist/esm/index.js";

// vite.alias.ts
import { resolve } from "path";
var __vite_injected_original_dirname = "/home/mob/mob99/projects/prestamos";
var root = resolve(__vite_injected_original_dirname, "src");
var alias = {
"@": root
};
var vite_alias_default = alias;

// vite.config.ts
var vite_config_default = defineConfig({
resolve: {
alias: vite_alias_default
},
plugins: [react(), legacy({}), progress(), svgr(), TanStackRouterVite()]
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiLCAidml0ZS5hbGlhcy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiY29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2Rpcm5hbWUgPSBcIi9ob21lL21vYi9tb2I5OS9wcm9qZWN0cy9wcmVzdGFtb3NcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9ob21lL21vYi9tb2I5OS9wcm9qZWN0cy9wcmVzdGFtb3Mvdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL2hvbWUvbW9iL21vYjk5L3Byb2plY3RzL3ByZXN0YW1vcy92aXRlLmNvbmZpZy50c1wiO2ltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnXG5pbXBvcnQgcmVhY3QgZnJvbSAnQHZpdGVqcy9wbHVnaW4tcmVhY3Qtc3djJ1xuaW1wb3J0IGxlZ2FjeSBmcm9tICdAdml0ZWpzL3BsdWdpbi1sZWdhY3knXG5pbXBvcnQgcHJvZ3Jlc3MgZnJvbSAndml0ZS1wbHVnaW4tcHJvZ3Jlc3MnXG5pbXBvcnQgc3ZnciBmcm9tICd2aXRlLXBsdWdpbi1zdmdyJ1xuaW1wb3J0IHsgVGFuU3RhY2tSb3V0ZXJWaXRlIH0gZnJvbSAnQHRhbnN0YWNrL3JvdXRlci12aXRlLXBsdWdpbidcbmltcG9ydCBhbGlhcyBmcm9tICcuL3ZpdGUuYWxpYXMnXG5cbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuICByZXNvbHZlOiB7XG4gICAgYWxpYXMsXG4gIH0sXG4gIHBsdWdpbnM6IFtyZWFjdCgpLCBsZWdhY3koe30pLCBwcm9ncmVzcygpLCBzdmdyKCksIFRhblN0YWNrUm91dGVyVml0ZSgpXSxcbn0pXG4iLCAiY29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2Rpcm5hbWUgPSBcIi9ob21lL21vYi9tb2I5OS9wcm9qZWN0cy9wcmVzdGFtb3NcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9ob21lL21vYi9tb2I5OS9wcm9qZWN0cy9wcmVzdGFtb3Mvdml0ZS5hbGlhcy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vaG9tZS9tb2IvbW9iOTkvcHJvamVjdHMvcHJlc3RhbW9zL3ZpdGUuYWxpYXMudHNcIjtpbXBvcnQgeyByZXNvbHZlIH0gZnJvbSAncGF0aCdcblxuY29uc3Qgcm9vdCA9IHJlc29sdmUoX19kaXJuYW1lLCAnc3JjJylcblxuZXhwb3J0IGNvbnN0IGFsaWFzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAnQCc6IHJvb3QsXG59XG5cbmV4cG9ydCBkZWZhdWx0IGFsaWFzXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQXdSLFNBQVMsb0JBQW9CO0FBQ3JULE9BQU8sV0FBVztBQUNsQixPQUFPLFlBQVk7QUFDbkIsT0FBTyxjQUFjO0FBQ3JCLE9BQU8sVUFBVTtBQUNqQixTQUFTLDBCQUEwQjs7O0FDTG1QLFNBQVMsZUFBZTtBQUE5UyxJQUFNLG1DQUFtQztBQUV6QyxJQUFNLE9BQU8sUUFBUSxrQ0FBVyxLQUFLO0FBRTlCLElBQU0sUUFBZ0M7QUFBQSxFQUMzQyxLQUFLO0FBQ1A7QUFFQSxJQUFPLHFCQUFROzs7QURDZixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTO0FBQUEsSUFDUDtBQUFBLEVBQ0Y7QUFBQSxFQUNBLFNBQVMsQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLENBQUMsR0FBRyxTQUFTLEdBQUcsS0FBSyxHQUFHLG1CQUFtQixDQUFDO0FBQ3pFLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==

0 comments on commit a0f75ef

Please sign in to comment.