@donotdev/ui 0.0.9 → 0.0.11

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 (134) hide show
  1. package/dist/crud/components/EntityCardList.d.ts +16 -0
  2. package/dist/crud/components/EntityCardList.d.ts.map +1 -0
  3. package/dist/crud/components/EntityCardList.js +175 -0
  4. package/dist/crud/components/EntityDisplayRenderer.d.ts +13 -21
  5. package/dist/crud/components/EntityDisplayRenderer.d.ts.map +1 -1
  6. package/dist/crud/components/EntityDisplayRenderer.js +138 -23
  7. package/dist/crud/components/EntityFormRenderer.d.ts +18 -0
  8. package/dist/crud/components/EntityFormRenderer.d.ts.map +1 -0
  9. package/dist/crud/components/EntityFormRenderer.js +275 -0
  10. package/dist/crud/components/EntityList.d.ts +14 -0
  11. package/dist/crud/components/EntityList.d.ts.map +1 -0
  12. package/dist/crud/components/EntityList.js +201 -0
  13. package/dist/crud/components/index.d.ts +7 -5
  14. package/dist/crud/components/index.d.ts.map +1 -1
  15. package/dist/crud/components/index.js +6 -5
  16. package/dist/dndev.css +467 -143
  17. package/dist/index.js +4 -64
  18. package/dist/internal/common/RouteErrorFallback.d.ts.map +1 -1
  19. package/dist/internal/common/RouteErrorFallback.js +1 -2
  20. package/dist/internal/layout/DnDevLayout.d.ts.map +1 -1
  21. package/dist/internal/layout/DnDevLayout.js +3 -2
  22. package/dist/internal/layout/components/AutoMetaTags.d.ts.map +1 -1
  23. package/dist/internal/layout/components/AutoMetaTags.js +36 -6
  24. package/dist/internal/layout/components/NextJsAutoMetaTags.d.ts.map +1 -1
  25. package/dist/internal/layout/components/NextJsAutoMetaTags.js +38 -10
  26. package/dist/internal/layout/components/footer/FooterBranding.d.ts +0 -2
  27. package/dist/internal/layout/components/footer/FooterBranding.d.ts.map +1 -1
  28. package/dist/internal/layout/components/footer/FooterBranding.js +4 -8
  29. package/dist/internal/layout/components/footer/FooterCopyright.d.ts +0 -2
  30. package/dist/internal/layout/components/footer/FooterCopyright.d.ts.map +1 -1
  31. package/dist/internal/layout/components/footer/FooterCopyright.js +2 -6
  32. package/dist/internal/layout/config/presets/moolti.js +2 -2
  33. package/dist/internal/layout/zones/DnDevFooter.d.ts.map +1 -1
  34. package/dist/internal/layout/zones/DnDevFooter.js +2 -2
  35. package/dist/routing/GoTo.d.ts +1 -1
  36. package/dist/routing/GoTo.d.ts.map +1 -1
  37. package/dist/routing/GoTo.js +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/useFormNavigationBlocker.d.ts +14 -0
  41. package/dist/routing/hooks/useFormNavigationBlocker.d.ts.map +1 -0
  42. package/dist/routing/hooks/useFormNavigationBlocker.js +42 -0
  43. package/dist/routing/hooks/useNavigate.next.d.ts +1 -1
  44. package/dist/routing/hooks/useNavigate.next.d.ts.map +1 -1
  45. package/dist/routing/hooks/useNavigate.next.js +7 -1
  46. package/dist/routing/hooks/useNavigate.vite.d.ts +1 -1
  47. package/dist/routing/hooks/useNavigate.vite.d.ts.map +1 -1
  48. package/dist/routing/hooks/useNavigate.vite.js +7 -1
  49. package/dist/styles/index.css +467 -143
  50. package/dist/utils/index.d.ts +1 -0
  51. package/dist/utils/index.d.ts.map +1 -1
  52. package/dist/utils/index.js +1 -0
  53. package/dist/utils/useFormStoreSafe.d.ts +59 -0
  54. package/dist/utils/useFormStoreSafe.d.ts.map +1 -0
  55. package/dist/utils/useFormStoreSafe.js +115 -0
  56. package/package.json +12 -12
  57. package/dist/crud/components/DisplayFieldRenderer.d.ts +0 -26
  58. package/dist/crud/components/DisplayFieldRenderer.d.ts.map +0 -1
  59. package/dist/crud/components/DisplayFieldRenderer.js +0 -107
  60. package/dist/crud/components/fields/display/AvatarFieldDisplay.d.ts +0 -23
  61. package/dist/crud/components/fields/display/AvatarFieldDisplay.d.ts.map +0 -1
  62. package/dist/crud/components/fields/display/AvatarFieldDisplay.js +0 -38
  63. package/dist/crud/components/fields/display/BadgeFieldDisplay.d.ts +0 -21
  64. package/dist/crud/components/fields/display/BadgeFieldDisplay.d.ts.map +0 -1
  65. package/dist/crud/components/fields/display/BadgeFieldDisplay.js +0 -31
  66. package/dist/crud/components/fields/display/ButtonFieldDisplay.d.ts +0 -29
  67. package/dist/crud/components/fields/display/ButtonFieldDisplay.d.ts.map +0 -1
  68. package/dist/crud/components/fields/display/ButtonFieldDisplay.js +0 -12
  69. package/dist/crud/components/fields/display/CheckboxFieldDisplay.d.ts +0 -21
  70. package/dist/crud/components/fields/display/CheckboxFieldDisplay.d.ts.map +0 -1
  71. package/dist/crud/components/fields/display/CheckboxFieldDisplay.js +0 -27
  72. package/dist/crud/components/fields/display/DateFieldDisplay.d.ts +0 -24
  73. package/dist/crud/components/fields/display/DateFieldDisplay.d.ts.map +0 -1
  74. package/dist/crud/components/fields/display/DateFieldDisplay.js +0 -42
  75. package/dist/crud/components/fields/display/DropdownDisplay.d.ts +0 -21
  76. package/dist/crud/components/fields/display/DropdownDisplay.d.ts.map +0 -1
  77. package/dist/crud/components/fields/display/DropdownDisplay.js +0 -25
  78. package/dist/crud/components/fields/display/FileFieldDisplay.d.ts +0 -21
  79. package/dist/crud/components/fields/display/FileFieldDisplay.d.ts.map +0 -1
  80. package/dist/crud/components/fields/display/FileFieldDisplay.js +0 -25
  81. package/dist/crud/components/fields/display/GeoPointFieldDisplay.d.ts +0 -25
  82. package/dist/crud/components/fields/display/GeoPointFieldDisplay.d.ts.map +0 -1
  83. package/dist/crud/components/fields/display/GeoPointFieldDisplay.js +0 -25
  84. package/dist/crud/components/fields/display/HiddenFieldDisplay.d.ts +0 -30
  85. package/dist/crud/components/fields/display/HiddenFieldDisplay.d.ts.map +0 -1
  86. package/dist/crud/components/fields/display/HiddenFieldDisplay.js +0 -12
  87. package/dist/crud/components/fields/display/ImageFieldDisplay.d.ts +0 -24
  88. package/dist/crud/components/fields/display/ImageFieldDisplay.d.ts.map +0 -1
  89. package/dist/crud/components/fields/display/ImageFieldDisplay.js +0 -38
  90. package/dist/crud/components/fields/display/LinkFieldDisplay.d.ts +0 -22
  91. package/dist/crud/components/fields/display/LinkFieldDisplay.d.ts.map +0 -1
  92. package/dist/crud/components/fields/display/LinkFieldDisplay.js +0 -47
  93. package/dist/crud/components/fields/display/MapFieldDisplay.d.ts +0 -25
  94. package/dist/crud/components/fields/display/MapFieldDisplay.d.ts.map +0 -1
  95. package/dist/crud/components/fields/display/MapFieldDisplay.js +0 -25
  96. package/dist/crud/components/fields/display/MultiDropdownDisplay.d.ts +0 -22
  97. package/dist/crud/components/fields/display/MultiDropdownDisplay.d.ts.map +0 -1
  98. package/dist/crud/components/fields/display/MultiDropdownDisplay.js +0 -25
  99. package/dist/crud/components/fields/display/MultiInputTextFieldDisplay.d.ts +0 -22
  100. package/dist/crud/components/fields/display/MultiInputTextFieldDisplay.d.ts.map +0 -1
  101. package/dist/crud/components/fields/display/MultiInputTextFieldDisplay.js +0 -25
  102. package/dist/crud/components/fields/display/NumberFieldDisplay.d.ts +0 -24
  103. package/dist/crud/components/fields/display/NumberFieldDisplay.d.ts.map +0 -1
  104. package/dist/crud/components/fields/display/NumberFieldDisplay.js +0 -29
  105. package/dist/crud/components/fields/display/PasswordFieldDisplay.d.ts +0 -24
  106. package/dist/crud/components/fields/display/PasswordFieldDisplay.d.ts.map +0 -1
  107. package/dist/crud/components/fields/display/PasswordFieldDisplay.js +0 -31
  108. package/dist/crud/components/fields/display/PhoneNumberDisplay.d.ts +0 -22
  109. package/dist/crud/components/fields/display/PhoneNumberDisplay.d.ts.map +0 -1
  110. package/dist/crud/components/fields/display/PhoneNumberDisplay.js +0 -26
  111. package/dist/crud/components/fields/display/RadioFieldDisplay.d.ts +0 -22
  112. package/dist/crud/components/fields/display/RadioFieldDisplay.d.ts.map +0 -1
  113. package/dist/crud/components/fields/display/RadioFieldDisplay.js +0 -25
  114. package/dist/crud/components/fields/display/RangeFieldDisplay.d.ts +0 -22
  115. package/dist/crud/components/fields/display/RangeFieldDisplay.d.ts.map +0 -1
  116. package/dist/crud/components/fields/display/RangeFieldDisplay.js +0 -25
  117. package/dist/crud/components/fields/display/ReferenceFieldDisplay.d.ts +0 -22
  118. package/dist/crud/components/fields/display/ReferenceFieldDisplay.d.ts.map +0 -1
  119. package/dist/crud/components/fields/display/ReferenceFieldDisplay.js +0 -26
  120. package/dist/crud/components/fields/display/RichTextDisplay.d.ts +0 -25
  121. package/dist/crud/components/fields/display/RichTextDisplay.d.ts.map +0 -1
  122. package/dist/crud/components/fields/display/RichTextDisplay.js +0 -104
  123. package/dist/crud/components/fields/display/TextAreaDisplay.d.ts +0 -22
  124. package/dist/crud/components/fields/display/TextAreaDisplay.d.ts.map +0 -1
  125. package/dist/crud/components/fields/display/TextAreaDisplay.js +0 -25
  126. package/dist/crud/components/fields/display/TextFieldDisplay.d.ts +0 -42
  127. package/dist/crud/components/fields/display/TextFieldDisplay.d.ts.map +0 -1
  128. package/dist/crud/components/fields/display/TextFieldDisplay.js +0 -97
  129. package/dist/crud/components/fields/display/TimestampFieldDisplay.d.ts +0 -22
  130. package/dist/crud/components/fields/display/TimestampFieldDisplay.d.ts.map +0 -1
  131. package/dist/crud/components/fields/display/TimestampFieldDisplay.js +0 -33
  132. package/dist/crud/components/fields/display/index.d.ts +0 -32
  133. package/dist/crud/components/fields/display/index.d.ts.map +0 -1
  134. package/dist/crud/components/fields/display/index.js +0 -32
@@ -1 +1 @@
1
- {"version":3,"file":"RouteErrorFallback.d.ts","sourceRoot":"","sources":["../../../src/internal/common/RouteErrorFallback.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,UAAU,uBAAuB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC,uBAAuB,CAqPrE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"RouteErrorFallback.d.ts","sourceRoot":"","sources":["../../../src/internal/common/RouteErrorFallback.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,UAAU,uBAAuB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC,uBAAuB,CAyPrE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -14,7 +14,6 @@ import { useEffect, useState } from 'react';
14
14
  import { Button, BUTTON_VARIANT, Card, CARD_VARIANT, Alert, ALERT_VARIANT, Stack, } from '@donotdev/components';
15
15
  import { handleError, useTranslation } from '@donotdev/core';
16
16
  import { useLocation, useBack } from '@donotdev/ui/routing/hooks';
17
- import { PageContainer } from '../../components/layout/PageContainer';
18
17
  // Platform-specific hooks via conditional exports
19
18
  import { Link } from '../../routing/Link';
20
19
  /**
@@ -95,7 +94,7 @@ export const RouteErrorFallback = ({ error, resetError, componentStack, eventId,
95
94
  }
96
95
  }
97
96
  };
98
- return (_jsx(PageContainer, { children: _jsx(Card, { variant: CARD_VARIANT.DEFAULT, elevated: true, className: "dndev-w-full", style: { maxWidth: '65ch', margin: '0 auto' }, children: _jsxs(Stack, { gap: "large", align: "center", className: "dndev-text-center", children: [_jsx(Stack, { align: "center", justify: "center", style: {
97
+ return (_jsx("div", { className: "dndev-container", "data-variant": "standard", "data-centered": "true", children: _jsx(Card, { variant: CARD_VARIANT.DEFAULT, elevated: true, className: "dndev-w-full", style: { maxWidth: '65ch', margin: '0 auto' }, children: _jsxs(Stack, { gap: "large", align: "center", className: "dndev-text-center", children: [_jsx(Stack, { align: "center", justify: "center", style: {
99
98
  width: '5rem',
100
99
  height: '5rem',
101
100
  backgroundColor: 'rgba(220, 38, 38, 0.1)',
@@ -1 +1 @@
1
- {"version":3,"file":"DnDevLayout.d.ts","sourceRoot":"","sources":["../../../src/internal/layout/DnDevLayout.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAIV,YAAY,EACb,MAAM,gBAAgB,CAAC;AA0CxB,UAAU,yBAAyB;IACjC,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,GAAI,kCAIzB,yBAAyB,4CA2T3B,CAAC"}
1
+ {"version":3,"file":"DnDevLayout.d.ts","sourceRoot":"","sources":["../../../src/internal/layout/DnDevLayout.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAIV,YAAY,EACb,MAAM,gBAAgB,CAAC;AA0CxB,UAAU,yBAAyB;IACjC,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,GAAI,kCAIzB,yBAAyB,4CAwT3B,CAAC"}
@@ -66,8 +66,9 @@ export const DnDevLayout = ({ children, layout, className, }) => {
66
66
  const app = useAppConfig('app');
67
67
  // Subscribe to language changes for zone re-renders
68
68
  useTranslation('dndev');
69
- // Breadcrumbs from layout prop only
69
+ // Layout config props
70
70
  const breadcrumbs = layout?.breadcrumbs ?? 'smart';
71
+ const footerMode = layout?.footerMode;
71
72
  // Route detection for animations
72
73
  const location = useLocation();
73
74
  const pathname = location.pathname;
@@ -246,5 +247,5 @@ export const DnDevLayout = ({ children, layout, className, }) => {
246
247
  };
247
248
  startTransition(updateDOM);
248
249
  }, [effectivePreset]);
249
- return (_jsxs("div", { className: cn('dndev-layout', className), children: [resolvedHeader, resolvedSidebar, _jsxs("main", { ref: mainRef, role: "main", className: "main", children: [breadcrumbs !== 'never' && (_jsx("div", { className: "breadcrumbs-container", children: _jsx(Breadcrumbs, { variant: breadcrumbs === 'always' ? 'default' : 'smart' }) })), isDev() && (_jsx(Suspense, { fallback: null, children: _jsx(DebugTools, {}) })), _jsx(Stack, { flex: "1", className: "dndev-min-h-0", children: content }, pathname), resolvedFooter && (_jsx("div", { className: "footer-mobile", children: resolvedFooter }))] }), resolvedFooter && _jsx("div", { className: "footer-desktop", children: resolvedFooter }), resolvedMergedBar, _jsx(GoToWrapper, {})] }));
250
+ return (_jsxs("div", { className: cn('dndev-layout', className), "data-footer-mode": footerMode, children: [resolvedHeader, resolvedSidebar, _jsxs("main", { ref: mainRef, role: "main", className: "main", children: [breadcrumbs !== 'never' && (_jsx("div", { className: "breadcrumbs-container", children: _jsx(Breadcrumbs, { variant: breadcrumbs === 'always' ? 'default' : 'smart' }) })), isDev() && (_jsx(Suspense, { fallback: null, children: _jsx(DebugTools, {}) })), content] }), resolvedFooter, resolvedMergedBar, _jsx(GoToWrapper, {})] }));
250
251
  };
@@ -1 +1 @@
1
- {"version":3,"file":"AutoMetaTags.d.ts","sourceRoot":"","sources":["../../../../src/internal/layout/components/AutoMetaTags.tsx"],"names":[],"mappings":"AA0DA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAwC3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,EAAE,aAoN1B,CAAC;AAEF;;;GAGG;AAEH,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"AutoMetaTags.d.ts","sourceRoot":"","sources":["../../../../src/internal/layout/components/AutoMetaTags.tsx"],"names":[],"mappings":"AA0DA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAyE3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,EAAE,aAoN1B,CAAC;AAEF;;;GAGG;AAEH,eAAe,YAAY,CAAC"}
@@ -49,12 +49,34 @@ import { useMemo } from 'react';
49
49
  import { Helmet } from 'react-helmet-async';
50
50
  import { useTranslation, useIsClient } from '@donotdev/core';
51
51
  import { getDndevConfig } from '@donotdev/core';
52
- import { useSeoConfig, useAppConfig } from '@donotdev/core';
52
+ import { useSeoConfig, useAppConfig, getPlatformEnvVar } from '@donotdev/core';
53
53
  import { AssetResolver } from '@donotdev/ui';
54
54
  // Platform-specific hooks via conditional exports
55
55
  import { useLocation } from '@donotdev/ui/routing/hooks';
56
56
  /**
57
- * Get route metadata from window object (injected by build system)
57
+ * Match pathname against route path pattern (e.g. /cars/:id or /blog/:slug/comments/:id).
58
+ * Supports arbitrary nesting: any depth, any mix of static and :param segments.
59
+ */
60
+ function matchRoutePath(pattern, pathname) {
61
+ const patternSegments = pattern.split('/').filter(Boolean);
62
+ const pathnameSegments = pathname.split('/').filter(Boolean);
63
+ if (patternSegments.length !== pathnameSegments.length)
64
+ return false;
65
+ for (let i = 0; i < patternSegments.length; i++) {
66
+ const segment = patternSegments[i];
67
+ const pathSegment = pathnameSegments[i];
68
+ if (segment === undefined || pathSegment === undefined)
69
+ return false;
70
+ if (segment.startsWith(':'))
71
+ continue; // param matches any value
72
+ if (segment !== pathSegment)
73
+ return false;
74
+ }
75
+ return true;
76
+ }
77
+ /**
78
+ * Get route metadata from window object (injected by build system).
79
+ * Resolves dynamic routes by pattern (e.g. /cars/:id) so PageMeta (including namespace) is used for SEO.
58
80
  */
59
81
  function getRouteMetadata(pathname, isClient) {
60
82
  if (!isClient)
@@ -63,12 +85,20 @@ function getRouteMetadata(pathname, isClient) {
63
85
  const config = getDndevConfig();
64
86
  const routeData = config?.routes?.mapping;
65
87
  if (routeData && Array.isArray(routeData)) {
66
- const route = routeData.find((r) => r.path === pathname);
67
- if (route) {
88
+ // 1. Exact match (static routes)
89
+ let route = routeData.find((r) => r.path === pathname);
90
+ if (route)
91
+ return route.meta;
92
+ // 2. Pattern match (dynamic routes: /cars/:id, /blog/:slug/comments/:id, etc.)
93
+ const dynamicRoutes = routeData.filter((r) => typeof r.path === 'string' && r.path.includes(':'));
94
+ const matches = dynamicRoutes.filter((r) => matchRoutePath(r.path, pathname));
95
+ // Prefer most specific (longest path) so /blog/:slug/comments wins over /blog/:slug for /blog/a/comments
96
+ if (matches.length > 0) {
97
+ route = matches.reduce((best, r) => r.path.length > best.path.length ? r : best);
68
98
  return route.meta;
69
99
  }
70
100
  }
71
- // Extract potential namespace from path
101
+ // Fallback: first path segment as namespace (e.g. /cars/abc123 → ns: cars)
72
102
  const segments = pathname.split('/').filter(Boolean);
73
103
  if (segments.length > 0) {
74
104
  return { ns: segments[0] };
@@ -97,7 +127,7 @@ export const AutoMetaTags = () => {
97
127
  return null;
98
128
  const { defaultNamespace = 'home', defaultImage, twitterHandle, staticTags = {}, } = seoConfig;
99
129
  const siteName = useAppConfig('name');
100
- const baseUrl = useAppConfig('url');
130
+ const baseUrl = getPlatformEnvVar('APP_URL') || '';
101
131
  // Get route metadata
102
132
  const routeMeta = getRouteMetadata(location.pathname, isClient);
103
133
  // Memoize computed values for performance
@@ -1 +1 @@
1
- {"version":3,"file":"NextJsAutoMetaTags.d.ts","sourceRoot":"","sources":["../../../../src/internal/layout/components/NextJsAutoMetaTags.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,UAAU,cAAc;IACtB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED,UAAU,aAAa;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,uBAAuB;IAC/B,kCAAkC;IAClC,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AA+BD;;;;;;;;GAQG;AACH,QAAA,MAAM,kBAAkB,EAAE,aAAa,CAAC,uBAAuB,CA8L9D,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"NextJsAutoMetaTags.d.ts","sourceRoot":"","sources":["../../../../src/internal/layout/components/NextJsAutoMetaTags.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,UAAU,cAAc;IACtB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED,UAAU,aAAa;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,uBAAuB;IAC/B,kCAAkC;IAClC,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AA+DD;;;;;;;;GAQG;AACH,QAAA,MAAM,kBAAkB,EAAE,aAAa,CAAC,uBAAuB,CA8L9D,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -17,9 +17,31 @@ import { usePathname } from 'next/navigation';
17
17
  import { useMemo } from 'react';
18
18
  import { Helmet } from 'react-helmet-async';
19
19
  import { useTranslation, getDndevConfig, useIsClient } from '@donotdev/core';
20
- import { useAppConfig } from '@donotdev/core';
20
+ import { useAppConfig, getPlatformEnvVar } from '@donotdev/core';
21
21
  /**
22
- * Get route metadata from config (injected by build system)
22
+ * Match pathname against route path pattern (e.g. /cars/:id or /blog/:slug/comments/:id).
23
+ * Supports arbitrary nesting: any depth, any mix of static and :param segments.
24
+ */
25
+ function matchRoutePath(pattern, pathname) {
26
+ const patternSegments = pattern.split('/').filter(Boolean);
27
+ const pathnameSegments = pathname.split('/').filter(Boolean);
28
+ if (patternSegments.length !== pathnameSegments.length)
29
+ return false;
30
+ for (let i = 0; i < patternSegments.length; i++) {
31
+ const segment = patternSegments[i];
32
+ const pathSegment = pathnameSegments[i];
33
+ if (segment === undefined || pathSegment === undefined)
34
+ return false;
35
+ if (segment.startsWith(':'))
36
+ continue; // param matches any value
37
+ if (segment !== pathSegment)
38
+ return false;
39
+ }
40
+ return true;
41
+ }
42
+ /**
43
+ * Get route metadata from config (injected by build system).
44
+ * Resolves dynamic routes by pattern so PageMeta (including namespace) is used for SEO.
23
45
  */
24
46
  function getRouteMetadata(pathname, isClient) {
25
47
  if (!isClient)
@@ -28,16 +50,22 @@ function getRouteMetadata(pathname, isClient) {
28
50
  const config = getDndevConfig();
29
51
  const routeData = config?.routes?.mapping;
30
52
  if (routeData && Array.isArray(routeData)) {
31
- const route = routeData.find((r) => r.path === pathname);
53
+ // 1. Exact match (static routes)
54
+ let route = routeData.find((r) => r.path === pathname);
32
55
  if (route && route.meta) {
33
- // Map route.meta.namespace to routeMeta.ns
34
- return {
35
- ...route.meta,
36
- ns: route.meta.namespace, // Map namespace -> ns for compatibility
37
- };
56
+ return { ...route.meta, ns: route.meta.namespace };
57
+ }
58
+ // 2. Pattern match (dynamic routes: /cars/:id, /blog/:slug/comments/:id, etc.)
59
+ const dynamicRoutes = routeData.filter((r) => typeof r.path === 'string' && r.path.includes(':'));
60
+ const matches = dynamicRoutes.filter((r) => matchRoutePath(r.path, pathname));
61
+ if (matches.length > 0) {
62
+ const best = matches.reduce((a, b) => b.path.length > a.path.length ? b : a);
63
+ if (best.meta) {
64
+ return { ...best.meta, ns: best.meta.namespace };
65
+ }
38
66
  }
39
67
  }
40
- // Extract potential namespace from path
68
+ // Fallback: first path segment as namespace
41
69
  const segments = pathname.split('/').filter(Boolean);
42
70
  if (segments.length > 0) {
43
71
  return { ns: segments[0] };
@@ -64,7 +92,7 @@ const NextJsAutoMetaTags = ({ config = {}, routeMeta: routeMetaOverride, title:
64
92
  const { t } = useTranslation(namespaces);
65
93
  const { defaultNamespace = 'home', defaultImage = '/og-image.png', twitterHandle, defaultAuthor, staticTags = {}, } = config;
66
94
  const siteName = config.siteName || useAppConfig('name');
67
- const baseUrl = useAppConfig('url');
95
+ const baseUrl = getPlatformEnvVar('APP_URL') || '';
68
96
  // Get route metadata
69
97
  const routeMeta = getRouteMetadata(pathname || '/', isClient);
70
98
  const finalRouteMeta = { ...routeMeta, ...routeMetaOverride };
@@ -1,8 +1,6 @@
1
1
  export interface FooterBrandingProps {
2
2
  /** Additional CSS classes */
3
3
  className?: string;
4
- /** Text size */
5
- size?: 'xs' | 'sm';
6
4
  }
7
5
  /**
8
6
  * FooterBranding - Framework branding link (power move)
@@ -1 +1 @@
1
- {"version":3,"file":"FooterBranding.d.ts","sourceRoot":"","sources":["../../../../../src/internal/layout/components/footer/FooterBranding.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,gBAAgB;IAChB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,aAAa,CAAC,mBAAmB,CAoCnE,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"FooterBranding.d.ts","sourceRoot":"","sources":["../../../../../src/internal/layout/components/footer/FooterBranding.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,aAAa,CAAC,mBAAmB,CA8BnE,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -9,7 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  * @author AMBROISE PARK Consulting
10
10
  */
11
11
  import { ExternalLink } from 'lucide-react';
12
- import { useAppConfig } from '@donotdev/core';
12
+ import { useAppConfig, getPlatformEnvVar } from '@donotdev/core';
13
13
  /**
14
14
  * FooterBranding - Framework branding link (power move)
15
15
  *
@@ -20,20 +20,16 @@ import { useAppConfig } from '@donotdev/core';
20
20
  * @since 0.0.1
21
21
  * @author AMBROISE PARK Consulting
22
22
  */
23
- export const FooterBranding = ({ className, size = 'sm', }) => {
24
- const appUrl = useAppConfig('url');
23
+ export const FooterBranding = ({ className, }) => {
24
+ const appUrl = getPlatformEnvVar('APP_URL');
25
25
  const isFrameworkSite = appUrl === 'https://donotdev.com';
26
- const sizeStyles = {
27
- xs: { fontSize: 'var(--font-size-xs)' },
28
- sm: { fontSize: 'var(--font-size-sm)' },
29
- };
30
26
  return (_jsxs("a", { href: isFrameworkSite
31
27
  ? 'https://www.ambroise-park.com'
32
28
  : 'https://donotdev.com', target: "_blank", rel: "noopener noreferrer", className: className, style: {
33
29
  display: 'inline-flex',
34
30
  alignItems: 'center',
35
31
  gap: 'var(--gap-sm)',
36
- ...sizeStyles[size],
32
+ fontSize: 'var(--font-size-xs)',
37
33
  }, children: [isFrameworkSite ? 'AMBROISE-PARK' : 'DoNotDev', _jsx(ExternalLink, { style: { width: '12px', height: '12px' }, "aria-hidden": "true" })] }));
38
34
  };
39
35
  export default FooterBranding;
@@ -5,8 +5,6 @@ export interface FooterCopyrightProps {
5
5
  year?: number;
6
6
  /** Additional CSS classes */
7
7
  className?: string;
8
- /** Text size */
9
- size?: 'xs' | 'sm';
10
8
  }
11
9
  /**
12
10
  * FooterCopyright - Copyright notice component
@@ -1 +1 @@
1
- {"version":3,"file":"FooterCopyright.d.ts","sourceRoot":"","sources":["../../../../../src/internal/layout/components/footer/FooterCopyright.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,gBAAgB;IAChB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,aAAa,CAAC,oBAAoB,CA2BrE,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"FooterCopyright.d.ts","sourceRoot":"","sources":["../../../../../src/internal/layout/components/footer/FooterCopyright.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,aAAa,CAAC,oBAAoB,CAqBrE,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -20,18 +20,14 @@ import { useTranslation, useAppConfig } from '@donotdev/core';
20
20
  * @since 0.0.1
21
21
  * @author AMBROISE PARK Consulting
22
22
  */
23
- export const FooterCopyright = ({ appName, year = new Date().getFullYear(), className, size = 'xs', }) => {
23
+ export const FooterCopyright = ({ appName, year = new Date().getFullYear(), className, }) => {
24
24
  const { t } = useTranslation('dndev');
25
25
  const app = useAppConfig('app');
26
26
  const resolvedAppName = appName || app?.name || 'App';
27
- const sizeStyles = {
28
- xs: { fontSize: 'var(--font-size-xs)' },
29
- sm: { fontSize: 'var(--font-size-sm)' },
30
- };
31
27
  return (_jsxs("span", { className: cn(className), style: {
32
28
  flexShrink: 0,
33
29
  color: 'var(--muted-foreground)',
34
- ...sizeStyles[size],
30
+ fontSize: 'var(--font-size-xs)',
35
31
  }, children: ["\u00A9 ", year, " ", resolvedAppName, ". ", t('footer.legal.allRightsReserved')] }));
36
32
  };
37
33
  export default FooterCopyright;
@@ -32,7 +32,7 @@ export const mooltiPreset = {
32
32
  sidebar: {
33
33
  top: () => _jsx(AppBranding, { display: DISPLAY.AUTO }),
34
34
  content: () => (_jsxs(_Fragment, { children: [_jsx(GoTo, { display: DISPLAY.AUTO }), _jsx(DnDevNavigationMenu, { vertical: true, display: DISPLAY.AUTO })] })),
35
- bottom: () => (_jsxs(Stack, { direction: "column", gap: "tight", align: "stretch", children: [_jsx(AuthHeader, { display: DISPLAY.AUTO }), _jsx(LanguageSelector, { display: DISPLAY.AUTO }), _jsx(ThemeToggle, { display: DISPLAY.AUTO }), _jsx(FooterCopyright, { size: "xs" })] })),
35
+ bottom: () => (_jsxs(Stack, { direction: "column", gap: "tight", align: "stretch", children: [_jsx(AuthHeader, { display: DISPLAY.AUTO }), _jsx(LanguageSelector, { display: DISPLAY.AUTO }), _jsx(ThemeToggle, { display: DISPLAY.AUTO }), _jsx(FooterCopyright, {})] })),
36
36
  defaultWidth: 256,
37
37
  minWidth: 48,
38
38
  maxWidth: 400,
@@ -48,7 +48,7 @@ export const mooltiPreset = {
48
48
  _jsxs(_Fragment, { children: [_jsx(GoTo, { display: DISPLAY.AUTO }), _jsx(DnDevNavigationMenu, { vertical: true, display: DISPLAY.AUTO })] })),
49
49
  bottom: () => (
50
50
  // sidebar.bottom → bottom (Auth, Language, Theme, Footer)
51
- _jsxs(Stack, { direction: "column", gap: "tight", align: "stretch", children: [_jsx(AuthHeader, { display: DISPLAY.AUTO }), _jsx(LanguageSelector, { display: DISPLAY.AUTO }), _jsx(ThemeToggle, { display: DISPLAY.AUTO }), _jsx(FooterCopyright, { size: "xs" })] })),
51
+ _jsxs(Stack, { direction: "column", gap: "tight", align: "stretch", children: [_jsx(AuthHeader, { display: DISPLAY.AUTO }), _jsx(LanguageSelector, { display: DISPLAY.AUTO }), _jsx(ThemeToggle, { display: DISPLAY.AUTO }), _jsx(FooterCopyright, {})] })),
52
52
  },
53
53
  },
54
54
  };
@@ -1 +1 @@
1
- {"version":3,"file":"DnDevFooter.d.ts","sourceRoot":"","sources":["../../../../src/internal/layout/zones/DnDevFooter.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAUlD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,GAAG,CAAC,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAS,oBAAoB,CAAC,EAAE,GAAQ,EAAE,EAAE,gBAAgB,GAAG,SAAS,CAsEvE;AAED,eAAO,MAAM,WAAW,kEAA6B,CAAC"}
1
+ {"version":3,"file":"DnDevFooter.d.ts","sourceRoot":"","sources":["../../../../src/internal/layout/zones/DnDevFooter.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAUlD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,GAAG,CAAC,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAS,oBAAoB,CAAC,EAAE,GAAQ,EAAE,EAAE,gBAAgB,GAAG,SAAS,CA8EvE;AAED,eAAO,MAAM,WAAW,kEAA6B,CAAC"}
@@ -49,9 +49,9 @@ function DnDevFooterComponent({ app = {} }) {
49
49
  : copyrightConfig;
50
50
  // Desktop/Wide: 2-zone layout [Copyright] | [Links + DoNotDev]
51
51
  if (isLaptopOrDesktop) {
52
- return (_jsx("footer", { role: "contentinfo", className: "footer", children: _jsxs(Stack, { direction: "row", align: "center", justify: "between", gap: "none", children: [showCopyright && (_jsx("div", { className: "dndev-flex dndev-justify-start", children: _jsx("span", { className: "footer-copyright", children: copyrightText }) })), _jsx("div", { className: "dndev-flex dndev-justify-end", children: _jsxs(Stack, { direction: "row", gap: "medium", align: "center", children: [links.map((link) => (_jsx(Link, { path: link.path, children: maybeTranslate(t, link.label) }, link.path))), _jsx(FooterBranding, {})] }) })] }) }));
52
+ return (_jsx("footer", { role: "contentinfo", className: "footer", children: _jsxs(Stack, { direction: "row", align: "center", justify: "between", gap: "none", children: [showCopyright && (_jsx("div", { className: "dndev-flex dndev-justify-start", children: _jsx("span", { className: "footer-copyright", children: copyrightText }) })), _jsx("div", { className: "dndev-flex dndev-justify-end", children: _jsxs(Stack, { direction: "row", gap: "medium", align: "center", children: [links.map((link) => (_jsx(Link, { path: link.path, style: { fontSize: 'var(--font-size-xs)' }, children: maybeTranslate(t, link.label) }, link.path))), _jsx(FooterBranding, {})] }) })] }) }));
53
53
  }
54
54
  // Mobile/Tablet: stacked layout
55
- return (_jsx("footer", { role: "contentinfo", className: "footer", children: _jsxs(Stack, { align: "center", gap: "tight", children: [showCopyright && (_jsx("span", { className: "footer-copyright", children: copyrightText })), _jsxs(Stack, { direction: "row", wrap: "wrap", gap: "tight", justify: "center", align: "center", children: [links.map((link) => (_jsx(Link, { path: link.path, children: maybeTranslate(t, link.label) }, link.path))), _jsx(FooterBranding, {})] })] }) }));
55
+ return (_jsx("footer", { role: "contentinfo", className: "footer", children: _jsxs(Stack, { align: "center", gap: "tight", children: [showCopyright && (_jsx("span", { className: "footer-copyright", children: copyrightText })), _jsxs(Stack, { direction: "row", wrap: "wrap", gap: "tight", justify: "center", align: "center", children: [links.map((link) => (_jsx(Link, { path: link.path, style: { fontSize: 'var(--font-size-xs)' }, children: maybeTranslate(t, link.label) }, link.path))), _jsx(FooterBranding, {})] })] }) }));
56
56
  }
57
57
  export const DnDevFooter = memo(DnDevFooterComponent);
@@ -16,6 +16,6 @@ export interface GoToProps {
16
16
  * Simple button: Search icon + "Go to" label + keyboard shortcut.
17
17
  * Opens command dialog on click.
18
18
  */
19
- export declare const GoTo: ({ display, className, onOpen }: GoToProps) => import("react/jsx-runtime").JSX.Element;
19
+ export declare const GoTo: ({ display, className, onOpen, }: GoToProps) => import("react/jsx-runtime").JSX.Element;
20
20
  export default GoTo;
21
21
  //# sourceMappingURL=GoTo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GoTo.d.ts","sourceRoot":"","sources":["../../src/routing/GoTo.tsx"],"names":[],"mappings":"AAaA,OAAO,EAA0B,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGvE,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;IACjD,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,gCAA+C,SAAS,4CAsC5E,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"GoTo.d.ts","sourceRoot":"","sources":["../../src/routing/GoTo.tsx"],"names":[],"mappings":"AAaA,OAAO,EAA0B,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGvE,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;IACjD,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,iCAIlB,SAAS,4CAsCX,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -17,7 +17,7 @@ import { useOverlayStore, useTranslation } from '@donotdev/core';
17
17
  * Simple button: Search icon + "Go to" label + keyboard shortcut.
18
18
  * Opens command dialog on click.
19
19
  */
20
- export const GoTo = ({ display = DISPLAY.AUTO, className, onOpen }) => {
20
+ export const GoTo = ({ display = DISPLAY.AUTO, className, onOpen, }) => {
21
21
  const { t } = useTranslation('dndev');
22
22
  const openCommandDialog = useOverlayStore((state) => state.openCommandDialog);
23
23
  // Platform-aware shortcut (Mac: ⌘K, others: Ctrl+K)
@@ -1 +1 @@
1
- "use client";import{useRouter as f}from"next/navigation";import{useCallback as h}from"react";import{isClient as S,useOverlayStore as y}from"@donotdev/core";function d(){let e=f(),t=y(r=>r.closeAll);return h((r,i)=>{if(r==="back")return t(),e.back();if(t(),i?.preserveScroll&&S()){let o=window.scrollY;i?.replace?e.replace(r):e.push(r),requestAnimationFrame(()=>{window.scrollTo({top:o,behavior:"auto"})});return}return i?.replace?e.replace(r):e.push(r)},[e,t])}function k(){let e=f();return h(()=>e.back(),[e])}function v(){let e=f();return h(()=>e.refresh(),[e])}function T(){let e=f();return h(t=>e.prefetch(t),[e])}import{usePathname as b,useSearchParams as w}from"next/navigation";function l(){let e=b(),t=w(),r=t.toString()?`?${t.toString()}`:"";return{pathname:e||"/",search:r,hash:"",state:null}}import{useParams as C}from"next/navigation";function g(){return C()}function I(e){let r=g()[e];if(typeof r=="string")return r;if(Array.isArray(r)&&r.length>0)return r[0]}import{useSearchParams as N}from"next/navigation";function R(){return N()}import{usePathname as G}from"next/navigation";function $(e){let t=G(),r=e.replace(/:[^/]+/g,"([^/]+)").replace(/\*/g,".*"),i=new RegExp(`^${r}$`),o=t.match(i);if(!o)return null;let s=e.match(/:[^/]+/g)?.map(a=>a.slice(1))||[],n={};return s.forEach((a,c)=>{n[a]=o[c+1]||""}),{params:n,pathname:t,pattern:e}}import{useCallback as P}from"react";function K(){let e=R(),t=d(),r=l(),i=P((n,a)=>{let c=new URLSearchParams(e.toString());c.set(n,a);let m=c.toString(),A=`${r.pathname||"/"}${m?`?${m}`:""}`;t(A)},[e,t,r.pathname]),o=P(n=>{let a=new URLSearchParams(e.toString());a.delete(n);let c=a.toString(),u=`${r.pathname||"/"}${c?`?${c}`:""}`;t(u)},[e,t,r.pathname]),s=P(()=>{let n=r.pathname||"/";t(n)},[t,r.pathname]);return{query:e,setQuery:i,removeQuery:o,clearQueries:s}}import{useMemo as E}from"react";import{isClient as Q,FEATURE_STATUS as F}from"@donotdev/core";import{useAuthConfig as M}from"@donotdev/core";import*as O from"@donotdev/auth";import{DEGRADED_AUTH_API as U}from"@donotdev/core";var x=O?.useAuth;function L(e){return U[e]}function p(e){return x?x(e):L(e)}function j(e={}){let{auth:t,redirectTo:r,condition:i}=e;if(!Q())return{shouldRedirect:!1,redirectTo:null,isChecking:!1};let o=l(),s=M(),n=p("user"),a=p("can"),c=p("status");return E(()=>{if(c===F.INITIALIZING)return{shouldRedirect:!1,redirectTo:null,isChecking:!0};if(i){let u=i(n,c);return{shouldRedirect:u,redirectTo:u&&r||null,isChecking:!1}}if(t!==!1&&t!==void 0){if(!a)return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(!a.navigate(t)){let u=null;return typeof t=="object"&&t.required&&!n?o.search.includes("code=")||o.search.includes("state=")||o.search.includes("error=")?u=`${s.authRoute}${o.search}`:u=s.authRoute:typeof t=="object"&&t.role?u=s.roleRoute:typeof t=="object"&&t.tier?u=s.tierRoute:u=s.roleRoute,{shouldRedirect:!0,redirectTo:r||u,isChecking:!1}}}return{shouldRedirect:!1,redirectTo:null,isChecking:!1}},[t,c,n,a,i,r,o.search,s.authRoute,s.roleRoute,s.tierRoute])}export{k as useBack,l as useLocation,$ as useMatch,d as useNavigate,g as useParams,T as usePrefetch,K as useQueryParams,j as useRedirectGuard,v as useRefresh,I as useRouteParam,R as useSearchParams};
1
+ "use client";import{useRouter as h}from"next/navigation";import{useCallback as p}from"react";import{isClient as F,useOverlayStore as w}from"@donotdev/core";import*as D from"@donotdev/crud";var m=D?.useFormStore,A={forms:{},startSubmit:()=>{},setUploading:()=>{},setValidating:()=>{},setSubmitting:()=>{},setSuccess:()=>{},setError:()=>{},reset:()=>{},cleanup:()=>{},setIsDirty:()=>{},hasDirtyForms:()=>!1,getDirtyFormIds:()=>[],getStatus:()=>"idle",isLoading:()=>!1,getUploadProgress:()=>0,getError:()=>null,getIsDirty:()=>!1};function x(e){return m?m(e):e(A)}x.getState=()=>m&&typeof m.getState=="function"?m.getState():A;async function T(e="You have unsaved changes. Discard them?"){return x.getState().hasDirtyForms()&&typeof window<"u"&&window.confirm?window.confirm(e):!0}function g(){let e=h(),t=w(r=>r.closeAll);return p(async(r,u)=>{if(r==="back")return t(),e.back();if(await T()){if(t(),u?.preserveScroll&&F()){let o=window.scrollY;u?.replace?e.replace(r):e.push(r),requestAnimationFrame(()=>{window.scrollTo({top:o,behavior:"auto"})});return}return u?.replace?e.replace(r):e.push(r)}},[e,t])}function b(){let e=h();return p(()=>e.back(),[e])}function k(){let e=h();return p(()=>e.refresh(),[e])}function E(){let e=h();return p(t=>e.prefetch(t),[e])}import{usePathname as I,useSearchParams as N}from"next/navigation";function f(){let e=I(),t=N(),r=t.toString()?`?${t.toString()}`:"";return{pathname:e||"/",search:r,hash:"",state:null}}import{useParams as C}from"next/navigation";function R(){return C()}function G(e){let r=R()[e];if(typeof r=="string")return r;if(Array.isArray(r)&&r.length>0)return r[0]}import{useSearchParams as O}from"next/navigation";function S(){return O()}import{usePathname as U}from"next/navigation";function $(e){let t=U(),r=e.replace(/:[^/]+/g,"([^/]+)").replace(/\*/g,".*"),u=new RegExp(`^${r}$`),i=t.match(u);if(!i)return null;let o=e.match(/:[^/]+/g)?.map(n=>n.slice(1))||[],s={};return o.forEach((n,c)=>{s[n]=i[c+1]||""}),{params:s,pathname:t,pattern:e}}import{useCallback as y}from"react";function K(){let e=S(),t=g(),r=f(),u=y((s,n)=>{let c=new URLSearchParams(e.toString());c.set(s,n);let l=c.toString(),P=`${r.pathname||"/"}${l?`?${l}`:""}`;t(P)},[e,t,r.pathname]),i=y(s=>{let n=new URLSearchParams(e.toString());n.delete(s);let c=n.toString(),a=`${r.pathname||"/"}${c?`?${c}`:""}`;t(a)},[e,t,r.pathname]),o=y(()=>{let s=r.pathname||"/";t(s)},[t,r.pathname]);return{query:e,setQuery:u,removeQuery:i,clearQueries:o}}import{useMemo as Q}from"react";import{isClient as j,FEATURE_STATUS as q}from"@donotdev/core";import{useAuthConfig as Y}from"@donotdev/core";import*as _ from"@donotdev/auth";import{DEGRADED_AUTH_API as L}from"@donotdev/core";var v=_?.useAuth;function M(e){return L[e]}function d(e){return v?v(e):M(e)}function B(e={}){let{auth:t,redirectTo:r,condition:u}=e;if(!j())return{shouldRedirect:!1,redirectTo:null,isChecking:!1};let i=f(),o=Y(),s=d("user"),n=d("can"),c=d("status");return Q(()=>{if(c===q.INITIALIZING)return{shouldRedirect:!1,redirectTo:null,isChecking:!0};if(u){let a=u(s,c);return{shouldRedirect:a,redirectTo:a&&r||null,isChecking:!1}}if(t!==!1&&t!==void 0){if(!n)return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(!n.navigate(t)){let a=null;return typeof t=="object"&&t.required&&!s?i.search.includes("code=")||i.search.includes("state=")||i.search.includes("error=")?a=`${o.authRoute}${i.search}`:a=o.authRoute:typeof t=="object"&&t.role?a=o.roleRoute:typeof t=="object"&&t.tier?a=o.tierRoute:a=o.roleRoute,{shouldRedirect:!0,redirectTo:r||a,isChecking:!1}}}return{shouldRedirect:!1,redirectTo:null,isChecking:!1}},[t,c,s,n,u,r,i.search,o.authRoute,o.roleRoute,o.tierRoute])}export{b as useBack,f as useLocation,$ as useMatch,g as useNavigate,R as useParams,E as usePrefetch,K as useQueryParams,B as useRedirectGuard,k as useRefresh,G as useRouteParam,S as useSearchParams};
@@ -1 +1 @@
1
- "use client";import{useCallback as m}from"react";import{useNavigate as P}from"react-router-dom";import{isClient as y,useOverlayStore as S}from"@donotdev/core";function h(){let e=P(),t=S(r=>r.closeAll);return m((r,i)=>r==="back"?(t(),e(-1)):(t(),i?.replace?e(r,{replace:!0}):e(r)),[e,t])}function v(){let e=P();return m(()=>e(-1),[e])}function k(){return m(()=>{y()&&window.location.reload()},[])}function T(){return m(e=>{},[])}import{useLocation as C}from"react-router-dom";function f(){return C()}import{useParams as I}from"react-router-dom";function d(){return I()}function b(e){let r=d()[e];if(typeof r=="string")return r;if(Array.isArray(r)&&r.length>0)return r[0]}import{useSearchParams as G}from"react-router-dom";function g(){return G()[0]}import{useMatch as w}from"react-router-dom";function N(e){return w(e)}import{useCallback as R}from"react";function K(){let e=g(),t=h(),r=f(),i=R((n,u)=>{let a=new URLSearchParams(e.toString());a.set(n,u);let l=a.toString(),A=`${r.pathname||"/"}${l?`?${l}`:""}`;t(A)},[e,t,r.pathname]),c=R(n=>{let u=new URLSearchParams(e.toString());u.delete(n);let a=u.toString(),o=`${r.pathname||"/"}${a?`?${a}`:""}`;t(o)},[e,t,r.pathname]),s=R(()=>{let n=r.pathname||"/";t(n)},[t,r.pathname]);return{query:e,setQuery:i,removeQuery:c,clearQueries:s}}import{useMemo as $}from"react";import{isClient as M,FEATURE_STATUS as Q}from"@donotdev/core";import{useAuthConfig as E}from"@donotdev/core";import*as L from"@donotdev/auth";import{DEGRADED_AUTH_API as O}from"@donotdev/core";var x=L?.useAuth;function U(e){return O[e]}function p(e){return x?x(e):U(e)}function _(e={}){let{auth:t,redirectTo:r,condition:i}=e;if(!M())return{shouldRedirect:!1,redirectTo:null,isChecking:!1};let c=f(),s=E(),n=p("user"),u=p("can"),a=p("status");return $(()=>{if(a===Q.INITIALIZING)return{shouldRedirect:!1,redirectTo:null,isChecking:!0};if(i){let o=i(n,a);return{shouldRedirect:o,redirectTo:o&&r||null,isChecking:!1}}if(t!==!1&&t!==void 0){if(!u)return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(!u.navigate(t)){let o=null;return typeof t=="object"&&t.required&&!n?c.search.includes("code=")||c.search.includes("state=")||c.search.includes("error=")?o=`${s.authRoute}${c.search}`:o=s.authRoute:typeof t=="object"&&t.role?o=s.roleRoute:typeof t=="object"&&t.tier?o=s.tierRoute:o=s.roleRoute,{shouldRedirect:!0,redirectTo:r||o,isChecking:!1}}}return{shouldRedirect:!1,redirectTo:null,isChecking:!1}},[t,a,n,u,i,r,c.search,s.authRoute,s.roleRoute,s.tierRoute])}export{v as useBack,f as useLocation,N as useMatch,h as useNavigate,d as useParams,T as usePrefetch,K as useQueryParams,_ as useRedirectGuard,k as useRefresh,b as useRouteParam,g as useSearchParams};
1
+ "use client";import{useCallback as d}from"react";import{useNavigate as T}from"react-router-dom";import{isClient as F,useOverlayStore as k}from"@donotdev/core";import*as D from"@donotdev/crud";var f=D?.useFormStore,A={forms:{},startSubmit:()=>{},setUploading:()=>{},setValidating:()=>{},setSubmitting:()=>{},setSuccess:()=>{},setError:()=>{},reset:()=>{},cleanup:()=>{},setIsDirty:()=>{},hasDirtyForms:()=>!1,getDirtyFormIds:()=>[],getStatus:()=>"idle",isLoading:()=>!1,getUploadProgress:()=>0,getError:()=>null,getIsDirty:()=>!1};function P(e){return f?f(e):e(A)}P.getState=()=>f&&typeof f.getState=="function"?f.getState():A;async function x(e="You have unsaved changes. Discard them?"){return P.getState().hasDirtyForms()&&typeof window<"u"&&window.confirm?window.confirm(e):!0}function h(){let e=T(),t=k(r=>r.closeAll);return d(async(r,c)=>{if(r==="back")return t(),e(-1);if(await x())return t(),c?.replace?e(r,{replace:!0}):e(r)},[e,t])}function w(){let e=T();return d(()=>e(-1),[e])}function b(){return d(()=>{F()&&window.location.reload()},[])}function I(){return d(e=>{},[])}import{useLocation as E}from"react-router-dom";function m(){return E()}import{useParams as C}from"react-router-dom";function g(){return C()}function G(e){let r=g()[e];if(typeof r=="string")return r;if(Array.isArray(r)&&r.length>0)return r[0]}import{useSearchParams as O}from"react-router-dom";function R(){return O()[0]}import{useMatch as N}from"react-router-dom";function U(e){return N(e)}import{useCallback as y}from"react";function L(){let e=R(),t=h(),r=m(),c=y((n,i)=>{let a=new URLSearchParams(e.toString());a.set(n,i);let l=a.toString(),S=`${r.pathname||"/"}${l?`?${l}`:""}`;t(S)},[e,t,r.pathname]),u=y(n=>{let i=new URLSearchParams(e.toString());i.delete(n);let a=i.toString(),o=`${r.pathname||"/"}${a?`?${a}`:""}`;t(o)},[e,t,r.pathname]),s=y(()=>{let n=r.pathname||"/";t(n)},[t,r.pathname]);return{query:e,setQuery:c,removeQuery:u,clearQueries:s}}import{useMemo as $}from"react";import{isClient as Q,FEATURE_STATUS as j}from"@donotdev/core";import{useAuthConfig as q}from"@donotdev/core";import*as _ from"@donotdev/auth";import{DEGRADED_AUTH_API as K}from"@donotdev/core";var v=_?.useAuth;function M(e){return K[e]}function p(e){return v?v(e):M(e)}function B(e={}){let{auth:t,redirectTo:r,condition:c}=e;if(!Q())return{shouldRedirect:!1,redirectTo:null,isChecking:!1};let u=m(),s=q(),n=p("user"),i=p("can"),a=p("status");return $(()=>{if(a===j.INITIALIZING)return{shouldRedirect:!1,redirectTo:null,isChecking:!0};if(c){let o=c(n,a);return{shouldRedirect:o,redirectTo:o&&r||null,isChecking:!1}}if(t!==!1&&t!==void 0){if(!i)return{shouldRedirect:!1,redirectTo:null,isChecking:!1};if(!i.navigate(t)){let o=null;return typeof t=="object"&&t.required&&!n?u.search.includes("code=")||u.search.includes("state=")||u.search.includes("error=")?o=`${s.authRoute}${u.search}`:o=s.authRoute:typeof t=="object"&&t.role?o=s.roleRoute:typeof t=="object"&&t.tier?o=s.tierRoute:o=s.roleRoute,{shouldRedirect:!0,redirectTo:r||o,isChecking:!1}}}return{shouldRedirect:!1,redirectTo:null,isChecking:!1}},[t,a,n,i,c,r,u.search,s.authRoute,s.roleRoute,s.tierRoute])}export{w as useBack,m as useLocation,U as useMatch,h as useNavigate,g as useParams,I as usePrefetch,L as useQueryParams,B as useRedirectGuard,b as useRefresh,G as useRouteParam,R as useSearchParams};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Hook to block React Router navigation when forms are dirty.
3
+ * Uses FormStore as single source of truth.
4
+ * Safe - gracefully degrades if CRUD package not installed.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * // In your app root/layout
9
+ * useFormNavigationBlocker();
10
+ * ```
11
+ */
12
+ export declare function useFormNavigationBlocker(): void;
13
+ export default useFormNavigationBlocker;
14
+ //# sourceMappingURL=useFormNavigationBlocker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFormNavigationBlocker.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useFormNavigationBlocker.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAmB/C;AAED,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ // packages/ui/src/routing/hooks/useFormNavigationBlocker.ts
3
+ /**
4
+ * @fileoverview Form Navigation Blocker Hook
5
+ * @description React Router integration for blocking navigation when forms are dirty.
6
+ * Uses FormStore as single source of truth. Safe - works if CRUD package not installed.
7
+ *
8
+ * @version 0.0.1
9
+ * @since 0.0.1
10
+ * @author AMBROISE PARK Consulting
11
+ */
12
+ import { useBlocker } from 'react-router-dom';
13
+ import { useEffect } from 'react';
14
+ import { useHasDirtyFormsSafe, checkFormNavigationSafe, } from '../../utils/useFormStoreSafe';
15
+ /**
16
+ * Hook to block React Router navigation when forms are dirty.
17
+ * Uses FormStore as single source of truth.
18
+ * Safe - gracefully degrades if CRUD package not installed.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * // In your app root/layout
23
+ * useFormNavigationBlocker();
24
+ * ```
25
+ */
26
+ export function useFormNavigationBlocker() {
27
+ const hasDirtyForms = useHasDirtyFormsSafe();
28
+ const blocker = useBlocker(({ currentLocation, nextLocation }) => hasDirtyForms && currentLocation.pathname !== nextLocation.pathname);
29
+ useEffect(() => {
30
+ if (blocker.state === 'blocked') {
31
+ checkFormNavigationSafe().then((proceed) => {
32
+ if (proceed) {
33
+ blocker.proceed();
34
+ }
35
+ else {
36
+ blocker.reset();
37
+ }
38
+ });
39
+ }
40
+ }, [blocker]);
41
+ }
42
+ export default useFormNavigationBlocker;
@@ -1,5 +1,5 @@
1
1
  import type { NavigateOptions } from './types';
2
- export declare function useNavigate(): (to: string, options?: NavigateOptions) => void;
2
+ export declare function useNavigate(): (to: string, options?: NavigateOptions) => Promise<void>;
3
3
  export declare function useBack(): () => void;
4
4
  export declare function useRefresh(): () => void;
5
5
  export declare function usePrefetch(): (to: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useNavigate.next.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useNavigate.next.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,WAAW,SAKlB,MAAM,YAAY,eAAe,UA0BzC;AAED,wBAAgB,OAAO,eAGtB;AAED,wBAAgB,UAAU,eAGzB;AAED,wBAAgB,WAAW,SAED,MAAM,UAC/B"}
1
+ {"version":3,"file":"useNavigate.next.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useNavigate.next.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,WAAW,SAKZ,MAAM,YAAY,eAAe,mBAgC/C;AAED,wBAAgB,OAAO,eAGtB;AAED,wBAAgB,UAAU,eAGzB;AAED,wBAAgB,WAAW,SAED,MAAM,UAC/B"}
@@ -11,14 +11,20 @@
11
11
  import { useRouter as useNextRouter } from 'next/navigation';
12
12
  import { useCallback } from 'react';
13
13
  import { isClient, useOverlayStore } from '@donotdev/core';
14
+ import { checkFormNavigationSafe } from '../../utils/useFormStoreSafe';
14
15
  export function useNavigate() {
15
16
  const router = useNextRouter();
16
17
  const closeAll = useOverlayStore((state) => state.closeAll);
17
- return useCallback((to, options) => {
18
+ return useCallback(async (to, options) => {
18
19
  if (to === 'back') {
19
20
  closeAll();
20
21
  return router.back();
21
22
  }
23
+ // Check FormStore for dirty forms before navigation (safe - works if CRUD not installed)
24
+ const shouldProceed = await checkFormNavigationSafe();
25
+ if (!shouldProceed) {
26
+ return; // Navigation blocked by user
27
+ }
22
28
  closeAll();
23
29
  if (options?.preserveScroll && isClient()) {
24
30
  const scrollY = window.scrollY;
@@ -1,5 +1,5 @@
1
1
  import type { NavigateOptions } from './types';
2
- export declare function useNavigate(): (to: string, options?: NavigateOptions) => void | Promise<void>;
2
+ export declare function useNavigate(): (to: string, options?: NavigateOptions) => Promise<void>;
3
3
  export declare function useBack(): () => void | Promise<void>;
4
4
  export declare function useRefresh(): () => void;
5
5
  export declare function usePrefetch(): (_to: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useNavigate.vite.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useNavigate.vite.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,WAAW,SAKlB,MAAM,YAAY,eAAe,0BAazC;AAED,wBAAgB,OAAO,+BAGtB;AAED,wBAAgB,UAAU,eAIzB;AAED,wBAAgB,WAAW,UACA,MAAM,UAGhC"}
1
+ {"version":3,"file":"useNavigate.vite.d.ts","sourceRoot":"","sources":["../../../src/routing/hooks/useNavigate.vite.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,wBAAgB,WAAW,SAKZ,MAAM,YAAY,eAAe,mBAmB/C;AAED,wBAAgB,OAAO,+BAGtB;AAED,wBAAgB,UAAU,eAIzB;AAED,wBAAgB,WAAW,UACA,MAAM,UAGhC"}
@@ -11,14 +11,20 @@
11
11
  import { useCallback } from 'react';
12
12
  import { useNavigate as useRouterNavigate } from 'react-router-dom';
13
13
  import { isClient, useOverlayStore } from '@donotdev/core';
14
+ import { checkFormNavigationSafe } from '../../utils/useFormStoreSafe';
14
15
  export function useNavigate() {
15
16
  const navigate = useRouterNavigate();
16
17
  const closeAll = useOverlayStore((state) => state.closeAll);
17
- return useCallback((to, options) => {
18
+ return useCallback(async (to, options) => {
18
19
  if (to === 'back') {
19
20
  closeAll();
20
21
  return navigate(-1);
21
22
  }
23
+ // Check FormStore for dirty forms before navigation (safe - works if CRUD not installed)
24
+ const shouldProceed = await checkFormNavigationSafe();
25
+ if (!shouldProceed) {
26
+ return; // Navigation blocked by user
27
+ }
22
28
  closeAll();
23
29
  if (options?.replace)
24
30
  return navigate(to, { replace: true });