@axinom/mosaic-agent-skills 0.0.1

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 (72) hide show
  1. package/README.md +121 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +58 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/logger.d.ts +6 -0
  7. package/dist/logger.d.ts.map +1 -0
  8. package/dist/logger.js +15 -0
  9. package/dist/logger.js.map +1 -0
  10. package/dist/tools/graphql/index.d.ts +3 -0
  11. package/dist/tools/graphql/index.d.ts.map +1 -0
  12. package/dist/tools/graphql/index.js +84 -0
  13. package/dist/tools/graphql/index.js.map +1 -0
  14. package/dist/tools/graphql/tools.d.ts +71 -0
  15. package/dist/tools/graphql/tools.d.ts.map +1 -0
  16. package/dist/tools/graphql/tools.js +187 -0
  17. package/dist/tools/graphql/tools.js.map +1 -0
  18. package/dist/tools/graphql/utils.d.ts +20 -0
  19. package/dist/tools/graphql/utils.d.ts.map +1 -0
  20. package/dist/tools/graphql/utils.js +140 -0
  21. package/dist/tools/graphql/utils.js.map +1 -0
  22. package/dist/tools/skills/index.d.ts +3 -0
  23. package/dist/tools/skills/index.d.ts.map +1 -0
  24. package/dist/tools/skills/index.js +62 -0
  25. package/dist/tools/skills/index.js.map +1 -0
  26. package/dist/tools/skills/tools.d.ts +5 -0
  27. package/dist/tools/skills/tools.d.ts.map +1 -0
  28. package/dist/tools/skills/tools.js +67 -0
  29. package/dist/tools/skills/tools.js.map +1 -0
  30. package/dist/tools/skills/types.d.ts +12 -0
  31. package/dist/tools/skills/types.d.ts.map +1 -0
  32. package/dist/tools/skills/types.js +3 -0
  33. package/dist/tools/skills/types.js.map +1 -0
  34. package/dist/tools/skills/utils.d.ts +11 -0
  35. package/dist/tools/skills/utils.d.ts.map +1 -0
  36. package/dist/tools/skills/utils.js +127 -0
  37. package/dist/tools/skills/utils.js.map +1 -0
  38. package/package.json +40 -0
  39. package/skills/_shared/actions/execution-summary.md +53 -0
  40. package/skills/_shared/actions/typescript-codegen.md +32 -0
  41. package/skills/_shared/actions/typescript-validation.md +32 -0
  42. package/skills/_shared/conventions/field-component-mapping.md +155 -0
  43. package/skills/_shared/conventions/field-validation-schema.md +77 -0
  44. package/skills/_shared/discovery/discover-paths.md +52 -0
  45. package/skills/_shared/discovery/extract-entity-features.md +565 -0
  46. package/skills/generate-create-station/SKILL.md +93 -0
  47. package/skills/generate-create-station/refs/finalization/station-registration.md +163 -0
  48. package/skills/generate-create-station/refs/templates/create-form-station.md +206 -0
  49. package/skills/generate-create-station/refs/templates/graphql-operations.md +56 -0
  50. package/skills/generate-details-station/SKILL.md +125 -0
  51. package/skills/generate-details-station/refs/finalization/explorer-integration.md +242 -0
  52. package/skills/generate-details-station/refs/finalization/station-registration.md +139 -0
  53. package/skills/generate-details-station/refs/templates/actions.md +127 -0
  54. package/skills/generate-details-station/refs/templates/breadcrumb.md +67 -0
  55. package/skills/generate-details-station/refs/templates/details-form-station.md +589 -0
  56. package/skills/generate-details-station/refs/templates/details-wrapper.md +54 -0
  57. package/skills/generate-details-station/refs/templates/form-data-types.md +62 -0
  58. package/skills/generate-details-station/refs/templates/graphql-operations.md +256 -0
  59. package/skills/generate-details-station/refs/templates/managed-integrations/image-management-station.md +322 -0
  60. package/skills/generate-details-station/refs/templates/managed-integrations/video-management-station.md +283 -0
  61. package/skills/generate-explorer-station/SKILL.md +121 -0
  62. package/skills/generate-explorer-station/refs/finalization/station-registration.md +145 -0
  63. package/skills/generate-explorer-station/refs/select-columns-filters.md +63 -0
  64. package/skills/generate-explorer-station/refs/templates/bulk-edit.md +369 -0
  65. package/skills/generate-explorer-station/refs/templates/common/bulk-actions.md +64 -0
  66. package/skills/generate-explorer-station/refs/templates/common/columns.md +131 -0
  67. package/skills/generate-explorer-station/refs/templates/common/data-provider.md +83 -0
  68. package/skills/generate-explorer-station/refs/templates/common/filters.md +220 -0
  69. package/skills/generate-explorer-station/refs/templates/common/inline-actions.md +45 -0
  70. package/skills/generate-explorer-station/refs/templates/common/types.md +28 -0
  71. package/skills/generate-explorer-station/refs/templates/graphql-operations.md +235 -0
  72. package/skills/generate-explorer-station/refs/templates/navigation-explorer-station.md +144 -0
@@ -0,0 +1,242 @@
1
+ ---
2
+ name: explorer-integration
3
+ input:
4
+ - EntityFeatures
5
+ - Generated components (Details, Image Management, Video Management)
6
+ output:
7
+ - path: '{workflowsPath}/src/Stations/{EntityPlural}/{Entity}Details/{Entity}DetailsQuickEdit.tsx'
8
+ description: 'Details quick edit component'
9
+ - path: '{workflowsPath}/src/Stations/{EntityPlural}/{EntityPlural}Explorer/{EntityPlural}.tsx'
10
+ description:
11
+ 'Explorer updated with quick edit registrations and inline actions'
12
+ - path: '{workflowsPath}/src/Stations/{EntityPlural}/{EntityPlural}Explorer/common/{EntityPlural}.inlineActions.ts'
13
+ description: 'Inline actions updated with Open Details and Delete actions'
14
+ ---
15
+
16
+ # Integrate Details Station with Explorer
17
+
18
+ Creates quick edit wrapper components and fully integrates the details station
19
+ with the explorer by updating quick edit registrations, inline actions, and
20
+ navigation URLs.
21
+
22
+ ## Step 1: Generate Details Quick Edit Component
23
+
24
+ **Path:** `{Entity}Details/{Entity}DetailsQuickEdit.tsx`
25
+
26
+ ```typescript
27
+ import { QuickEditContext, QuickEditContextType } from '@axinom/mosaic-ui';
28
+ import React, { useContext } from 'react';
29
+ import { {Entity}Data } from '../{EntityPlural}Explorer/common/{EntityPlural}.types';
30
+ import { {Entity}DetailsForm } from './{Entity}DetailsForm';
31
+
32
+ export const {Entity}DetailsQuickEdit: React.FC = () => {
33
+ const { selectedItem } =
34
+ useContext<QuickEditContextType<{Entity}Data>>(QuickEditContext);
35
+
36
+ return <{Entity}DetailsForm {entityCamel}Id={selectedItem.id} />;
37
+ };
38
+ ```
39
+
40
+ ## Step 2: Check if Explorer Exists
41
+
42
+ **Discovery:** Check if `{EntityPlural}Explorer/{EntityPlural}.tsx` exists
43
+
44
+ - If **exists** → Proceed to Step 3
45
+ - If **does not exist** → Skip Step 3, move to next step
46
+
47
+ ## Step 3: Integrate with Explorer
48
+
49
+ **File:** `{EntityPlural}Explorer/{EntityPlural}.tsx`
50
+
51
+ ### 3.1: Add Imports
52
+
53
+ Add at the top of the file (after existing imports):
54
+
55
+ ```typescript
56
+ import { {Entity}DetailsQuickEdit } from '../{Entity}Details/{Entity}DetailsQuickEdit';
57
+ // If features.capabilities.images exists:
58
+ import { {Entity}ImageManagementQuickEdit } from '../{Entity}ImageManagement/{Entity}ImageManagementQuickEdit';
59
+ // End if
60
+ // If features.capabilities.videos exists:
61
+ import { {Entity}VideoManagementQuickEdit } from '../{Entity}VideoManagement/{Entity}VideoManagementQuickEdit';
62
+ // End if
63
+ ```
64
+
65
+ ### 3.2: Update quickEditRegistrations
66
+
67
+ **Find** the `quickEditRegistrations` prop in the `NavigationExplorer`
68
+ component.
69
+
70
+ **Replace** the entire prop (including TODO comments) with:
71
+
72
+ ```typescript
73
+ quickEditRegistrations={[
74
+ { component: <{Entity}DetailsQuickEdit />, label: '{Entity} Details' },
75
+ // If features.capabilities.images exists:
76
+ { component: <{Entity}ImageManagementQuickEdit />, label: 'Manage Images' },
77
+ // End if
78
+ // If features.capabilities.videos exists:
79
+ { component: <{Entity}VideoManagementQuickEdit />, label: 'Manage Videos' },
80
+ // End if
81
+ ]}
82
+ ```
83
+
84
+ **Before (with TODOs):**
85
+
86
+ ```typescript
87
+ quickEditRegistrations={[
88
+ // TODO: Add quick edit registrations once Details/Image/Video stations exist
89
+ // Example structure:
90
+ // { component: <{Entity}DetailsQuickEdit />, label: '{Entity} Details' },
91
+ // { component: <{Entity}ImageManagementQuickEdit />, label: 'Manage Images' },
92
+ // { component: <{Entity}VideoManagementQuickEdit />, label: 'Manage Videos' },
93
+ ]}
94
+ ```
95
+
96
+ **After (integrated):**
97
+
98
+ ```typescript
99
+ quickEditRegistrations={[
100
+ { component: <MovieDetailsQuickEdit />, label: 'Movie Details' },
101
+ { component: <MovieImageManagementQuickEdit />, label: 'Manage Images' },
102
+ { component: <MovieVideoManagementQuickEdit />, label: 'Manage Videos' },
103
+ ]}
104
+ ```
105
+
106
+ ### 3.3: Remove calculateNavigateUrl TODO
107
+
108
+ **Find** the `calculateNavigateUrl` prop.
109
+
110
+ **Remove** the TODO comment above it (if exists):
111
+
112
+ ```typescript
113
+ // Before:
114
+ // TODO: Update once Details station exists
115
+ calculateNavigateUrl={(item) => `/{kebabCasePlural}/${item.id}`}
116
+
117
+ // After:
118
+ calculateNavigateUrl={(item) => `/{kebabCasePlural}/${item.id}`}
119
+ ```
120
+
121
+ ### 3.4: Update Inline Actions
122
+
123
+ **File:** `{EntityPlural}Explorer/common/{EntityPlural}.inlineActions.ts`
124
+
125
+ Once the details station exists with delete mutation, update the inline actions
126
+ to enable "Open Details" navigation and "Delete" actions.
127
+
128
+ **Add imports** at the top:
129
+
130
+ ```typescript
131
+ import { ActionData, IconName } from '@axinom/mosaic-ui';
132
+ import { client } from '{apolloClientPath}';
133
+ import { useDelete{Entity}Mutation } from '../../../../generated/graphql';
134
+ import { {Entity}Data } from './{EntityPlural}.types';
135
+ ```
136
+
137
+ **Replace** the TODO placeholders in the hook with actual implementations:
138
+
139
+ ```typescript
140
+ export function use{EntityPlural}InlineActions(): {
141
+ readonly generateInlineMenuActions: (data: {Entity}Data) => ActionData[];
142
+ } {
143
+ const [delete{Entity}Mutation] = useDelete{Entity}Mutation({
144
+ client,
145
+ fetchPolicy: 'no-cache',
146
+ });
147
+
148
+ const generateInlineMenuActions = ({ id }: {Entity}Data): ActionData[] => {
149
+ return [
150
+ {
151
+ label: 'Open Details',
152
+ path: `/{kebabCasePlural}/${id}`,
153
+ },
154
+ {
155
+ label: 'Delete',
156
+ onActionSelected: async () => {
157
+ await delete{Entity}Mutation({ variables: { input: { id } } });
158
+ },
159
+ icon: IconName.Delete,
160
+ confirmationMode: 'Simple',
161
+ },
162
+ ];
163
+ };
164
+
165
+ return { generateInlineMenuActions };
166
+ }
167
+ ```
168
+
169
+ ## Example (Complete Integration)
170
+
171
+ **MovieDetailsQuickEdit.tsx:**
172
+
173
+ ```typescript
174
+ import { QuickEditContext, QuickEditContextType } from '@axinom/mosaic-ui';
175
+ import React, { useContext } from 'react';
176
+ import { MovieData } from '../MoviesExplorer/common/Movies.types';
177
+ import { MovieDetailsForm } from './MovieDetailsForm';
178
+
179
+ export const MovieDetailsQuickEdit: React.FC = () => {
180
+ const { selectedItem } =
181
+ useContext<QuickEditContextType<MovieData>>(QuickEditContext);
182
+
183
+ return <MovieDetailsForm movieId={selectedItem.id} />;
184
+ };
185
+ ```
186
+
187
+ **Movies.tsx (Explorer) - Updated:**
188
+
189
+ ```typescript
190
+ import { NavigationExplorer } from '@axinom/mosaic-ui';
191
+ import React from 'react';
192
+ import { MovieDetailsQuickEdit } from '../MovieDetails/MovieDetailsQuickEdit';
193
+ import { MovieImageManagementQuickEdit } from '../MovieImageManagement/MovieImageManagementQuickEdit';
194
+ import { MovieVideoManagementQuickEdit } from '../MovieVideoManagement/MovieVideoManagementQuickEdit';
195
+ import { useMoviesBulkActions } from './common/Movies.bulkActions';
196
+ import { moviesColumns } from './common/Movies.columns';
197
+ import { createMoviesDataProvider } from './common/Movies.dataProvider';
198
+ import { useMoviesFilters } from './common/Movies.filters';
199
+ import { useMoviesInlineActions } from './common/Movies.inlineActions';
200
+ import { MovieData } from './common/Movies.types';
201
+
202
+ export const Movies: React.FC = () => {
203
+ const { transformFilters, filterOptions } = useMoviesFilters();
204
+ const { bulkActions } = useMoviesBulkActions();
205
+ const { generateInlineMenuActions } = useMoviesInlineActions();
206
+ const dataProvider = createMoviesDataProvider(transformFilters);
207
+
208
+ return (
209
+ <NavigationExplorer<MovieData>
210
+ title="Movies"
211
+ stationKey="MoviesExplorer"
212
+ calculateNavigateUrl={(item) => `/movies/${item.id}`}
213
+ onCreateAction="/movies/create"
214
+ columns={moviesColumns}
215
+ dataProvider={dataProvider}
216
+ filterOptions={filterOptions}
217
+ defaultSortOrder={{ column: 'updatedDate', direction: 'desc' }}
218
+ inlineMenuActions={generateInlineMenuActions}
219
+ bulkActions={bulkActions}
220
+ quickEditRegistrations={[
221
+ { component: <MovieDetailsQuickEdit />, label: 'Movie Details' },
222
+ {
223
+ component: <MovieImageManagementQuickEdit />,
224
+ label: 'Manage Images',
225
+ },
226
+ {
227
+ component: <MovieVideoManagementQuickEdit />,
228
+ label: 'Manage Videos',
229
+ },
230
+ ]}
231
+ />
232
+ );
233
+ };
234
+ ```
235
+
236
+ ## Notes
237
+
238
+ - Quick edit components reuse the same form components as full-page views
239
+ - Explorer must exist for integration; if not found, only generate the component
240
+ - All quick edit components follow same pattern (context → extract ID → render
241
+ form)
242
+ - Quick edit registrations appear in order: Details, Images, Videos
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: station-registration
3
+ input:
4
+ - EntityFeatures
5
+ - Generated components
6
+ - DiscoveredPaths
7
+ output:
8
+ - Updated registrations.tsx with details routes
9
+ ---
10
+
11
+ # Register Details Station
12
+
13
+ Update entity registrations file with details, image management, and video
14
+ management routes.
15
+
16
+ ## File Path
17
+
18
+ `{workflowsPath}/src/Stations/{EntityPlural}/registrations.tsx`
19
+
20
+ ## Add Imports
21
+
22
+ ```typescript
23
+ import { {Entity}Details } from './{Entity}Details/{Entity}Details';
24
+ import { {Entity}DetailsCrumb } from './{Entity}Details/{Entity}DetailsCrumb';
25
+ // If features.capabilities.images exists:
26
+ import { {Entity}ImageManagement } from './{Entity}ImageManagement/{Entity}ImageManagement';
27
+ // End if
28
+ // If features.capabilities.videos exists:
29
+ import { {Entity}VideoManagement } from './{Entity}VideoManagement/{Entity}VideoManagement';
30
+ // End if
31
+ ```
32
+
33
+ ## Add Route Registrations
34
+
35
+ Add inside the `register()` function:
36
+
37
+ ```typescript
38
+ // Details route
39
+ app.registerPage(
40
+ '/{kebabCasePlural}/:{entityCamel}Id',
41
+ () => (
42
+ <ExtensionsContext.Provider value={extensions}>
43
+ <{Entity}Details />
44
+ </ExtensionsContext.Provider>
45
+ ),
46
+ {
47
+ breadcrumb: {Entity}DetailsCrumb,
48
+ permissions: { '{features.service.serviceId}': {features.service.permissions} },
49
+ },
50
+ );
51
+
52
+ // If features.capabilities.images exists:
53
+ // Image management route
54
+ app.registerPage(
55
+ '/{kebabCasePlural}/:{entityCamel}Id/images',
56
+ () => (
57
+ <ExtensionsContext.Provider value={extensions}>
58
+ <{Entity}ImageManagement />
59
+ </ExtensionsContext.Provider>
60
+ ),
61
+ {
62
+ breadcrumb: () => 'Manage Images',
63
+ permissions: { '{features.service.serviceId}': {features.service.permissions} },
64
+ },
65
+ );
66
+ // End if
67
+
68
+ // If features.capabilities.videos exists:
69
+ // Video management route
70
+ app.registerPage(
71
+ '/{kebabCasePlural}/:{entityCamel}Id/videos',
72
+ () => (
73
+ <ExtensionsContext.Provider value={extensions}>
74
+ <{Entity}VideoManagement />
75
+ </ExtensionsContext.Provider>
76
+ ),
77
+ {
78
+ breadcrumb: () => 'Manage Videos',
79
+ permissions: { '{features.service.serviceId}': {features.service.permissions} },
80
+ },
81
+ );
82
+ // End if
83
+ ```
84
+
85
+ ## Example
86
+
87
+ ```typescript
88
+ // In registrations.tsx
89
+ import { MovieDetails } from './MovieDetails/MovieDetails';
90
+ import { MovieDetailsCrumb } from './MovieDetails/MovieDetailsCrumb';
91
+ import { MovieImageManagement } from './MovieImageManagement/MovieImageManagement';
92
+ import { MovieVideoManagement } from './MovieVideoManagement/MovieVideoManagement';
93
+
94
+ export function register(app: PiletApi, extensions: Extensions): void {
95
+ // ... existing explorer registration ...
96
+
97
+ // Details route
98
+ app.registerPage(
99
+ '/movies/:movieId',
100
+ () => (
101
+ <ExtensionsContext.Provider value={extensions}>
102
+ <MovieDetails />
103
+ </ExtensionsContext.Provider>
104
+ ),
105
+ {
106
+ breadcrumb: MovieDetailsCrumb,
107
+ permissions: { 'media-service': ['ADMIN', 'MOVIES_EDIT', 'MOVIES_VIEW'] },
108
+ },
109
+ );
110
+
111
+ // Image management route
112
+ app.registerPage(
113
+ '/movies/:movieId/images',
114
+ () => (
115
+ <ExtensionsContext.Provider value={extensions}>
116
+ <MovieImageManagement />
117
+ </ExtensionsContext.Provider>
118
+ ),
119
+ {
120
+ breadcrumb: () => 'Manage Images',
121
+ permissions: { 'media-service': ['ADMIN', 'MOVIES_EDIT', 'MOVIES_VIEW'] },
122
+ },
123
+ );
124
+
125
+ // Video management route
126
+ app.registerPage(
127
+ '/movies/:movieId/videos',
128
+ () => (
129
+ <ExtensionsContext.Provider value={extensions}>
130
+ <MovieVideoManagement />
131
+ </ExtensionsContext.Provider>
132
+ ),
133
+ {
134
+ breadcrumb: () => 'Manage Videos',
135
+ permissions: { 'media-service': ['ADMIN', 'MOVIES_EDIT', 'MOVIES_VIEW'] },
136
+ },
137
+ );
138
+ }
139
+ ```
@@ -0,0 +1,127 @@
1
+ ---
2
+ name: details-actions
3
+ input:
4
+ - EntityFeatures
5
+ - DiscoveredPaths
6
+ output:
7
+ - path: '{workflowsPath}/src/Stations/{EntityPlural}/{Entity}Details/{Entity}Details.actions.ts'
8
+ description: 'Actions hook with navigation and mutation actions'
9
+ ---
10
+
11
+ # Generate Actions Hook
12
+
13
+ Provides action buttons for details form (delete, navigate to child resources).
14
+
15
+ ## Template
16
+
17
+ ```typescript
18
+ import { FormActionData } from '@axinom/mosaic-ui';
19
+ import { useHistory } from 'react-router';
20
+ import { client } from '../../../apolloClient';
21
+ import {
22
+ // If features.mutations.delete === true:
23
+ useDelete{Entity}Mutation,
24
+ // End if
25
+ } from '../../../generated/graphql';
26
+ import { {Entity}DetailsFormData } from './{Entity}Details.types';
27
+
28
+ export function use{Entity}DetailsActions(id: number): {
29
+ readonly actions: FormActionData<{Entity}DetailsFormData}[];
30
+ } {
31
+ const history = useHistory();
32
+ // If features.mutations.delete === true:
33
+ const [delete{Entity}Mutation] = useDelete{Entity}Mutation({
34
+ client,
35
+ fetchPolicy: 'no-cache',
36
+ });
37
+
38
+ const delete{Entity} = async (): Promise<void> => {
39
+ await delete{Entity}Mutation({ variables: { input: { id } } });
40
+ history.push('/{kebabCasePlural}');
41
+ };
42
+ // End if
43
+
44
+ const actions: FormActionData<{Entity}DetailsFormData}[] = [
45
+ // If features.capabilities.videos exists:
46
+ {
47
+ label: 'Manage Videos',
48
+ path: `/{kebabCasePlural}/${id}/videos`,
49
+ },
50
+ // End if
51
+
52
+ // If features.capabilities.images exists:
53
+ {
54
+ label: 'Manage Images',
55
+ path: `/{kebabCasePlural}/${id}/images`,
56
+ },
57
+ // End if
58
+
59
+ // If features.mutations.delete === true:
60
+ {
61
+ label: 'Delete',
62
+ confirmationMode: 'Simple' as const,
63
+ onActionSelected: delete{Entity},
64
+ },
65
+ // End if
66
+ ];
67
+
68
+ return { actions } as const;
69
+ }
70
+ ```
71
+
72
+ ## Action Types
73
+
74
+ **Navigation Actions** (`path` property):
75
+
76
+ - Manage Videos (if `features.capabilities.videos` exists)
77
+ - Manage Images (if `features.capabilities.images` exists)
78
+
79
+ **Mutation Actions** (`onActionSelected` property):
80
+
81
+ - Delete (if `features.mutations.delete === true`)
82
+ - Requires `confirmationMode: 'Simple'`
83
+ - Navigates to list view after successful deletion
84
+
85
+ ## Example
86
+
87
+ ```typescript
88
+ import { FormActionData } from '@axinom/mosaic-ui';
89
+ import { useHistory } from 'react-router';
90
+ import { client } from '../../../apolloClient';
91
+ import { useDeleteMovieMutation } from '../../../generated/graphql';
92
+ import { MovieDetailsFormData } from './MovieDetails.types';
93
+
94
+ export function useMovieDetailsActions(id: number): {
95
+ readonly actions: FormActionData<MovieDetailsFormData>[];
96
+ } {
97
+ const history = useHistory();
98
+
99
+ const [deleteMovieMutation] = useDeleteMovieMutation({
100
+ client,
101
+ fetchPolicy: 'no-cache',
102
+ });
103
+
104
+ const deleteMovie = async (): Promise<void> => {
105
+ await deleteMovieMutation({ variables: { input: { id } } });
106
+ history.push('/movies');
107
+ };
108
+
109
+ const actions: FormActionData<MovieDetailsFormData>[] = [
110
+ {
111
+ label: 'Manage Videos',
112
+ path: `/movies/${id}/videos`,
113
+ },
114
+ {
115
+ label: 'Manage Images',
116
+ path: `/movies/${id}/images`,
117
+ },
118
+ {
119
+ label: 'Delete',
120
+ confirmationMode: 'Simple' as const,
121
+ onActionSelected: deleteMovie,
122
+ },
123
+ ];
124
+
125
+ return { actions } as const;
126
+ }
127
+ ```
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: details-breadcrumb
3
+ input:
4
+ - EntityFeatures
5
+ output:
6
+ - path: '{workflowsPath}/src/Stations/{EntityPlural}/{Entity}Details/{Entity}DetailsCrumb.tsx'
7
+ description: 'Async breadcrumb resolver'
8
+ ---
9
+
10
+ # Generate Breadcrumb Resolver
11
+
12
+ Fetches entity title for breadcrumb display.
13
+
14
+ ## Template
15
+
16
+ ```typescript
17
+ import { BreadcrumbResolver } from '@axinom/mosaic-portal';
18
+ import { client } from '../../../apolloClient';
19
+ import {
20
+ {Entity}TitleDocument,
21
+ {Entity}TitleQuery,
22
+ } from '../../../generated/graphql';
23
+
24
+ export const {Entity}DetailsCrumb: BreadcrumbResolver = (params) => {
25
+ return async (): Promise<string> => {
26
+ const response = await client.query<{Entity}TitleQuery>({
27
+ query: {Entity}TitleDocument,
28
+ variables: {
29
+ id: Number(params['{entityCamel}Id']),
30
+ },
31
+ errorPolicy: 'ignore',
32
+ });
33
+ return response.data.{entityCamel}?.{titleField} ?? '{Entity} Details';
34
+ };
35
+ };
36
+ ```
37
+
38
+ ## Notes
39
+
40
+ - Extracts `{entityCamel}Id` from route params
41
+ - Queries `{titleField}` from entity
42
+ - Falls back to `'{Entity} Details'` if entity not found
43
+ - Uses `errorPolicy: 'ignore'` to handle deleted/missing entities gracefully
44
+
45
+ ## Example
46
+
47
+ ```typescript
48
+ import { BreadcrumbResolver } from '@axinom/mosaic-portal';
49
+ import { client } from '../../../apolloClient';
50
+ import {
51
+ MovieTitleDocument,
52
+ MovieTitleQuery,
53
+ } from '../../../generated/graphql';
54
+
55
+ export const MovieDetailsCrumb: BreadcrumbResolver = (params) => {
56
+ return async (): Promise<string> => {
57
+ const response = await client.query<MovieTitleQuery>({
58
+ query: MovieTitleDocument,
59
+ variables: {
60
+ id: Number(params['movieId']),
61
+ },
62
+ errorPolicy: 'ignore',
63
+ });
64
+ return response.data.movie?.title ?? 'Movie Details';
65
+ };
66
+ };
67
+ ```