+
+
{line1}
- {line2} · {line3}
+ {line2} - {line3}
+
);
diff --git a/modules/customers/src/web/list/ui/pages/list-customers-page.tsx b/modules/customers/src/web/list/ui/pages/list-customers-page.tsx
index a6efebfe..437d2608 100644
--- a/modules/customers/src/web/list/ui/pages/list-customers-page.tsx
+++ b/modules/customers/src/web/list/ui/pages/list-customers-page.tsx
@@ -7,18 +7,18 @@ import { useNavigate } from "react-router-dom";
import { useTranslation } from "../../../i18n";
import { ErrorAlert } from "../../../shared/ui";
import { useListCustomersPageController } from "../../controllers";
-import { CustomerSheet, CustomersGrid, useCustomersGridColumns } from "../blocks";
+import { CustomerSummaryPanel, CustomersGrid, useCustomersGridColumns } from "../blocks";
export const ListCustomersPage = () => {
const { t } = useTranslation();
const navigate = useNavigate();
- const { listCtrl, sheetCtrl } = useListCustomersPageController();
+ const { listCtrl, panelCtrl } = useListCustomersPageController();
const columns = useCustomersGridColumns({
onEditClick: (customer) => navigate(`/customers/${customer.id}/edit`),
onViewClick: (customer) => navigate(`/customers/${customer.id}`),
- onSummaryClick: (customer) => sheetCtrl.openCustomerSheet(customer.id),
+ onSummaryClick: (customer) => panelCtrl.openCustomerPanel(customer.id, "view"),
//onDeleteClick: (customer) => null, //confirmDelete(inv.id),
});
@@ -51,38 +51,43 @@ export const ListCustomersPage = () => {
title={t("pages.list.title")}
/>
-
-
-
+
+
+
+
+
+
+
null}
+ pageIndex={listCtrl.pageIndex}
+ pageSize={listCtrl.pageSize}
+ />
+
+
+
navigate(`/customers/${customer.id}/edit`)}
+ onOpenChange={(open) => {
+ if (open) panelCtrl.panelState.onOpenChange(true);
+ else panelCtrl.closePanel();
+ }}
+ onTogglePinned={panelCtrl.panelState.togglePinned}
+ open={panelCtrl.panelState.isOpen}
+ visibility={panelCtrl.panelState.visibility}
/>
-
- null}
- pageIndex={listCtrl.pageIndex}
- pageSize={listCtrl.pageSize}
- />
-
- {/* Customer Sheet */}
- navigate(`/customers/${customer.id}/edit`)}
- onOpenChange={sheetCtrl.sheetState.onOpenChange}
- onPinnedChange={sheetCtrl.sheetState.setPinned}
- open={sheetCtrl.sheetState.sheetIsOpen}
- pinned={sheetCtrl.sheetState.sheetIsPinned}
- />
>
);
diff --git a/packages/rdx-ui/package.json b/packages/rdx-ui/package.json
index 47e00620..269fcc3f 100644
--- a/packages/rdx-ui/package.json
+++ b/packages/rdx-ui/package.json
@@ -10,7 +10,7 @@
"./helpers": "./src/helpers/index.ts",
"./globals.css": "./src/styles/globals.css",
"./postcss.config": "./postcss.config.mjs",
- "./components": "./src/components/index.tsx",
+ "./components": "./src/components/index.ts",
"./components/*": "./src/components/*.tsx",
"./locales/*": "./src/locales/*",
"./hooks": [
diff --git a/packages/rdx-ui/src/components/buttons/index.tsx b/packages/rdx-ui/src/components/buttons/index.ts
similarity index 100%
rename from packages/rdx-ui/src/components/buttons/index.tsx
rename to packages/rdx-ui/src/components/buttons/index.ts
diff --git a/packages/rdx-ui/src/components/datatable/index.tsx b/packages/rdx-ui/src/components/datatable/index.ts
similarity index 100%
rename from packages/rdx-ui/src/components/datatable/index.tsx
rename to packages/rdx-ui/src/components/datatable/index.ts
diff --git a/packages/rdx-ui/src/components/entity-sheet/entity-sheet-shell.tsx b/packages/rdx-ui/src/components/entity-sheet/entity-sheet-shell.tsx
new file mode 100644
index 00000000..7909663d
--- /dev/null
+++ b/packages/rdx-ui/src/components/entity-sheet/entity-sheet-shell.tsx
@@ -0,0 +1,69 @@
+// entity-sheet-shell.tsx
+
+import { Button, Sheet, SheetContent, SheetTitle } from "@repo/shadcn-ui/components";
+import { cn } from "@repo/shadcn-ui/lib/utils";
+import { PinIcon, PinOffIcon, XIcon } from "lucide-react";
+import type { ReactNode } from "react";
+
+export interface EntitySheetShellProps {
+ open: boolean;
+ pinned: boolean;
+ title: string;
+ onOpenChange: (open: boolean) => void;
+ onTogglePinned: () => void;
+ headerActions?: ReactNode;
+ children: ReactNode;
+}
+
+export const EntitySheetShell = ({
+ open,
+ pinned,
+ title,
+ onOpenChange,
+ onTogglePinned,
+ headerActions,
+ children,
+}: EntitySheetShellProps) => {
+ return (
+
+
+
+
{title}
+
+
+
+
+ {headerActions}
+ {pinned ? null : (
+
+ )}
+
+
+
+ {children}
+
+
+ );
+};
diff --git a/packages/rdx-ui/src/components/entity-sheet/index.ts b/packages/rdx-ui/src/components/entity-sheet/index.ts
new file mode 100644
index 00000000..9c45adc4
--- /dev/null
+++ b/packages/rdx-ui/src/components/entity-sheet/index.ts
@@ -0,0 +1 @@
+export * from "./entity-sheet-shell.tsx";
diff --git a/packages/rdx-ui/src/components/form/index.tsx b/packages/rdx-ui/src/components/form/index.ts
similarity index 99%
rename from packages/rdx-ui/src/components/form/index.tsx
rename to packages/rdx-ui/src/components/form/index.ts
index 8440cea8..10483cea 100644
--- a/packages/rdx-ui/src/components/form/index.tsx
+++ b/packages/rdx-ui/src/components/form/index.ts
@@ -1,9 +1,7 @@
-export * from "./date-picker-input-field/index.ts";
export * from "./DatePickerField.tsx";
-
+export * from "./date-picker-input-field/index.ts";
export * from "./multi-select-field.tsx";
export * from "./SelectField.tsx";
export * from "./TextAreaField.tsx";
export * from "./TextField.tsx";
export type * from "./types.d.ts";
-
diff --git a/packages/rdx-ui/src/components/index.tsx b/packages/rdx-ui/src/components/index.ts
similarity index 55%
rename from packages/rdx-ui/src/components/index.tsx
rename to packages/rdx-ui/src/components/index.ts
index 13c81e76..241f5018 100644
--- a/packages/rdx-ui/src/components/index.tsx
+++ b/packages/rdx-ui/src/components/index.ts
@@ -1,16 +1,18 @@
-export * from "./buttons/index.tsx";
+export * from "./buttons/index.ts";
export * from "./custom-dialog.tsx";
-export * from "./datatable/index.tsx";
+export * from "./datatable/index.ts";
export * from "./dynamics-tabs.tsx";
+export * from "./entity-sheet/index.ts";
export * from "./error-overlay.tsx";
-export * from "./form/index.tsx";
+export * from "./form/index.ts";
export * from "./full-screen-modal.tsx";
export * from "./grid/index.ts";
-export * from "./layout/index.tsx";
-export * from "./loading-overlay/index.tsx";
+export * from "./layout/index.ts";
+export * from "./loading-overlay/index.ts";
export * from "./logo-verifactu.tsx";
-export * from "./lookup-dialog/index.tsx";
+export * from "./lookup-dialog/index.ts";
export * from "./multi-select.tsx";
export * from "./multiple-selector.tsx";
+export * from "./right-panel/index.ts";
export * from "./scroll-to-top.tsx";
export * from "./tailwind-indicator.tsx";
diff --git a/packages/rdx-ui/src/components/layout/app-breadcrumb.tsx b/packages/rdx-ui/src/components/layout/app-breadcrumb.tsx
index 01e62e7f..88d8e3bb 100644
--- a/packages/rdx-ui/src/components/layout/app-breadcrumb.tsx
+++ b/packages/rdx-ui/src/components/layout/app-breadcrumb.tsx
@@ -11,16 +11,16 @@ import {
export const AppBreadcrumb = () => {
return (
-
-
-
-
+
+
+
+
-
- Building Your Application
+
+ Building Your Application
-
+
Data Fetching
diff --git a/packages/rdx-ui/src/components/layout/index.tsx b/packages/rdx-ui/src/components/layout/index.ts
similarity index 100%
rename from packages/rdx-ui/src/components/layout/index.tsx
rename to packages/rdx-ui/src/components/layout/index.ts
diff --git a/packages/rdx-ui/src/components/loading-overlay/index.tsx b/packages/rdx-ui/src/components/loading-overlay/index.ts
similarity index 100%
rename from packages/rdx-ui/src/components/loading-overlay/index.tsx
rename to packages/rdx-ui/src/components/loading-overlay/index.ts
diff --git a/packages/rdx-ui/src/components/lookup-dialog/index.tsx b/packages/rdx-ui/src/components/lookup-dialog/index.ts
similarity index 100%
rename from packages/rdx-ui/src/components/lookup-dialog/index.tsx
rename to packages/rdx-ui/src/components/lookup-dialog/index.ts
diff --git a/packages/rdx-ui/src/components/right-panel/index.ts b/packages/rdx-ui/src/components/right-panel/index.ts
new file mode 100644
index 00000000..aeb6aa2c
--- /dev/null
+++ b/packages/rdx-ui/src/components/right-panel/index.ts
@@ -0,0 +1 @@
+export * from "./right-panel.tsx";
diff --git a/packages/rdx-ui/src/components/right-panel/right-panel-header.tsx b/packages/rdx-ui/src/components/right-panel/right-panel-header.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/packages/rdx-ui/src/components/right-panel/right-panel-types.ts b/packages/rdx-ui/src/components/right-panel/right-panel-types.ts
new file mode 100644
index 00000000..1227d928
--- /dev/null
+++ b/packages/rdx-ui/src/components/right-panel/right-panel-types.ts
@@ -0,0 +1,10 @@
+import type { ReactNode } from "react";
+
+export interface RightPanelProps {
+ open: boolean;
+ title: string;
+ className?: string;
+ headerActions?: ReactNode;
+ children: ReactNode;
+ onOpenChange?: (open: boolean) => void;
+}
diff --git a/packages/rdx-ui/src/components/right-panel/right-panel.tsx b/packages/rdx-ui/src/components/right-panel/right-panel.tsx
new file mode 100644
index 00000000..33deacd4
--- /dev/null
+++ b/packages/rdx-ui/src/components/right-panel/right-panel.tsx
@@ -0,0 +1,53 @@
+import { Button } from "@repo/shadcn-ui/components";
+import { cn } from "@repo/shadcn-ui/lib/utils";
+import { XIcon } from "lucide-react";
+
+import type { RightPanelProps } from "./right-panel-types.ts";
+
+export const RightPanel = ({
+ open,
+ title,
+ className,
+ headerActions,
+ children,
+ onOpenChange,
+}: RightPanelProps) => {
+ if (!open) return null;
+
+ return (
+
+ );
+};
diff --git a/packages/rdx-ui/src/hooks/index.ts b/packages/rdx-ui/src/hooks/index.ts
index 6e0460dd..020db2ab 100644
--- a/packages/rdx-ui/src/hooks/index.ts
+++ b/packages/rdx-ui/src/hooks/index.ts
@@ -1,3 +1,4 @@
export * from "./sheet/index.ts";
+export * from "./side-panel/index.ts";
export * from "./use-device-info.ts";
export * from "./use-row-selection.ts";
diff --git a/packages/rdx-ui/src/hooks/side-panel/index.ts b/packages/rdx-ui/src/hooks/side-panel/index.ts
new file mode 100644
index 00000000..d0adcfab
--- /dev/null
+++ b/packages/rdx-ui/src/hooks/side-panel/index.ts
@@ -0,0 +1 @@
+export * from "./use-side-panel-state.ts";
diff --git a/packages/rdx-ui/src/hooks/side-panel/use-side-panel-state.ts b/packages/rdx-ui/src/hooks/side-panel/use-side-panel-state.ts
new file mode 100644
index 00000000..b83e2d16
--- /dev/null
+++ b/packages/rdx-ui/src/hooks/side-panel/use-side-panel-state.ts
@@ -0,0 +1,88 @@
+import { useCallback, useState } from "react";
+
+export type RightPanelMode = "view" | "edit" | "create";
+export type RightPanelVisibility = "hidden" | "temporary" | "persistent";
+
+export interface RightPanelStateOptions {
+ defaultMode?: RightPanelMode;
+ defaultVisibility?: RightPanelVisibility;
+}
+
+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;
+ close: () => void;
+ reset: () => void;
+}
+
+export type RightPanelStateController = RightPanelState & RightPanelStateActions;
+
+const DEFAULT_MODE: RightPanelMode = "view";
+const DEFAULT_VISIBILITY: RightPanelVisibility = "hidden";
+
+export const useRightPanelState = (
+ options: RightPanelStateOptions = {}
+): RightPanelStateController => {
+ const { defaultMode = DEFAULT_MODE, defaultVisibility = DEFAULT_VISIBILITY } = options;
+
+ const [mode, setMode] = useState(defaultMode);
+ const [visibility, setVisibility] = useState(defaultVisibility);
+
+ const isOpen = visibility !== "hidden";
+ const isPinned = visibility === "persistent";
+
+ 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 reset = useCallback(() => {
+ setMode(defaultMode);
+ setVisibility(defaultVisibility);
+ }, [defaultMode, defaultVisibility]);
+
+ return {
+ mode,
+ visibility,
+ isOpen,
+ isPinned,
+ onOpenChange,
+ openTemporary,
+ openPersistent,
+ togglePinned,
+ close,
+ reset,
+ };
+};
diff --git a/packages/rdx-ui/src/index.ts b/packages/rdx-ui/src/index.ts
index 3bdf4f2b..56dbe579 100644
--- a/packages/rdx-ui/src/index.ts
+++ b/packages/rdx-ui/src/index.ts
@@ -1,5 +1,5 @@
export const PACKAGE_NAME = "rdx-ui";
-export * from "./components/index.tsx";
+export * from "./components/index.ts";
export * from "./helpers/index.ts";
export * from "./hooks/index.ts";
diff --git a/packages/shadcn-ui/src/components/separator.tsx b/packages/shadcn-ui/src/components/separator.tsx
index aea5b66d..c9737d58 100644
--- a/packages/shadcn-ui/src/components/separator.tsx
+++ b/packages/shadcn-ui/src/components/separator.tsx
@@ -1,7 +1,6 @@
-import * as React from "react"
-import { Separator as SeparatorPrimitive } from "radix-ui"
-
-import { cn } from "@repo/shadcn-ui/lib/utils"
+import { cn } from "@repo/shadcn-ui/lib/utils";
+import { Separator as SeparatorPrimitive } from "radix-ui";
+import type * as React from "react";
function Separator({
className,
@@ -11,16 +10,16 @@ function Separator({
}: React.ComponentProps) {
return (
- )
+ );
}
-export { Separator }
+export { Separator };