@donotdev/ui 0.0.12 → 0.0.13
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/common/FeatureCard.js +1 -1
- package/dist/components/common/RedirectOverlay.js +1 -1
- package/dist/components/cookie-consent/CookieConsent.js +3 -3
- package/dist/components/layout/GameContainer.d.ts +24 -8
- package/dist/components/layout/GameContainer.d.ts.map +1 -1
- package/dist/components/layout/GameContainer.js +21 -3
- package/dist/components/layout/GameFlow.d.ts.map +1 -1
- package/dist/components/layout/GameFlow.js +27 -11
- package/dist/components/layout/components/header/CacheSettings.js +1 -1
- package/dist/crud/components/EntityCardList.d.ts.map +1 -1
- package/dist/crud/components/EntityCardList.js +16 -10
- package/dist/crud/components/EntityDisplayRenderer.js +2 -2
- package/dist/crud/components/EntityFormRenderer.d.ts.map +1 -1
- package/dist/crud/components/EntityList.d.ts.map +1 -1
- package/dist/crud/components/EntityList.js +7 -9
- package/dist/crud/components/Form.js +1 -1
- package/dist/dndev.css +99 -39
- package/dist/index.js +4 -4
- package/dist/internal/common/RouteErrorFallback.js +3 -3
- package/dist/internal/devtools/components/ConfigTab.js +1 -1
- package/dist/internal/devtools/components/CookieTab.js +1 -1
- package/dist/internal/devtools/components/DesignTab.js +2 -2
- package/dist/internal/devtools/components/StoresTab.js +2 -2
- package/dist/internal/layout/zones/DnDevFooter.js +1 -1
- package/dist/internal/layout/zones/DnDevMergedBar.js +1 -1
- package/dist/routing/404.js +3 -3
- package/dist/routing/AuthGuardFallback.js +2 -2
- package/dist/styles/index.css +99 -39
- package/package.json +1 -1
|
@@ -56,7 +56,7 @@ const FeatureCard = ({ icon, title, subtitle, content, href, variant, elevated,
|
|
|
56
56
|
margin: 0,
|
|
57
57
|
};
|
|
58
58
|
// Build title with icon if provided
|
|
59
|
-
const titleContent = icon ? (_jsxs(Stack, { direction: "row", align: "center",
|
|
59
|
+
const titleContent = icon ? (_jsxs(Stack, { direction: "row", align: "center", style: { width: '100%' }, children: [_jsx(IconBox, { icon: icon }), _jsx(Text, { as: "div", level: "h3", style: titleStyle, children: title })] })) : (_jsx(Text, { as: "div", level: "h3", style: titleStyle, children: title }));
|
|
60
60
|
const card = (_jsx(Card, { variant: variant, elevated: elevated, onClick: onClick, className: className, "data-clickable": href || onClick ? 'true' : undefined, style: {
|
|
61
61
|
paddingInlineStart: 'var(--gap-md)',
|
|
62
62
|
paddingInlineEnd: 'var(--gap-md)',
|
|
@@ -219,7 +219,7 @@ export function RedirectOverlay() {
|
|
|
219
219
|
textAlign: 'center',
|
|
220
220
|
maxWidth: '400px',
|
|
221
221
|
padding: 'var(--gap-lg)',
|
|
222
|
-
}, children: [_jsxs(Stack, { direction: "row",
|
|
222
|
+
}, children: [_jsxs(Stack, { direction: "row", align: "center", children: [IconComponent && (_jsx(IconComponent, { style: {
|
|
223
223
|
width: '1.5rem',
|
|
224
224
|
height: '1.5rem',
|
|
225
225
|
color: 'var(--primary)',
|
|
@@ -141,14 +141,14 @@ function CookieConsent({ position = 'bottom', showBranding = true, brandingText
|
|
|
141
141
|
declineAll();
|
|
142
142
|
setShowBanner(false);
|
|
143
143
|
};
|
|
144
|
-
const bannerContent = (_jsxs(Stack, {
|
|
144
|
+
const bannerContent = (_jsxs(Stack, { children: [_jsx(Text, { as: "p", variant: "muted", level: "small", children: t('cookieBannerDescription') }), hasOptionalCategories && (_jsx(Accordion, { type: "single", collapsible: true, value: showPreferences ? 'preferences' : '', onValueChange: (value) => setShowPreferences(value === 'preferences'), items: [
|
|
145
145
|
{
|
|
146
146
|
value: 'preferences',
|
|
147
147
|
trigger: (_jsxs(Stack, { direction: "row", align: "center", gap: "tight", children: [_jsx(Settings, { className: "dndev-size-md" }), t('customize')] })),
|
|
148
|
-
content: (_jsx(Stack, {
|
|
148
|
+
content: (_jsx(Stack, { children: categoryDefs.map((cat) => {
|
|
149
149
|
const Icon = cat.icon;
|
|
150
150
|
const enabled = preferenceCategories[cat.id] ?? false;
|
|
151
|
-
return (_jsx(Card, { content: _jsxs(Stack, { direction: "row", align: "center", justify: "between",
|
|
151
|
+
return (_jsx(Card, { content: _jsxs(Stack, { direction: "row", align: "center", justify: "between", children: [_jsxs(Stack, { direction: "row", align: "start", gap: "tight", style: { flex: 1 }, children: [_jsx(Icon, { className: "dndev-size-md", style: {
|
|
152
152
|
color: 'var(--primary)',
|
|
153
153
|
marginTop: '0.125rem',
|
|
154
154
|
} }), _jsxs(Stack, { gap: "tight", children: [_jsx(Text, { as: "span", level: "body", style: { fontWeight: 600 }, children: cat.title }), _jsx(Text, { as: "span", variant: "muted", level: "small", children: cat.description }), cat.examples && (_jsx(Text, { as: "span", variant: "muted", level: "small", style: { fontStyle: 'italic' }, children: cat.examples }))] })] }), cat.locked ? (_jsx(Text, { as: "span", variant: "muted", level: "small", children: t('alwaysOn') })) : (_jsx(Switch, { checked: enabled, onCheckedChange: (val) => setPreferenceCategories((prev) => ({
|
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* CTA button definition
|
|
4
|
+
*/
|
|
5
|
+
export interface CTAButton {
|
|
6
|
+
label: string;
|
|
7
|
+
onClick: () => void;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
variant?: 'default' | 'outline' | 'ghost' | 'link';
|
|
10
|
+
}
|
|
2
11
|
/**
|
|
3
12
|
* GameContainer props
|
|
4
13
|
*/
|
|
5
14
|
export interface GameContainerProps {
|
|
6
15
|
/** Main content area (scrollable, centered by default) */
|
|
7
16
|
content: ReactNode;
|
|
8
|
-
/** Optional fixed CTA button at bottom */
|
|
9
|
-
cta?:
|
|
10
|
-
label: string;
|
|
11
|
-
onClick: () => void;
|
|
12
|
-
disabled?: boolean;
|
|
13
|
-
variant?: 'default' | 'outline' | 'ghost' | 'link';
|
|
14
|
-
};
|
|
17
|
+
/** Optional fixed CTA button(s) at bottom - single button or array for multiple buttons */
|
|
18
|
+
cta?: CTAButton | CTAButton[];
|
|
15
19
|
/** Alignment variant for content area */
|
|
16
20
|
align?: 'center' | 'start' | 'stretch';
|
|
17
21
|
/** Justify variant for content area */
|
|
18
22
|
justify?: 'center' | 'start' | 'end' | 'between';
|
|
19
23
|
/** Disable ScrollArea (use plain overflow) - for interactive content */
|
|
20
24
|
disableScrollArea?: boolean;
|
|
21
|
-
/** Content width: '
|
|
25
|
+
/** Content width: 'narrow' (default, constrained) or 'full' */
|
|
22
26
|
contentVariant?: 'full' | 'narrow';
|
|
23
27
|
/** Additional className for content wrapper */
|
|
24
28
|
contentClassName?: string;
|
|
@@ -68,6 +72,18 @@ export interface GameContainerProps {
|
|
|
68
72
|
* />
|
|
69
73
|
* ```
|
|
70
74
|
*
|
|
75
|
+
* @example
|
|
76
|
+
* Multiple CTA buttons (side by side)
|
|
77
|
+
* ```tsx
|
|
78
|
+
* <GameContainer
|
|
79
|
+
* content={<ChallengeContent />}
|
|
80
|
+
* cta={[
|
|
81
|
+
* { label: 'Accept', onClick: handleAccept, variant: 'default' },
|
|
82
|
+
* { label: 'Decline', onClick: handleDecline, variant: 'outline' }
|
|
83
|
+
* ]}
|
|
84
|
+
* />
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
71
87
|
* @version 0.0.1
|
|
72
88
|
* @since 0.0.1
|
|
73
89
|
* @author AMBROISE PARK Consulting
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameContainer.d.ts","sourceRoot":"","sources":["../../../src/components/layout/GameContainer.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"GameContainer.d.ts","sourceRoot":"","sources":["../../../src/components/layout/GameContainer.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;CACpD;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,OAAO,EAAE,SAAS,CAAC;IAEnB,2FAA2F;IAC3F,GAAG,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;IAE9B,yCAAyC;IACzC,KAAK,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAEvC,uCAAuC;IACvC,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IAEjD,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAEnC,+CAA+C;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,GAAG,EACH,KAAgB,EAChB,OAAkB,EAClB,iBAAyB,EACzB,cAAyB,EACzB,gBAAgB,EAChB,YAAY,GACb,EAAE,kBAAkB,2CAoEpB"}
|
|
@@ -53,11 +53,29 @@ import { cn } from '@donotdev/components';
|
|
|
53
53
|
* />
|
|
54
54
|
* ```
|
|
55
55
|
*
|
|
56
|
+
* @example
|
|
57
|
+
* Multiple CTA buttons (side by side)
|
|
58
|
+
* ```tsx
|
|
59
|
+
* <GameContainer
|
|
60
|
+
* content={<ChallengeContent />}
|
|
61
|
+
* cta={[
|
|
62
|
+
* { label: 'Accept', onClick: handleAccept, variant: 'default' },
|
|
63
|
+
* { label: 'Decline', onClick: handleDecline, variant: 'outline' }
|
|
64
|
+
* ]}
|
|
65
|
+
* />
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
56
68
|
* @version 0.0.1
|
|
57
69
|
* @since 0.0.1
|
|
58
70
|
* @author AMBROISE PARK Consulting
|
|
59
71
|
*/
|
|
60
|
-
export function GameContainer({ content, cta, align = 'center', justify = 'center', disableScrollArea = false, contentVariant = '
|
|
61
|
-
const
|
|
62
|
-
|
|
72
|
+
export function GameContainer({ content, cta, align = 'center', justify = 'center', disableScrollArea = false, contentVariant = 'narrow', contentClassName, ctaClassName, }) {
|
|
73
|
+
const narrowWrapper = contentVariant === 'narrow' && (_jsx("div", { className: "dndev-game-container__content-narrow", children: content }));
|
|
74
|
+
const scrollContent = contentVariant === 'narrow' ? narrowWrapper : content;
|
|
75
|
+
const contentInner = disableScrollArea ? (_jsx("div", { style: {
|
|
76
|
+
overflow: 'auto',
|
|
77
|
+
overscrollBehavior: 'contain',
|
|
78
|
+
height: '100%',
|
|
79
|
+
}, children: scrollContent })) : (_jsx(ScrollArea, { className: "dndev-game-container__scroll", children: scrollContent }));
|
|
80
|
+
return (_jsxs("div", { className: "dndev-game-container", children: [_jsx("div", { className: cn('dndev-game-container__content', contentClassName), "data-align": align, "data-justify": justify, "data-content-variant": contentVariant, children: _jsx("div", { className: "dndev-game-container__scroll-wrapper", children: contentInner }) }), cta && (_jsx("div", { className: cn('dndev-game-container__cta', ctaClassName), children: Array.isArray(cta) ? (_jsx("div", { className: "dndev-game-container__cta-buttons", children: cta.map((button, index) => (_jsx(Button, { onClick: button.onClick, disabled: button.disabled ?? false, variant: button.variant ?? 'default', className: "dndev-game-container__cta-button", fullWidth: true, children: button.label }, index))) })) : (_jsx(Button, { onClick: cta.onClick, disabled: cta.disabled ?? false, variant: cta.variant ?? 'default', className: "dndev-game-container__cta-button", children: cta.label })) }))] }));
|
|
63
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameFlow.d.ts","sourceRoot":"","sources":["../../../src/components/layout/GameFlow.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"GameFlow.d.ts","sourceRoot":"","sources":["../../../src/components/layout/GameFlow.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CACjC,MAAM,EACN,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,CAAA;CAAE,CAAC,CAC/C,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,cAAc,EAAE,cAAc,CAAC;IAC/B,oCAAoC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAmCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,QAAQ,CAAC,EACvB,cAAc,EACd,aAAa,EACb,UAAU,EACV,QAAe,GAChB,EAAE,aAAa,kDAgCf"}
|
|
@@ -8,12 +8,34 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
8
8
|
* @since 0.0.1
|
|
9
9
|
* @author AMBROISE PARK Consulting
|
|
10
10
|
*/
|
|
11
|
-
import { useEffect,
|
|
11
|
+
import { useEffect, Suspense, lazy, } from 'react';
|
|
12
12
|
/**
|
|
13
13
|
* Module-level cache for preloaded screens
|
|
14
14
|
* Tracks which screens have been preloaded to avoid duplicate preloading
|
|
15
15
|
*/
|
|
16
16
|
const preloadedScreens = new Set();
|
|
17
|
+
/**
|
|
18
|
+
* Module-level cache for lazy components.
|
|
19
|
+
* CRITICAL: lazy() must NOT be called inside a component (useMemo or otherwise).
|
|
20
|
+
* Doing so creates a new component TYPE on each call, which causes React to
|
|
21
|
+
* unmount/remount the entire subtree and can trigger Rules of Hooks violations.
|
|
22
|
+
* This cache ensures each screen name always resolves to the same lazy component ref.
|
|
23
|
+
*/
|
|
24
|
+
const lazyComponentCache = new Map();
|
|
25
|
+
/**
|
|
26
|
+
* Get or create a cached lazy component for a screen name
|
|
27
|
+
*/
|
|
28
|
+
function getLazyComponent(screenName, registry) {
|
|
29
|
+
const cached = lazyComponentCache.get(screenName);
|
|
30
|
+
if (cached)
|
|
31
|
+
return cached;
|
|
32
|
+
const importer = registry[screenName];
|
|
33
|
+
if (!importer)
|
|
34
|
+
return null;
|
|
35
|
+
const component = lazy(importer);
|
|
36
|
+
lazyComponentCache.set(screenName, component);
|
|
37
|
+
return component;
|
|
38
|
+
}
|
|
17
39
|
/**
|
|
18
40
|
* GameFlow - Screen router for game/session flows
|
|
19
41
|
*
|
|
@@ -59,16 +81,10 @@ export function GameFlow({ screenRegistry, currentScreen, nextScreen, fallback =
|
|
|
59
81
|
}
|
|
60
82
|
}
|
|
61
83
|
}, [nextScreen, screenRegistry]);
|
|
62
|
-
// Get lazy component for currentScreen
|
|
63
|
-
const ScreenComponent =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const importer = screenRegistry[currentScreen];
|
|
67
|
-
if (!importer)
|
|
68
|
-
return null;
|
|
69
|
-
// Create lazy component from registry
|
|
70
|
-
return lazy(importer);
|
|
71
|
-
}, [currentScreen, screenRegistry]);
|
|
84
|
+
// Get cached lazy component for currentScreen (stable reference per screen name)
|
|
85
|
+
const ScreenComponent = currentScreen
|
|
86
|
+
? getLazyComponent(currentScreen, screenRegistry)
|
|
87
|
+
: null;
|
|
72
88
|
// If no currentScreen or no component found, return null
|
|
73
89
|
if (!currentScreen || !ScreenComponent) {
|
|
74
90
|
return null;
|
|
@@ -205,7 +205,7 @@ function CacheSettings() {
|
|
|
205
205
|
setIsClearing(false);
|
|
206
206
|
}
|
|
207
207
|
};
|
|
208
|
-
return (_jsxs(Stack, {
|
|
208
|
+
return (_jsxs(Stack, { children: [_jsx(Stack, { children: Object.entries(cacheOptions).map(([key, checked]) => (_jsxs(Stack, { direction: "row", align: "center", gap: "tight", children: [_jsx(Checkbox, { id: key, checked: checked, onCheckedChange: (checked) => setCacheOptions((prev) => ({ ...prev, [key]: !!checked })) }), _jsx("label", { htmlFor: key, style: {
|
|
209
209
|
fontSize: 'var(--font-size-sm)',
|
|
210
210
|
fontWeight: 500,
|
|
211
211
|
lineHeight: 1,
|
|
@@ -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":"AAyCA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,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,2CA6RrB"}
|
|
@@ -13,12 +13,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
13
13
|
* @since 0.0.1
|
|
14
14
|
* @author AMBROISE PARK Consulting
|
|
15
15
|
*/
|
|
16
|
-
import { useMemo, useCallback
|
|
16
|
+
import { useMemo, useCallback } from 'react';
|
|
17
17
|
import { Heart } from 'lucide-react';
|
|
18
18
|
import { Grid, Card, Stack, Text, Spinner, Section, Button, } from '@donotdev/components';
|
|
19
19
|
import { useTranslation } from '@donotdev/core';
|
|
20
20
|
import { useNavigate } from '../../routing';
|
|
21
|
-
import { translateFieldLabel, useCrudCardList, EntityFilters, useEntityFavorites, matchesFilter, formatValue, } from '@donotdev/crud';
|
|
21
|
+
import { translateFieldLabel, useCrudCardList, EntityFilters, useEntityFavorites, matchesFilter, formatValue, useCrudFilters, } from '@donotdev/crud';
|
|
22
22
|
/**
|
|
23
23
|
* Entity Card List Component - Card grid view for public/user-facing browsing
|
|
24
24
|
*
|
|
@@ -41,9 +41,15 @@ filter, hideFilters = false, }) {
|
|
|
41
41
|
const { isFavorite, toggleFavorite, favoritesFilter } = useEntityFavorites({
|
|
42
42
|
collection: entity.collection,
|
|
43
43
|
});
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
const
|
|
44
|
+
// Favorites toggle from CrudStore (persists across navigation)
|
|
45
|
+
// Note: EntityFilters now manages its own filters internally via useCrudFilters
|
|
46
|
+
const { showFavoritesOnly, setShowFavoritesOnly } = useCrudFilters({
|
|
47
|
+
collection: entity.collection,
|
|
48
|
+
});
|
|
49
|
+
// Get filters for applying to data (EntityFilters manages its own state)
|
|
50
|
+
const { filters } = useCrudFilters({
|
|
51
|
+
collection: entity.collection,
|
|
52
|
+
});
|
|
47
53
|
// Apply filters from EntityFilters component
|
|
48
54
|
const applyFilters = useCallback((item) => {
|
|
49
55
|
if (Object.keys(filters).length === 0)
|
|
@@ -110,22 +116,22 @@ filter, hideFilters = false, }) {
|
|
|
110
116
|
return (_jsxs(_Fragment, { children: [!hideFilters && (_jsx(Section, { title: tCrud('filters.title', {
|
|
111
117
|
entity: entityName,
|
|
112
118
|
defaultValue: `Browse ${entityName} - Filters`,
|
|
113
|
-
}), collapsible: true, defaultOpen: true, children: _jsxs(Stack, { direction: "column",
|
|
119
|
+
}), 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
|
|
114
120
|
? tCrud('favorites.showAll', { defaultValue: 'Show All' })
|
|
115
121
|
: tCrud('favorites.showFavorites', {
|
|
116
122
|
defaultValue: 'Show Favorites',
|
|
117
|
-
}) }), _jsx(EntityFilters, { entity: entity, data: rawData,
|
|
123
|
+
}) }), _jsx(EntityFilters, { entity: entity, data: rawData, fieldsToFilter: entity.listCardFields })] }) })), _jsx(Section, { title: loading
|
|
118
124
|
? tCrud('results.title.fetching', { defaultValue: 'Fetching...' })
|
|
119
125
|
: tCrud('results.title.count', {
|
|
120
126
|
count: data.length,
|
|
121
127
|
defaultValue: data.length === 1
|
|
122
128
|
? 'Found 1 occurrence'
|
|
123
129
|
: `Found ${data.length} occurrences`,
|
|
124
|
-
}), collapsible: true, defaultOpen: true, children: loading ? (_jsx(Stack, { align: "center", justify: "center",
|
|
130
|
+
}), 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', {
|
|
125
131
|
defaultValue: `No ${entity.name.toLowerCase()} found`,
|
|
126
132
|
}) }), _jsx(Text, { style: { color: 'var(--muted-foreground)' }, children: tCrud('emptyState.description', {
|
|
127
133
|
defaultValue: `No ${entity.name.toLowerCase()} available at this time.`,
|
|
128
|
-
}) })] })) : (_jsx(Grid, { cols: cols,
|
|
134
|
+
}) })] })) : (_jsx(Grid, { cols: cols, children: data.map((item) => {
|
|
129
135
|
const imageValue = imageField ? item[imageField] : null;
|
|
130
136
|
// Backend optimizes picture fields for listCard: returns thumbUrl string directly
|
|
131
137
|
// (or fullUrl if thumbUrl missing, or null if no picture)
|
|
@@ -152,7 +158,7 @@ filter, hideFilters = false, }) {
|
|
|
152
158
|
})
|
|
153
159
|
: tCrud('favorites.add', {
|
|
154
160
|
defaultValue: 'Add to favorites',
|
|
155
|
-
}) }), _jsxs(Stack, { direction: "column",
|
|
161
|
+
}) }), _jsxs(Stack, { direction: "column", children: [imageUrl && (_jsx("div", { style: {
|
|
156
162
|
width: '100%',
|
|
157
163
|
aspectRatio: '16/9',
|
|
158
164
|
borderRadius: 'var(--radius-md)',
|
|
@@ -135,7 +135,7 @@ export function EntityDisplayRenderer({ entity, id, t, className = '', backend =
|
|
|
135
135
|
}
|
|
136
136
|
// Error or not found state
|
|
137
137
|
if (displayError || !displayData) {
|
|
138
|
-
return (_jsx(Stack, { align: "center", justify: "center",
|
|
138
|
+
return (_jsx(Stack, { align: "center", justify: "center", style: {
|
|
139
139
|
padding: 'var(--gap-3xl)',
|
|
140
140
|
textAlign: 'center',
|
|
141
141
|
}, className: className, children: _jsxs(Stack, { direction: "column", gap: "tight", children: [_jsx("h3", { style: { color: 'var(--muted-foreground)' }, children: notFoundMessage ||
|
|
@@ -149,7 +149,7 @@ export function EntityDisplayRenderer({ entity, id, t, className = '', backend =
|
|
|
149
149
|
: String(displayError) }))] }) }));
|
|
150
150
|
}
|
|
151
151
|
// Render all visible fields with values
|
|
152
|
-
return (_jsx(Stack, { direction: "column",
|
|
152
|
+
return (_jsx(Stack, { direction: "column", className: className, children: visibleFields.map(([fieldName, fieldConfig]) => {
|
|
153
153
|
return (_jsx(DisplayFieldRenderer, { name: fieldName, config: fieldConfig, value: displayData[fieldName], t: translate }, fieldName));
|
|
154
154
|
}) }));
|
|
155
155
|
}
|
|
@@ -1 +1 @@
|
|
|
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,EACV,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,
|
|
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,EACV,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,2CAic5B;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityList.d.ts","sourceRoot":"","sources":["../../../src/crud/components/EntityList.tsx"],"names":[],"mappings":"
|
|
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,QAAkB,EAClB,QAAQ,EACR,OAAO,EACP,WAAmB,EACnB,UAAqB,EACrB,QAAQ,EAAE,YAAY,EACtB,YAAY,EACZ,UAAiB,GAClB,EAAE,eAAe,2CAkTjB"}
|
|
@@ -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, } from '@donotdev/crud';
|
|
21
|
+
import { translateFieldLabel, useCrud, useCrudList, EntityFilters, matchesFilter, formatValue, useCrudFilters, } from '@donotdev/crud';
|
|
22
22
|
/**
|
|
23
23
|
* Entity List Component - Table view for admin/internal operations
|
|
24
24
|
*
|
|
@@ -54,8 +54,10 @@ export function EntityList({ entity, userRole = 'guest', basePath, onClick, hide
|
|
|
54
54
|
const data = listData?.items || [];
|
|
55
55
|
// Entity + crud namespaces so formatValue can resolve crud:price.* etc.
|
|
56
56
|
const { t } = useTranslation([entity.namespace, 'crud']);
|
|
57
|
-
//
|
|
58
|
-
const
|
|
57
|
+
// Get filters for applying to data (EntityFilters manages its own state)
|
|
58
|
+
const { filters } = useCrudFilters({
|
|
59
|
+
collection: entity.collection,
|
|
60
|
+
});
|
|
59
61
|
const [searchQuery, setSearchQuery] = useState('');
|
|
60
62
|
// Refresh handler - triggers manual refetch in useList
|
|
61
63
|
const handleRefresh = useCallback(async () => {
|
|
@@ -82,10 +84,6 @@ export function EntityList({ entity, userRole = 'guest', basePath, onClick, hide
|
|
|
82
84
|
const handleDelete = useCallback(async (itemId) => {
|
|
83
85
|
await deleteItem(itemId);
|
|
84
86
|
}, [deleteItem]);
|
|
85
|
-
// Update filters (for EntityFilters component)
|
|
86
|
-
const handleFiltersChange = useCallback((newFilters) => {
|
|
87
|
-
setFilters(newFilters);
|
|
88
|
-
}, []);
|
|
89
87
|
// Apply search and filters to data
|
|
90
88
|
// @todo Server-side filtering: Currently only handles client-side filtering.
|
|
91
89
|
// When pagination='server', filters should be sent to the server via useCrudList options.
|
|
@@ -170,9 +168,9 @@ export function EntityList({ entity, userRole = 'guest', basePath, onClick, hide
|
|
|
170
168
|
return (_jsxs(_Fragment, { children: [_jsx(Section, { title: tCrud('filters.title', {
|
|
171
169
|
entity: entityName,
|
|
172
170
|
defaultValue: `Browse ${entityName} - Filters`,
|
|
173
|
-
}), collapsible: true, defaultOpen: true, children: _jsxs(Stack, {
|
|
171
|
+
}), 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', {
|
|
174
172
|
defaultValue: 'Search...',
|
|
175
|
-
}), 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,
|
|
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
|
|
176
174
|
? tCrud('results.title.fetching', {
|
|
177
175
|
defaultValue: 'Fetching...',
|
|
178
176
|
})
|
|
@@ -64,7 +64,7 @@ const FormItemContext = createContext({});
|
|
|
64
64
|
*/
|
|
65
65
|
const FormItem = ({ className, ...props }) => {
|
|
66
66
|
const id = useId();
|
|
67
|
-
return (_jsx(FormItemContext.Provider, { value: { id }, children: _jsx(Stack, {
|
|
67
|
+
return (_jsx(FormItemContext.Provider, { value: { id }, children: _jsx(Stack, { className: className, ...props }) }));
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
70
70
|
* Hook for accessing form field context
|