Add end-to-end tests for proforma customer selection and persistence
- Implement tests to verify that the selected customer is retained when creating a proforma. - Add functionality to change the customer and ensure the change is persisted. - Include tests to confirm the customer change and refresh the recipient name. - Test cancellation of customer change to ensure the previous customer remains selected.
This commit is contained in:
parent
0b086047ee
commit
ba257fd489
@ -98,7 +98,7 @@ export const CompanyDetailsSettingsPage = () => {
|
||||
>
|
||||
<RdxPanel
|
||||
description={t("settings.companyDetails.sections.general.description")}
|
||||
icon={<ScrollTextIcon className="size-5" />}
|
||||
icon={<ScrollTextIcon className="size-6" />}
|
||||
title={t("settings.companyDetails.sections.general.title")}
|
||||
variant="plain"
|
||||
>
|
||||
@ -148,7 +148,7 @@ export const CompanyDetailsSettingsPage = () => {
|
||||
|
||||
<RdxPanel
|
||||
description={t("settings.companyDetails.sections.contact.description")}
|
||||
icon={<ContactIcon className="size-5" />}
|
||||
icon={<ContactIcon className="size-6" />}
|
||||
title={t("settings.companyDetails.sections.contact.title")}
|
||||
variant="plain"
|
||||
>
|
||||
@ -198,7 +198,7 @@ export const CompanyDetailsSettingsPage = () => {
|
||||
</RdxPanel>
|
||||
<RdxPanel
|
||||
description="Datos fiscales de la empresa"
|
||||
icon={<ContactIcon className="size-5" />}
|
||||
icon={<ContactIcon className="size-6" />}
|
||||
title="Dirección fiscal"
|
||||
variant="plain"
|
||||
>
|
||||
@ -238,7 +238,7 @@ export const CompanyDetailsSettingsPage = () => {
|
||||
|
||||
<RdxPanel
|
||||
description={t("settings.companyDetails.sections.paymentTerm.description")}
|
||||
icon={<LandmarkIcon className="size-5" />}
|
||||
icon={<LandmarkIcon className="size-6" />}
|
||||
title={t("settings.companyDetails.sections.paymentTerm.title")}
|
||||
variant="plain"
|
||||
>
|
||||
|
||||
@ -157,7 +157,7 @@ test("update registra customer y recipient en una sola mutación Audit", async (
|
||||
city: Maybe.some(unwrap(City.create("Madrid"))),
|
||||
postalCode: Maybe.some(unwrap(PostalCode.create("28001"))),
|
||||
province: Maybe.some(unwrap(Province.create("Madrid"))),
|
||||
country: Maybe.some(unwrap(Country.create("ES"))),
|
||||
countryCode: Maybe.some(unwrap(Country.create("ES"))),
|
||||
},
|
||||
}),
|
||||
} as never,
|
||||
|
||||
@ -213,7 +213,7 @@ export class ProformaUpdater implements IProformaUpdater {
|
||||
city: customer.address.city,
|
||||
postalCode: customer.address.postalCode,
|
||||
province: customer.address.province,
|
||||
country: customer.address.country,
|
||||
country: customer.address.countryCode,
|
||||
});
|
||||
|
||||
if (recipientResult.isFailure) {
|
||||
|
||||
@ -13,6 +13,7 @@ import { ArrowRightIcon, CheckIcon, LockIcon } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { useTranslation } from "../../../i18n";
|
||||
import { ProformaStatusBadge } from "../../list/ui/components";
|
||||
import { PROFORMA_STATUS, type ProformaStatus } from "../../shared";
|
||||
import type { ChangeProformaStatusTarget } from "../entities";
|
||||
import {
|
||||
@ -49,7 +50,7 @@ const StatusBadge = ({ status }: { status: ProformaStatus }) => {
|
||||
getProformaStatusColor(status)
|
||||
)}
|
||||
>
|
||||
<Icon className="size-3.5" />
|
||||
<Icon className="size-" />
|
||||
{t(`catalog.proformas.status.${status}.label`)}
|
||||
</span>
|
||||
);
|
||||
@ -149,7 +150,7 @@ export const ChangeProformaStatusDialog = ({
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{t("proformas.change_proforma_status_dialog.current_status", "Estado actual")}
|
||||
</span>
|
||||
<StatusBadge status={currentTarget.status} />
|
||||
<ProformaStatusBadge status={currentTarget.status} />
|
||||
</div>
|
||||
|
||||
{orderedAvailableStatuses.length === 0 ? (
|
||||
|
||||
@ -44,7 +44,7 @@ export const ProformaCreateInitialInfoCard = ({
|
||||
|
||||
return (
|
||||
<RdxPanel
|
||||
icon={<FileTextIcon className="size-5" />}
|
||||
icon={<FileTextIcon className="size-6" />}
|
||||
title={t("proformas.create.initial_info.title", "Información inicial")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -52,7 +52,7 @@ export const ProformaTotalsSummary = ({
|
||||
className={className}
|
||||
description={t("proformas.update.totals.description")}
|
||||
disabled={disabled}
|
||||
icon={<CurrencyIcon className="size-5" />}
|
||||
icon={<CurrencyIcon className="size-6" />}
|
||||
title={t("proformas.update.totals.title", "Totales")}
|
||||
>
|
||||
<FormSectionGrid className="gap-5 md:grid-cols-1">
|
||||
|
||||
@ -172,12 +172,21 @@ export const ProformaUpdateCustomerChangeDialog = ({
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<Button disabled={isSubmitting} onClick={() => onOpenChange(false)} variant="outline">
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
id="proforma-customer-change-cancel"
|
||||
onClick={() => onOpenChange(false)}
|
||||
variant="outline"
|
||||
>
|
||||
{t("proformas.update.customer_change.dialog.cancel", "Cancelar")}
|
||||
</Button>
|
||||
|
||||
{status === "compatible" ? (
|
||||
<Button disabled={isSubmitting} onClick={onConfirm}>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
id="proforma-customer-change-confirm"
|
||||
onClick={onConfirm}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Spinner aria-hidden className="mr-2 size-4" />
|
||||
|
||||
@ -28,7 +28,7 @@ export const ProformaUpdatePaymentEditor = ({
|
||||
<RdxPanel
|
||||
className={className}
|
||||
description={t("form_groups.proformas.payment.description", "Forma de pago")}
|
||||
icon={<CreditCardIcon className="size-5" />}
|
||||
icon={<CreditCardIcon className="size-6" />}
|
||||
title={t("form_groups.proformas.payment.title", "Condiciones de pago")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -22,7 +22,7 @@ export const ProformaUpdateSettingsEditor = ({
|
||||
className={className}
|
||||
description={t("form_groups.proformas.settings.description", "Configuración de la proforma")}
|
||||
disabled={disabled}
|
||||
icon={<Settings2Icon className="size-5" />}
|
||||
icon={<Settings2Icon className="size-6" />}
|
||||
title={t("form_groups.proformas.settings.title", "Configuración")}
|
||||
>
|
||||
<FormSectionGrid className="md:grid-cols-1">
|
||||
|
||||
@ -31,7 +31,7 @@ export const ProformaUpdateHeaderEditor = ({
|
||||
return (
|
||||
<RdxPanel
|
||||
className={className}
|
||||
icon={<FileTextIcon className="size-5" />}
|
||||
icon={<FileTextIcon className="size-6" />}
|
||||
title={t("form_groups.proformas.basic_info.title")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -38,7 +38,7 @@ export const ProformaUpdateRecipientEditor = ({
|
||||
"form_groups.proformas.customer.description",
|
||||
"Cliente destinatario de la proforma"
|
||||
)}
|
||||
icon={<UserIcon className="size-5" />}
|
||||
icon={<UserIcon className="size-6" />}
|
||||
title={t("form_groups.proformas.customer.title", "Cliente")}
|
||||
variant="card"
|
||||
>
|
||||
@ -60,6 +60,7 @@ export const ProformaUpdateRecipientEditor = ({
|
||||
<Button
|
||||
className="justify-center"
|
||||
disabled={disabled}
|
||||
id="proforma-change-customer"
|
||||
onClick={onChangeCustomerClick}
|
||||
type="button"
|
||||
variant={selectedCustomer ? "outline" : "default"}
|
||||
|
||||
@ -44,7 +44,7 @@ export const ProformaUpdateTaxEditor = ({
|
||||
"form_groups.proformas.taxes.description",
|
||||
"Configura los valores fiscales por defecto de la proforma. Los impuestos aplicados y los totales se calculan desde las líneas valoradas."
|
||||
)}
|
||||
icon={<LandmarkIcon className="size-5" />}
|
||||
icon={<LandmarkIcon className="size-6" />}
|
||||
title={t("form_groups.proformas.taxes.title", "Configuración fiscal")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -30,7 +30,7 @@ export const CompanyVerifactuConfigurationSection = ({
|
||||
return (
|
||||
<RdxPanel
|
||||
description={t("settings.verifactu.configuration.description")}
|
||||
icon={<ShieldCheckIcon className="size-5" />}
|
||||
icon={<ShieldCheckIcon className="size-6" />}
|
||||
title={t("settings.verifactu.configuration.title")}
|
||||
variant="plain"
|
||||
>
|
||||
|
||||
@ -19,7 +19,7 @@ export const CompanyVerifactuDefaultsSection = ({
|
||||
return (
|
||||
<RdxPanel
|
||||
description={t("settings.verifactu.defaults.description")}
|
||||
icon={<LandmarkIcon className="size-5" />}
|
||||
icon={<LandmarkIcon className="size-6" />}
|
||||
title={t("settings.verifactu.defaults.title")}
|
||||
variant="plain"
|
||||
>
|
||||
|
||||
@ -21,7 +21,7 @@ export const CompanyVerifactuStatusSection = () => {
|
||||
return (
|
||||
<RdxPanel
|
||||
description={t("settings.verifactu.status.description")}
|
||||
icon={<FileCogIcon className="size-5" />}
|
||||
icon={<FileCogIcon className="size-6" />}
|
||||
title={t("settings.verifactu.status.title")}
|
||||
variant="plain"
|
||||
>
|
||||
|
||||
@ -18,7 +18,7 @@ export const CustomerAdditionalConfigEditor = ({
|
||||
return (
|
||||
<RdxPanel
|
||||
description={t("form_groups.preferences.description")}
|
||||
icon={<Settings2Icon className="size-5" />}
|
||||
icon={<Settings2Icon className="size-6" />}
|
||||
title={t("form_groups.preferences.title")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -18,7 +18,7 @@ export const CustomerAddressEditor = ({
|
||||
return (
|
||||
<RdxPanel
|
||||
description={t("form_groups.address.description")}
|
||||
icon={<MapPinIcon className="size-5" />}
|
||||
icon={<MapPinIcon className="size-6" />}
|
||||
title={t("form_groups.address.title")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -34,7 +34,7 @@ export const CustomerBasicInfoEditor = ({
|
||||
return (
|
||||
<RdxPanel
|
||||
description={t("form_groups.basic_info.description")}
|
||||
icon={<FileTextIcon className="size-5" />}
|
||||
icon={<FileTextIcon className="size-6" />}
|
||||
title={t("form_groups.basic_info.title")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -25,7 +25,7 @@ export const CustomerContactEditor = ({
|
||||
return (
|
||||
<RdxPanel
|
||||
description={t("form_groups.contact_info.description")}
|
||||
icon={<PhoneIcon className="size-5" />}
|
||||
icon={<PhoneIcon className="size-6" />}
|
||||
title={t("form_groups.contact_info.title")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -29,7 +29,7 @@ export const CustomerFiscalEditor = ({
|
||||
return (
|
||||
<RdxPanel
|
||||
description={t("form_groups.fiscal_info.description")}
|
||||
icon={<LandmarkIcon className="size-5" />}
|
||||
icon={<LandmarkIcon className="size-6" />}
|
||||
title={t("form_groups.fiscal_info.title")}
|
||||
variant="card"
|
||||
>
|
||||
|
||||
@ -60,7 +60,7 @@ export const SupplierEditorForm = ({
|
||||
<form className="flex min-w-0 flex-col gap-6" id={formId} noValidate onSubmit={onSubmit}>
|
||||
<RdxPanel
|
||||
description={t("form.sections.identity.description")}
|
||||
icon={<FileTextIcon className="size-5" />}
|
||||
icon={<FileTextIcon className="size-6" />}
|
||||
title={t("form.sections.identity.title")}
|
||||
>
|
||||
<RdxFormGrid className="md:grid-cols-2">
|
||||
@ -109,7 +109,7 @@ export const SupplierEditorForm = ({
|
||||
|
||||
<RdxPanel
|
||||
description={t("form.sections.address.description")}
|
||||
icon={<MapPinIcon className="size-5" />}
|
||||
icon={<MapPinIcon className="size-6" />}
|
||||
title={t("form.sections.address.title")}
|
||||
>
|
||||
<RdxFormGrid className="md:grid-cols-2">
|
||||
@ -129,7 +129,7 @@ export const SupplierEditorForm = ({
|
||||
|
||||
<RdxPanel
|
||||
description={t("form.sections.contact.description")}
|
||||
icon={<PhoneIcon className="size-5" />}
|
||||
icon={<PhoneIcon className="size-6" />}
|
||||
title={t("form.sections.contact.title")}
|
||||
>
|
||||
<RdxFormGrid className="md:grid-cols-2">
|
||||
@ -172,7 +172,7 @@ export const SupplierEditorForm = ({
|
||||
|
||||
<RdxPanel
|
||||
description={t("form.sections.preferences.description")}
|
||||
icon={<LanguagesIcon className="size-5" />}
|
||||
icon={<LanguagesIcon className="size-6" />}
|
||||
title={t("form.sections.preferences.title")}
|
||||
>
|
||||
<RdxFormGrid className="md:grid-cols-2">
|
||||
|
||||
@ -19,7 +19,9 @@
|
||||
"./components": "./src/components/index.ts",
|
||||
"./components/*": "./src/components/*.tsx",
|
||||
"./locales/*": "./src/locales/*",
|
||||
"./hooks": ["./src/hooks/index.ts"]
|
||||
"./hooks": [
|
||||
"./src/hooks/index.ts"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.4.11",
|
||||
@ -36,22 +38,22 @@
|
||||
"esbuild-plugin-react18-css": "^0.0.4",
|
||||
"rimraf": "^6.1.3",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"tsx": "^4.21.0",
|
||||
"tsup": "^8.5.1",
|
||||
"tsx": "^4.21.0",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"typescript": "^6.0.2",
|
||||
"typescript-plugin-css-modules": "^5.2.0",
|
||||
"vite-tsconfig-paths": "^6.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui/react": "^1.3.0",
|
||||
"@base-ui/react": "1.8.0",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@repo/i18next": "workspace:*",
|
||||
"@repo/shadcn-ui": "workspace:*",
|
||||
"@repo/rdx-utils": "workspace:*",
|
||||
"@repo/shadcn-ui": "workspace:*",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"cmdk": "^1.1.1",
|
||||
@ -67,4 +69,4 @@
|
||||
"sonner": "^2.0.7",
|
||||
"zod": "^4.3.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,12 +54,12 @@ export const RdxPanel = ({
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="flex min-w-0 items-start gap-3">
|
||||
{icon ? (
|
||||
<div className="flex size-6 shrink-0 items-center justify-center text-primary">
|
||||
<div className="flex size-8 shrink-0 items-center justify-center text-primary">
|
||||
{icon}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="min-w-0 space-y-1">
|
||||
<div className="min-w-0 space-y-0.5">
|
||||
<h2
|
||||
className="min-w-0 text-base font-semibold tracking-tight text-foreground"
|
||||
id={titleId}
|
||||
|
||||
@ -393,7 +393,7 @@ export function PDFViewer({
|
||||
contentClassName="space-y-3 px-2 sm:space-y-4 sm:px-4 md:space-y-5 md:px-6"
|
||||
description={description}
|
||||
headerClassName="px-2 sm:px-4 md:px-6"
|
||||
icon={<EyeIcon className="size-5" />}
|
||||
icon={<EyeIcon className="size-6" />}
|
||||
title={title}
|
||||
>
|
||||
{viewerContent}
|
||||
|
||||
@ -7,5 +7,5 @@ export const RdxBadge = ({
|
||||
className,
|
||||
...props
|
||||
}: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) => (
|
||||
<Badge className={cn("tracking-wide px-4 h-6 rounded-md", className)} {...props} />
|
||||
<Badge className={cn("tracking-wide px-3 h-6 rounded-lg", className)} {...props} />
|
||||
);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "@repo/shadcn-ui/globals.css",
|
||||
"css": "src/styles/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
@ -21,4 +21,4 @@
|
||||
"rtl": false,
|
||||
"menuColor": "default",
|
||||
"menuAccent": "subtle"
|
||||
}
|
||||
}
|
||||
@ -39,11 +39,10 @@
|
||||
"typescript": "^6.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui/react": "^1.3.0",
|
||||
"@base-ui/react": "^1.8.0",
|
||||
"@fontsource-variable/geist": "^5.2.8",
|
||||
"@fontsource-variable/geist-mono": "^5.2.7",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-toggle-group": "^1.1.11",
|
||||
"@shadcn/react": "^0.3.1",
|
||||
"add": "^2.0.6",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@ -57,7 +56,6 @@
|
||||
"lucide-react": "^1.8.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"pnpm": "^10.33.0",
|
||||
"radix-ui": "latest",
|
||||
"react-day-picker": "10.0.1",
|
||||
"react-hook-form": "^7.72.1",
|
||||
"react-resizable-panels": "^4.9.0",
|
||||
@ -66,7 +64,6 @@
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^4.3.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Label } from "@repo/shadcn-ui/components/label";
|
||||
import { useRender } from "@base-ui/react/use-render";
|
||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
import * as React from "react";
|
||||
import {
|
||||
@ -82,39 +82,55 @@ const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivEl
|
||||
);
|
||||
FormItem.displayName = "FormItem";
|
||||
|
||||
const FormLabel = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { error, formItemId } = useFormField();
|
||||
const FormLabel = React.forwardRef<HTMLLabelElement, useRender.ComponentProps<"label">>(
|
||||
({ className, render, ...props }, ref) => {
|
||||
const { error, formItemId } = useFormField();
|
||||
|
||||
return (
|
||||
<Label
|
||||
className={cn(error && "text-destructive", className)}
|
||||
htmlFor={formItemId}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return useRender({
|
||||
defaultTagName: "label",
|
||||
props: {
|
||||
className: cn(
|
||||
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||
error && "text-destructive",
|
||||
className
|
||||
),
|
||||
...props,
|
||||
htmlFor: formItemId,
|
||||
},
|
||||
ref,
|
||||
render,
|
||||
state: {
|
||||
slot: "label",
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
FormLabel.displayName = "FormLabel";
|
||||
|
||||
const FormControl = React.forwardRef<
|
||||
React.ElementRef<typeof Slot>,
|
||||
React.ComponentPropsWithoutRef<typeof Slot>
|
||||
>(({ ...props }, ref) => {
|
||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
||||
type FormControlProps = Omit<useRender.ComponentProps<"div">, "children" | "render"> & {
|
||||
children: React.ReactElement;
|
||||
};
|
||||
|
||||
return (
|
||||
<Slot
|
||||
aria-describedby={error ? `${formDescriptionId} ${formMessageId}` : `${formDescriptionId}`}
|
||||
aria-invalid={!!error}
|
||||
id={formItemId}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
const FormControl = React.forwardRef<HTMLElement, FormControlProps>(
|
||||
({ children, ...props }, ref) => {
|
||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
||||
|
||||
return useRender({
|
||||
defaultTagName: "div",
|
||||
props: {
|
||||
"aria-describedby": error ? `${formDescriptionId} ${formMessageId}` : formDescriptionId,
|
||||
"aria-invalid": !!error,
|
||||
id: formItemId,
|
||||
...props,
|
||||
},
|
||||
ref,
|
||||
render: children,
|
||||
state: {
|
||||
slot: "form-control",
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
FormControl.displayName = "FormControl";
|
||||
|
||||
const FormDescription = React.forwardRef<
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
@import "@fontsource-variable/geist-mono";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@source "../../../../apps/**/*.{ts,tsx}";
|
||||
@source "../../../../modules/**/*.{ts,tsx}";
|
||||
@source "../../../../packages/**/*.{ts,tsx}";
|
||||
@ -66,7 +67,7 @@
|
||||
--font-serif: "Geist Variable", serif;
|
||||
--font-mono: "Geist Mono Variable", monospace;
|
||||
|
||||
--radius: 0.625rem;
|
||||
--radius: 0.45rem;
|
||||
|
||||
--shadow-2xs: 0 1px 3px 0px rgb(0 0 0 / 0.05);
|
||||
--shadow-xs: 0 1px 3px 0px rgb(0 0 0 / 0.05);
|
||||
@ -185,10 +186,13 @@
|
||||
--font-mono: var(--font-mono);
|
||||
--font-serif: var(--font-serif);
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-sm: calc(var(--radius) * 0.6);
|
||||
--radius-md: calc(var(--radius) * 0.8);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--radius-xl: calc(var(--radius) * 1.4);
|
||||
--radius-2xl: calc(var(--radius) * 1.8);
|
||||
--radius-3xl: calc(var(--radius) * 2.2);
|
||||
--radius-4xl: calc(var(--radius) * 2.6);
|
||||
|
||||
--shadow-2xs: var(--shadow-2xs);
|
||||
--shadow-xs: var(--shadow-xs);
|
||||
@ -212,4 +216,8 @@
|
||||
[role="button"]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
html {
|
||||
@apply font-sans;
|
||||
}
|
||||
}
|
||||
|
||||
1725
pnpm-lock.yaml
1725
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
104
tests/e2e/customer-invoices/proformas/proforma-customer.spec.ts
Normal file
104
tests/e2e/customer-invoices/proformas/proforma-customer.spec.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
import type { Page } from "@playwright/test";
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { createCustomerViaUi } from "../../customers/support/create-customer-via-ui";
|
||||
import { loginViaUi } from "../../support/login-via-ui";
|
||||
|
||||
import { createProformaViaUi } from "./support/create-proforma-via-ui";
|
||||
|
||||
const selectCandidate = async (page: Page, customer: { id: string; search: string }) => {
|
||||
await page.locator("#proforma-change-customer").click();
|
||||
const responsePromise = page.waitForResponse((response) => {
|
||||
const url = new URL(response.url());
|
||||
return (
|
||||
response.request().method() === "GET" &&
|
||||
/\/customers\/?$/.test(url.pathname) &&
|
||||
url.searchParams.get("q") === customer.search
|
||||
);
|
||||
});
|
||||
await page.getByRole("dialog").locator('input[inputmode="search"]').fill(customer.search);
|
||||
const response = await responsePromise;
|
||||
expect(response.status()).toBe(200);
|
||||
await page.getByTestId(`customer-selection-${customer.id}`).click();
|
||||
await page.locator("#customer-selection-confirm").click();
|
||||
};
|
||||
|
||||
const confirmCandidate = async (page: Page) => {
|
||||
await expect(page.locator("#proforma-customer-change-confirm")).toBeVisible();
|
||||
await page.locator("#proforma-customer-change-confirm").click();
|
||||
};
|
||||
|
||||
const save = async (page: Page, id: string) => {
|
||||
const response = page.waitForResponse(
|
||||
(value) =>
|
||||
value.request().method() === "PUT" &&
|
||||
new URL(value.url()).pathname.endsWith(`/proformas/${id}`)
|
||||
);
|
||||
await page.getByTestId("proforma-update-submit").click();
|
||||
const result = await response;
|
||||
expect(result.status()).toBe(200);
|
||||
};
|
||||
|
||||
const returnToProforma = async (page: Page, id: string) => {
|
||||
await page.goto(`/proformas/${id}/edit?returnTo=/proformas`);
|
||||
await expect(page.locator("#proforma-change-customer")).toBeVisible();
|
||||
};
|
||||
|
||||
test("CUSTOMER-INVOICES-PROFORMA-CUSTOMER-001: conserva el cliente seleccionado al crear", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await loginViaUi(page);
|
||||
const proforma = await createProformaViaUi(page, testInfo);
|
||||
await page.reload();
|
||||
await expect(page.locator("#proforma-recipient-name")).toHaveText(proforma.customer.name);
|
||||
});
|
||||
|
||||
test("CUSTOMER-INVOICES-PROFORMA-CUSTOMER-002: cambia de cliente y lo persiste", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await loginViaUi(page);
|
||||
const proforma = await createProformaViaUi(page, testInfo);
|
||||
const search = randomUUID().replaceAll("-", "");
|
||||
const customer = await createCustomerViaUi(page, testInfo, `E2E Customer B ${search}`);
|
||||
await returnToProforma(page, proforma.id);
|
||||
await selectCandidate(page, { id: customer.id, search });
|
||||
await confirmCandidate(page);
|
||||
await expect(page.locator("#proforma-recipient-name")).toHaveText(customer.name);
|
||||
await save(page, proforma.id);
|
||||
await page.reload();
|
||||
await expect(page.locator("#proforma-recipient-name")).toHaveText(customer.name);
|
||||
});
|
||||
|
||||
test("CUSTOMER-INVOICES-PROFORMA-CUSTOMER-003: confirma el cambio y refresca el recipient", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await loginViaUi(page);
|
||||
const proforma = await createProformaViaUi(page, testInfo);
|
||||
const search = randomUUID().replaceAll("-", "");
|
||||
const customer = await createCustomerViaUi(page, testInfo, `E2E Customer Derived ${search}`);
|
||||
await returnToProforma(page, proforma.id);
|
||||
await selectCandidate(page, { id: customer.id, search });
|
||||
await confirmCandidate(page);
|
||||
await expect(page.locator("#proforma-recipient-name")).toHaveText(customer.name);
|
||||
await save(page, proforma.id);
|
||||
await page.reload();
|
||||
await expect(page.locator("#proforma-recipient-name")).toHaveText(customer.name);
|
||||
});
|
||||
|
||||
test("CUSTOMER-INVOICES-PROFORMA-CUSTOMER-004: cancelar conserva el cliente anterior", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await loginViaUi(page);
|
||||
const proforma = await createProformaViaUi(page, testInfo);
|
||||
const search = randomUUID().replaceAll("-", "");
|
||||
const customer = await createCustomerViaUi(page, testInfo, `E2E Customer Cancel ${search}`);
|
||||
await returnToProforma(page, proforma.id);
|
||||
await selectCandidate(page, { id: customer.id, search });
|
||||
await expect(page.locator("#proforma-customer-change-cancel")).toBeVisible();
|
||||
await page.locator("#proforma-customer-change-cancel").click();
|
||||
await expect(page.locator("#proforma-recipient-name")).toHaveText(proforma.customer.name);
|
||||
await page.reload();
|
||||
await expect(page.locator("#proforma-recipient-name")).toHaveText(proforma.customer.name);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user