Compare commits

...

4 Commits

Author SHA1 Message Date
3826f40048 Colores de fondo en layout 2026-04-01 18:01:35 +02:00
d955d903a0 . 2026-04-01 18:01:23 +02:00
32bbc96c51 Quitar basurilla 2026-04-01 18:00:30 +02:00
7f6dc09b7e . 2026-03-31 20:48:29 +02:00
9 changed files with 24 additions and 207 deletions

View File

@ -1,137 +0,0 @@
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>
);
};

View File

@ -1 +0,0 @@
export * from "../../../list/ui/pages/list-customers-page";

View File

@ -18,7 +18,7 @@ export const useCustomerSummaryPanelController = ({
const panelState = useRightPanelState({
defaultMode: initialMode,
defaultVisibility: initialOpen ? "temporary" : "hidden",
defaultVisibility: initialOpen ? "visible" : "hidden",
});
const query = useCustomerGetQuery(customerId, {
@ -26,16 +26,11 @@ export const useCustomerSummaryPanelController = ({
});
const openCustomerPanel = useCallback(
(nextCustomerId: string, mode: RightPanelMode = "view") => {
setCustomerId(nextCustomerId);
if (panelState.isPinned) {
panelState.openPersistent(mode);
} else {
panelState.openTemporary(mode);
}
(id: string, mode: RightPanelMode = "view") => {
setCustomerId(id);
panelState.open(mode);
},
[panelState.isPinned, panelState.openTemporary, panelState.openPersistent]
[panelState.open]
);
const closePanel = useCallback(() => {

View File

@ -2,7 +2,7 @@ import { RightPanel } from "@repo/rdx-ui/components";
import type { RightPanelMode, RightPanelVisibility } from "@repo/rdx-ui/hooks";
import { Button } from "@repo/shadcn-ui/components";
import { cn } from "@repo/shadcn-ui/lib/utils";
import { PencilIcon, PinIcon, PinOffIcon } from "lucide-react";
import { PencilIcon } from "lucide-react";
import type { Customer } from "../../../../shared";
@ -51,7 +51,6 @@ interface CustomerSummaryPanelProps {
mode: RightPanelMode;
onOpenChange: (open: boolean) => void;
onTogglePinned: () => void;
onEdit?: (customer: Customer) => void;
className?: string;
@ -63,12 +62,9 @@ export const CustomerSummaryPanel = ({
visibility,
mode,
onOpenChange,
onTogglePinned,
onEdit,
className,
}: CustomerSummaryPanelProps) => {
const isPinned = visibility === "persistent";
const titleMap: Record<RightPanelMode, string> = {
view: "Ficha de cliente",
edit: "Editar cliente",
@ -80,16 +76,6 @@ export const CustomerSummaryPanel = ({
className={cn("bg-transparent", className)}
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 ? (
<Button
aria-label="Editar cliente"

View File

@ -82,7 +82,7 @@ export const ListCustomersPage = () => {
/>
</AppHeader>
<AppContent className="min-h-0">
<AppContent>
{isPanelOpen ? (
<ResizablePanelGroup
autoSave="list-customers-page"
@ -110,7 +110,6 @@ export const ListCustomersPage = () => {
panelCtrl.panelState.onOpenChange(true);
}}
onTogglePinned={panelCtrl.panelState.togglePinned}
open={panelCtrl.panelState.isOpen}
visibility={panelCtrl.panelState.visibility}
/>

View File

@ -6,7 +6,6 @@ export const GetCustomerByIdAdapter = {
const taxesAdapter = (taxes: string) => taxes.split(";").filter((item) => item !== "#") || [];
const defaultTaxes = taxesAdapter(dto.default_taxes);
console.log("defaultTaxes", defaultTaxes);
return {
id: dto.id,

View File

@ -8,10 +8,7 @@ export const AppContent = ({
}: PropsWithChildren<{ className?: string }>) => {
return (
<div
className={cn(
"app-content flex flex-1 flex-col gap-4 p-4 pt-6 min-h-screen bg-muted/20",
className
)}
className={cn("app-content flex flex-1 flex-col gap-4 p-4 pt-6 min-h-screen", className)}
{...props}
>
{children}

View File

@ -6,6 +6,7 @@ import { AppSidebar } from "./app-sidebar.tsx";
export const AppLayout = () => {
return (
<SidebarProvider
className="bg-amber-500"
style={
{
"--sidebar-width": "calc(var(--spacing) * 72)",
@ -13,9 +14,9 @@ export const AppLayout = () => {
} as React.CSSProperties
}
>
<AppSidebar variant="inset" />
<AppSidebar className="bg-sidebar" variant="inset" />
{/* Aquí está el MAIN */}
<SidebarInset className="app-main">
<SidebarInset className="app-main bg-muted">
<Outlet />
</SidebarInset>
</SidebarProvider>

View File

@ -1,7 +1,7 @@
import { useCallback, useState } from "react";
export type RightPanelMode = "view" | "edit" | "create";
export type RightPanelVisibility = "hidden" | "temporary" | "persistent";
export type RightPanelVisibility = "hidden" | "visible";
export interface RightPanelStateOptions {
defaultMode?: RightPanelMode;
@ -12,14 +12,11 @@ export interface RightPanelState {
mode: RightPanelMode;
visibility: RightPanelVisibility;
isOpen: boolean;
isPinned: boolean;
}
export interface RightPanelStateActions {
onOpenChange: (next: boolean) => void;
openTemporary: (mode?: RightPanelMode) => void;
openPersistent: (mode?: RightPanelMode) => void;
togglePinned: () => void;
onOpenChange: (open: boolean) => void;
open: (mode?: RightPanelMode) => void;
close: () => void;
reset: () => void;
}
@ -37,35 +34,19 @@ export const useRightPanelState = (
const [mode, setMode] = useState<RightPanelMode>(defaultMode);
const [visibility, setVisibility] = useState<RightPanelVisibility>(defaultVisibility);
const isOpen = visibility !== "hidden";
const isPinned = visibility === "persistent";
const isOpen = visibility === "visible";
const open = useCallback((nextMode: RightPanelMode = DEFAULT_MODE) => {
setMode(nextMode);
setVisibility("visible");
}, []);
const close = useCallback(() => {
setVisibility("hidden");
}, []);
const onOpenChange = useCallback((next: boolean) => {
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 onOpenChange = useCallback((open: boolean) => {
setVisibility(open ? "visible" : "hidden");
}, []);
const reset = useCallback(() => {
@ -77,12 +58,9 @@ export const useRightPanelState = (
mode,
visibility,
isOpen,
isPinned,
onOpenChange,
openTemporary,
openPersistent,
togglePinned,
open,
close,
onOpenChange,
reset,
};
};