@donotdev/ui 0.0.14 → 0.0.15
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.
- package/dist/components/layout/components/index.d.ts +3 -0
- package/dist/components/layout/components/index.d.ts.map +1 -1
- package/dist/components/layout/components/index.js +3 -0
- package/dist/crud/components/EntityCardList.d.ts +1 -1
- package/dist/crud/components/EntityCardList.d.ts.map +1 -1
- package/dist/crud/components/EntityCardList.js +16 -13
- package/dist/crud/components/EntityDisplayRenderer.d.ts +1 -1
- package/dist/crud/components/EntityDisplayRenderer.d.ts.map +1 -1
- package/dist/crud/components/EntityDisplayRenderer.js +6 -2
- package/dist/crud/components/EntityFormRenderer.d.ts +1 -1
- package/dist/crud/components/EntityFormRenderer.d.ts.map +1 -1
- package/dist/crud/components/EntityFormRenderer.js +12 -29
- package/dist/crud/components/EntityRecommendations.d.ts +1 -0
- package/dist/crud/components/EntityRecommendations.d.ts.map +1 -1
- package/dist/crud/components/EntityRecommendations.js +3 -2
- package/dist/dndev.css +11558 -0
- package/dist/index.js +4 -4
- package/dist/internal/providers/NavigationProvider.d.ts.map +1 -1
- package/dist/internal/providers/NavigationProvider.js +3 -5
- package/dist/providers/ViteAppProviders.d.ts.map +1 -1
- package/dist/providers/ViteAppProviders.js +3 -5
- package/dist/routing/GoToInput.d.ts.map +1 -1
- package/dist/routing/GoToInput.js +4 -5
- package/dist/routing/hooks/hooks.next.js +1 -1
- package/dist/routing/hooks/hooks.vite.js +1 -1
- package/dist/routing/hooks/useNavigate.next.d.ts +1 -1
- package/dist/routing/hooks/useNavigate.next.d.ts.map +1 -1
- package/dist/routing/hooks/useNavigate.next.js +1 -7
- package/dist/routing/hooks/useNavigate.vite.d.ts +1 -1
- package/dist/routing/hooks/useNavigate.vite.d.ts.map +1 -1
- package/dist/routing/hooks/useNavigate.vite.js +1 -7
- package/dist/routing/useRouteDiscovery.d.ts +4 -15
- package/dist/routing/useRouteDiscovery.d.ts.map +1 -1
- package/dist/routing/useRouteDiscovery.js +6 -5
- package/dist/styles/index.css +16 -6
- package/dist/utils/useFormStoreSafe.d.ts +2 -15
- package/dist/utils/useFormStoreSafe.d.ts.map +1 -1
- package/dist/utils/useFormStoreSafe.js +3 -33
- package/dist/vite-routing/RootLayout.d.ts.map +1 -1
- package/dist/vite-routing/RootLayout.js +10 -15
- package/package.json +10 -10
|
@@ -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';
|
|
@@ -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":"
|
|
1
|
+
{"version":3,"file":"EntityCardList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityCardList.tsx"],"names":[],"mappings":"AAuCA,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,2CA4MrB"}
|
|
@@ -15,7 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
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
21
|
import { useCrudCardList, EntityFilters, useEntityFavorites, matchesFilter, useCrudFilters, } from '@donotdev/crud';
|
|
@@ -32,7 +32,7 @@ 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
38
|
// useCrudCardList -> handles fetching optimized for cards automatically
|
|
@@ -89,18 +89,20 @@ filter, hideFilters = false, }) {
|
|
|
89
89
|
return (_jsxs(_Fragment, { children: [!hideFilters && (_jsx(Section, { title: tCrud('filters.title', {
|
|
90
90
|
entity: entityName,
|
|
91
91
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
}), 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
|
|
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
|
|
97
97
|
? tCrud('results.title.fetching', { defaultValue: 'Fetching...' })
|
|
98
|
-
:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
:
|
|
103
|
-
|
|
98
|
+
: resultLabel
|
|
99
|
+
? resultLabel(data.length)
|
|
100
|
+
: tCrud('results.title.count', {
|
|
101
|
+
count: data.length,
|
|
102
|
+
defaultValue: data.length === 1
|
|
103
|
+
? 'Found 1 occurrence'
|
|
104
|
+
: `Found ${data.length} occurrences`,
|
|
105
|
+
}), 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
106
|
defaultValue: `No ${entity.name.toLowerCase()} found`,
|
|
105
107
|
}) }), _jsx(Text, { style: { color: 'var(--muted-foreground)' }, children: tCrud('emptyState.description', {
|
|
106
108
|
defaultValue: `No ${entity.name.toLowerCase()} available at this time.`,
|
|
@@ -108,6 +110,7 @@ filter, hideFilters = false, }) {
|
|
|
108
110
|
const itemIsFavorite = isFavorite(item.id);
|
|
109
111
|
const detailHref = onClick ? undefined : `${base}/${item.id}`;
|
|
110
112
|
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) => {
|
|
113
|
+
e.preventDefault();
|
|
111
114
|
e.stopPropagation();
|
|
112
115
|
toggleFavorite(item.id);
|
|
113
116
|
}, 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,
|
|
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,
|
|
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":"
|
|
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,
|
|
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',
|
|
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
|
-
|
|
111
|
+
useFormStore.getState().setIsDirty(formId, isDirtyForBlocking);
|
|
114
112
|
}
|
|
115
|
-
}, [formId, isDirtyForBlocking
|
|
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
|
-
//
|
|
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
|
-
//
|
|
137
|
-
const
|
|
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
|
-
|
|
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":"EntityRecommendations.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityRecommendations.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAMjE
|
|
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 {
|
|
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 } =
|
|
24
|
+
const { items, loading } = useCrudCardList(entity, {
|
|
24
25
|
queryOptions,
|
|
25
26
|
});
|
|
26
27
|
if (!loading && items.length === 0) {
|