Compare commits
No commits in common. "3826f40048b61d07674ce6bd0a79b889fb1dbae6" and "c055b8f9dff49abf99628ddbd7514723a593b5c8" have entirely different histories.
3826f40048
...
c055b8f9df
@ -0,0 +1,137 @@
|
|||||||
|
import { SimpleSearchInput } from "@erp/core/components";
|
||||||
|
import { DataTable, SkeletonDataTable } from "@repo/rdx-ui/components";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
import { useTranslation } from "../../../i18n";
|
||||||
|
import type { CustomerSummaryFormData, CustomersPageFormData } from "../../schemas";
|
||||||
|
|
||||||
|
import { useCustomersListColumns } from "./use-customers-list-columns";
|
||||||
|
|
||||||
|
export type CustomerUpdateCompProps = {
|
||||||
|
customersPage: CustomersPageFormData;
|
||||||
|
loading?: boolean;
|
||||||
|
|
||||||
|
pageIndex: number;
|
||||||
|
pageSize: number;
|
||||||
|
onPageChange?: (pageNumber: number) => void;
|
||||||
|
onPageSizeChange?: (pageSize: number) => void;
|
||||||
|
|
||||||
|
searchValue: string;
|
||||||
|
onSearchChange: (value: string) => void;
|
||||||
|
|
||||||
|
onRowClick?: (
|
||||||
|
row: CustomerSummaryFormData,
|
||||||
|
index: number,
|
||||||
|
event: React.MouseEvent<HTMLTableRowElement>
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomersListGrid = ({
|
||||||
|
customersPage,
|
||||||
|
loading,
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
onPageChange,
|
||||||
|
onPageSizeChange,
|
||||||
|
searchValue,
|
||||||
|
onSearchChange,
|
||||||
|
onRowClick,
|
||||||
|
}: CustomerUpdateCompProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { items, total_items } = customersPage;
|
||||||
|
|
||||||
|
const [statusFilter, setStatusFilter] = useState("todas");
|
||||||
|
|
||||||
|
const columns = useCustomersListColumns({
|
||||||
|
onEdit: (customer) => navigate(`/customers/${customer.id}/edit`),
|
||||||
|
onView: (customer) => navigate(`/customers/${customer.id}`),
|
||||||
|
onDelete: (customer) => null, //confirmDelete(inv.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Navegación centralizada (click/teclado)
|
||||||
|
const goToRow = useCallback(
|
||||||
|
(id: string, newTab = false) => {
|
||||||
|
const url = `/customers/${id}`;
|
||||||
|
if (newTab) {
|
||||||
|
window.open(url, "_blank", "noopener,noreferrer");
|
||||||
|
} else {
|
||||||
|
navigate(url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[navigate]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handlers de búsqueda
|
||||||
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
onSearchChange(e.target.value);
|
||||||
|
const handleClear = () => onSearchChange("");
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
// Envío inmediato: forzar “salto” del debounce
|
||||||
|
onSearchChange((e.target as HTMLInputElement).value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*const handleRowClick = useCallback(
|
||||||
|
(customer: CustomerSummaryFormData, _i: number, e: React.MouseEvent) => {
|
||||||
|
const url = `/customer-invoices/${customer.id}/edit`;
|
||||||
|
if (e.metaKey || e.ctrlKey) { window.open(url, "_blank", "noopener,noreferrer"); return; }
|
||||||
|
preview.open(customer);
|
||||||
|
},
|
||||||
|
[preview]
|
||||||
|
);*/
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<SkeletonDataTable
|
||||||
|
columns={columns.length}
|
||||||
|
footerProps={{ pageIndex, pageSize, totalItems: total_items ?? 0 }}
|
||||||
|
rows={Math.max(6, pageSize)}
|
||||||
|
showFooter
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render principal
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{/* Barra de filtros */}
|
||||||
|
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
||||||
|
<SimpleSearchInput loading={loading} onSearchChange={onSearchChange} />
|
||||||
|
</div>
|
||||||
|
<div className="relative flex">
|
||||||
|
<div className={/*preview.isPinned ? "flex-1 mr-[500px]" : */ "flex-1"}>
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={items}
|
||||||
|
enablePagination
|
||||||
|
enableRowSelection
|
||||||
|
manualPagination
|
||||||
|
onPageChange={onPageChange}
|
||||||
|
onPageSizeChange={onPageSizeChange}
|
||||||
|
onRowClick={() => null /*handleRowClick*/}
|
||||||
|
pageIndex={pageIndex}
|
||||||
|
pageSize={pageSize}
|
||||||
|
readOnly
|
||||||
|
totalItems={total_items}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/*<preview.Preview>
|
||||||
|
{({ item, isPinned, close, togglePin }) => (
|
||||||
|
<InvoicePreviewPanel
|
||||||
|
invoice={item}
|
||||||
|
isPinned={isPinned}
|
||||||
|
onClose={close}
|
||||||
|
onTogglePin={togglePin}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</preview.Preview>*/}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
1
modules/customers/src/web/_archived/pages/list/index.ts
Normal file
1
modules/customers/src/web/_archived/pages/list/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "../../../list/ui/pages/list-customers-page";
|
||||||
@ -18,7 +18,7 @@ export const useCustomerSummaryPanelController = ({
|
|||||||
|
|
||||||
const panelState = useRightPanelState({
|
const panelState = useRightPanelState({
|
||||||
defaultMode: initialMode,
|
defaultMode: initialMode,
|
||||||
defaultVisibility: initialOpen ? "visible" : "hidden",
|
defaultVisibility: initialOpen ? "temporary" : "hidden",
|
||||||
});
|
});
|
||||||
|
|
||||||
const query = useCustomerGetQuery(customerId, {
|
const query = useCustomerGetQuery(customerId, {
|
||||||
@ -26,11 +26,16 @@ export const useCustomerSummaryPanelController = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const openCustomerPanel = useCallback(
|
const openCustomerPanel = useCallback(
|
||||||
(id: string, mode: RightPanelMode = "view") => {
|
(nextCustomerId: string, mode: RightPanelMode = "view") => {
|
||||||
setCustomerId(id);
|
setCustomerId(nextCustomerId);
|
||||||
panelState.open(mode);
|
|
||||||
|
if (panelState.isPinned) {
|
||||||
|
panelState.openPersistent(mode);
|
||||||
|
} else {
|
||||||
|
panelState.openTemporary(mode);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[panelState.open]
|
[panelState.isPinned, panelState.openTemporary, panelState.openPersistent]
|
||||||
);
|
);
|
||||||
|
|
||||||
const closePanel = useCallback(() => {
|
const closePanel = useCallback(() => {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { RightPanel } from "@repo/rdx-ui/components";
|
|||||||
import type { RightPanelMode, RightPanelVisibility } from "@repo/rdx-ui/hooks";
|
import type { RightPanelMode, RightPanelVisibility } from "@repo/rdx-ui/hooks";
|
||||||
import { Button } from "@repo/shadcn-ui/components";
|
import { Button } from "@repo/shadcn-ui/components";
|
||||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||||
import { PencilIcon } from "lucide-react";
|
import { PencilIcon, PinIcon, PinOffIcon } from "lucide-react";
|
||||||
|
|
||||||
import type { Customer } from "../../../../shared";
|
import type { Customer } from "../../../../shared";
|
||||||
|
|
||||||
@ -51,6 +51,7 @@ interface CustomerSummaryPanelProps {
|
|||||||
mode: RightPanelMode;
|
mode: RightPanelMode;
|
||||||
|
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
|
onTogglePinned: () => void;
|
||||||
|
|
||||||
onEdit?: (customer: Customer) => void;
|
onEdit?: (customer: Customer) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -62,9 +63,12 @@ export const CustomerSummaryPanel = ({
|
|||||||
visibility,
|
visibility,
|
||||||
mode,
|
mode,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
|
onTogglePinned,
|
||||||
onEdit,
|
onEdit,
|
||||||
className,
|
className,
|
||||||
}: CustomerSummaryPanelProps) => {
|
}: CustomerSummaryPanelProps) => {
|
||||||
|
const isPinned = visibility === "persistent";
|
||||||
|
|
||||||
const titleMap: Record<RightPanelMode, string> = {
|
const titleMap: Record<RightPanelMode, string> = {
|
||||||
view: "Ficha de cliente",
|
view: "Ficha de cliente",
|
||||||
edit: "Editar cliente",
|
edit: "Editar cliente",
|
||||||
@ -76,6 +80,16 @@ export const CustomerSummaryPanel = ({
|
|||||||
className={cn("bg-transparent", className)}
|
className={cn("bg-transparent", className)}
|
||||||
headerActions={
|
headerActions={
|
||||||
<>
|
<>
|
||||||
|
<Button
|
||||||
|
aria-label={isPinned ? "Desfijar panel" : "Fijar panel"}
|
||||||
|
aria-pressed={isPinned}
|
||||||
|
onClick={onTogglePinned}
|
||||||
|
size="icon-sm"
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
{isPinned ? <PinOffIcon className="size-4" /> : <PinIcon className="size-4" />}
|
||||||
|
</Button>
|
||||||
|
|
||||||
{customer ? (
|
{customer ? (
|
||||||
<Button
|
<Button
|
||||||
aria-label="Editar cliente"
|
aria-label="Editar cliente"
|
||||||
|
|||||||
@ -82,7 +82,7 @@ export const ListCustomersPage = () => {
|
|||||||
/>
|
/>
|
||||||
</AppHeader>
|
</AppHeader>
|
||||||
|
|
||||||
<AppContent>
|
<AppContent className="min-h-0">
|
||||||
{isPanelOpen ? (
|
{isPanelOpen ? (
|
||||||
<ResizablePanelGroup
|
<ResizablePanelGroup
|
||||||
autoSave="list-customers-page"
|
autoSave="list-customers-page"
|
||||||
@ -110,6 +110,7 @@ export const ListCustomersPage = () => {
|
|||||||
|
|
||||||
panelCtrl.panelState.onOpenChange(true);
|
panelCtrl.panelState.onOpenChange(true);
|
||||||
}}
|
}}
|
||||||
|
onTogglePinned={panelCtrl.panelState.togglePinned}
|
||||||
open={panelCtrl.panelState.isOpen}
|
open={panelCtrl.panelState.isOpen}
|
||||||
visibility={panelCtrl.panelState.visibility}
|
visibility={panelCtrl.panelState.visibility}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export const GetCustomerByIdAdapter = {
|
|||||||
const taxesAdapter = (taxes: string) => taxes.split(";").filter((item) => item !== "#") || [];
|
const taxesAdapter = (taxes: string) => taxes.split(";").filter((item) => item !== "#") || [];
|
||||||
|
|
||||||
const defaultTaxes = taxesAdapter(dto.default_taxes);
|
const defaultTaxes = taxesAdapter(dto.default_taxes);
|
||||||
|
console.log("defaultTaxes", defaultTaxes);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: dto.id,
|
id: dto.id,
|
||||||
|
|||||||
@ -8,7 +8,10 @@ export const AppContent = ({
|
|||||||
}: PropsWithChildren<{ className?: string }>) => {
|
}: PropsWithChildren<{ className?: string }>) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn("app-content flex flex-1 flex-col gap-4 p-4 pt-6 min-h-screen", className)}
|
className={cn(
|
||||||
|
"app-content flex flex-1 flex-col gap-4 p-4 pt-6 min-h-screen bg-muted/20",
|
||||||
|
className
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { AppSidebar } from "./app-sidebar.tsx";
|
|||||||
export const AppLayout = () => {
|
export const AppLayout = () => {
|
||||||
return (
|
return (
|
||||||
<SidebarProvider
|
<SidebarProvider
|
||||||
className="bg-amber-500"
|
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
"--sidebar-width": "calc(var(--spacing) * 72)",
|
"--sidebar-width": "calc(var(--spacing) * 72)",
|
||||||
@ -14,9 +13,9 @@ export const AppLayout = () => {
|
|||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<AppSidebar className="bg-sidebar" variant="inset" />
|
<AppSidebar variant="inset" />
|
||||||
{/* Aquí está el MAIN */}
|
{/* Aquí está el MAIN */}
|
||||||
<SidebarInset className="app-main bg-muted">
|
<SidebarInset className="app-main">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
</SidebarProvider>
|
</SidebarProvider>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export type RightPanelMode = "view" | "edit" | "create";
|
export type RightPanelMode = "view" | "edit" | "create";
|
||||||
export type RightPanelVisibility = "hidden" | "visible";
|
export type RightPanelVisibility = "hidden" | "temporary" | "persistent";
|
||||||
|
|
||||||
export interface RightPanelStateOptions {
|
export interface RightPanelStateOptions {
|
||||||
defaultMode?: RightPanelMode;
|
defaultMode?: RightPanelMode;
|
||||||
@ -12,11 +12,14 @@ export interface RightPanelState {
|
|||||||
mode: RightPanelMode;
|
mode: RightPanelMode;
|
||||||
visibility: RightPanelVisibility;
|
visibility: RightPanelVisibility;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
isPinned: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RightPanelStateActions {
|
export interface RightPanelStateActions {
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (next: boolean) => void;
|
||||||
open: (mode?: RightPanelMode) => void;
|
openTemporary: (mode?: RightPanelMode) => void;
|
||||||
|
openPersistent: (mode?: RightPanelMode) => void;
|
||||||
|
togglePinned: () => void;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
}
|
}
|
||||||
@ -34,19 +37,35 @@ export const useRightPanelState = (
|
|||||||
const [mode, setMode] = useState<RightPanelMode>(defaultMode);
|
const [mode, setMode] = useState<RightPanelMode>(defaultMode);
|
||||||
const [visibility, setVisibility] = useState<RightPanelVisibility>(defaultVisibility);
|
const [visibility, setVisibility] = useState<RightPanelVisibility>(defaultVisibility);
|
||||||
|
|
||||||
const isOpen = visibility === "visible";
|
const isOpen = visibility !== "hidden";
|
||||||
|
const isPinned = visibility === "persistent";
|
||||||
const open = useCallback((nextMode: RightPanelMode = DEFAULT_MODE) => {
|
|
||||||
setMode(nextMode);
|
|
||||||
setVisibility("visible");
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const close = useCallback(() => {
|
const close = useCallback(() => {
|
||||||
setVisibility("hidden");
|
setVisibility("hidden");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onOpenChange = useCallback((open: boolean) => {
|
const onOpenChange = useCallback((next: boolean) => {
|
||||||
setVisibility(open ? "visible" : "hidden");
|
setVisibility((prev) => {
|
||||||
|
if (!next) return "hidden";
|
||||||
|
return prev === "persistent" ? "persistent" : "temporary";
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const openTemporary = useCallback((nextMode: RightPanelMode = DEFAULT_MODE) => {
|
||||||
|
setMode(nextMode);
|
||||||
|
setVisibility("temporary");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const openPersistent = useCallback((nextMode: RightPanelMode = DEFAULT_MODE) => {
|
||||||
|
setMode(nextMode);
|
||||||
|
setVisibility("persistent");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const togglePinned = useCallback(() => {
|
||||||
|
setVisibility((prev) => {
|
||||||
|
if (prev === "hidden") return prev;
|
||||||
|
return prev === "persistent" ? "temporary" : "persistent";
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
const reset = useCallback(() => {
|
||||||
@ -58,9 +77,12 @@ export const useRightPanelState = (
|
|||||||
mode,
|
mode,
|
||||||
visibility,
|
visibility,
|
||||||
isOpen,
|
isOpen,
|
||||||
open,
|
isPinned,
|
||||||
close,
|
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
|
openTemporary,
|
||||||
|
openPersistent,
|
||||||
|
togglePinned,
|
||||||
|
close,
|
||||||
reset,
|
reset,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user