@carlonicora/nextjs-jsonapi 1.110.0 → 1.111.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.
- package/dist/{BlockNoteEditor-K7IIWM3S.mjs → BlockNoteEditor-A5LNFWHW.mjs} +2 -2
- package/dist/{BlockNoteEditor-7OKW6HHN.js → BlockNoteEditor-HNQNKCFP.js} +9 -9
- package/dist/{BlockNoteEditor-7OKW6HHN.js.map → BlockNoteEditor-HNQNKCFP.js.map} +1 -1
- package/dist/billing/index.js +299 -299
- package/dist/billing/index.mjs +1 -1
- package/dist/{chunk-AKXUNE3A.js → chunk-DRJUN4R6.js} +22 -19
- package/dist/chunk-DRJUN4R6.js.map +1 -0
- package/dist/{chunk-TXVVZLUQ.mjs → chunk-LYLGU2BK.mjs} +67 -64
- package/dist/chunk-LYLGU2BK.mjs.map +1 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/index.js +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/contexts/index.js +2 -2
- package/dist/contexts/index.mjs +1 -1
- package/dist/features/help/index.js +30 -30
- package/dist/features/help/index.mjs +1 -1
- package/dist/scripts/generate-web-module/__tests__/container.spec.d.ts +2 -0
- package/dist/scripts/generate-web-module/__tests__/container.spec.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/container.spec.js +54 -0
- package/dist/scripts/generate-web-module/__tests__/container.spec.js.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/data-service.spec.d.ts +2 -0
- package/dist/scripts/generate-web-module/__tests__/data-service.spec.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/data-service.spec.js +19 -0
- package/dist/scripts/generate-web-module/__tests__/data-service.spec.js.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/editor.spec.d.ts +2 -0
- package/dist/scripts/generate-web-module/__tests__/editor.spec.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/editor.spec.js +35 -0
- package/dist/scripts/generate-web-module/__tests__/editor.spec.js.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/fixtures.d.ts +4 -0
- package/dist/scripts/generate-web-module/__tests__/fixtures.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/fixtures.js +45 -0
- package/dist/scripts/generate-web-module/__tests__/fixtures.js.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/model-interface.spec.d.ts +2 -0
- package/dist/scripts/generate-web-module/__tests__/model-interface.spec.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/model-interface.spec.js +74 -0
- package/dist/scripts/generate-web-module/__tests__/model-interface.spec.js.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/plumbing.spec.d.ts +2 -0
- package/dist/scripts/generate-web-module/__tests__/plumbing.spec.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/plumbing.spec.js +21 -0
- package/dist/scripts/generate-web-module/__tests__/plumbing.spec.js.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/selector.spec.d.ts +2 -0
- package/dist/scripts/generate-web-module/__tests__/selector.spec.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/selector.spec.js +25 -0
- package/dist/scripts/generate-web-module/__tests__/selector.spec.js.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/table-module-list.spec.d.ts +2 -0
- package/dist/scripts/generate-web-module/__tests__/table-module-list.spec.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/__tests__/table-module-list.spec.js +55 -0
- package/dist/scripts/generate-web-module/__tests__/table-module-list.spec.js.map +1 -0
- package/dist/scripts/generate-web-module/generator.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/generator.js +14 -11
- package/dist/scripts/generate-web-module/generator.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/container.template.d.ts +5 -1
- package/dist/scripts/generate-web-module/templates/components/container.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/container.template.js +43 -63
- package/dist/scripts/generate-web-module/templates/components/container.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/content.template.d.ts +7 -4
- package/dist/scripts/generate-web-module/templates/components/content.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/content.template.js +103 -14
- package/dist/scripts/generate-web-module/templates/components/content.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/editor.template.d.ts +1 -1
- package/dist/scripts/generate-web-module/templates/components/editor.template.js +139 -150
- package/dist/scripts/generate-web-module/templates/components/editor.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/index.d.ts +0 -1
- package/dist/scripts/generate-web-module/templates/components/index.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/index.js +1 -3
- package/dist/scripts/generate-web-module/templates/components/index.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/list-container.template.js +2 -2
- package/dist/scripts/generate-web-module/templates/components/list.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/list.template.js +6 -1
- package/dist/scripts/generate-web-module/templates/components/list.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/selector.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/components/selector.template.js +48 -52
- package/dist/scripts/generate-web-module/templates/components/selector.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/data/interface.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/data/interface.template.js +38 -11
- package/dist/scripts/generate-web-module/templates/data/interface.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/data/model.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/data/model.template.js +83 -26
- package/dist/scripts/generate-web-module/templates/data/model.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/data/service.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/data/service.template.js +21 -1
- package/dist/scripts/generate-web-module/templates/data/service.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/index.d.ts +1 -1
- package/dist/scripts/generate-web-module/templates/index.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/index.js +1 -2
- package/dist/scripts/generate-web-module/templates/index.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/module.template.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/templates/module.template.js +11 -3
- package/dist/scripts/generate-web-module/templates/module.template.js.map +1 -1
- package/dist/scripts/generate-web-module/templates/table-hook.template.js +31 -1
- package/dist/scripts/generate-web-module/templates/table-hook.template.js.map +1 -1
- package/dist/scripts/generate-web-module/transformers/import-resolver.d.ts +0 -1
- package/dist/scripts/generate-web-module/transformers/import-resolver.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/transformers/import-resolver.js +0 -1
- package/dist/scripts/generate-web-module/transformers/import-resolver.js.map +1 -1
- package/dist/scripts/generate-web-module/transformers/relationship-key.d.ts +5 -0
- package/dist/scripts/generate-web-module/transformers/relationship-key.d.ts.map +1 -0
- package/dist/scripts/generate-web-module/transformers/relationship-key.js +21 -0
- package/dist/scripts/generate-web-module/transformers/relationship-key.js.map +1 -0
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.js +2 -0
- package/dist/scripts/generate-web-module/transformers/relationship-resolver.js.map +1 -1
- package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts +21 -0
- package/dist/scripts/generate-web-module/types/json-schema.interface.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/types/template-data.interface.d.ts +17 -0
- package/dist/scripts/generate-web-module/types/template-data.interface.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-web-module/__tests__/container.spec.ts +58 -0
- package/scripts/generate-web-module/__tests__/data-service.spec.ts +18 -0
- package/scripts/generate-web-module/__tests__/editor.spec.ts +36 -0
- package/scripts/generate-web-module/__tests__/fixtures.ts +43 -0
- package/scripts/generate-web-module/__tests__/model-interface.spec.ts +77 -0
- package/scripts/generate-web-module/__tests__/plumbing.spec.ts +21 -0
- package/scripts/generate-web-module/__tests__/selector.spec.ts +24 -0
- package/scripts/generate-web-module/__tests__/table-module-list.spec.ts +59 -0
- package/scripts/generate-web-module/generator.ts +14 -13
- package/scripts/generate-web-module/templates/components/container.template.ts +51 -66
- package/scripts/generate-web-module/templates/components/content.template.ts +107 -15
- package/scripts/generate-web-module/templates/components/editor.template.ts +140 -152
- package/scripts/generate-web-module/templates/components/index.ts +0 -1
- package/scripts/generate-web-module/templates/components/list-container.template.ts +2 -2
- package/scripts/generate-web-module/templates/components/list.template.ts +6 -1
- package/scripts/generate-web-module/templates/components/selector.template.ts +48 -52
- package/scripts/generate-web-module/templates/data/interface.template.ts +44 -12
- package/scripts/generate-web-module/templates/data/model.template.ts +88 -28
- package/scripts/generate-web-module/templates/data/service.template.ts +23 -1
- package/scripts/generate-web-module/templates/index.ts +0 -1
- package/scripts/generate-web-module/templates/module.template.ts +13 -3
- package/scripts/generate-web-module/templates/table-hook.template.ts +31 -2
- package/scripts/generate-web-module/transformers/import-resolver.ts +0 -2
- package/scripts/generate-web-module/transformers/relationship-key.ts +18 -0
- package/scripts/generate-web-module/transformers/relationship-resolver.ts +2 -0
- package/scripts/generate-web-module/types/json-schema.interface.ts +9 -0
- package/scripts/generate-web-module/types/template-data.interface.ts +7 -0
- package/src/features/notification/components/modals/NotificationModal.tsx +2 -5
- package/src/features/notification/contexts/NotificationContext.tsx +29 -13
- package/src/features/notification/contexts/__tests__/NotificationContext.spec.tsx +70 -0
- package/dist/chunk-AKXUNE3A.js.map +0 -1
- package/dist/chunk-TXVVZLUQ.mjs.map +0 -1
- package/dist/scripts/generate-web-module/templates/components/details.template.d.ts +0 -14
- package/dist/scripts/generate-web-module/templates/components/details.template.d.ts.map +0 -1
- package/dist/scripts/generate-web-module/templates/components/details.template.js +0 -112
- package/dist/scripts/generate-web-module/templates/components/details.template.js.map +0 -1
- package/scripts/generate-web-module/templates/components/details.template.ts +0 -116
- /package/dist/{BlockNoteEditor-K7IIWM3S.mjs.map → BlockNoteEditor-A5LNFWHW.mjs.map} +0 -0
|
@@ -12,7 +12,6 @@ export { generateMultiSelectorTemplate } from "./multi-selector.template";
|
|
|
12
12
|
|
|
13
13
|
// Display components
|
|
14
14
|
export { generateListTemplate } from "./list.template";
|
|
15
|
-
export { generateDetailsTemplate } from "./details.template";
|
|
16
15
|
export { generateContentTemplate } from "./content.template";
|
|
17
16
|
export { generateContainerTemplate } from "./container.template";
|
|
18
17
|
export { generateListContainerTemplate } from "./list-container.template";
|
|
@@ -23,8 +23,8 @@ import { Modules } from "@carlonicora/nextjs-jsonapi/core";
|
|
|
23
23
|
|
|
24
24
|
function ${names.pascalCase}ListContainerInternal() {
|
|
25
25
|
return (
|
|
26
|
-
<RoundPageContainer module={Modules.${names.pascalCase}}>
|
|
27
|
-
<${names.pascalCase}List />
|
|
26
|
+
<RoundPageContainer module={Modules.${names.pascalCase}} fullWidth>
|
|
27
|
+
<${names.pascalCase}List fullWidth />
|
|
28
28
|
</RoundPageContainer>
|
|
29
29
|
);
|
|
30
30
|
}
|
|
@@ -32,7 +32,11 @@ import { DataListRetriever, useDataListRetriever } from "@carlonicora/nextjs-jso
|
|
|
32
32
|
import { useTranslations } from "next-intl";
|
|
33
33
|
import { ReactNode } from "react";
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
type ${names.pascalCase}ListProps = {
|
|
36
|
+
fullWidth?: boolean;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default function ${names.pascalCase}List({ fullWidth }: ${names.pascalCase}ListProps) {
|
|
36
40
|
const t = useTranslations();
|
|
37
41
|
|
|
38
42
|
const data: DataListRetriever<${names.pascalCase}Interface> = useDataListRetriever({
|
|
@@ -50,6 +54,7 @@ export default function ${names.pascalCase}List() {
|
|
|
50
54
|
tableGeneratorType={Modules.${names.pascalCase}}
|
|
51
55
|
functions={functions}
|
|
52
56
|
title={t(\`entities.${names.pluralCamel.toLowerCase()}\`, { count: 2 })}
|
|
57
|
+
fullWidth={fullWidth}
|
|
53
58
|
/>
|
|
54
59
|
);
|
|
55
60
|
}
|
|
@@ -21,11 +21,14 @@ export function generateSelectorTemplate(data: FrontendTemplateData): string {
|
|
|
21
21
|
return `"use client";
|
|
22
22
|
|
|
23
23
|
import {
|
|
24
|
+
Button,
|
|
24
25
|
Command,
|
|
26
|
+
CommandEmpty,
|
|
27
|
+
CommandGroup,
|
|
28
|
+
CommandInput,
|
|
25
29
|
CommandItem,
|
|
26
30
|
CommandList,
|
|
27
31
|
FormFieldWrapper,
|
|
28
|
-
Input,
|
|
29
32
|
Popover,
|
|
30
33
|
PopoverContent,
|
|
31
34
|
PopoverTrigger,
|
|
@@ -36,7 +39,7 @@ import { DataListRetriever, useDataListRetriever } from "@carlonicora/nextjs-jso
|
|
|
36
39
|
import { useDebounce } from "@carlonicora/nextjs-jsonapi/client";
|
|
37
40
|
import { Modules } from "@carlonicora/nextjs-jsonapi/core";
|
|
38
41
|
|
|
39
|
-
import {
|
|
42
|
+
import { ChevronsUpDown, Loader2, XIcon } from "lucide-react";
|
|
40
43
|
import { useTranslations } from "next-intl";
|
|
41
44
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
42
45
|
|
|
@@ -98,12 +101,12 @@ export default function ${names.pascalCase}Selector({
|
|
|
98
101
|
const set${names.pascalCase} = (${names.camelCase}?: ${names.pascalCase}Interface) => {
|
|
99
102
|
if (onChange) onChange(${names.camelCase});
|
|
100
103
|
if (!${names.camelCase}) {
|
|
101
|
-
form.setValue(id, undefined
|
|
104
|
+
form.setValue(id, undefined);
|
|
102
105
|
setOpen(false);
|
|
103
106
|
return;
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
form.setValue(id, { id: ${names.camelCase}.id, name: ${names.camelCase}.${displayProp} }
|
|
109
|
+
form.setValue(id, { id: ${names.camelCase}.id, name: ${names.camelCase}.${displayProp} });
|
|
107
110
|
setOpen(false);
|
|
108
111
|
|
|
109
112
|
setTimeout(() => {
|
|
@@ -118,62 +121,55 @@ export default function ${names.pascalCase}Selector({
|
|
|
118
121
|
<Popover open={open} onOpenChange={setOpen} modal={true}>
|
|
119
122
|
<div className="flex w-full flex-row items-center justify-between">
|
|
120
123
|
<PopoverTrigger className="w-full">
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</
|
|
124
|
+
<Button
|
|
125
|
+
variant="outline"
|
|
126
|
+
role="combobox"
|
|
127
|
+
aria-expanded={open}
|
|
128
|
+
className="h-auto min-h-10 w-full justify-between px-3 py-2"
|
|
129
|
+
>
|
|
130
|
+
<span className={field.value ? "" : "text-muted-foreground"}>
|
|
131
|
+
{field.value?.name ?? placeholder ?? t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
|
|
132
|
+
</span>
|
|
133
|
+
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
134
|
+
</Button>
|
|
132
135
|
</PopoverTrigger>
|
|
133
136
|
{field.value && (
|
|
134
|
-
<
|
|
135
|
-
className="text-muted hover:text-destructive ml-2 h-
|
|
137
|
+
<XIcon
|
|
138
|
+
className="text-muted-foreground hover:text-destructive ml-2 h-5 w-5 cursor-pointer"
|
|
136
139
|
onClick={() => set${names.pascalCase}()}
|
|
137
140
|
/>
|
|
138
141
|
)}
|
|
139
142
|
</div>
|
|
140
|
-
<PopoverContent
|
|
143
|
+
<PopoverContent className="w-(--radix-popover-trigger-width) p-0" align="start">
|
|
141
144
|
<Command shouldFilter={false}>
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
className="w-full pr-8 pl-8"
|
|
148
|
-
onChange={(e) => setSearchTerm(e.target.value)}
|
|
149
|
-
value={searchTerm}
|
|
150
|
-
/>
|
|
151
|
-
{isSearching ? (
|
|
152
|
-
<RefreshCwIcon className="text-muted-foreground absolute top-2.5 right-2.5 h-4 w-4 animate-spin" />
|
|
153
|
-
) : searchTermRef.current ? (
|
|
154
|
-
<XIcon
|
|
155
|
-
className={\`absolute top-2.5 right-2.5 h-4 w-4 \${searchTermRef.current ? "cursor-pointer" : "text-muted-foreground"}\`}
|
|
156
|
-
onClick={() => {
|
|
157
|
-
setSearchTerm("");
|
|
158
|
-
search("");
|
|
159
|
-
}}
|
|
160
|
-
/>
|
|
161
|
-
) : (
|
|
162
|
-
<></>
|
|
163
|
-
)}
|
|
164
|
-
</div>
|
|
145
|
+
<CommandInput
|
|
146
|
+
placeholder={t(\`generic.search.placeholder\`, { type: t(\`entities.${i18nKey}\`, { count: 1 }) })}
|
|
147
|
+
value={searchTerm}
|
|
148
|
+
onValueChange={setSearchTerm}
|
|
149
|
+
/>
|
|
165
150
|
<CommandList>
|
|
166
|
-
{
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
151
|
+
{isSearching && (
|
|
152
|
+
<div className="flex items-center justify-center py-6">
|
|
153
|
+
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
|
|
154
|
+
</div>
|
|
155
|
+
)}
|
|
156
|
+
{!isSearching && data.data && data.data.length === 0 && (
|
|
157
|
+
<CommandEmpty>{t(\`generic.search.no_results\`)}</CommandEmpty>
|
|
158
|
+
)}
|
|
159
|
+
{!isSearching && data.data && data.data.length > 0 && (
|
|
160
|
+
<CommandGroup>
|
|
161
|
+
{(data.data as ${names.pascalCase}Interface[]).map((${names.camelCase}: ${names.pascalCase}Interface) => (
|
|
162
|
+
<CommandItem
|
|
163
|
+
key={${names.camelCase}.id}
|
|
164
|
+
value={${names.camelCase}.id}
|
|
165
|
+
onSelect={() => set${names.pascalCase}(${names.camelCase})}
|
|
166
|
+
className="hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent cursor-pointer"
|
|
167
|
+
>
|
|
168
|
+
{${names.camelCase}.${displayProp}}
|
|
169
|
+
</CommandItem>
|
|
170
|
+
))}
|
|
171
|
+
</CommandGroup>
|
|
172
|
+
)}
|
|
177
173
|
</CommandList>
|
|
178
174
|
</Command>
|
|
179
175
|
</PopoverContent>
|
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { FrontendTemplateData, FrontendField, FrontendRelationship } from "../../types/template-data.interface";
|
|
8
|
-
import { toCamelCase, pluralize } from "../../transformers/name-transformer";
|
|
8
|
+
import { toCamelCase, toPascalCase, pluralize } from "../../transformers/name-transformer";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Build the named `<Pascal>RelationshipMeta` interface name for a relationship
|
|
12
|
+
* that carries edge fields (mirrors live OpportunityInterface.ts).
|
|
13
|
+
*/
|
|
14
|
+
function relationshipMetaName(rel: FrontendRelationship): string {
|
|
15
|
+
return `${toPascalCase(rel.alias ?? rel.name)}RelationshipMeta`;
|
|
16
|
+
}
|
|
9
17
|
|
|
10
18
|
/**
|
|
11
19
|
* Generate the interface file content
|
|
@@ -17,17 +25,35 @@ export function generateInterfaceTemplate(data: FrontendTemplateData): string {
|
|
|
17
25
|
const { names, extendsContent, fields, relationships } = data;
|
|
18
26
|
|
|
19
27
|
const imports = generateImports(data);
|
|
28
|
+
const metaInterfaces = generateMetaInterfaces(data);
|
|
20
29
|
const inputType = generateInputType(data);
|
|
21
30
|
const interfaceDefinition = generateInterface(data);
|
|
22
31
|
|
|
23
32
|
return `${imports}
|
|
24
|
-
|
|
33
|
+
${metaInterfaces ? `\n${metaInterfaces}\n` : ""}
|
|
25
34
|
${inputType}
|
|
26
35
|
|
|
27
36
|
${interfaceDefinition}
|
|
28
37
|
`;
|
|
29
38
|
}
|
|
30
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Generate named `<Pascal>RelationshipMeta` interfaces for relationships that
|
|
42
|
+
* carry edge fields. Reused by getters and the Input type's meta arrays.
|
|
43
|
+
*/
|
|
44
|
+
function generateMetaInterfaces(data: FrontendTemplateData): string {
|
|
45
|
+
return data.relationships
|
|
46
|
+
.filter((rel) => rel.fields && rel.fields.length > 0)
|
|
47
|
+
.map((rel) => {
|
|
48
|
+
const metaName = relationshipMetaName(rel);
|
|
49
|
+
const body = rel
|
|
50
|
+
.fields!.map((f) => ` ${f.name}${f.nullable ? "?" : ""}: ${f.tsType};`)
|
|
51
|
+
.join("\n");
|
|
52
|
+
return `export interface ${metaName} {\n${body}\n}`;
|
|
53
|
+
})
|
|
54
|
+
.join("\n\n");
|
|
55
|
+
}
|
|
56
|
+
|
|
31
57
|
/**
|
|
32
58
|
* Generate import statements
|
|
33
59
|
*/
|
|
@@ -64,10 +90,11 @@ function generateInputType(data: FrontendTemplateData): string {
|
|
|
64
90
|
|
|
65
91
|
const fieldLines: string[] = [" id: string;"];
|
|
66
92
|
|
|
67
|
-
// Add fields (excluding inherited ones if extending Content
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
: fields
|
|
93
|
+
// Add fields (excluding inherited ones if extending Content, and read-only
|
|
94
|
+
// fields which are server-derived and never sent back in the Input).
|
|
95
|
+
const fieldsToInclude = (
|
|
96
|
+
extendsContent ? fields.filter((f) => !["name", "tldr", "abstract"].includes(f.name)) : fields
|
|
97
|
+
).filter((f) => !f.readOnly);
|
|
71
98
|
|
|
72
99
|
fieldsToInclude.forEach((field) => {
|
|
73
100
|
const optional = field.nullable ? "?" : "";
|
|
@@ -93,6 +120,12 @@ function generateInputType(data: FrontendTemplateData): string {
|
|
|
93
120
|
const effectiveMany = rel.alias || rel.name;
|
|
94
121
|
const key = `${toCamelCase(effectiveMany)}Ids`;
|
|
95
122
|
fieldLines.push(` ${key}?: string[];`);
|
|
123
|
+
|
|
124
|
+
// For many relationships with edge fields, also emit a parallel meta array.
|
|
125
|
+
if (rel.fields && rel.fields.length > 0) {
|
|
126
|
+
const metaShape = rel.fields.map((f) => `${f.name}: ${f.tsType}`).join("; ");
|
|
127
|
+
fieldLines.push(` ${toCamelCase(effectiveMany)}Meta?: { id: string; ${metaShape} }[];`);
|
|
128
|
+
}
|
|
96
129
|
}
|
|
97
130
|
});
|
|
98
131
|
|
|
@@ -130,11 +163,11 @@ function generateInterface(data: FrontendTemplateData): string {
|
|
|
130
163
|
if (rel.single) {
|
|
131
164
|
const propertyName = toCamelCase(effectiveName);
|
|
132
165
|
|
|
133
|
-
// Build return type -
|
|
166
|
+
// Build return type - intersect with the named meta interface if the
|
|
167
|
+
// relationship has edge fields.
|
|
134
168
|
let baseType = rel.interfaceName;
|
|
135
169
|
if (rel.fields && rel.fields.length > 0) {
|
|
136
|
-
|
|
137
|
-
baseType = `${rel.interfaceName} & { ${metaFields} }`;
|
|
170
|
+
baseType = `${rel.interfaceName} & ${relationshipMetaName(rel)}`;
|
|
138
171
|
}
|
|
139
172
|
|
|
140
173
|
const type = rel.nullable ? `(${baseType}) | undefined` : baseType;
|
|
@@ -142,10 +175,9 @@ function generateInterface(data: FrontendTemplateData): string {
|
|
|
142
175
|
} else {
|
|
143
176
|
const effectiveMany = rel.alias || rel.name;
|
|
144
177
|
const propertyName = pluralize(toCamelCase(effectiveMany));
|
|
145
|
-
//
|
|
178
|
+
// Intersect with the named meta interface if the relationship has edge fields.
|
|
146
179
|
if (rel.fields && rel.fields.length > 0) {
|
|
147
|
-
|
|
148
|
-
getterLines.push(` get ${propertyName}(): (${rel.interfaceName} & { ${metaFields} })[];`);
|
|
180
|
+
getterLines.push(` get ${propertyName}(): (${rel.interfaceName} & ${relationshipMetaName(rel)})[];`);
|
|
149
181
|
} else {
|
|
150
182
|
getterLines.push(` get ${propertyName}(): ${rel.interfaceName}[];`);
|
|
151
183
|
}
|
|
@@ -4,9 +4,33 @@
|
|
|
4
4
|
* Generates {Module}.ts class file with rehydrate and createJsonApi methods.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { pluralize, toCamelCase } from "../../transformers/name-transformer";
|
|
7
|
+
import { pluralize, toCamelCase, toPascalCase } from "../../transformers/name-transformer";
|
|
8
|
+
import { resolveRelationshipKey } from "../../transformers/relationship-key";
|
|
8
9
|
import { AUTHOR_VARIANT } from "../../types/field-mapping.types";
|
|
9
|
-
import { FrontendField, FrontendTemplateData } from "../../types/template-data.interface";
|
|
10
|
+
import { FrontendField, FrontendRelationship, FrontendTemplateData } from "../../types/template-data.interface";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Build the named `<Pascal>RelationshipMeta` interface name for a relationship
|
|
14
|
+
* that carries edge fields (mirrors live Opportunity.ts / OpportunityInterface.ts).
|
|
15
|
+
*
|
|
16
|
+
* These named interfaces are declared in the module's OWN `<Module>Interface.ts`
|
|
17
|
+
* (emitted by interface.template.ts), so the model imports them from there.
|
|
18
|
+
*/
|
|
19
|
+
function relMetaName(rel: FrontendRelationship): string {
|
|
20
|
+
return `${toPascalCase(rel.alias ?? rel.name)}RelationshipMeta`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Determine whether the model needs the `formatLocalDate` helper imported.
|
|
25
|
+
*
|
|
26
|
+
* True when any data field is a date/datetime, or any relationship edge field
|
|
27
|
+
* is a date/datetime (edge dates would also be serialized via the helper).
|
|
28
|
+
*/
|
|
29
|
+
function hasDateField(data: FrontendTemplateData): boolean {
|
|
30
|
+
const fieldIsDate = (type: string) => type === "date" || type === "datetime";
|
|
31
|
+
if (data.fields.some((f) => fieldIsDate(f.type))) return true;
|
|
32
|
+
return data.relationships.some((rel) => (rel.fields ?? []).some((f) => fieldIsDate(f.type)));
|
|
33
|
+
}
|
|
10
34
|
|
|
11
35
|
/**
|
|
12
36
|
* Generate the model file content
|
|
@@ -46,9 +70,20 @@ function generateImports(data: FrontendTemplateData): string {
|
|
|
46
70
|
const { names, extendsContent, relationships } = data;
|
|
47
71
|
const imports: string[] = [];
|
|
48
72
|
|
|
49
|
-
// Own interface import
|
|
73
|
+
// Own interface import — also pull in the named `<Pascal>RelationshipMeta`
|
|
74
|
+
// interfaces, which are all declared in this module's own Interface file.
|
|
75
|
+
const ownNamedImports = [`${names.pascalCase}Input`, `${names.pascalCase}Interface`];
|
|
76
|
+
const seenMeta = new Set<string>();
|
|
77
|
+
relationships.forEach((rel) => {
|
|
78
|
+
if (!rel.fields || rel.fields.length === 0) return;
|
|
79
|
+
const metaName = relMetaName(rel);
|
|
80
|
+
if (seenMeta.has(metaName)) return;
|
|
81
|
+
seenMeta.add(metaName);
|
|
82
|
+
ownNamedImports.push(metaName);
|
|
83
|
+
});
|
|
84
|
+
ownNamedImports.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
50
85
|
imports.push(
|
|
51
|
-
`import { ${
|
|
86
|
+
`import { ${ownNamedImports.join(", ")} } from "@/features/${data.importTargetDir}/${names.kebabCase}/data/${names.pascalCase}Interface";`,
|
|
52
87
|
);
|
|
53
88
|
|
|
54
89
|
// Relationship interface imports (deduplicated, skip self-referential)
|
|
@@ -62,13 +97,16 @@ function generateImports(data: FrontendTemplateData): string {
|
|
|
62
97
|
imports.push(`import { ${rel.interfaceName} } from "${rel.interfaceImportPath}";`);
|
|
63
98
|
});
|
|
64
99
|
|
|
65
|
-
// Base class and core imports
|
|
100
|
+
// Base class and core imports (sorted case-insensitively to match live output)
|
|
101
|
+
const byName = (a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase());
|
|
102
|
+
const coreNames = ["JsonApiHydratedDataInterface", "Modules"];
|
|
103
|
+
if (hasDateField(data)) coreNames.push("formatLocalDate");
|
|
66
104
|
if (extendsContent) {
|
|
67
105
|
imports.push(`import { Content } from "@/features/content/data/Content";`);
|
|
68
|
-
imports.push(`import {
|
|
106
|
+
imports.push(`import { ${coreNames.sort(byName).join(", ")} } from "@carlonicora/nextjs-jsonapi/core";`);
|
|
69
107
|
} else {
|
|
70
108
|
imports.push(
|
|
71
|
-
`import { AbstractApiData,
|
|
109
|
+
`import { ${["AbstractApiData", ...coreNames].sort(byName).join(", ")} } from "@carlonicora/nextjs-jsonapi/core";`,
|
|
72
110
|
);
|
|
73
111
|
}
|
|
74
112
|
|
|
@@ -97,20 +135,18 @@ function generatePrivateFields(data: FrontendTemplateData): string {
|
|
|
97
135
|
const effectiveName = rel.alias || rel.variant || rel.name;
|
|
98
136
|
if (rel.single) {
|
|
99
137
|
const propName = toCamelCase(effectiveName);
|
|
100
|
-
// Use intersection
|
|
138
|
+
// Use intersection with the named meta interface if relationship has fields
|
|
101
139
|
let typeDecl = rel.interfaceName;
|
|
102
140
|
if (rel.fields && rel.fields.length > 0) {
|
|
103
|
-
|
|
104
|
-
typeDecl = `${rel.interfaceName} & { ${metaFields} }`;
|
|
141
|
+
typeDecl = `${rel.interfaceName} & ${relMetaName(rel)}`;
|
|
105
142
|
}
|
|
106
143
|
lines.push(` private _${propName}?: ${typeDecl};`);
|
|
107
144
|
} else {
|
|
108
145
|
const effectiveMany = rel.alias || rel.name;
|
|
109
146
|
const propName = pluralize(toCamelCase(effectiveMany));
|
|
110
|
-
// Use intersection
|
|
147
|
+
// Use intersection with the named meta interface if relationship has fields (edge properties)
|
|
111
148
|
if (rel.fields && rel.fields.length > 0) {
|
|
112
|
-
|
|
113
|
-
lines.push(` private _${propName}?: (${rel.interfaceName} & { ${metaFields} })[];`);
|
|
149
|
+
lines.push(` private _${propName}?: (${rel.interfaceName} & ${relMetaName(rel)})[];`);
|
|
114
150
|
} else {
|
|
115
151
|
lines.push(` private _${propName}?: ${rel.interfaceName}[];`);
|
|
116
152
|
}
|
|
@@ -147,11 +183,10 @@ function generateGetters(data: FrontendTemplateData): string {
|
|
|
147
183
|
if (rel.single) {
|
|
148
184
|
const propName = toCamelCase(effectiveName);
|
|
149
185
|
|
|
150
|
-
// Build return type -
|
|
186
|
+
// Build return type - intersect with the named meta interface if relationship has fields
|
|
151
187
|
let baseType = rel.interfaceName;
|
|
152
188
|
if (rel.fields && rel.fields.length > 0) {
|
|
153
|
-
|
|
154
|
-
baseType = `${rel.interfaceName} & { ${metaFields} }`;
|
|
189
|
+
baseType = `${rel.interfaceName} & ${relMetaName(rel)}`;
|
|
155
190
|
}
|
|
156
191
|
|
|
157
192
|
if (rel.nullable) {
|
|
@@ -167,10 +202,9 @@ function generateGetters(data: FrontendTemplateData): string {
|
|
|
167
202
|
} else {
|
|
168
203
|
const effectiveMany = rel.alias || rel.name;
|
|
169
204
|
const propName = pluralize(toCamelCase(effectiveMany));
|
|
170
|
-
//
|
|
205
|
+
// Intersect with the named meta interface if relationship has fields (edge properties)
|
|
171
206
|
if (rel.fields && rel.fields.length > 0) {
|
|
172
|
-
|
|
173
|
-
lines.push(` get ${propName}(): (${rel.interfaceName} & { ${metaFields} })[] {
|
|
207
|
+
lines.push(` get ${propName}(): (${rel.interfaceName} & ${relMetaName(rel)})[] {
|
|
174
208
|
return this._${propName} ?? [];
|
|
175
209
|
}`);
|
|
176
210
|
} else {
|
|
@@ -223,11 +257,11 @@ function generateRehydrateMethod(data: FrontendTemplateData): string {
|
|
|
223
257
|
const effectiveName = rel.alias || rel.variant || rel.name;
|
|
224
258
|
if (rel.single) {
|
|
225
259
|
const propName = toCamelCase(effectiveName);
|
|
226
|
-
const relationshipKey =
|
|
260
|
+
const relationshipKey = resolveRelationshipKey(rel);
|
|
227
261
|
|
|
228
262
|
// Use _readIncludedWithMeta for relationships with fields
|
|
229
263
|
if (rel.fields && rel.fields.length > 0) {
|
|
230
|
-
const metaType =
|
|
264
|
+
const metaType = relMetaName(rel);
|
|
231
265
|
const singleCast = rel.nullable
|
|
232
266
|
? ` as (${rel.interfaceName} & ${metaType}) | undefined`
|
|
233
267
|
: ` as ${rel.interfaceName} & ${metaType}`;
|
|
@@ -242,11 +276,11 @@ function generateRehydrateMethod(data: FrontendTemplateData): string {
|
|
|
242
276
|
} else {
|
|
243
277
|
const effectiveMany = rel.alias || rel.name;
|
|
244
278
|
const propName = pluralize(toCamelCase(effectiveMany));
|
|
245
|
-
const relationshipKey =
|
|
279
|
+
const relationshipKey = resolveRelationshipKey(rel);
|
|
246
280
|
|
|
247
281
|
// Use _readIncludedWithMeta for relationships with fields (edge properties)
|
|
248
282
|
if (rel.fields && rel.fields.length > 0) {
|
|
249
|
-
const metaType =
|
|
283
|
+
const metaType = relMetaName(rel);
|
|
250
284
|
lines.push(
|
|
251
285
|
` this._${propName} = this._readIncludedWithMeta<${rel.interfaceName}, ${metaType}>(data, "${relationshipKey}", Modules.${rel.name}) as (${rel.interfaceName} & ${metaType})[];`,
|
|
252
286
|
);
|
|
@@ -292,16 +326,25 @@ function generateCreateJsonApiMethod(data: FrontendTemplateData): string {
|
|
|
292
326
|
lines.push(``);
|
|
293
327
|
}
|
|
294
328
|
|
|
295
|
-
// Field serialization (excluding inherited)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
: fields
|
|
329
|
+
// Field serialization (excluding inherited and read-only fields).
|
|
330
|
+
// read-only fields are server-derived: present in rehydrate/getters but never sent back.
|
|
331
|
+
const fieldsToInclude = (
|
|
332
|
+
extendsContent ? fields.filter((f) => !["name", "tldr", "abstract"].includes(f.name)) : fields
|
|
333
|
+
).filter((f) => !f.readOnly);
|
|
299
334
|
|
|
300
335
|
fieldsToInclude.forEach((field) => {
|
|
301
336
|
if (field.isContentField) {
|
|
302
337
|
lines.push(
|
|
303
338
|
` if (data.${field.name} !== undefined) response.data.attributes.${field.name} = JSON.stringify(data.${field.name});`,
|
|
304
339
|
);
|
|
340
|
+
} else if (field.type === "date") {
|
|
341
|
+
lines.push(
|
|
342
|
+
` if (data.${field.name} !== undefined) response.data.attributes.${field.name} = formatLocalDate(data.${field.name});`,
|
|
343
|
+
);
|
|
344
|
+
} else if (field.type === "datetime") {
|
|
345
|
+
lines.push(
|
|
346
|
+
` if (data.${field.name} !== undefined) response.data.attributes.${field.name} = data.${field.name}.toISOString();`,
|
|
347
|
+
);
|
|
305
348
|
} else {
|
|
306
349
|
lines.push(
|
|
307
350
|
` if (data.${field.name} !== undefined) response.data.attributes.${field.name} = data.${field.name};`,
|
|
@@ -317,7 +360,7 @@ function generateCreateJsonApiMethod(data: FrontendTemplateData): string {
|
|
|
317
360
|
relationshipsToSerialize.forEach((rel) => {
|
|
318
361
|
const effectiveName = rel.alias || rel.variant || rel.name;
|
|
319
362
|
const payloadKey = rel.single ? `${toCamelCase(effectiveName)}Id` : `${toCamelCase(effectiveName)}Ids`;
|
|
320
|
-
const relationshipKey =
|
|
363
|
+
const relationshipKey = resolveRelationshipKey(rel);
|
|
321
364
|
|
|
322
365
|
if (rel.single) {
|
|
323
366
|
lines.push(` if (data.${payloadKey}) {`);
|
|
@@ -337,6 +380,23 @@ function generateCreateJsonApiMethod(data: FrontendTemplateData): string {
|
|
|
337
380
|
lines.push(` },`);
|
|
338
381
|
}
|
|
339
382
|
|
|
383
|
+
lines.push(` };`);
|
|
384
|
+
lines.push(` }`);
|
|
385
|
+
} else if (rel.fields && rel.fields.length > 0) {
|
|
386
|
+
// MANY relationship with edge fields: per-item meta resolved from a meta array.
|
|
387
|
+
const metaProp = `${toCamelCase(effectiveName)}Meta`;
|
|
388
|
+
lines.push(` if (data.${payloadKey} !== undefined) {`);
|
|
389
|
+
lines.push(` response.data.relationships.${relationshipKey} = {`);
|
|
390
|
+
lines.push(` data: data.${payloadKey}.map((id) => {`);
|
|
391
|
+
lines.push(` const meta = data.${metaProp}?.find((m) => m.id === id);`);
|
|
392
|
+
lines.push(` return {`);
|
|
393
|
+
lines.push(` type: Modules.${rel.name}.name,`);
|
|
394
|
+
lines.push(` id,`);
|
|
395
|
+
lines.push(
|
|
396
|
+
` ...(meta ? { meta: { ${rel.fields.map((f) => `${f.name}: meta.${f.name}`).join(", ")} } } : {}),`,
|
|
397
|
+
);
|
|
398
|
+
lines.push(` };`);
|
|
399
|
+
lines.push(` }),`);
|
|
340
400
|
lines.push(` };`);
|
|
341
401
|
lines.push(` }`);
|
|
342
402
|
} else {
|
|
@@ -22,6 +22,7 @@ export function generateServiceTemplate(data: FrontendTemplateData): string {
|
|
|
22
22
|
const relationshipMethods = generateRelationshipMethods(data);
|
|
23
23
|
const createMethod = generateCreateMethod(data);
|
|
24
24
|
const updateMethod = generateUpdateMethod(data);
|
|
25
|
+
const patchMethod = generatePatchMethod(data);
|
|
25
26
|
const deleteMethod = generateDeleteMethod(data);
|
|
26
27
|
|
|
27
28
|
return `${imports}
|
|
@@ -36,6 +37,8 @@ ${createMethod}
|
|
|
36
37
|
|
|
37
38
|
${updateMethod}
|
|
38
39
|
|
|
40
|
+
${patchMethod}
|
|
41
|
+
|
|
39
42
|
${deleteMethod}
|
|
40
43
|
}
|
|
41
44
|
`;
|
|
@@ -60,10 +63,13 @@ function generateFindOneMethod(data: FrontendTemplateData): string {
|
|
|
60
63
|
const { names } = data;
|
|
61
64
|
|
|
62
65
|
return ` static async findOne(params: { id: string }): Promise<${names.pascalCase}Interface> {
|
|
66
|
+
const endpoint = new EndpointCreator({ endpoint: Modules.${names.pascalCase}, id: params.id });
|
|
67
|
+
if (Modules.${names.pascalCase}.inclusions?.lists?.fields) endpoint.limitToFields(Modules.${names.pascalCase}.inclusions.lists.fields);
|
|
68
|
+
if (Modules.${names.pascalCase}.inclusions?.lists?.types) endpoint.limitToType(Modules.${names.pascalCase}.inclusions.lists.types);
|
|
63
69
|
return this.callApi<${names.pascalCase}Interface>({
|
|
64
70
|
type: Modules.${names.pascalCase},
|
|
65
71
|
method: HttpMethod.GET,
|
|
66
|
-
endpoint:
|
|
72
|
+
endpoint: endpoint.generate(),
|
|
67
73
|
});
|
|
68
74
|
}`;
|
|
69
75
|
}
|
|
@@ -174,6 +180,22 @@ function generateUpdateMethod(data: FrontendTemplateData): string {
|
|
|
174
180
|
}`;
|
|
175
181
|
}
|
|
176
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Generate patch method
|
|
185
|
+
*/
|
|
186
|
+
function generatePatchMethod(data: FrontendTemplateData): string {
|
|
187
|
+
const { names } = data;
|
|
188
|
+
|
|
189
|
+
return ` static async patch(params: { id: string } & Partial<Omit<${names.pascalCase}Input, "id">>): Promise<${names.pascalCase}Interface> {
|
|
190
|
+
return this.callApi({
|
|
191
|
+
type: Modules.${names.pascalCase},
|
|
192
|
+
method: HttpMethod.PATCH,
|
|
193
|
+
endpoint: new EndpointCreator({ endpoint: Modules.${names.pascalCase}, id: params.id }).generate(),
|
|
194
|
+
input: params,
|
|
195
|
+
});
|
|
196
|
+
}`;
|
|
197
|
+
}
|
|
198
|
+
|
|
177
199
|
/**
|
|
178
200
|
* Generate delete method
|
|
179
201
|
*/
|
|
@@ -20,11 +20,21 @@ export function generateModuleTemplate(data: FrontendTemplateData): string {
|
|
|
20
20
|
const listFieldNames = getListFieldNames(data);
|
|
21
21
|
const listInclusionFields = listFieldNames.map((f) => `\`${f}\``).join(", ");
|
|
22
22
|
|
|
23
|
+
// Feature gating (optional)
|
|
24
|
+
const featureImport = data.featureId ? `import { FeatureIds } from "@/enums/feature.ids";\n` : "";
|
|
25
|
+
const featureLine = data.featureId ? ` feature: FeatureIds.${data.featureId},\n` : "";
|
|
26
|
+
|
|
27
|
+
// Cross-module inclusions for related entities (optional)
|
|
28
|
+
const relatedInclusionLines = data.relatedInclusions
|
|
29
|
+
.map((inc) => ` createJsonApiInclusion("${inc.endpoint}", [${inc.fields.map((f) => `\`${f}\``).join(", ")}]),`)
|
|
30
|
+
.join("\n");
|
|
31
|
+
const relatedInclusionBlock = relatedInclusionLines ? `\n${relatedInclusionLines}` : "";
|
|
32
|
+
|
|
23
33
|
return `import { ${names.pascalCase} } from "@/features/${data.importTargetDir}/${names.kebabCase}/data/${names.pascalCase}";
|
|
24
34
|
import { createJsonApiInclusion } from "@carlonicora/nextjs-jsonapi/core";
|
|
25
35
|
import { ModuleFactory } from "@carlonicora/nextjs-jsonapi/core";
|
|
26
36
|
import { ${DEFAULT_MODULE_ICON} } from "lucide-react";
|
|
27
|
-
|
|
37
|
+
${featureImport}
|
|
28
38
|
export const ${names.pascalCase}Module = (factory: ModuleFactory) =>
|
|
29
39
|
factory({
|
|
30
40
|
pageUrl: "/${names.pluralKebab}",
|
|
@@ -32,11 +42,11 @@ export const ${names.pascalCase}Module = (factory: ModuleFactory) =>
|
|
|
32
42
|
model: ${names.pascalCase},
|
|
33
43
|
moduleId: "${moduleId}",
|
|
34
44
|
icon: ${DEFAULT_MODULE_ICON},
|
|
35
|
-
inclusions: {
|
|
45
|
+
${featureLine} inclusions: {
|
|
36
46
|
lists: {
|
|
37
47
|
fields: [
|
|
38
48
|
createJsonApiInclusion("${endpoint}", [${listInclusionFields}]),
|
|
39
|
-
createJsonApiInclusion("users", [\`name\`, \`avatar\`])
|
|
49
|
+
createJsonApiInclusion("users", [\`name\`, \`avatar\`]),${relatedInclusionBlock}
|
|
40
50
|
],
|
|
41
51
|
},
|
|
42
52
|
},
|