@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,283 @@
1
+ ---
2
+ name: video-management-station
3
+ input:
4
+ - EntityFeatures (with capabilities.videos)
5
+ - DiscoveredPaths
6
+ output:
7
+ - path: '{workflowsPath}/src/Stations/{EntityPlural}/{Entity}VideoManagement/'
8
+ description: 'Complete video management station (4 files)'
9
+ ---
10
+
11
+ # Generate Video Management Station
12
+
13
+ Creates station for managing entity videos (main video + trailers).
14
+
15
+ `Conditional` - only generate if `EntityFeatures.capabilities.videos` exists.
16
+
17
+ ## Prerequisites
18
+
19
+ **Verify capability exists:**
20
+
21
+ ```typescript
22
+ if (!EntityFeatures.capabilities.videos) {
23
+ skip this step
24
+ }
25
+ ```
26
+
27
+ ## Files to Generate
28
+
29
+ 1. `{Entity}VideoManagement.tsx` - Router wrapper
30
+ 2. `{Entity}VideoManagementForm.tsx` - Form component
31
+ 3. `{Entity}VideoManagement.graphql` - Query
32
+ 4. `{Entity}VideoManagementQuickEdit.tsx` - Quick edit wrapper
33
+
34
+ ## File 1: Router Wrapper
35
+
36
+ **Path:** `{Entity}VideoManagement/{Entity}VideoManagement.tsx`
37
+
38
+ ```typescript
39
+ import React from 'react';
40
+ import { useParams } from 'react-router-dom';
41
+ import { {Entity}VideoManagementForm } from './{Entity}VideoManagementForm';
42
+
43
+ interface UrlParams {
44
+ {entityCamel}Id: string;
45
+ }
46
+
47
+ export const {Entity}VideoManagement: React.FC = () => {
48
+ const { {entityCamel}Id } = useParams<UrlParams>();
49
+
50
+ return <{Entity}VideoManagementForm {entityCamel}Id={Number({entityCamel}Id)} />;
51
+ };
52
+ ```
53
+
54
+ ## File 2: GraphQL Query
55
+
56
+ **Path:** `{Entity}VideoManagement/{Entity}VideoManagement.graphql`
57
+
58
+ ```graphql
59
+ query {Entity}Videos($id: {features.idType}!) {
60
+ {features.camelCase}(id: $id) {
61
+ # If mainVideoField exists:
62
+ {features.capabilities.videos.mainVideoField}
63
+ # End if
64
+ # If trailersConnection exists:
65
+ {features.capabilities.videos.trailersConnection} {
66
+ nodes {
67
+ videoId
68
+ }
69
+ }
70
+ # End if
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## File 3: Form Component
76
+
77
+ **Path:** `{Entity}VideoManagement/{Entity}VideoManagementForm.tsx`
78
+
79
+ ```typescript
80
+ import { ID } from '@axinom/mosaic-managed-workflow-integration';
81
+ import {
82
+ createUpdateGQLFragmentGenerator,
83
+ Details,
84
+ DetailsProps,
85
+ generateArrayMutations,
86
+ } from '@axinom/mosaic-ui';
87
+ import { Field } from 'formik';
88
+ import gql from 'graphql-tag';
89
+ import { ObjectSchemaDefinition } from 'ObjectSchemaDefinition';
90
+ import React, { useCallback, useContext } from 'react';
91
+ import * as Yup from 'yup';
92
+ import { client } from '../../../apolloClient';
93
+ import { ExtensionsContext } from '{discoveredPaths.extensionsContextPath}';
94
+ import {
95
+ Mutation,
96
+ // If trailersConnection exists:
97
+ MutationCreate{features.capabilities.videos.trailersNodeType}Args,
98
+ MutationDelete{features.capabilities.videos.trailersNodeType}Args,
99
+ // End if
100
+ // If mainVideoField exists:
101
+ MutationUpdate{Entity}Args,
102
+ // End if
103
+ use{Entity}VideosQuery,
104
+ } from '../../../generated/graphql';
105
+
106
+ interface FormData {
107
+ // If mainVideoField exists:
108
+ mainVideo: ID[];
109
+ // End if
110
+ // If trailersConnection exists:
111
+ trailerVideos: ID[];
112
+ // End if
113
+ }
114
+
115
+ interface {Entity}VideoManagementFormProps {
116
+ {entityCamel}Id: number;
117
+ }
118
+
119
+ const {entityCamel}VideoManagementSchema = Yup.object().shape<
120
+ ObjectSchemaDefinition<FormData>
121
+ >({
122
+ // If mainVideoField exists:
123
+ mainVideo: Yup.array().of(Yup.mixed()).max(1),
124
+ // End if
125
+ // If trailersConnection exists:
126
+ trailerVideos: Yup.array().of(Yup.mixed()),
127
+ // End if
128
+ });
129
+
130
+ export const {Entity}VideoManagementForm: React.FC<
131
+ {Entity}VideoManagementFormProps
132
+ > = ({ {entityCamel}Id }) => {
133
+ const { VideoSelectField } = useContext(ExtensionsContext);
134
+
135
+ const { loading, data, error } = use{Entity}VideosQuery({
136
+ client,
137
+ variables: { id: {entityCamel}Id },
138
+ fetchPolicy: 'no-cache',
139
+ });
140
+
141
+ const onSubmit = useCallback(
142
+ async (
143
+ formData: FormData,
144
+ initialData: DetailsProps<FormData>['initialData'],
145
+ ): Promise<void> => {
146
+ const generateUpdateGQLFragment =
147
+ createUpdateGQLFragmentGenerator<Mutation>();
148
+
149
+ // If trailersConnection exists:
150
+ const trailerAssignmentMutations = generateArrayMutations({
151
+ current: formData.trailerVideos,
152
+ original: initialData.data?.trailerVideos,
153
+ generateCreateMutation: (videoId) =>
154
+ generateUpdateGQLFragment<MutationCreate{features.capabilities.videos.trailersNodeType}Args>(
155
+ 'create{features.capabilities.videos.trailersNodeType}',
156
+ { input: { {camelTrailersNodeType}: { videoId, {entityCamel}Id } } },
157
+ ),
158
+ generateDeleteMutation: (videoId) =>
159
+ generateUpdateGQLFragment<MutationDelete{features.capabilities.videos.trailersNodeType}Args>(
160
+ 'delete{features.capabilities.videos.trailersNodeType}',
161
+ { input: { {entityCamel}Id, videoId } },
162
+ ),
163
+ prefix: '{entityCamel}Trailers',
164
+ });
165
+ // End if
166
+
167
+ // If mainVideoField exists:
168
+ const mainVideoUpdateMutation =
169
+ initialData.data?.mainVideo[0] !== formData.mainVideo[0]
170
+ ? generateUpdateGQLFragment<MutationUpdate{Entity}Args>('update{Entity}', {
171
+ input: {
172
+ id: {entityCamel}Id,
173
+ patch: { {mainVideoField}: formData.mainVideo[0] ?? null },
174
+ },
175
+ })
176
+ : '';
177
+ // End if
178
+
179
+ const GqlMutationDocument = gql`mutation Update{Entity}Videos {
180
+ // If mainVideoField exists:
181
+ ${mainVideoUpdateMutation}
182
+ // End if
183
+ // If trailersConnection exists:
184
+ ${trailerAssignmentMutations}
185
+ // End if
186
+ }`;
187
+
188
+ await client.mutate({ mutation: GqlMutationDocument });
189
+ },
190
+ [{entityCamel}Id],
191
+ );
192
+
193
+ return (
194
+ <Details<FormData>
195
+ defaultTitle="Video Management"
196
+ validationSchema={{entityCamel}VideoManagementSchema}
197
+ initialData={{
198
+ data: {
199
+ // If mainVideoField exists:
200
+ mainVideo: data?.{entityCamel}?.{mainVideoField} ? [data?.{entityCamel}?.{mainVideoField}] : [],
201
+ // End if
202
+ // If trailersConnection exists:
203
+ trailerVideos:
204
+ data?.{entityCamel}?.{trailersConnection}.nodes.map(
205
+ (trailer) => trailer.videoId,
206
+ ) ?? [],
207
+ // End if
208
+ },
209
+ loading,
210
+ entityNotFound: data?.{entityCamel} === null,
211
+ error: error?.message,
212
+ }}
213
+ saveData={onSubmit}
214
+ >
215
+ <Form videoSelectField={VideoSelectField} />
216
+ </Details>
217
+ );
218
+ };
219
+
220
+ const Form: React.FC<{ videoSelectField: unknown }> = ({
221
+ videoSelectField,
222
+ }) => {
223
+ return (
224
+ <>
225
+ // If mainVideoField exists:
226
+ <Field
227
+ name="mainVideo"
228
+ label="Main Video"
229
+ as={videoSelectField}
230
+ maxItems={1}
231
+ defaultFilterTag="MAIN"
232
+ title="Select Main Video"
233
+ />
234
+ // End if
235
+ // If trailersConnection exists:
236
+ <Field
237
+ name="trailerVideos"
238
+ label="Trailer Videos"
239
+ as={videoSelectField}
240
+ defaultFilterTag="TRAILER"
241
+ title="Select Trailer Video(s)"
242
+ />
243
+ // End if
244
+ </>
245
+ );
246
+ };
247
+ ```
248
+
249
+ ## File 4: Quick Edit Wrapper
250
+
251
+ **Path:** `{Entity}VideoManagement/{Entity}VideoManagementQuickEdit.tsx`
252
+
253
+ ```typescript
254
+ import { QuickEditContext, QuickEditContextType } from '@axinom/mosaic-ui';
255
+ import React, { useContext } from 'react';
256
+ import { {Entity}Data } from '../{EntityPlural}Explorer/common/{EntityPlural}.types';
257
+ import { {Entity}VideoManagementForm } from './{Entity}VideoManagementForm';
258
+
259
+ export const {Entity}VideoManagementQuickEdit: React.FC = () => {
260
+ const { selectedItem } =
261
+ useContext<QuickEditContextType<{Entity}Data>>(QuickEditContext);
262
+
263
+ return <{Entity}VideoManagementForm {entityCamel}Id={selectedItem.id} />;
264
+ };
265
+ ```
266
+
267
+ ## Conditional Generation
268
+
269
+ **Generate fields based on capability detection:**
270
+
271
+ - If `features.capabilities.videos.mainVideoField` exists → include main video
272
+ field
273
+ - If `features.capabilities.videos.trailersConnection` exists → include trailers
274
+ array field
275
+ - Both, one, or neither may be present
276
+
277
+ ## Notes
278
+
279
+ - Uses `VideoSelectField` from `ExtensionsContext`
280
+ - Main video uses `maxItems={1}` (single selection)
281
+ - Trailers allows multiple selections
282
+ - Main video updates via `update{Entity}` mutation (patch field)
283
+ - Trailers use array mutations (create/delete)
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: generate-explorer-station
3
+ description: |
4
+ Generates an Explorer station for a Mosaic entity from scratch using
5
+ components from the `@axinom/mosaic-ui` library and GraphQL schema
6
+ introspection.
7
+
8
+ This skill is SELF-CONTAINED with all conventions, naming patterns, code
9
+ structures, and templates defined in its `references`. The skill's templates
10
+ and conventions are the AUTHORITATIVE source - rely on them as the primary
11
+ guide for all generation decisions.
12
+
13
+ Use the `get_skill_references` to retrieve ALL reference
14
+ files mentioned in the steps below BEFORE starting any code generation.
15
+ allowed-tools:
16
+ - search_schema
17
+ - get_type_definitions
18
+ - get_query_fields
19
+ - get_mutation_fields
20
+ - get_subscription_fields
21
+ - list_skills
22
+ - get_skill
23
+ - get_skill_references
24
+ - execute_skill
25
+ ---
26
+
27
+ # Generate Explorer Station
28
+
29
+ ## Input
30
+
31
+ **Entity name** (PascalCase): e.g., "Movie", "Product", "ServiceAccount"
32
+
33
+ ## Output
34
+
35
+ Complete explorer station with:
36
+
37
+ - NavigationExplorer component with data provider, columns, filters
38
+ - Common utility files (types, columns, dataProvider, filters, inlineActions,
39
+ bulkActions)
40
+ - GraphQL operations (fragment, query, filter queries, subscription)
41
+ - Optional bulk edit functionality
42
+ - Station registration (routes, navigation, home tiles)
43
+
44
+ ## Steps
45
+
46
+ ### 1. Discover Project Paths → `../_shared/discovery/discover-paths.md`
47
+
48
+ Discover: schema path, apollo client, generated types path, workflows path,
49
+ service name
50
+
51
+ ### 2. Extract Entity Features → `../_shared/discovery/extract-entity-features.md`
52
+
53
+ Extract `EntityFeatures` from schema using MCP tools (fields, associations,
54
+ capabilities, mutations)
55
+
56
+ ### 3. Select Columns & Filters → `refs/select-columns-filters.md`
57
+
58
+ AI recommends columns/filters → present to developer and proceeds with selection
59
+
60
+ ### 4. Generate GraphQL Operations → `refs/templates/graphql-operations.md`
61
+
62
+ Generate `.graphql` file with fragment, main query, filter queries, and
63
+ subscription
64
+
65
+ ### 5. Run Codegen → `../_shared/actions/typescript-codegen.md`
66
+
67
+ Generate TypeScript types from GraphQL operations
68
+
69
+ ### 6. Generate Common Files
70
+
71
+ Generate explorer utility files in order:
72
+
73
+ #### 6.1. Generate Types → `refs/templates/common/types.md`
74
+
75
+ Generate TypeScript type definitions and selection explorer props
76
+
77
+ #### 6.2. Generate Columns → `refs/templates/common/columns.md`
78
+
79
+ Generate column definitions with appropriate renderers (thumbnail, date, enum,
80
+ connection)
81
+
82
+ #### 6.3. Generate Data Provider → `refs/templates/common/data-provider.md`
83
+
84
+ Generate data provider with loadData and optional subscription support
85
+
86
+ #### 6.4. Generate Filters → `refs/templates/common/filters.md`
87
+
88
+ Generate filter options and transformers for PostGraphile filters
89
+
90
+ #### 6.5. Generate Inline Actions → `refs/templates/common/inline-actions.md`
91
+
92
+ Generate inline action menu generator (with TODOs for delete/open details)
93
+
94
+ #### 6.6. Generate Bulk Actions _(conditional)_ → `refs/templates/common/bulk-actions.md`
95
+
96
+ Generate bulk action hooks and actions (only if
97
+ `features.mutations.bulkActions === true`)
98
+
99
+ ### 7. Generate Explorer Station → `refs/templates/navigation-explorer-station.md`
100
+
101
+ Generate main `{EntityPlural}.tsx` NavigationExplorer component with TODOs for
102
+ missing features
103
+
104
+ ### 8. Generate Bulk Edit _(conditional)_ → `refs/templates/bulk-edit.md`
105
+
106
+ Generate BulkEdit configuration and component (only if
107
+ `features.mutations.bulkEdit === true`)
108
+
109
+ ### 9. Validate TypeScript → `../_shared/actions/typescript-validation.md`
110
+
111
+ Run `tsc --noEmit` to verify all generated files compile successfully
112
+
113
+ ### 10. Register Station → `refs/finalization/station-registration.md`
114
+
115
+ Create/update registrations.tsx with explorer routes, navigation items, and home
116
+ tiles
117
+
118
+ ### 11. Print Summary → `../_shared/actions/execution-summary.md`
119
+
120
+ Display execution summary with generated files, configuration, columns, filters,
121
+ routes, TODOs
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: station-registration
3
+ input:
4
+ - EntityFeatures
5
+ - DiscoveredPaths
6
+ - Generated explorer files
7
+ output:
8
+ - path: '{workflowsPath}/src/Stations/{EntityPlural}/registrations.tsx'
9
+ description: 'Station registration with routes and navigation'
10
+ ---
11
+
12
+ # Register Explorer
13
+
14
+ ## 1. Create registrations.tsx
15
+
16
+ **File:** `{workflowsPath}/src/Stations/{EntityPlural}/registrations.tsx`
17
+
18
+ **Discovery:** Icon mapping
19
+
20
+ - Look at entity name to pick best icon from `MediaIconName` enum
21
+ - If no perfect match, pick the best match and add a `TODO`
22
+
23
+ ```typescript
24
+ import { PiletApi } from '@axinom/mosaic-portal';
25
+ import React from 'react';
26
+ import { Extensions, ExtensionsContext } from '{discoveredPaths.extensionsContextPath}';
27
+ import { MediaIconName, MediaIcons } from '../../MediaIcons';
28
+ import { {EntityPlural} } from './{EntityPlural}Explorer/{EntityPlural}';
29
+
30
+ export function register(app: PiletApi, extensions: Extensions): void {
31
+ const {entityCamelPlural}Nav = {
32
+ name: '{entityCamelPlural}',
33
+ path: '/{kebabCasePlural}',
34
+ label: '{EntityPlural}',
35
+ // TODO: Update icon to match entity type
36
+ icon: <MediaIcons icon={MediaIconName.Movie} />,
37
+ };
38
+
39
+ // Home tile
40
+ app.registerTile(
41
+ {
42
+ ...{entityCamelPlural}Nav,
43
+ kind: 'home',
44
+ type: 'large',
45
+ },
46
+ false,
47
+ );
48
+
49
+ // Navigation item
50
+ app.registerNavigationItem({
51
+ ...{entityCamelPlural}Nav,
52
+ categoryName: 'Content',
53
+ });
54
+
55
+ // Explorer route
56
+ app.registerPage(
57
+ '/{kebabCasePlural}',
58
+ () => (
59
+ <ExtensionsContext.Provider value={extensions}>
60
+ <{EntityPlural} />
61
+ </ExtensionsContext.Provider>
62
+ ),
63
+ {
64
+ breadcrumb: () => '{EntityPlural}',
65
+ permissions: { '{features.service.serviceId}': {features.service.permissions} },
66
+ },
67
+ );
68
+
69
+ // If features.mutations.create === true:
70
+ // TODO: Add create route once Create station exists
71
+ // app.registerPage('/{kebabCasePlural}/create', {Entity}Create, {
72
+ // breadcrumb: () => 'New {Entity}',
73
+ // permissions: { '{features.service.serviceId}': {features.service.permissions} },
74
+ // });
75
+ // End if
76
+
77
+ // TODO: Add details route once Details station exists
78
+ // app.registerPage('/{kebabCasePlural}/:{entityCamel}Id', {Entity}Details, {
79
+ // breadcrumb: {Entity}DetailsCrumb,
80
+ // permissions: { '{features.service.serviceId}': {features.service.permissions} },
81
+ // });
82
+ }
83
+ ```
84
+
85
+ ## 2. Register in main index
86
+
87
+ **File:** `{workflowsPath}/src/index.tsx`
88
+
89
+ Add import:
90
+
91
+ ```typescript
92
+ import { register as register{EntityPlural} } from './Stations/{EntityPlural}/registrations';
93
+ ```
94
+
95
+ Add registration call in `setup()`:
96
+
97
+ ```typescript
98
+ register{EntityPlural}(app, extensions);
99
+ ```
100
+
101
+ ## Example (Product)
102
+
103
+ ```typescript
104
+ // registrations.tsx
105
+ import { PiletApi } from '@axinom/mosaic-portal';
106
+ import React from 'react';
107
+ import {
108
+ Extensions,
109
+ ExtensionsContext,
110
+ } from '{discoveredPaths.extensionsContextPath}';
111
+ import { MediaIconName, MediaIcons } from '../../MediaIcons';
112
+ import { Products } from './ProductsExplorer/Products';
113
+
114
+ export function register(app: PiletApi, extensions: Extensions): void {
115
+ const productsNav = {
116
+ name: 'products',
117
+ path: '/products',
118
+ label: 'Products',
119
+ // TODO: Update icon to match entity type
120
+ icon: <MediaIcons icon={MediaIconName.Movie} />,
121
+ };
122
+
123
+ app.registerTile({ ...productsNav, kind: 'home', type: 'large' }, false);
124
+
125
+ app.registerNavigationItem({ ...productsNav, categoryName: 'Content' });
126
+
127
+ app.registerPage(
128
+ '/products',
129
+ () => (
130
+ <ExtensionsContext.Provider value={extensions}>
131
+ <Products />
132
+ </ExtensionsContext.Provider>
133
+ ),
134
+ {
135
+ breadcrumb: () => 'Products',
136
+ permissions: {
137
+ 'product-service': ['ADMIN', 'PRODUCTS_EDIT', 'PRODUCTS_VIEW'],
138
+ },
139
+ },
140
+ );
141
+
142
+ // TODO: Add create route once Create station exists
143
+ // TODO: Add details route once Details station exists
144
+ }
145
+ ```
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: select-columns-filters
3
+ input:
4
+ - EntityFeatures
5
+ output:
6
+ - selectedColumns array
7
+ - selectedFilters array
8
+ ---
9
+
10
+ # Select Columns & Filters
11
+
12
+ ## Column Selection Logic
13
+
14
+ ### Always Include
15
+
16
+ 1. **Thumbnail** (if `features.capabilities.images !== undefined`) - first
17
+ column
18
+ 2. **Title/Name field** - primary human identifier
19
+ 3. **ID** - primary technical identifier
20
+
21
+ ### AI Judgment
22
+
23
+ Analyze fields by priority:
24
+
25
+ - **High:** title, name, ID, status fields, tag-like (i.e. `Tags` for easy
26
+ filtering)
27
+ - **Medium:** description fields, M:N associations (i.e. `Category`, `Genre` for
28
+ easy filtering)
29
+ - **Low:** other fields, audit fields
30
+
31
+ ### Target
32
+
33
+ Enough columns to distinguish the important metadata of the entity
34
+
35
+ ### Order
36
+
37
+ 1. Thumbnail (if `features.capabilities.images !== undefined`)
38
+ 2. Title field
39
+ 3. Important fields
40
+ 4. Associations
41
+ 5. Date fields
42
+ 6. Updated date (last)
43
+
44
+ ## Filter Selection Logic
45
+
46
+ ### Always Include
47
+
48
+ - **Title field** - text search
49
+ - **ID** - numeric filter
50
+
51
+ ### AI Judgment
52
+
53
+ - Identity fields
54
+ - Text fields that users search by
55
+ - Enum fields (status, type, etc.)
56
+ - Date fields (released, created, updated)
57
+ - Tag-Like fields (tags)
58
+ - Associations (genres, categories)
59
+ - Audit fields (createdDate, updatedDate)
60
+
61
+ ### Target
62
+
63
+ Enough filters to search by important fields of the entity