.
This commit is contained in:
parent
520373cbd6
commit
700445499b
@ -13,6 +13,7 @@ import type {
|
|||||||
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders/full";
|
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders/full";
|
||||||
import { GetProformaByIdUseCase, ListProformasUseCase, ReportProformaUseCase } from "../use-cases";
|
import { GetProformaByIdUseCase, ListProformasUseCase, ReportProformaUseCase } from "../use-cases";
|
||||||
import { CreateProformaUseCase } from "../use-cases/create-proforma";
|
import { CreateProformaUseCase } from "../use-cases/create-proforma";
|
||||||
|
import { IssueProformaUseCase } from "../use-cases/issue-proforma.use-case";
|
||||||
|
|
||||||
export function buildGetProformaByIdUseCase(deps: {
|
export function buildGetProformaByIdUseCase(deps: {
|
||||||
finder: IProformaFinder;
|
finder: IProformaFinder;
|
||||||
@ -64,6 +65,10 @@ export function buildCreateProformaUseCase(deps: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildIssueProformaUseCase(deps: { finder: IProformaFinder }) {
|
||||||
|
return new IssueProformaUseCase(deps.finder);
|
||||||
|
}
|
||||||
|
|
||||||
/*export function buildUpdateProformaUseCase(deps: {
|
/*export function buildUpdateProformaUseCase(deps: {
|
||||||
finder: IProformaFinder;
|
finder: IProformaFinder;
|
||||||
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||||
@ -75,9 +80,6 @@ export function buildDeleteProformaUseCase(deps: { finder: IProformaFinder }) {
|
|||||||
return new DeleteProformaUseCase(deps.finder);
|
return new DeleteProformaUseCase(deps.finder);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildIssueProformaUseCase(deps: { finder: IProformaFinder }) {
|
|
||||||
return new IssueProformaUseCase(deps.finder);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildChangeStatusProformaUseCase(deps: {
|
export function buildChangeStatusProformaUseCase(deps: {
|
||||||
finder: IProformaFinder;
|
finder: IProformaFinder;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
export * from "./create-proforma";
|
export * from "./create-proforma";
|
||||||
//export * from "./delete-proforma.use-case";
|
//export * from "./delete-proforma.use-case";
|
||||||
export * from "./get-proforma-by-id.use-case";
|
export * from "./get-proforma-by-id.use-case";
|
||||||
//export * from "./issue-proforma.use-case";
|
export * from "./issue-proforma.use-case";
|
||||||
export * from "./list-proformas.use-case";
|
export * from "./list-proformas.use-case";
|
||||||
export * from "./report-proforma.use-case";
|
export * from "./report-proforma.use-case";
|
||||||
//export * from "./update-proforma";
|
//export * from "./update-proforma";
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
CreateProformaRequestSchema,
|
CreateProformaRequestSchema,
|
||||||
GetProformaByIdRequestSchema,
|
GetProformaByIdRequestSchema,
|
||||||
|
IssueProformaByIdParamsRequestSchema,
|
||||||
ListProformasRequestSchema,
|
ListProformasRequestSchema,
|
||||||
ReportProformaByIdParamsRequestSchema,
|
ReportProformaByIdParamsRequestSchema,
|
||||||
ReportProformaByIdQueryRequestSchema,
|
ReportProformaByIdQueryRequestSchema,
|
||||||
@ -129,7 +130,7 @@ export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDep
|
|||||||
const controller = new ChangeStatusProformaController(useCase);
|
const controller = new ChangeStatusProformaController(useCase);
|
||||||
return controller.execute(req, res, next);
|
return controller.execute(req, res, next);
|
||||||
}
|
}
|
||||||
);
|
);*/
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/:proforma_id/issue",
|
"/:proforma_id/issue",
|
||||||
@ -142,7 +143,7 @@ export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDep
|
|||||||
const controller = new IssuedProformaController(useCase);
|
const controller = new IssuedProformaController(useCase);
|
||||||
return controller.execute(req, res, next);
|
return controller.execute(req, res, next);
|
||||||
}
|
}
|
||||||
);*/
|
);
|
||||||
|
|
||||||
app.use(`${config.server.apiBasePath}/proformas`, router);
|
app.use(`${config.server.apiBasePath}/proformas`, router);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export class SequelizeProformaNumberGenerator implements IProformaNumberGenerato
|
|||||||
): Promise<Result<InvoiceNumber, Error>> {
|
): Promise<Result<InvoiceNumber, Error>> {
|
||||||
const where: WhereOptions = {
|
const where: WhereOptions = {
|
||||||
company_id: companyId.toString(),
|
company_id: companyId.toString(),
|
||||||
is_proforma: false,
|
is_proforma: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
series.match(
|
series.match(
|
||||||
@ -41,12 +41,12 @@ export class SequelizeProformaNumberGenerator implements IProformaNumberGenerato
|
|||||||
lock: transaction.LOCK.UPDATE, // requiere InnoDB y TX abierta
|
lock: transaction.LOCK.UPDATE, // requiere InnoDB y TX abierta
|
||||||
});
|
});
|
||||||
|
|
||||||
let nextValue = "001"; // valor inicial por defecto
|
let nextValue = "0001"; // valor inicial por defecto
|
||||||
|
|
||||||
if (lastInvoice) {
|
if (lastInvoice) {
|
||||||
const current = Number(lastInvoice.invoice_number);
|
const current = Number(lastInvoice.invoice_number);
|
||||||
const next = Number.isFinite(current) && current > 0 ? current + 1 : 1;
|
const next = Number.isFinite(current) && current > 0 ? current + 1 : 1;
|
||||||
nextValue = String(next).padStart(3, "0");
|
nextValue = String(next).padStart(4, "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberResult = InvoiceNumber.create(nextValue);
|
const numberResult = InvoiceNumber.create(nextValue);
|
||||||
|
|||||||
@ -226,6 +226,8 @@ export function useProformasGridColumns(
|
|||||||
const availableTransitions =
|
const availableTransitions =
|
||||||
PROFORMA_STATUS_TRANSITIONS[proforma.status as ProformaStatus] ?? [];
|
PROFORMA_STATUS_TRANSITIONS[proforma.status as ProformaStatus] ?? [];
|
||||||
|
|
||||||
|
console.log(availableTransitions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{!isIssued && actionHandlers.onEditClick && (
|
{!isIssued && actionHandlers.onEditClick && (
|
||||||
|
|||||||
@ -36,10 +36,10 @@ export const ProformaListPage = () => {
|
|||||||
} = useProformaListPageController();
|
} = useProformaListPageController();
|
||||||
|
|
||||||
const columns = useProformasGridColumns({
|
const columns = useProformasGridColumns({
|
||||||
//onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`),
|
onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`),
|
||||||
//onIssueClick: handleIssueProforma,
|
onIssueClick: handleIssueProforma,
|
||||||
//onDeleteClick: handleDeleteProforma,
|
onDeleteClick: handleDeleteProforma,
|
||||||
//onChangeStatusClick: handleChangeStatusProforma,
|
onChangeStatusClick: handleChangeStatusProforma,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (listCtrl.isError || !listCtrl.data) {
|
if (listCtrl.isError || !listCtrl.data) {
|
||||||
|
|||||||
@ -80,8 +80,8 @@
|
|||||||
"description": "The street address of the customer"
|
"description": "The street address of the customer"
|
||||||
},
|
},
|
||||||
"street2": {
|
"street2": {
|
||||||
"label": "Street",
|
"label": "Street 2",
|
||||||
"placeholder": "Enter street",
|
"placeholder": "Enter street 2",
|
||||||
"description": "The street address of the customer"
|
"description": "The street address of the customer"
|
||||||
},
|
},
|
||||||
"city": {
|
"city": {
|
||||||
@ -114,7 +114,6 @@
|
|||||||
"placeholder": "Enter secondary email",
|
"placeholder": "Enter secondary email",
|
||||||
"description": "The secondary email address of the customer"
|
"description": "The secondary email address of the customer"
|
||||||
},
|
},
|
||||||
|
|
||||||
"phone_primary": {
|
"phone_primary": {
|
||||||
"label": "Primary phone",
|
"label": "Primary phone",
|
||||||
"placeholder": "Enter primary phone number",
|
"placeholder": "Enter primary phone number",
|
||||||
@ -125,7 +124,6 @@
|
|||||||
"placeholder": "Enter secondary phone number ",
|
"placeholder": "Enter secondary phone number ",
|
||||||
"description": "The secondary phone number of the customer"
|
"description": "The secondary phone number of the customer"
|
||||||
},
|
},
|
||||||
|
|
||||||
"mobile_primary": {
|
"mobile_primary": {
|
||||||
"label": "Primary mobile",
|
"label": "Primary mobile",
|
||||||
"placeholder": "Enter primary mobile number",
|
"placeholder": "Enter primary mobile number",
|
||||||
|
|||||||
@ -82,7 +82,7 @@
|
|||||||
"description": "La dirección de la calle del cliente"
|
"description": "La dirección de la calle del cliente"
|
||||||
},
|
},
|
||||||
"street2": {
|
"street2": {
|
||||||
"label": "Calle",
|
"label": "Calle (ampliación)",
|
||||||
"placeholder": "Ingrese la calle",
|
"placeholder": "Ingrese la calle",
|
||||||
"description": "La dirección de la calle del cliente"
|
"description": "La dirección de la calle del cliente"
|
||||||
},
|
},
|
||||||
@ -106,67 +106,56 @@
|
|||||||
"placeholder": "Seleccione el país",
|
"placeholder": "Seleccione el país",
|
||||||
"description": "El país del cliente"
|
"description": "El país del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"email_primary": {
|
"email_primary": {
|
||||||
"label": "Email principal",
|
"label": "Email principal",
|
||||||
"placeholder": "Ingrese el correo electrónico",
|
"placeholder": "Ingrese el correo electrónico",
|
||||||
"description": "La dirección de correo electrónico principal del cliente"
|
"description": "La dirección de correo electrónico principal del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"email_secondary": {
|
"email_secondary": {
|
||||||
"label": "Email secundario",
|
"label": "Email secundario",
|
||||||
"placeholder": "Ingrese el correo electrónico",
|
"placeholder": "Ingrese el correo electrónico",
|
||||||
"description": "La dirección de correo electrónico secundario del clientºe"
|
"description": "La dirección de correo electrónico secundario del clientºe"
|
||||||
},
|
},
|
||||||
|
|
||||||
"phone_primary": {
|
"phone_primary": {
|
||||||
"label": "Teléfono",
|
"label": "Teléfono",
|
||||||
"placeholder": "Ingrese el número de teléfono",
|
"placeholder": "Ingrese el número de teléfono",
|
||||||
"description": "El número de teléfono del cliente"
|
"description": "El número de teléfono del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"phone_secondary": {
|
"phone_secondary": {
|
||||||
"label": "Teléfono secundario",
|
"label": "Teléfono secundario",
|
||||||
"placeholder": "Ingrese el número de teléfono secundario",
|
"placeholder": "Ingrese el número de teléfono secundario",
|
||||||
"description": "El número de teléfono secundario del cliente"
|
"description": "El número de teléfono secundario del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"mobile_primary": {
|
"mobile_primary": {
|
||||||
"label": "Teléfono",
|
"label": "Teléfono",
|
||||||
"placeholder": "Ingrese el número de teléfono",
|
"placeholder": "Ingrese el número de teléfono",
|
||||||
"description": "El número de teléfono del cliente"
|
"description": "El número de teléfono del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"mobile_secondary": {
|
"mobile_secondary": {
|
||||||
"label": "Teléfono secundario",
|
"label": "Teléfono secundario",
|
||||||
"placeholder": "Ingrese el número de teléfono secundario",
|
"placeholder": "Ingrese el número de teléfono secundario",
|
||||||
"description": "El número de teléfono secundario del cliente"
|
"description": "El número de teléfono secundario del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"fax": {
|
"fax": {
|
||||||
"label": "Fax",
|
"label": "Fax",
|
||||||
"placeholder": "Ingrese el número de fax",
|
"placeholder": "Ingrese el número de fax",
|
||||||
"description": "El número de fax del cliente"
|
"description": "El número de fax del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"website": {
|
"website": {
|
||||||
"label": "Sitio web",
|
"label": "Sitio web",
|
||||||
"placeholder": "Ingrese la URL del sitio web",
|
"placeholder": "Ingrese la URL del sitio web",
|
||||||
"description": "El sitio web del cliente"
|
"description": "El sitio web del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"default_taxes": {
|
"default_taxes": {
|
||||||
"label": "Impuesto por defecto",
|
"label": "Impuesto por defecto",
|
||||||
"placeholder": "Seleccione el impuesto por defecto",
|
"placeholder": "Seleccione el impuesto por defecto",
|
||||||
"description": "La tasa de impuesto por defecto para el cliente"
|
"description": "La tasa de impuesto por defecto para el cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"language_code": {
|
"language_code": {
|
||||||
"label": "Idioma",
|
"label": "Idioma",
|
||||||
"placeholder": "Seleccione el idioma",
|
"placeholder": "Seleccione el idioma",
|
||||||
"description": "El idioma preferido del cliente"
|
"description": "El idioma preferido del cliente"
|
||||||
},
|
},
|
||||||
|
|
||||||
"currency_code": {
|
"currency_code": {
|
||||||
"label": "Moneda",
|
"label": "Moneda",
|
||||||
"placeholder": "Seleccione la moneda",
|
"placeholder": "Seleccione la moneda",
|
||||||
|
|||||||
@ -55,7 +55,8 @@ export const CustomerBasicInfoFields = ({
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field className="lg:col-span-2">
|
|
||||||
|
<Field className="lg:col-span-1 lg:col-start-1">
|
||||||
<FormField
|
<FormField
|
||||||
control={control}
|
control={control}
|
||||||
name="is_company"
|
name="is_company"
|
||||||
@ -94,6 +95,17 @@ export const CustomerBasicInfoFields = ({
|
|||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<Field className="lg:col-span-1 " ref={focusRef}>
|
||||||
|
<TextField
|
||||||
|
control={control}
|
||||||
|
description={t("form_fields.tin.description")}
|
||||||
|
label={t("form_fields.tin.label")}
|
||||||
|
name="tin"
|
||||||
|
placeholder={t("form_fields.tin.placeholder")}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
className="lg:col-span-full"
|
className="lg:col-span-full"
|
||||||
control={control}
|
control={control}
|
||||||
@ -118,10 +130,7 @@ export const CustomerBasicInfoFields = ({
|
|||||||
name="default_taxes"
|
name="default_taxes"
|
||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<Field className={"gap-1"} data-invalid={fieldState.invalid}>
|
<Field className={"gap-1"} data-invalid={fieldState.invalid}>
|
||||||
<FieldLabel
|
<FieldLabel htmlFor={"default_taxes"}>
|
||||||
className="text-xs text-muted-foreground text-nowrap"
|
|
||||||
htmlFor={"default_taxes"}
|
|
||||||
>
|
|
||||||
{t("form_fields.default_taxes.label")}
|
{t("form_fields.default_taxes.label")}
|
||||||
</FieldLabel>
|
</FieldLabel>
|
||||||
<CustomerTaxesMultiSelect
|
<CustomerTaxesMultiSelect
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { FormDebug } from "@erp/core/components";
|
||||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||||
|
|
||||||
import { CustomerAdditionalConfigFields } from "./customer-additional-config-fields";
|
import { CustomerAdditionalConfigFields } from "./customer-additional-config-fields";
|
||||||
@ -15,6 +16,7 @@ type CustomerFormProps = {
|
|||||||
export const CustomerEditForm = ({ formId, onSubmit, className, focusRef }: CustomerFormProps) => {
|
export const CustomerEditForm = ({ formId, onSubmit, className, focusRef }: CustomerFormProps) => {
|
||||||
return (
|
return (
|
||||||
<form id={formId} noValidate onSubmit={onSubmit}>
|
<form id={formId} noValidate onSubmit={onSubmit}>
|
||||||
|
<FormDebug enabled />
|
||||||
<section className={cn("space-y-12 p-6", className)}>
|
<section className={cn("space-y-12 p-6", className)}>
|
||||||
<CustomerBasicInfoFields focusRef={focusRef} />
|
<CustomerBasicInfoFields focusRef={focusRef} />
|
||||||
<CustomerAddressFields />
|
<CustomerAddressFields />
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { DataTableColumnHeader } from "@repo/rdx-ui/components";
|
|||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
AvatarFallback,
|
AvatarFallback,
|
||||||
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@ -11,15 +12,12 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@repo/shadcn-ui/components";
|
} from "@repo/shadcn-ui/components";
|
||||||
import type { ColumnDef } from "@tanstack/react-table";
|
import type { ColumnDef } from "@tanstack/react-table";
|
||||||
import { EyeIcon, MoreHorizontalIcon } from "lucide-react";
|
import { Building2Icon, EyeIcon, MoreHorizontalIcon, UserIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { useTranslation } from "../../../../i18n";
|
import { useTranslation } from "../../../../i18n";
|
||||||
import { CustomerStatusBadge } from "../../../../ui";
|
|
||||||
import type { CustomerSummaryData } from "../../../types";
|
import type { CustomerSummaryData } from "../../../types";
|
||||||
import { AddressCell, ContactCell, Initials } from "../../components";
|
import { AddressCell, ContactCell, Initials } from "../../components";
|
||||||
import { KindBadge } from "../../components/kind-badge";
|
|
||||||
import { Soft } from "../../components/soft";
|
|
||||||
|
|
||||||
type GridActionHandlers = {
|
type GridActionHandlers = {
|
||||||
onEditClick?: (customer: CustomerSummaryData) => void;
|
onEditClick?: (customer: CustomerSummaryData) => void;
|
||||||
@ -81,8 +79,48 @@ export function useCustomersGridColumns(
|
|||||||
const customer = row.original;
|
const customer = row.original;
|
||||||
const isCompany = String(customer.is_company).toLowerCase() === "true";
|
const isCompany = String(customer.is_company).toLowerCase() === "true";
|
||||||
return (
|
return (
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<Avatar className="size-10 border-2 border-background shadow-sm">
|
||||||
|
<AvatarFallback
|
||||||
|
className={
|
||||||
|
customer.status === "active"
|
||||||
|
? "bg-blue-100 text-blue-700"
|
||||||
|
: "bg-muted text-muted-foreground"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Initials name={customer.name} />
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-medium text-foreground">{customer.name}</span>
|
||||||
|
{customer.trade_name && (
|
||||||
|
<span className="text-muted-foreground">({customer.trade_name})</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
|
<span className="font-mono">{customer.tin}</span>
|
||||||
|
<Badge className="gap-1 px-1.5 py-0 text-xs font-normal" variant="outline">
|
||||||
|
{customer.is_company ? (
|
||||||
|
<>
|
||||||
|
<Building2Icon className="size-3" />
|
||||||
|
Empresa
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<UserIcon className="size-3" />
|
||||||
|
Particular
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
/*return (
|
||||||
<div className="flex items-start gap-1 my-1.5">
|
<div className="flex items-start gap-1 my-1.5">
|
||||||
<Avatar className="size-10 hidden">
|
<Avatar className="size-10">
|
||||||
<AvatarFallback aria-label={customer.name}>
|
<AvatarFallback aria-label={customer.name}>
|
||||||
<Initials name={customer.name} />
|
<Initials name={customer.name} />
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
@ -99,7 +137,7 @@ export function useCustomersGridColumns(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);*/
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,30 @@
|
|||||||
import type { CustomerSummaryData } from "../../types";
|
import { MapPinIcon } from "lucide-react";
|
||||||
|
|
||||||
import { Soft } from "./soft";
|
import type { CustomerSummaryData } from "../../types";
|
||||||
|
|
||||||
export const AddressCell = ({ customer }: { customer: CustomerSummaryData }) => {
|
export const AddressCell = ({ customer }: { customer: CustomerSummaryData }) => {
|
||||||
const line1 = [customer.street, customer.street2].filter(Boolean).join(", ");
|
const line1 = [customer.street, customer.street2].filter(Boolean).join(", ");
|
||||||
const line2 = [customer.postal_code, customer.city].filter(Boolean).join(" ");
|
const line2 = [customer.postal_code, customer.city].filter(Boolean).join(" ");
|
||||||
const line3 = [customer.province, customer.country].filter(Boolean).join(", ");
|
const line3 = [customer.province, customer.country].filter(Boolean).join(", ");
|
||||||
return (
|
return (
|
||||||
|
<address className="not-italic flex items-start gap-2">
|
||||||
|
<MapPinIcon className="mt-0.5 size-3.5" />
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
<p>{line1}</p>
|
||||||
|
<p>
|
||||||
|
{line2} · {line3}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</address>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
<address className="not-italic grid gap-1 text-foreground text-sm">
|
<address className="not-italic grid gap-1 text-foreground text-sm">
|
||||||
<div>{line1 || <Soft>-</Soft>}</div>
|
<div>{line1 || <Soft>-</Soft>}</div>
|
||||||
<div>{[line2, line3].filter(Boolean).join(" • ")}</div>
|
<div>{[line2, line3].filter(Boolean).join(" • ")}</div>
|
||||||
</address>
|
</address>
|
||||||
);
|
|
||||||
};
|
|
||||||
|
*/
|
||||||
|
|||||||
@ -2,9 +2,42 @@ import { MailIcon, PhoneIcon } from "lucide-react";
|
|||||||
|
|
||||||
import type { CustomerSummaryData } from "../../types";
|
import type { CustomerSummaryData } from "../../types";
|
||||||
|
|
||||||
import { Soft } from "./soft";
|
|
||||||
|
|
||||||
export const ContactCell = ({ customer }: { customer: CustomerSummaryData }) => (
|
export const ContactCell = ({ customer }: { customer: CustomerSummaryData }) => (
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<a
|
||||||
|
className="flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
||||||
|
href={`mailto:${customer.email_primary}`}
|
||||||
|
>
|
||||||
|
<MailIcon className="size-3.5" />
|
||||||
|
{customer.email_primary}
|
||||||
|
</a>
|
||||||
|
{customer.email_secondary && (
|
||||||
|
<a
|
||||||
|
className="flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
||||||
|
href={`mailto:${customer.email_secondary}`}
|
||||||
|
>
|
||||||
|
<MailIcon className="size-3.5" />
|
||||||
|
{customer.email_secondary}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{customer.phone_primary ? (
|
||||||
|
<a
|
||||||
|
className="flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
||||||
|
href={`tel:${customer.phone_primary}`}
|
||||||
|
>
|
||||||
|
<PhoneIcon className="size-3.5" />
|
||||||
|
{customer.phone_primary}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center gap-2 text-sm text-muted-foreground/50">
|
||||||
|
<PhoneIcon className="size-3.5" />-
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
<div className="grid gap-1 text-foreground text-sm my-1.5">
|
<div className="grid gap-1 text-foreground text-sm my-1.5">
|
||||||
{customer.email_primary && (
|
{customer.email_primary && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@ -31,4 +64,6 @@ export const ContactCell = ({ customer }: { customer: CustomerSummaryData }) =>
|
|||||||
</div>
|
</div>
|
||||||
{false}
|
{false}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
"typescript": "^5.6.0"
|
"typescript": "^5.6.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/geist": "^5.2.8",
|
"@fontsource-variable/inter": "^5.2.8",
|
||||||
"@hookform/resolvers": "^5.2.2",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"add": "^2.0.6",
|
"add": "^2.0.6",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "tw-animate-css";
|
@import "tw-animate-css";
|
||||||
@import "@fontsource-variable/geist";
|
@import "@fontsource-variable/inter";
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
@ -98,7 +98,7 @@
|
|||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
body {
|
body {
|
||||||
font-family: "Geist Variable", ui-sans-serif, sans-serif, system-ui;
|
font-family: "Inter", ui-sans-serif, sans-serif, system-ui;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +111,7 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--font-sans: "Geist Variable", ui-sans-serif, sans-serif, system-ui;
|
--font-sans: "Inter", ui-sans-serif, sans-serif, system-ui;
|
||||||
|
|
||||||
--background: oklch(1 0 0);
|
--background: oklch(1 0 0);
|
||||||
--foreground: oklch(0.145 0 0);
|
--foreground: oklch(0.145 0 0);
|
||||||
|
|||||||
@ -912,7 +912,7 @@ importers:
|
|||||||
|
|
||||||
packages/shadcn-ui:
|
packages/shadcn-ui:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fontsource-variable/geist':
|
'@fontsource-variable/inter':
|
||||||
specifier: ^5.2.8
|
specifier: ^5.2.8
|
||||||
version: 5.2.8
|
version: 5.2.8
|
||||||
'@hookform/resolvers':
|
'@hookform/resolvers':
|
||||||
@ -1597,8 +1597,8 @@ packages:
|
|||||||
'@floating-ui/utils@0.2.10':
|
'@floating-ui/utils@0.2.10':
|
||||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||||
|
|
||||||
'@fontsource-variable/geist@5.2.8':
|
'@fontsource-variable/inter@5.2.8':
|
||||||
resolution: {integrity: sha512-cJ6m9e+8MQ5dCYJsLylfZrgBh6KkG4bOLckB35Tr9J/EqdkEM6QllH5PxqP1dhTvFup+HtMRPuz9xOjxXJggxw==}
|
resolution: {integrity: sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==}
|
||||||
|
|
||||||
'@hapi/hoek@9.3.0':
|
'@hapi/hoek@9.3.0':
|
||||||
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
|
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
|
||||||
@ -7635,7 +7635,7 @@ snapshots:
|
|||||||
|
|
||||||
'@floating-ui/utils@0.2.10': {}
|
'@floating-ui/utils@0.2.10': {}
|
||||||
|
|
||||||
'@fontsource-variable/geist@5.2.8': {}
|
'@fontsource-variable/inter@5.2.8': {}
|
||||||
|
|
||||||
'@hapi/hoek@9.3.0': {}
|
'@hapi/hoek@9.3.0': {}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user