@donotdev/ui 0.0.14 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/components/common/TechBento.d.ts.map +1 -1
  2. package/dist/components/common/TechBento.js +1 -1
  3. package/dist/components/layout/components/header/HeaderNavigation.d.ts.map +1 -1
  4. package/dist/components/layout/components/header/HeaderNavigation.js +2 -2
  5. package/dist/components/layout/components/index.d.ts +3 -0
  6. package/dist/components/layout/components/index.d.ts.map +1 -1
  7. package/dist/components/layout/components/index.js +3 -0
  8. package/dist/crud/components/CrudCardLink.d.ts.map +1 -1
  9. package/dist/crud/components/EntityCardList.d.ts +1 -1
  10. package/dist/crud/components/EntityCardList.d.ts.map +1 -1
  11. package/dist/crud/components/EntityCardList.js +24 -35
  12. package/dist/crud/components/EntityDisplayRenderer.d.ts +1 -1
  13. package/dist/crud/components/EntityDisplayRenderer.d.ts.map +1 -1
  14. package/dist/crud/components/EntityDisplayRenderer.js +6 -2
  15. package/dist/crud/components/EntityFormRenderer.d.ts +1 -1
  16. package/dist/crud/components/EntityFormRenderer.d.ts.map +1 -1
  17. package/dist/crud/components/EntityFormRenderer.js +12 -29
  18. package/dist/crud/components/EntityList.d.ts.map +1 -1
  19. package/dist/crud/components/EntityList.js +7 -43
  20. package/dist/crud/components/EntityRecommendations.d.ts +1 -0
  21. package/dist/crud/components/EntityRecommendations.d.ts.map +1 -1
  22. package/dist/crud/components/EntityRecommendations.js +3 -2
  23. package/dist/dndev.css +11581 -0
  24. package/dist/index.js +4 -4
  25. package/dist/internal/devtools/components/DesignTab.d.ts.map +1 -1
  26. package/dist/internal/layout/components/AutoMetaTags.d.ts.map +1 -1
  27. package/dist/internal/layout/components/AutoMetaTags.js +2 -9
  28. package/dist/internal/layout/components/FontPreloadLinks.d.ts.map +1 -1
  29. package/dist/internal/layout/components/FontPreloadLinks.js +1 -0
  30. package/dist/internal/layout/components/footer/useLegalLinks.d.ts.map +1 -1
  31. package/dist/internal/providers/NavigationProvider.d.ts.map +1 -1
  32. package/dist/internal/providers/NavigationProvider.js +3 -5
  33. package/dist/providers/ViteAppProviders.d.ts.map +1 -1
  34. package/dist/providers/ViteAppProviders.js +3 -5
  35. package/dist/routing/GoToInput.d.ts.map +1 -1
  36. package/dist/routing/GoToInput.js +6 -6
  37. package/dist/routing/Link.d.ts.map +1 -1
  38. package/dist/routing/hooks/hooks.next.js +1 -1
  39. package/dist/routing/hooks/hooks.vite.js +1 -1
  40. package/dist/routing/hooks/useNavigate.next.d.ts +1 -1
  41. package/dist/routing/hooks/useNavigate.next.d.ts.map +1 -1
  42. package/dist/routing/hooks/useNavigate.next.js +1 -7
  43. package/dist/routing/hooks/useNavigate.vite.d.ts +1 -1
  44. package/dist/routing/hooks/useNavigate.vite.d.ts.map +1 -1
  45. package/dist/routing/hooks/useNavigate.vite.js +1 -7
  46. package/dist/routing/useRouteDiscovery.d.ts +4 -15
  47. package/dist/routing/useRouteDiscovery.d.ts.map +1 -1
  48. package/dist/routing/useRouteDiscovery.js +6 -5
  49. package/dist/styles/index.css +96 -63
  50. package/dist/utils/sanitizeSvg.d.ts.map +1 -1
  51. package/dist/utils/useFormStoreSafe.d.ts +2 -15
  52. package/dist/utils/useFormStoreSafe.d.ts.map +1 -1
  53. package/dist/utils/useFormStoreSafe.js +3 -33
  54. package/dist/vite-routing/AppRoutes.d.ts.map +1 -1
  55. package/dist/vite-routing/AppRoutes.js +1 -1
  56. package/dist/vite-routing/RootLayout.d.ts.map +1 -1
  57. package/dist/vite-routing/RootLayout.js +10 -15
  58. package/package.json +10 -10
  59. package/dist/routing/hooks/useFormNavigationBlocker.d.ts +0 -14
  60. package/dist/routing/hooks/useFormNavigationBlocker.d.ts.map +0 -1
  61. package/dist/routing/hooks/useFormNavigationBlocker.js +0 -42
@@ -1 +1 @@
1
- {"version":3,"file":"TechBento.d.ts","sourceRoot":"","sources":["../../../src/components/common/TechBento.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,cAAc,EACd,IAAI,EACL,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAK/D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,OAAO,CAAC;QACd,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;QAC3C,OAAO,CAAC,EAAE,WAAW,CAAC;KACvB,CAAC,CAAC;IACH;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IAC/B;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwCF,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,KAAK,EACL,IAAQ,EACR,OAAO,EACP,GAAc,EACd,SAAiB,EACjB,IAAiB,EACjB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,SAAS,GACV,EAAE,cAAc,2CA4DhB;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"TechBento.d.ts","sourceRoot":"","sources":["../../../src/components/common/TechBento.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,cAAc,EACd,IAAI,EACL,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAK/D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,OAAO,CAAC;QACd,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;QAC3C,OAAO,CAAC,EAAE,WAAW,CAAC;KACvB,CAAC,CAAC;IACH;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC;IAC/B;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,0CAA0C;IAC1C,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA0CF,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,KAAK,EACL,IAAQ,EACR,OAAO,EACP,GAAc,EACd,SAAiB,EACjB,IAAiB,EACjB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,SAAS,GACV,EAAE,cAAc,2CA4DhB;AAED,eAAe,SAAS,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // packages/ui/src/components/common/TechBento.tsx
3
3
  import { useState } from 'react';
4
- import { Bento, Card, Section, Stack, Text, TONE, cn } from '@donotdev/components';
4
+ import { Bento, Card, Section, Stack, Text, TONE, cn, } from '@donotdev/components';
5
5
  import { techLogos } from '../../data/techLogos';
6
6
  import { sanitizeSvg } from '../../utils/sanitizeSvg';
7
7
  function TechCard({ techKey, variant, }) {
@@ -1 +1 @@
1
- {"version":3,"file":"HeaderNavigation.d.ts","sourceRoot":"","sources":["../../../../../src/components/layout/components/header/HeaderNavigation.tsx"],"names":[],"mappings":"AAsBA,OAAO,EACL,OAAO,EAIR,MAAM,sBAAsB,CAAC;AAU9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IACpC,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;IACjD;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,gBAAgB,EAAE,aAAa,CAAC,qBAAqB,CAwE1D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"HeaderNavigation.d.ts","sourceRoot":"","sources":["../../../../../src/components/layout/components/header/HeaderNavigation.tsx"],"names":[],"mappings":"AAsBA,OAAO,EACL,OAAO,EAIR,MAAM,sBAAsB,CAAC;AAa9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IACpC,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;IACjD;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,gBAAgB,EAAE,aAAa,CAAC,qBAAqB,CA4E1D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -24,7 +24,7 @@ import { Link } from '../../../../routing/Link';
24
24
  import { Icon } from '../../../common/icon';
25
25
  import { DnDevNavigationMenu } from '../../../../routing/DnDevNavigationMenu';
26
26
  import { NavigationItemComponent } from '../../../../routing/NavigationItem';
27
- import { useNavigationItems, useNavigationRoute } from '../../../../routing/useNavigation';
27
+ import { useNavigationItems, useNavigationRoute, } from '../../../../routing/useNavigation';
28
28
  /**
29
29
  * HeaderNavigation - DISPLAY-aware adaptive navigation component
30
30
  *
@@ -47,7 +47,7 @@ const HeaderNavigation = ({ className = '', showIcons = true, display = DISPLAY.
47
47
  if (path) {
48
48
  if (!singleRoute)
49
49
  return null;
50
- return (_jsx(Button, { variant: "ghost", display: display, icon: showIcons ? _jsx(Icon, { icon: singleRoute.icon, fallback: LinkIcon }) : undefined, className: className, render: ({ children, ...props }) => (_jsx(Link, { path: singleRoute.path, ...props, children: children })), children: singleRoute.label }));
50
+ return (_jsx(Button, { variant: "ghost", display: display, icon: showIcons ? (_jsx(Icon, { icon: singleRoute.icon, fallback: LinkIcon })) : undefined, className: className, render: ({ children, ...props }) => (_jsx(Link, { path: singleRoute.path, ...props, children: children })), children: singleRoute.label }));
51
51
  }
52
52
  const effectiveDisplay = display === DISPLAY.AUTO
53
53
  ? isMobile || isTablet
@@ -19,4 +19,7 @@ export { default as HeaderNavigation } from './header/HeaderNavigation';
19
19
  export { default as Notifications } from './Notifications';
20
20
  export { default as SettingsMenu } from './header/SettingsMenu';
21
21
  export { default as ThemeToggle } from './header/ThemeToggle';
22
+ export { FooterBranding } from '../../../internal/layout/components/footer/FooterBranding';
23
+ export { FooterCopyright } from '../../../internal/layout/components/footer/FooterCopyright';
24
+ export { FooterLegalLinks } from '../../../internal/layout/components/footer/FooterLegalLinks';
22
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/components/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/components/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,2DAA2D,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,4DAA4D,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,6DAA6D,CAAC"}
@@ -20,3 +20,6 @@ export { default as HeaderNavigation } from './header/HeaderNavigation';
20
20
  export { default as Notifications } from './Notifications';
21
21
  export { default as SettingsMenu } from './header/SettingsMenu';
22
22
  export { default as ThemeToggle } from './header/ThemeToggle';
23
+ export { FooterBranding } from '../../../internal/layout/components/footer/FooterBranding';
24
+ export { FooterCopyright } from '../../../internal/layout/components/footer/FooterCopyright';
25
+ export { FooterLegalLinks } from '../../../internal/layout/components/footer/FooterLegalLinks';
@@ -1 +1 @@
1
- {"version":3,"file":"CrudCardLink.d.ts","sourceRoot":"","sources":["../../../src/crud/components/CrudCardLink.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,UAAU,EACV,OAAO,EACP,GAAG,IAAI,EACR,EAAE,aAAa,2CAqBf;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"CrudCardLink.d.ts","sourceRoot":"","sources":["../../../src/crud/components/CrudCardLink.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,aAAa,2CAkBvE;AAED,eAAe,QAAQ,CAAC"}
@@ -12,5 +12,5 @@ export type { EntityCardListProps };
12
12
  * - Auto-routing when handler not provided
13
13
  */
14
14
  export declare function EntityCardList({ entity, basePath, onClick, cols, staleTime, // 30 minutes default cache
15
- filter, hideFilters, }: EntityCardListProps): import("react/jsx-runtime").JSX.Element;
15
+ filter, hideFilters, resultLabel, tone, }: EntityCardListProps): import("react/jsx-runtime").JSX.Element;
16
16
  //# sourceMappingURL=EntityCardList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EntityCardList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityCardList.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,mBAAmB,EAAgB,MAAM,gBAAgB,CAAC;AAIxE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAmB,EACnB,SAA0B,EAAE,2BAA2B;AACvD,MAAM,EACN,WAAmB,GACpB,EAAE,mBAAmB,2CAqMrB"}
1
+ {"version":3,"file":"EntityCardList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityCardList.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,mBAAmB,EAAgB,MAAM,gBAAgB,CAAC;AAIxE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAmB,EACnB,SAA0B,EAAE,2BAA2B;AACvD,MAAM,EACN,WAAmB,EACnB,WAAW,EACX,IAAI,GACL,EAAE,mBAAmB,2CA2LrB"}
@@ -9,16 +9,16 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
9
9
  * **Routing:** Convention basePath = `/${collection}`. View = basePath/:id.
10
10
  * Override basePath for nested routes; use onClick(id) to open sheet instead of navigating.
11
11
  *
12
- * @version 0.2.0
12
+ * @version 0.3.0
13
13
  * @since 0.0.1
14
14
  * @author AMBROISE PARK Consulting
15
15
  */
16
16
  import { useMemo, useCallback } from 'react';
17
17
  import { Heart } from 'lucide-react';
18
- import { Grid, Stack, Text, Spinner, Section, Button, } from '@donotdev/components';
18
+ import { Card, Grid, Stack, Text, Spinner, Section, Button, } from '@donotdev/components';
19
19
  import { useTranslation, getListCardFieldNames } from '@donotdev/core';
20
20
  import { useNavigate } from '../../routing';
21
- import { useCrudCardList, EntityFilters, useEntityFavorites, matchesFilter, useCrudFilters, } from '@donotdev/crud';
21
+ import { useCrudCardList, EntityFilters, useEntityFavorites, useCrudFilters, } from '@donotdev/crud';
22
22
  import { CrudCard } from './CrudCardLink';
23
23
  /**
24
24
  * Entity Card List Component - Card grid view for public/user-facing browsing
@@ -32,36 +32,22 @@ import { CrudCard } from './CrudCardLink';
32
32
  * - Auto-routing when handler not provided
33
33
  */
34
34
  export function EntityCardList({ entity, basePath, onClick, cols = [1, 2, 3, 4], staleTime = 1000 * 60 * 30, // 30 minutes default cache
35
- filter, hideFilters = false, }) {
35
+ filter, hideFilters = false, resultLabel, tone, }) {
36
36
  const navigate = useNavigate();
37
37
  const base = basePath ?? `/${entity.collection}`;
38
- // useCrudCardList -> handles fetching optimized for cards automatically
39
- const { data: listData, loading } = useCrudCardList(entity, { staleTime });
40
- const rawData = listData?.items || [];
38
+ // useCrudCardList -> handles fetching + client-side filter/search/sort via `processed`
39
+ const { items: rawData, processed, loading, } = useCrudCardList(entity, { staleTime });
41
40
  // Favorites - always enabled, no props needed
42
41
  const { isFavorite, toggleFavorite, favoritesFilter } = useEntityFavorites({
43
42
  collection: entity.collection,
44
43
  });
45
44
  // Favorites toggle from CrudStore (persists across navigation)
46
- // Note: EntityFilters now manages its own filters internally via useCrudFilters
47
- const { showFavoritesOnly, setShowFavoritesOnly, filters } = useCrudFilters({
45
+ const { showFavoritesOnly, setShowFavoritesOnly } = useCrudFilters({
48
46
  collection: entity.collection,
49
47
  });
50
- // Apply filters from EntityFilters component
51
- const applyFilters = useCallback((item) => {
52
- if (Object.keys(filters).length === 0)
53
- return true;
54
- return Object.entries(filters).every(([fieldName, filterValue]) => {
55
- const itemValue = item[fieldName];
56
- const fieldConfig = entity.fields[fieldName];
57
- const fieldType = fieldConfig?.type || 'text';
58
- return matchesFilter(itemValue, filterValue, fieldType);
59
- });
60
- }, [filters, entity.fields]);
61
- // Apply client-side filtering (including favorites)
48
+ // Apply UI-specific filters (favorites, consumer filter prop) on top of processed
62
49
  const data = useMemo(() => {
63
- let result = rawData;
64
- result = result.filter(applyFilters);
50
+ let result = processed;
65
51
  // Apply favorites filter if enabled
66
52
  if (showFavoritesOnly) {
67
53
  result = result.filter(favoritesFilter);
@@ -70,7 +56,7 @@ filter, hideFilters = false, }) {
70
56
  result = result.filter(filter);
71
57
  }
72
58
  return result;
73
- }, [rawData, applyFilters, showFavoritesOnly, favoritesFilter, filter]);
59
+ }, [processed, showFavoritesOnly, favoritesFilter, filter]);
74
60
  // Entity + crud namespaces so formatValue can resolve crud:price.* etc.
75
61
  const { t } = useTranslation([entity.namespace, 'crud']);
76
62
  const { t: tCrud } = useTranslation('crud');
@@ -89,18 +75,20 @@ filter, hideFilters = false, }) {
89
75
  return (_jsxs(_Fragment, { children: [!hideFilters && (_jsx(Section, { title: tCrud('filters.title', {
90
76
  entity: entityName,
91
77
  defaultValue: `Browse ${entityName} - Filters`,
92
- }), collapsible: true, defaultOpen: true, children: _jsxs(Stack, { direction: "column", children: [_jsx(Button, { variant: showFavoritesOnly ? 'primary' : 'outline', icon: _jsx(Heart, { size: 18 }), onClick: () => setShowFavoritesOnly(!showFavoritesOnly), children: showFavoritesOnly
93
- ? tCrud('favorites.showAll', { defaultValue: 'Show All' })
94
- : tCrud('favorites.showFavorites', {
95
- defaultValue: 'Show Favorites',
96
- }) }), _jsx(EntityFilters, { entity: entity, data: rawData, fieldsToFilter: fieldsToFilter })] }) })), _jsx(Section, { title: loading
78
+ }), collapsible: true, defaultOpen: true, tone: tone, children: _jsx(Card, { children: _jsxs(Stack, { direction: "column", children: [_jsx(Button, { variant: showFavoritesOnly ? 'primary' : 'outline', icon: _jsx(Heart, { size: 18 }), onClick: () => setShowFavoritesOnly(!showFavoritesOnly), children: showFavoritesOnly
79
+ ? tCrud('favorites.showAll', { defaultValue: 'Show All' })
80
+ : tCrud('favorites.showFavorites', {
81
+ defaultValue: 'Show Favorites',
82
+ }) }), _jsx(EntityFilters, { entity: entity, data: rawData, fieldsToFilter: fieldsToFilter })] }) }) })), _jsx(Section, { title: loading
97
83
  ? tCrud('results.title.fetching', { defaultValue: 'Fetching...' })
98
- : tCrud('results.title.count', {
99
- count: data.length,
100
- defaultValue: data.length === 1
101
- ? 'Found 1 occurrence'
102
- : `Found ${data.length} occurrences`,
103
- }), collapsible: true, defaultOpen: true, children: loading ? (_jsx(Stack, { align: "center", justify: "center", style: { padding: 'var(--gap-3xl)' }, children: _jsx(Spinner, {}) })) : data.length === 0 ? (_jsxs(Stack, { align: "center", justify: "center", style: { padding: 'var(--gap-3xl)', textAlign: 'center' }, children: [_jsx(Text, { level: "h3", style: { color: 'var(--muted-foreground)' }, children: tCrud('emptyState.title', {
84
+ : resultLabel
85
+ ? resultLabel(data.length)
86
+ : tCrud('results.title.count', {
87
+ count: data.length,
88
+ defaultValue: data.length === 1
89
+ ? 'Found 1 occurrence'
90
+ : `Found ${data.length} occurrences`,
91
+ }), collapsible: true, defaultOpen: true, tone: tone, children: loading ? (_jsx(Stack, { align: "center", justify: "center", style: { padding: 'var(--gap-3xl)' }, children: _jsx(Spinner, {}) })) : data.length === 0 ? (_jsxs(Stack, { align: "center", justify: "center", style: { padding: 'var(--gap-3xl)', textAlign: 'center' }, children: [_jsx(Text, { level: "h3", style: { color: 'var(--muted-foreground)' }, children: tCrud('emptyState.title', {
104
92
  defaultValue: `No ${entity.name.toLowerCase()} found`,
105
93
  }) }), _jsx(Text, { style: { color: 'var(--muted-foreground)' }, children: tCrud('emptyState.description', {
106
94
  defaultValue: `No ${entity.name.toLowerCase()} available at this time.`,
@@ -108,6 +96,7 @@ filter, hideFilters = false, }) {
108
96
  const itemIsFavorite = isFavorite(item.id);
109
97
  const detailHref = onClick ? undefined : `${base}/${item.id}`;
110
98
  return (_jsx(CrudCard, { item: item, entity: entity, detailHref: detailHref, onClick: onClick ? () => handleView(item.id) : undefined, renderActions: _jsx(Heart, { fill: itemIsFavorite ? '#ef4444' : '#ffffff', stroke: itemIsFavorite ? '#ef4444' : 'var(--muted-foreground)', onClick: (e) => {
99
+ e.preventDefault();
111
100
  e.stopPropagation();
112
101
  toggleFavorite(item.id);
113
102
  }, style: {
@@ -16,6 +16,6 @@ export type { EntityDisplayRendererProps };
16
16
  * <EntityDisplayRenderer entity={carEntity} id={carId} />
17
17
  * ```
18
18
  */
19
- export declare function EntityDisplayRenderer<T extends EntityRecord = EntityRecord>({ entity, id, t, className, loadingMessage, notFoundMessage, viewerRole: viewerRoleProp, }: EntityDisplayRendererProps<T>): import("react/jsx-runtime").JSX.Element;
19
+ export declare function EntityDisplayRenderer<T extends EntityRecord = EntityRecord>({ entity, id, t, className, loadingMessage, notFoundMessage, viewerRole: viewerRoleProp, excludeFields, }: EntityDisplayRendererProps<T>): import("react/jsx-runtime").JSX.Element;
20
20
  export default EntityDisplayRenderer;
21
21
  //# sourceMappingURL=EntityDisplayRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EntityDisplayRenderer.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityDisplayRenderer.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI/E,YAAY,EAAE,0BAA0B,EAAE,CAAC;AAE3C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EAC3E,MAAM,EACN,EAAE,EACF,CAAC,EACD,SAAc,EACd,cAAc,EACd,eAAe,EACf,UAAU,EAAE,cAAc,GAC3B,EAAE,0BAA0B,CAAC,CAAC,CAAC,2CAkM/B;AAED,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"EntityDisplayRenderer.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityDisplayRenderer.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI/E,YAAY,EAAE,0BAA0B,EAAE,CAAC;AAE3C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EAC3E,MAAM,EACN,EAAE,EACF,CAAC,EACD,SAAc,EACd,cAAc,EACd,eAAe,EACf,UAAU,EAAE,cAAc,EAC1B,aAAa,GACd,EAAE,0BAA0B,CAAC,CAAC,CAAC,2CAuM/B;AAED,eAAe,qBAAqB,CAAC"}
@@ -31,7 +31,7 @@ import { useAuthSafe } from '../../utils/useAuthSafe';
31
31
  * <EntityDisplayRenderer entity={carEntity} id={carId} />
32
32
  * ```
33
33
  */
34
- export function EntityDisplayRenderer({ entity, id, t, className = '', loadingMessage, notFoundMessage, viewerRole: viewerRoleProp, }) {
34
+ export function EntityDisplayRenderer({ entity, id, t, className = '', loadingMessage, notFoundMessage, viewerRole: viewerRoleProp, excludeFields, }) {
35
35
  // Auto-detect role from auth; prop overrides
36
36
  const authRole = useAuthSafe('userRole');
37
37
  const viewerRole = viewerRoleProp ?? authRole;
@@ -95,6 +95,10 @@ export function EntityDisplayRenderer({ entity, id, t, className = '', loadingMe
95
95
  if (!displayData)
96
96
  return [];
97
97
  return Object.entries(entity.fields).filter(([fieldName, fieldConfig]) => {
98
+ // Skip excluded fields
99
+ if (excludeFields?.includes(fieldName)) {
100
+ return false;
101
+ }
98
102
  // Check visibility first
99
103
  if (!isFieldVisible(fieldConfig.visibility, viewerRole)) {
100
104
  return false;
@@ -126,7 +130,7 @@ export function EntityDisplayRenderer({ entity, id, t, className = '', loadingMe
126
130
  }
127
131
  return true;
128
132
  });
129
- }, [entity.fields, viewerRole, displayData]);
133
+ }, [entity.fields, viewerRole, displayData, excludeFields]);
130
134
  // Loading state
131
135
  if (isLoading) {
132
136
  return (_jsx("div", { style: {
@@ -13,6 +13,6 @@ export type { EntityFormRendererProps };
13
13
  * @since 0.0.1
14
14
  * @author AMBROISE PARK Consulting
15
15
  */
16
- export declare function EntityFormRenderer<T extends EntityRecord = EntityRecord>({ entity, onSubmit, t, className, submitText, loading, defaultValues, submitVariant, secondaryButtonText, secondaryButtonVariant, onSecondarySubmit, viewerRole: viewerRoleProp, operation, autoSave, formId: externalFormId, cancelText, cancelPath, successPath, onCancel, warnOnUnsavedChanges, unsavedChangesMessage, hideVisibilityInfo, }: EntityFormRendererProps<T>): import("react/jsx-runtime").JSX.Element;
16
+ export declare function EntityFormRenderer<T extends EntityRecord = EntityRecord>({ entity, onSubmit, t, className, submitText, loading, defaultValues, submitVariant, secondaryButtonText, secondaryButtonVariant, onSecondarySubmit, viewerRole: viewerRoleProp, operation, formId: externalFormId, cancelText, cancelPath, successPath, onCancel, warnOnUnsavedChanges, unsavedChangesMessage, hideVisibilityInfo, }: EntityFormRendererProps<T>): import("react/jsx-runtime").JSX.Element;
17
17
  export default EntityFormRenderer;
18
18
  //# sourceMappingURL=EntityFormRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EntityFormRenderer.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityFormRenderer.tsx"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EACV,uBAAuB,EACvB,YAAY,EAEb,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,uBAAuB,EAAE,CAAC;AAExC;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EACxE,MAAM,EACN,QAAQ,EACR,CAAC,EACD,SAAc,EACd,UAAU,EACV,OAAe,EACf,aAAa,EACb,aAAyB,EACzB,mBAAmB,EACnB,sBAAkC,EAClC,iBAAiB,EACjB,UAAU,EAAE,cAAc,EAC1B,SAA6C,EAC7C,QAAiC,EACjC,MAAM,EAAE,cAAc,EACtB,UAAU,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,oBAA2B,EAC3B,qBAAqB,EACrB,kBAA0B,GAC3B,EAAE,uBAAuB,CAAC,CAAC,CAAC,2CA4c5B;AAED,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"EntityFormRenderer.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityFormRenderer.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EACV,uBAAuB,EACvB,YAAY,EAEb,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,uBAAuB,EAAE,CAAC;AAExC;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EACxE,MAAM,EACN,QAAQ,EACR,CAAC,EACD,SAAc,EACd,UAAU,EACV,OAAe,EACf,aAAa,EACb,aAAyB,EACzB,mBAAmB,EACnB,sBAAkC,EAClC,iBAAiB,EACjB,UAAU,EAAE,cAAc,EAC1B,SAA6C,EAC7C,MAAM,EAAE,cAAc,EACtB,UAAU,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,oBAA2B,EAC3B,qBAAqB,EACrB,kBAA0B,GAC3B,EAAE,uBAAuB,CAAC,CAAC,CAAC,2CAkb5B;AAED,eAAe,kBAAkB,CAAC"}
@@ -15,7 +15,7 @@ import { FormProvider } from 'react-hook-form';
15
15
  import { Badge, Button, DropdownMenu, Grid, Stack, Spinner, } from '@donotdev/components';
16
16
  import { hasRoleAccess, useTranslation } from '@donotdev/core';
17
17
  import { useNavigate } from '../../routing';
18
- import { DisplayFieldRenderer, FormFieldRenderer, UploadProvider, useEntityForm, useUnsavedChangesWarning, useConfirmNavigation, useFormStore, } from '@donotdev/crud';
18
+ import { DisplayFieldRenderer, FormFieldRenderer, UploadProvider, useEntityForm, useUnsavedChangesWarning, useFormStore, } from '@donotdev/crud';
19
19
  import { useAuthSafe } from '../../utils/useAuthSafe';
20
20
  /**
21
21
  * EntityFormRenderer - Dumb component that renders a form from entity definition.
@@ -30,7 +30,7 @@ import { useAuthSafe } from '../../utils/useAuthSafe';
30
30
  * @since 0.0.1
31
31
  * @author AMBROISE PARK Consulting
32
32
  */
33
- export function EntityFormRenderer({ entity, onSubmit, t, className = '', submitText, loading = false, defaultValues, submitVariant = 'primary', secondaryButtonText, secondaryButtonVariant = 'outline', onSecondarySubmit, viewerRole: viewerRoleProp, operation = defaultValues ? 'edit' : 'create', autoSave = operation === 'create', formId: externalFormId, cancelText, cancelPath, successPath, onCancel, warnOnUnsavedChanges = true, unsavedChangesMessage, hideVisibilityInfo = false, }) {
33
+ export function EntityFormRenderer({ entity, onSubmit, t, className = '', submitText, loading = false, defaultValues, submitVariant = 'primary', secondaryButtonText, secondaryButtonVariant = 'outline', onSecondarySubmit, viewerRole: viewerRoleProp, operation = defaultValues ? 'edit' : 'create', formId: externalFormId, cancelText, cancelPath, successPath, onCancel, warnOnUnsavedChanges = true, unsavedChangesMessage, hideVisibilityInfo = false, }) {
34
34
  const navigate = useNavigate();
35
35
  // Auto-detect role from auth; prop overrides (e.g. View-As preview)
36
36
  const authRole = useAuthSafe('userRole');
@@ -85,7 +85,6 @@ export function EntityFormRenderer({ entity, onSubmit, t, className = '', submit
85
85
  defaultValues,
86
86
  viewerRole,
87
87
  t: translate,
88
- autoSave,
89
88
  });
90
89
  const { control, handleSubmit, formState: { errors }, fields: rawFields, formStatus, uploadProgress, cleanup, isDirty, hasUserInteracted, resetForm, } = form;
91
90
  const isDirtyForBlocking = isDirty && hasUserInteracted;
@@ -105,14 +104,13 @@ export function EntityFormRenderer({ entity, onSubmit, t, className = '', submit
105
104
  return fieldRoleIndex <= previewRoleIndex;
106
105
  });
107
106
  }, [rawFields, canPreviewRole, previewRole]);
108
- // Subscribe to setIsDirty action via React-compatible selector
109
- const setFormIsDirty = useFormStore((state) => state.setIsDirty);
110
107
  // Sync isDirty to FormStore (single source of truth)
108
+ // Use getState() to avoid subscribing to the action reference (Zustand anti-pattern)
111
109
  useEffect(() => {
112
110
  if (formId) {
113
- setFormIsDirty(formId, isDirtyForBlocking);
111
+ useFormStore.getState().setIsDirty(formId, isDirtyForBlocking);
114
112
  }
115
- }, [formId, isDirtyForBlocking, setFormIsDirty]);
113
+ }, [formId, isDirtyForBlocking]);
116
114
  // Cleanup on unmount
117
115
  useEffect(() => {
118
116
  return cleanup;
@@ -122,39 +120,24 @@ export function EntityFormRenderer({ entity, onSubmit, t, className = '', submit
122
120
  tCrud('messages.unsavedChangesLeave', {
123
121
  defaultValue: 'You have unsaved changes. Are you sure you want to leave?',
124
122
  }), [unsavedChangesMessage, tCrud]);
125
- const unsavedChangesDiscardMessage = useMemo(() => unsavedChangesMessage ||
126
- tCrud('messages.unsavedChangesDiscard', {
127
- defaultValue: 'You have unsaved changes. Discard them?',
128
- }), [unsavedChangesMessage, tCrud]);
129
123
  // Warn about unsaved changes when navigating away (browser refresh/close)
130
- // SPA navigation blocking is handled by router hooks via FormStore
124
+ // beforeunload is the only place we can't toast browser requires native dialog
131
125
  useUnsavedChangesWarning({
132
126
  isDirty: isDirtyForBlocking,
133
127
  enabled: warnOnUnsavedChanges,
134
128
  message: unsavedChangesLeaveMessage,
135
129
  });
136
- // Use framework helper for cancel confirmation (DRY, SSR-safe)
137
- const confirmNavigation = useConfirmNavigation(isDirtyForBlocking, unsavedChangesDiscardMessage);
138
- // Handle cancel with confirmation
139
- const handleCancel = async () => {
140
- if (isDirtyForBlocking) {
141
- const confirmed = await confirmNavigation();
142
- if (!confirmed)
143
- return;
144
- }
130
+ // Handle cancel auto-save ensures draft is persisted, no confirm needed
131
+ const handleCancel = () => {
145
132
  resetForm();
146
- // If onCancel callback provided, use it (takes precedence)
147
133
  if (onCancel) {
148
134
  onCancel();
149
135
  }
136
+ else if (cancelPath) {
137
+ navigate(cancelPath);
138
+ }
150
139
  else {
151
- // Otherwise, navigate back or to cancelPath if provided
152
- if (cancelPath) {
153
- navigate(cancelPath);
154
- }
155
- else {
156
- navigate('back');
157
- }
140
+ navigate('back');
158
141
  }
159
142
  };
160
143
  // Wrap onSubmit to await the result before navigating
@@ -1 +1 @@
1
- {"version":3,"file":"EntityList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityList.tsx"],"names":[],"mappings":"AA0CA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAmB,EACnB,UAAqB,EACrB,QAAQ,EAAE,YAAY,EACtB,YAAY,EACZ,UAAiB,GAClB,EAAE,eAAe,2CAiTjB"}
1
+ {"version":3,"file":"EntityList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityList.tsx"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAmB,EACnB,UAAqB,EACrB,QAAQ,EAAE,YAAY,EACtB,YAAY,EACZ,UAAiB,GAClB,EAAE,eAAe,2CA2QjB"}
@@ -9,7 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
9
9
  * **Routing:** Convention basePath = `/${collection}`. View/Edit = basePath/:id, Create = basePath/new.
10
10
  * Override basePath for nested routes; use onClick(id) to open sheet instead of navigating.
11
11
  *
12
- * @version 0.3.2
12
+ * @version 0.4.0
13
13
  * @since 0.0.1
14
14
  * @author AMBROISE PARK Consulting
15
15
  */
@@ -18,7 +18,7 @@ import { useMemo, useCallback, useState } from 'react';
18
18
  import { DataTable, Button, Stack, ActionButton, Section, Input, } from '@donotdev/components';
19
19
  import { useTranslation } from '@donotdev/core';
20
20
  import { useNavigate } from '../../routing';
21
- import { translateFieldLabel, useCrud, useCrudList, EntityFilters, matchesFilter, formatValue, useCrudFilters, } from '@donotdev/crud';
21
+ import { translateFieldLabel, useCrud, useCrudList, EntityFilters, formatValue, } from '@donotdev/crud';
22
22
  /**
23
23
  * Entity List Component - Table view for admin/internal operations
24
24
  *
@@ -37,11 +37,11 @@ export function EntityList({ entity, basePath, onClick, hideFilters = false, pag
37
37
  // For server-side, we need to track pageSize state (DataTable will call onPageSizeChange)
38
38
  // For client-side, we just pass pageSizeProp to DataTable and it handles its own state
39
39
  const [serverPageSize, setServerPageSize] = useState(pageSizeProp);
40
- // Separation of Concerns:
41
- // Client mode: fetches ALL items, DataTable handles pagination
42
- // Server mode: fetches per page, controlled pagination
43
- const { data: listData, loading, mutate: refreshList, } = useCrudList(entity, {
40
+ const [searchQuery, setSearchQuery] = useState('');
41
+ // useCrudList now handles client-side filter + search + sort via `processed`
42
+ const { data: listData, items: rawData, processed: filteredData, loading, mutate: refreshList, } = useCrudList(entity, {
44
43
  pagination,
44
+ searchQuery,
45
45
  ...(queryOptions && { queryOptions }),
46
46
  ...(pagination === 'server' && {
47
47
  page: currentPage,
@@ -51,14 +51,8 @@ export function EntityList({ entity, basePath, onClick, hideFilters = false, pag
51
51
  // useCrud -> handles actions (delete)
52
52
  const { delete: deleteItem } = useCrud(entity);
53
53
  const { t: tCrud } = useTranslation('crud');
54
- const data = listData?.items || [];
55
54
  // Entity + crud namespaces so formatValue can resolve crud:price.* etc.
56
55
  const { t } = useTranslation([entity.namespace, 'crud']);
57
- // Get filters for applying to data (EntityFilters manages its own state)
58
- const { filters } = useCrudFilters({
59
- collection: entity.collection,
60
- });
61
- const [searchQuery, setSearchQuery] = useState('');
62
56
  // Refresh handler - triggers manual refetch in useList
63
57
  const handleRefresh = useCallback(async () => {
64
58
  await refreshList();
@@ -84,36 +78,6 @@ export function EntityList({ entity, basePath, onClick, hideFilters = false, pag
84
78
  const handleDelete = useCallback(async (itemId) => {
85
79
  await deleteItem(itemId);
86
80
  }, [deleteItem]);
87
- // Apply search and filters to data
88
- // @todo Server-side filtering: Currently only handles client-side filtering.
89
- // When pagination='server', filters should be sent to the server via useCrudList options.
90
- // Until then, filters are applied client-side after fetching.
91
- const filteredData = useMemo(() => {
92
- let result = data;
93
- // Apply search query (searches all fields)
94
- if (searchQuery) {
95
- const searchLower = searchQuery.toLowerCase();
96
- result = result.filter((item) => {
97
- return Object.values(item).some((value) => {
98
- if (value === null || value === undefined)
99
- return false;
100
- return String(value).toLowerCase().includes(searchLower);
101
- });
102
- });
103
- }
104
- // Apply column filters
105
- if (Object.keys(filters).length > 0) {
106
- result = result.filter((item) => {
107
- return Object.entries(filters).every(([fieldName, filterValue]) => {
108
- const itemValue = item[fieldName];
109
- const fieldConfig = entity.fields[fieldName];
110
- const fieldType = fieldConfig?.type || 'text';
111
- return matchesFilter(itemValue, filterValue, fieldType);
112
- });
113
- });
114
- }
115
- return result;
116
- }, [data, searchQuery, filters, entity.fields]);
117
81
  // Generate columns from entity.listFields or entity.fields
118
82
  const columns = useMemo(() => {
119
83
  const fieldsToShow = entity.listFields || Object.keys(entity.fields);
@@ -170,7 +134,7 @@ export function EntityList({ entity, basePath, onClick, hideFilters = false, pag
170
134
  defaultValue: `Browse ${entityName} - Filters`,
171
135
  }), collapsible: true, defaultOpen: true, children: _jsxs(Stack, { children: [_jsxs(Stack, { direction: "row", gap: "tight", align: "center", className: "dndev-w-full", style: { display: 'grid', gridTemplateColumns: '1fr auto auto' }, children: [_jsx(Input, { placeholder: tCrud('search.placeholder', {
172
136
  defaultValue: 'Search...',
173
- }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), icon: Search, className: "dndev-w-full" }), _jsx(Button, { icon: RefreshCw, variant: "outline", onClick: handleRefresh, disabled: loading, display: "compact", "aria-label": tCrud('refresh', { defaultValue: 'Refresh' }) }), _jsx(Button, { icon: Plus, onClick: handleCreate, display: "compact", children: tCrud('addNew', { defaultValue: 'Add New' }) })] }), !hideFilters && (_jsx(EntityFilters, { entity: entity, data: data, fieldsToFilter: entity.listFields }))] }) }), _jsx(Section, { title: loading
137
+ }), value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), icon: Search, className: "dndev-w-full" }), _jsx(Button, { icon: RefreshCw, variant: "outline", onClick: handleRefresh, disabled: loading, display: "compact", "aria-label": tCrud('refresh', { defaultValue: 'Refresh' }) }), _jsx(Button, { icon: Plus, onClick: handleCreate, display: "compact", children: tCrud('addNew', { defaultValue: 'Add New' }) })] }), !hideFilters && (_jsx(EntityFilters, { entity: entity, data: rawData, fieldsToFilter: entity.listFields }))] }) }), _jsx(Section, { title: loading
174
138
  ? tCrud('results.title.fetching', {
175
139
  defaultValue: 'Fetching...',
176
140
  })
@@ -10,6 +10,7 @@
10
10
  import type { EntityRecommendationsProps } from '@donotdev/core';
11
11
  /**
12
12
  * Displays a grid of related entity cards.
13
+ * Uses useCrudCardList (listCard schema) so cards render identically to EntityCardList.
13
14
  *
14
15
  * @example
15
16
  * <EntityRecommendations
@@ -1 +1 @@
1
- {"version":3,"file":"EntityRecommendations.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityRecommendations.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAMjE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,IAAQ,EACR,SAAS,GACV,EAAE,0BAA0B,kDAuB5B"}
1
+ {"version":3,"file":"EntityRecommendations.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityRecommendations.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAMjE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,IAAQ,EACR,SAAS,GACV,EAAE,0BAA0B,kDAuB5B"}
@@ -1,10 +1,11 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Section } from '@donotdev/components';
4
- import { useCrudList } from '@donotdev/crud';
4
+ import { useCrudCardList } from '@donotdev/crud';
5
5
  import { CrudCard } from './CrudCardLink';
6
6
  /**
7
7
  * Displays a grid of related entity cards.
8
+ * Uses useCrudCardList (listCard schema) so cards render identically to EntityCardList.
8
9
  *
9
10
  * @example
10
11
  * <EntityRecommendations
@@ -20,7 +21,7 @@ import { CrudCard } from './CrudCardLink';
20
21
  * />
21
22
  */
22
23
  export function EntityRecommendations({ entity, queryOptions, title, basePath, cols = 3, className, }) {
23
- const { items, loading } = useCrudList(entity, {
24
+ const { items, loading } = useCrudCardList(entity, {
24
25
  queryOptions,
25
26
  });
26
27
  if (!loading && items.length === 0) {