@carlonicora/nextjs-jsonapi 1.109.0 → 1.111.0
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/{AssistantMessageInterface-Cs1yb-gF.d.ts → AssistantMessageInterface-BNzf1etU.d.ts} +14 -1
- package/dist/{AssistantMessageInterface-DQ3mH5L8.d.mts → AssistantMessageInterface-DrgHDjZ5.d.mts} +14 -1
- package/dist/{BlockNoteEditor-3XYBZLWO.js → BlockNoteEditor-7OKW6HHN.js} +19 -20
- package/dist/BlockNoteEditor-7OKW6HHN.js.map +1 -0
- package/dist/{BlockNoteEditor-EBFZG7AL.mjs → BlockNoteEditor-K7IIWM3S.mjs} +4 -5
- package/dist/{BlockNoteEditor-EBFZG7AL.mjs.map → BlockNoteEditor-K7IIWM3S.mjs.map} +1 -1
- package/dist/BlockNoteViewer-OPSCTHKJ.mjs +31 -0
- package/dist/BlockNoteViewer-OPSCTHKJ.mjs.map +1 -0
- package/dist/BlockNoteViewer-UW5VZ5UF.js +31 -0
- package/dist/BlockNoteViewer-UW5VZ5UF.js.map +1 -0
- package/dist/billing/index.d.mts +41 -41
- package/dist/billing/index.d.ts +41 -41
- package/dist/billing/index.js +346 -347
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +3 -4
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-7E3O52U5.js → chunk-6XWW67F2.js} +25 -12
- package/dist/chunk-6XWW67F2.js.map +1 -0
- package/dist/{chunk-UHO3KUUH.js → chunk-AKXUNE3A.js} +977 -850
- package/dist/chunk-AKXUNE3A.js.map +1 -0
- package/dist/{chunk-UOYIWJEJ.mjs → chunk-CWVYU4YZ.mjs} +18 -5
- package/dist/{chunk-UOYIWJEJ.mjs.map → chunk-CWVYU4YZ.mjs.map} +1 -1
- package/dist/{chunk-CRTVAQEK.mjs → chunk-TXVVZLUQ.mjs} +1388 -1261
- package/dist/chunk-TXVVZLUQ.mjs.map +1 -0
- package/dist/{chunk-CFI4WZ5R.js → chunk-U2HR57MY.js} +111 -5
- package/dist/chunk-U2HR57MY.js.map +1 -0
- package/dist/chunk-WPAQRB5Q.mjs +7 -0
- package/dist/chunk-WPAQRB5Q.mjs.map +1 -0
- package/dist/chunk-YBZVWLPH.js +7 -0
- package/dist/chunk-YBZVWLPH.js.map +1 -0
- package/dist/{chunk-MSNNAHDB.mjs → chunk-ZYPNVEHB.mjs} +112 -6
- package/dist/{chunk-MSNNAHDB.mjs.map → chunk-ZYPNVEHB.mjs.map} +1 -1
- package/dist/client/index.d.mts +3 -4
- package/dist/client/index.d.ts +3 -4
- package/dist/client/index.js +4 -5
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +3 -4
- package/dist/components/index.d.mts +418 -415
- package/dist/components/index.d.ts +418 -415
- package/dist/components/index.js +6 -5
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +5 -4
- package/dist/{config-CN23v3eJ.d.ts → config-BrkU_Ynz.d.ts} +2 -2
- package/dist/{config-gh88Qn4h.d.mts → config-snd2ToEB.d.mts} +2 -2
- package/dist/contexts/index.d.mts +15 -15
- package/dist/contexts/index.d.ts +15 -15
- package/dist/contexts/index.js +4 -5
- package/dist/contexts/index.js.map +1 -1
- package/dist/contexts/index.mjs +3 -4
- package/dist/core/index.d.mts +35 -4
- package/dist/core/index.d.ts +35 -4
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/features/help/index.d.mts +25 -66
- package/dist/features/help/index.d.ts +25 -66
- package/dist/features/help/index.js +114 -124
- package/dist/features/help/index.js.map +1 -1
- package/dist/features/help/index.mjs +95 -105
- package/dist/features/help/index.mjs.map +1 -1
- package/dist/features/help/server.d.mts +27 -65
- package/dist/features/help/server.d.ts +27 -65
- package/dist/features/help/server.js +4 -119
- package/dist/features/help/server.js.map +1 -1
- package/dist/features/help/server.mjs +4 -119
- package/dist/features/help/server.mjs.map +1 -1
- package/dist/help-content-config.interface-C5ESIod9.d.mts +13 -0
- package/dist/help-content-config.interface-C5ESIod9.d.ts +13 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -3
- 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/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/dist/testing/index.d.mts +1 -2
- package/dist/testing/index.d.ts +1 -2
- package/package.json +1 -6
- 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/client/config.ts +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/viewers/BlockNoteViewer.tsx +27 -0
- package/src/components/viewers/BlockNoteViewerContainer.tsx +10 -0
- package/src/core/registry/helpStore.ts +2 -19
- package/src/features/assistant-message/components/parts/MessageSourcesContainer.tsx +4 -0
- package/src/features/assistant-message/components/parts/tabs/ContentsTab.tsx +5 -5
- package/src/features/assistant-message/components/parts/tabs/ReferencesTab.tsx +3 -2
- package/src/features/help/components/HelpArticleBody.tsx +27 -19
- package/src/features/help/components/HelpHint.tsx +20 -11
- package/src/features/help/components/HelpSearchResultRow.tsx +16 -6
- package/src/features/help/components/HelpSideNav.tsx +54 -19
- package/src/features/help/components/__tests__/HelpAskAi.spec.tsx +1 -1
- package/src/features/help/components/__tests__/HelpHint.spec.tsx +17 -17
- package/src/features/help/components/__tests__/HelpSearchResultRow.spec.tsx +18 -19
- package/src/features/help/components/__tests__/HelpSideNav.spec.tsx +20 -22
- package/src/features/help/contexts/HelpContext.spec.tsx +1 -3
- package/src/features/help/index.ts +3 -6
- package/src/features/help/interfaces/help-content-config.interface.ts +0 -5
- package/src/features/help/server-entry.ts +6 -15
- package/src/features/help/types/help-article.types.ts +2 -4
- package/src/features/how-to/HowToModule.ts +14 -1
- package/src/features/how-to/components/forms/HowToEditor.tsx +108 -4
- package/src/features/how-to/data/HowTo.spec.ts +43 -0
- package/src/features/how-to/data/HowTo.ts +47 -4
- package/src/features/how-to/data/HowToInterface.ts +14 -1
- package/src/features/how-to/data/HowToService.ts +61 -0
- package/dist/BlockNoteEditor-3XYBZLWO.js.map +0 -1
- package/dist/chunk-3J7RQBF3.js +0 -123
- package/dist/chunk-3J7RQBF3.js.map +0 -1
- package/dist/chunk-7E3O52U5.js.map +0 -1
- package/dist/chunk-CFI4WZ5R.js.map +0 -1
- package/dist/chunk-CQID6RCF.mjs +0 -38
- package/dist/chunk-CQID6RCF.mjs.map +0 -1
- package/dist/chunk-CRTVAQEK.mjs.map +0 -1
- package/dist/chunk-MZTKPPET.mjs +0 -123
- package/dist/chunk-MZTKPPET.mjs.map +0 -1
- package/dist/chunk-UHO3KUUH.js.map +0 -1
- package/dist/chunk-YQQHAFBS.js +0 -38
- package/dist/chunk-YQQHAFBS.js.map +0 -1
- package/dist/features/help/server/createHelpAssetRouteHandler.d.mts +0 -11
- package/dist/features/help/server/createHelpAssetRouteHandler.d.ts +0 -11
- package/dist/features/help/server/createHelpAssetRouteHandler.js +0 -43
- package/dist/features/help/server/createHelpAssetRouteHandler.js.map +0 -1
- package/dist/features/help/server/createHelpAssetRouteHandler.mjs +0 -43
- package/dist/features/help/server/createHelpAssetRouteHandler.mjs.map +0 -1
- package/dist/help-content-config.interface-B9L02u9i.d.mts +0 -50
- package/dist/help-content-config.interface-B9L02u9i.d.ts +0 -50
- 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/src/features/help/components/mdx/Callout.tsx +0 -21
- package/src/features/help/components/mdx/EntityRef.tsx +0 -18
- package/src/features/help/components/mdx/KeyBinding.tsx +0 -6
- package/src/features/help/components/mdx/Related.tsx +0 -33
- package/src/features/help/components/mdx/Screenshot.tsx +0 -9
- package/src/features/help/components/mdx/Steps.tsx +0 -21
- package/src/features/help/components/mdx/Video.tsx +0 -8
- package/src/features/help/components/mdx/mdx-server-components.ts +0 -23
- package/src/features/help/components/mdx/mdxComponents.ts +0 -9
- package/src/features/help/hooks/useHelpArticle.ts +0 -7
- package/src/features/help/hooks/useHelpManifest.ts +0 -5
- package/src/features/help/server/__tests__/createHelpAssetRouteHandler.spec.ts +0 -43
- package/src/features/help/server/createHelpAssetRouteHandler.ts +0 -35
- package/src/features/help/server/generateHelpArticleMetadata.ts +0 -18
- package/src/features/help/server/generateHelpArticleStaticParams.ts +0 -7
- package/src/features/help/server/generateHelpModeStaticParams.ts +0 -5
- package/src/features/help/server/getHelpContent.ts +0 -17
- package/src/features/help/server/index.ts +0 -8
- package/src/features/help/server/serializeHelpArticle.tsx +0 -46
- package/src/features/help/utils/__tests__/helpNavigation.spec.ts +0 -70
- package/src/features/help/utils/helpNavigation.ts +0 -29
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { FrontendTemplateData, FrontendField } from "../types/template-data.interface";
|
|
8
|
-
import { toCamelCase } from "../transformers/name-transformer";
|
|
8
|
+
import { toCamelCase, pluralize } from "../transformers/name-transformer";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Generate the table structure hook file content
|
|
@@ -96,7 +96,7 @@ registerTableGenerator(Modules.${names.pascalCase}, use${names.pascalCase}TableS
|
|
|
96
96
|
* Generate field column map entries
|
|
97
97
|
*/
|
|
98
98
|
function generateFieldColumnEntries(data: FrontendTemplateData): string {
|
|
99
|
-
const { names, fields, extendsContent } = data;
|
|
99
|
+
const { names, fields, extendsContent, relationships } = data;
|
|
100
100
|
const entries: string[] = [];
|
|
101
101
|
|
|
102
102
|
// Name field (with link and tooltip for tldr if Content-extending)
|
|
@@ -183,8 +183,37 @@ function generateFieldColumnEntries(data: FrontendTemplateData): string {
|
|
|
183
183
|
enableSorting: false,
|
|
184
184
|
enableHiding: false,
|
|
185
185
|
}),`);
|
|
186
|
+
} else if (field.type === "date" || field.type === "datetime") {
|
|
187
|
+
entries.push(` [${names.pascalCase}Fields.${field.name}]: () =>
|
|
188
|
+
cellDate({
|
|
189
|
+
name: "${field.name}",
|
|
190
|
+
title: t(\`features.${names.camelCase}.fields.${field.name}.label\`),
|
|
191
|
+
}),`);
|
|
186
192
|
}
|
|
187
193
|
});
|
|
188
194
|
|
|
195
|
+
// Relationship columns (rendered when flagged for table display)
|
|
196
|
+
relationships
|
|
197
|
+
.filter((rel) => rel.showInTable)
|
|
198
|
+
.forEach((rel) => {
|
|
199
|
+
const key = rel.single
|
|
200
|
+
? toCamelCase(rel.alias || rel.variant || rel.name)
|
|
201
|
+
: pluralize(toCamelCase(rel.alias || rel.name));
|
|
202
|
+
const accessor = rel.single
|
|
203
|
+
? `${names.camelCase}.${key}?.name ?? ""`
|
|
204
|
+
: `${names.camelCase}.${key}.map((r) => r.name).join(", ")`;
|
|
205
|
+
entries.push(` [${names.pascalCase}Fields.${key}]: () => ({
|
|
206
|
+
id: "${key}",
|
|
207
|
+
accessorKey: "${key}",
|
|
208
|
+
header: t(\`generic.relationships.${key}.label\`),
|
|
209
|
+
cell: ({ row }: { row: TableContent<${names.pascalCase}Interface> }) => {
|
|
210
|
+
const ${names.camelCase}: ${names.pascalCase}Interface = row.original.jsonApiData;
|
|
211
|
+
return <span>{${accessor}}</span>;
|
|
212
|
+
},
|
|
213
|
+
enableSorting: false,
|
|
214
|
+
enableHiding: false,
|
|
215
|
+
}),`);
|
|
216
|
+
});
|
|
217
|
+
|
|
189
218
|
return entries.join("\n");
|
|
190
219
|
}
|
|
@@ -177,7 +177,6 @@ export interface ModuleFilePaths {
|
|
|
177
177
|
selector: string;
|
|
178
178
|
multiSelector: string;
|
|
179
179
|
list: string;
|
|
180
|
-
details: string;
|
|
181
180
|
content: string;
|
|
182
181
|
container: string;
|
|
183
182
|
listContainer: string;
|
|
@@ -220,7 +219,6 @@ export function buildFilePaths(
|
|
|
220
219
|
selector: `${basePath}/components/forms/${names.pascalCase}Selector.tsx`,
|
|
221
220
|
multiSelector: `${basePath}/components/forms/${names.pascalCase}MultiSelector.tsx`,
|
|
222
221
|
list: `${basePath}/components/lists/${names.pascalCase}List.tsx`,
|
|
223
|
-
details: `${basePath}/components/details/${names.pascalCase}Details.tsx`,
|
|
224
222
|
content: `${basePath}/components/details/${names.pascalCase}Content.tsx`,
|
|
225
223
|
container: `${basePath}/components/containers/${names.pascalCase}Container.tsx`,
|
|
226
224
|
listContainer: `${basePath}/components/containers/${names.pascalCase}ListContainer.tsx`,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { FrontendRelationship } from "../types/template-data.interface";
|
|
2
|
+
import { toKebabCase, pluralize } from "./name-transformer";
|
|
3
|
+
|
|
4
|
+
/** JSON:API wire key for a relationship. Schema dtoKey wins; otherwise derive
|
|
5
|
+
* identically to the backend mapper so both sides of the wire agree. */
|
|
6
|
+
export function resolveRelationshipKey(rel: FrontendRelationship): string {
|
|
7
|
+
if (rel.dtoKey) return rel.dtoKey;
|
|
8
|
+
// NOTE: Author relationships are normally NOT serialized through this helper — the backend
|
|
9
|
+
// special-cases Author via contextKey/currentUser and the model template skips Author
|
|
10
|
+
// serialization, so callers must not rely on this helper for Author wire keys.
|
|
11
|
+
if (rel.variant) {
|
|
12
|
+
return rel.single ? rel.variant.toLowerCase() : pluralize(rel.variant.toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
if (rel.alias) {
|
|
15
|
+
return rel.single ? toKebabCase(rel.alias) : pluralize(toKebabCase(rel.alias));
|
|
16
|
+
}
|
|
17
|
+
return rel.single ? rel.name.toLowerCase() : pluralize(rel.name.toLowerCase());
|
|
18
|
+
}
|
|
@@ -122,6 +122,8 @@ export function resolveRelationship(rel: JsonRelationshipDefinition, targetHasNa
|
|
|
122
122
|
modelKebab,
|
|
123
123
|
fields,
|
|
124
124
|
targetHasName: targetHasNameMap?.get(rel.name) ?? true, // Foundation entities default to true
|
|
125
|
+
dtoKey: rel.dtoKey ?? "",
|
|
126
|
+
showInTable: rel.showInTable === true,
|
|
125
127
|
};
|
|
126
128
|
}
|
|
127
129
|
|
|
@@ -13,6 +13,7 @@ export interface JsonFieldDefinition {
|
|
|
13
13
|
name: string;
|
|
14
14
|
type: string;
|
|
15
15
|
nullable: boolean;
|
|
16
|
+
readOnly?: boolean;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -29,6 +30,10 @@ export interface JsonRelationshipDefinition {
|
|
|
29
30
|
toNode: boolean; // Backend-specific, ignored in frontend
|
|
30
31
|
nullable: boolean;
|
|
31
32
|
fields?: JsonFieldDefinition[]; // Relationship property fields (stored on edges)
|
|
33
|
+
/** Explicit JSON:API wire key. When absent it is derived identically to the backend mapper. */
|
|
34
|
+
dtoKey?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
showInTable?: boolean;
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
/**
|
|
@@ -44,4 +49,8 @@ export interface JsonModuleDefinition {
|
|
|
44
49
|
relationships: JsonRelationshipDefinition[];
|
|
45
50
|
/** Explicitly set whether this module extends Content. If not set, auto-detects based on fields. */
|
|
46
51
|
extendsContent?: boolean;
|
|
52
|
+
featureId?: string;
|
|
53
|
+
displayProp?: string;
|
|
54
|
+
containerTabs?: { activity?: boolean; relations?: { module: string; listProp: string; directory: string }[] };
|
|
55
|
+
inclusions?: { related?: { endpoint: string; fields: string[] }[] };
|
|
47
56
|
}
|
|
@@ -30,6 +30,7 @@ export interface FrontendField {
|
|
|
30
30
|
formComponent: FormComponentType;
|
|
31
31
|
nullable: boolean;
|
|
32
32
|
isContentField: boolean; // Indicates BlockNoteEditor
|
|
33
|
+
readOnly?: boolean;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -68,6 +69,8 @@ export interface FrontendRelationship {
|
|
|
68
69
|
modelKebab: string; // e.g., "user"
|
|
69
70
|
fields?: FrontendField[]; // Relationship property fields (stored on edges)
|
|
70
71
|
targetHasName: boolean; // Whether the target entity has a "name" field (or extends Content)
|
|
72
|
+
dtoKey: string; // resolved wire key; "" means derive at use-site
|
|
73
|
+
showInTable: boolean;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
/**
|
|
@@ -153,6 +156,10 @@ export interface FrontendTemplateData {
|
|
|
153
156
|
|
|
154
157
|
// For service generation
|
|
155
158
|
relationshipServiceMethods: RelationshipServiceMethod[];
|
|
159
|
+
featureId?: string;
|
|
160
|
+
displayProp: string;
|
|
161
|
+
containerTabs: { activity: boolean; relations: { module: string; listProp: string; directory: string }[] };
|
|
162
|
+
relatedInclusions: { endpoint: string; fields: string[] }[];
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
/**
|
package/src/client/config.ts
CHANGED
|
@@ -18,7 +18,7 @@ let _clientConfig: {
|
|
|
18
18
|
/**
|
|
19
19
|
* Configure the JSON:API client. This is the main configuration function.
|
|
20
20
|
* This is typically called during app initialization.
|
|
21
|
-
* @param config.helpContent - Optional help-content config (
|
|
21
|
+
* @param config.helpContent - Optional help-content config (brand only). Forwarded to the help feature's globalThis-backed store; not stored on the client config.
|
|
22
22
|
*/
|
|
23
23
|
export function configureJsonApi(config: {
|
|
24
24
|
apiUrl: string;
|
package/src/components/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./containers";
|
|
|
6
6
|
export * from "./contents";
|
|
7
7
|
export * from "./details";
|
|
8
8
|
export * from "./editors";
|
|
9
|
+
export { BlockNoteViewerContainer } from "./viewers/BlockNoteViewerContainer";
|
|
9
10
|
export * from "./errors";
|
|
10
11
|
export * from "./forms";
|
|
11
12
|
export * from "./navigations";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCreateBlockNote } from "@blocknote/react";
|
|
4
|
+
import type { PartialBlock } from "@blocknote/core";
|
|
5
|
+
// Mirror BlockNoteEditor.tsx's BlockNoteView import + style.css. Default schema
|
|
6
|
+
// only — no useCurrentUserContext, uploadImage, AI, or custom inline specs, so
|
|
7
|
+
// this can render on auth-free public pages.
|
|
8
|
+
import { BlockNoteView } from "@blocknote/shadcn";
|
|
9
|
+
import "@blocknote/shadcn/style.css";
|
|
10
|
+
|
|
11
|
+
function normalize(content: unknown): PartialBlock[] | undefined {
|
|
12
|
+
if (Array.isArray(content) && content.length > 0) return content as PartialBlock[];
|
|
13
|
+
if (typeof content === "string" && content.trim()) {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(content);
|
|
16
|
+
if (Array.isArray(parsed) && parsed.length > 0) return parsed as PartialBlock[];
|
|
17
|
+
} catch {
|
|
18
|
+
/* plain string — fall through to undefined (rendered as empty) */
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function BlockNoteViewer({ content }: { content: unknown }) {
|
|
25
|
+
const editor = useCreateBlockNote({ initialContent: normalize(content) });
|
|
26
|
+
return <BlockNoteView editor={editor} editable={false} theme="light" />;
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import dynamic from "next/dynamic";
|
|
4
|
+
import React from "react";
|
|
5
|
+
|
|
6
|
+
const BlockNoteViewer = dynamic(() => import("./BlockNoteViewer").then((m) => m.BlockNoteViewer), { ssr: false });
|
|
7
|
+
|
|
8
|
+
export const BlockNoteViewerContainer = React.memo(function ViewerContainer(props: { content: unknown }) {
|
|
9
|
+
return <BlockNoteViewer {...props} />;
|
|
10
|
+
});
|
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralized help-content store accessible from client and server contexts.
|
|
3
|
-
* Uses globalThis Symbol
|
|
3
|
+
* Uses a globalThis Symbol key to persist across HMR/Turbopack reloads and to
|
|
4
4
|
* bridge between the client-side `configureJsonApi` (in `client/config.ts`)
|
|
5
|
-
* and
|
|
5
|
+
* and the HelpProvider context. Holds the brand-only HelpContentConfig.
|
|
6
6
|
*
|
|
7
7
|
* NO external dependencies to avoid circular imports.
|
|
8
8
|
*
|
|
9
9
|
* Pattern mirrors `bootstrapStore.ts`.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
// Help content config shape is duplicated here as `unknown` to avoid pulling
|
|
13
|
-
// the help feature into this core file. Callers cast on read.
|
|
14
|
-
type HelpReader = (article: { path: string }) => Promise<string>;
|
|
15
|
-
|
|
16
12
|
const HELP_CONTENT_KEY = Symbol.for("nextjs-jsonapi:helpContent");
|
|
17
|
-
const HELP_READER_KEY = Symbol.for("nextjs-jsonapi:helpReader");
|
|
18
13
|
|
|
19
14
|
const globalStore = globalThis as unknown as {
|
|
20
15
|
[HELP_CONTENT_KEY]?: unknown | null;
|
|
21
|
-
[HELP_READER_KEY]?: HelpReader | null;
|
|
22
16
|
};
|
|
23
17
|
|
|
24
18
|
if (globalStore[HELP_CONTENT_KEY] === undefined) {
|
|
25
19
|
globalStore[HELP_CONTENT_KEY] = null;
|
|
26
20
|
}
|
|
27
|
-
if (globalStore[HELP_READER_KEY] === undefined) {
|
|
28
|
-
globalStore[HELP_READER_KEY] = null;
|
|
29
|
-
}
|
|
30
21
|
|
|
31
22
|
export function _setStaticHelpContent(cfg: unknown | null): void {
|
|
32
23
|
globalStore[HELP_CONTENT_KEY] = cfg;
|
|
@@ -35,11 +26,3 @@ export function _setStaticHelpContent(cfg: unknown | null): void {
|
|
|
35
26
|
export function _getStaticHelpContent<T = unknown>(): T | null {
|
|
36
27
|
return (globalStore[HELP_CONTENT_KEY] as T | null) ?? null;
|
|
37
28
|
}
|
|
38
|
-
|
|
39
|
-
export function setHelpReader(reader: HelpReader): void {
|
|
40
|
-
globalStore[HELP_READER_KEY] = reader;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function _getStaticHelpReader(): HelpReader | null {
|
|
44
|
-
return globalStore[HELP_READER_KEY] ?? null;
|
|
45
|
-
}
|
|
@@ -20,6 +20,10 @@ class SourcesFetcher extends ClientAbstractService {
|
|
|
20
20
|
}): Promise<ApiDataInterface[]> {
|
|
21
21
|
const endpoint = new EndpointCreator({ endpoint: params.module });
|
|
22
22
|
endpoint.addAdditionalParam(params.idsParam, params.ids.join(","));
|
|
23
|
+
// The list endpoint paginates and does not honour the `<x>Ids` filter, so a cited
|
|
24
|
+
// entity beyond the default first page never comes back and its citation falls back
|
|
25
|
+
// to "<Type> <id>". Request the full set and resolve client-side by id.
|
|
26
|
+
endpoint.addAdditionalParam("fetchAll", "true");
|
|
23
27
|
if (params.module.inclusions?.lists?.fields) endpoint.limitToFields(params.module.inclusions.lists.fields);
|
|
24
28
|
if (params.module.inclusions?.lists?.types) endpoint.limitToType(params.module.inclusions.lists.types);
|
|
25
29
|
return this.callApi<ApiDataInterface[]>({
|
|
@@ -64,11 +64,11 @@ export function ContentsTab({ citations, sources }: Props) {
|
|
|
64
64
|
} catch {
|
|
65
65
|
return null;
|
|
66
66
|
}
|
|
67
|
-
// Help
|
|
68
|
-
// /help/<mode>/<slug> instead of the module's /administration/howtos
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
const href =
|
|
67
|
+
// Help HowTos are public articles, not admin records: route to
|
|
68
|
+
// /help/<mode>/<slug> instead of the module's /administration/howtos page.
|
|
69
|
+
const howToType = (source as any).howToType as string | undefined;
|
|
70
|
+
const slug = (source as any).slug as string | undefined;
|
|
71
|
+
const href = howToType && slug ? `/help/${howToType}/${slug}` : generate({ page: module, id: source.id });
|
|
72
72
|
const name = (source as any).name ?? source.identifier;
|
|
73
73
|
return (
|
|
74
74
|
<TableRow key={`${source.type}/${source.id}`}>
|
|
@@ -33,8 +33,9 @@ export function ReferencesTab({ references }: Props) {
|
|
|
33
33
|
} catch {
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
|
-
const
|
|
37
|
-
const
|
|
36
|
+
const howToType = (ref as any).howToType as string | undefined;
|
|
37
|
+
const slug = (ref as any).slug as string | undefined;
|
|
38
|
+
const href = howToType && slug ? `/help/${howToType}/${slug}` : generate({ page: module, id: ref.id });
|
|
38
39
|
return (
|
|
39
40
|
<TableRow key={`${ref.type}/${ref.id}`}>
|
|
40
41
|
<TableCell>
|
|
@@ -1,48 +1,56 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
|
|
2
3
|
import { ReactNode } from "react";
|
|
3
4
|
import Link from "next/link";
|
|
4
5
|
import { useFormatter, useTranslations } from "next-intl";
|
|
5
|
-
import type { HelpArticle } from "../types/help-article.types";
|
|
6
6
|
import { usePageUrlGenerator } from "../../../client";
|
|
7
|
-
import { useHelp } from "../contexts/HelpContext";
|
|
8
|
-
import { articleUrl } from "../utils/articleUrl";
|
|
9
|
-
import { prevNextWithinMode } from "../utils/helpNavigation";
|
|
10
7
|
|
|
11
|
-
|
|
8
|
+
type Sibling = { howToType: string; slug: string; title: string };
|
|
9
|
+
|
|
10
|
+
export function HelpArticleBody(props: {
|
|
11
|
+
howToType: string;
|
|
12
|
+
title: string;
|
|
13
|
+
summary?: string;
|
|
14
|
+
updatedAt?: string;
|
|
15
|
+
prev?: Sibling | null;
|
|
16
|
+
next?: Sibling | null;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}) {
|
|
12
19
|
const t = useTranslations();
|
|
13
20
|
const format = useFormatter();
|
|
14
21
|
const generateUrl = usePageUrlGenerator();
|
|
15
|
-
const {
|
|
16
|
-
const { prev, next } = prevNextWithinMode(manifest, article);
|
|
22
|
+
const { howToType, title, summary, updatedAt, prev, next, children } = props;
|
|
17
23
|
|
|
18
24
|
return (
|
|
19
25
|
<article className="prose dark:prose-invert max-w-none">
|
|
20
26
|
<nav className="text-muted-foreground mb-2 text-xs">
|
|
21
27
|
<Link href={generateUrl({ page: "/help" })}>Help</Link>
|
|
22
28
|
{" · "}
|
|
23
|
-
<Link href={generateUrl({ page: `/help/${
|
|
29
|
+
<Link href={generateUrl({ page: `/help/${howToType}` })}>{t(`help.modes.${howToType}`)}</Link>
|
|
24
30
|
</nav>
|
|
25
|
-
<h1>{
|
|
26
|
-
<p className="text-muted-foreground !mt-0 text-base">{
|
|
31
|
+
<h1>{title}</h1>
|
|
32
|
+
{summary ? <p className="text-muted-foreground !mt-0 text-base">{summary}</p> : null}
|
|
27
33
|
{children}
|
|
28
34
|
<hr className="my-6" />
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
{updatedAt ? (
|
|
36
|
+
<div className="text-muted-foreground text-xs">
|
|
37
|
+
<span>
|
|
38
|
+
{t("help.article.lastUpdated", {
|
|
39
|
+
date: format.dateTime(new Date(updatedAt), { dateStyle: "short" }),
|
|
40
|
+
})}
|
|
41
|
+
</span>
|
|
42
|
+
</div>
|
|
43
|
+
) : null}
|
|
36
44
|
<div className="mt-4 flex justify-between text-sm">
|
|
37
45
|
{prev ? (
|
|
38
|
-
<Link href={
|
|
46
|
+
<Link href={generateUrl({ page: `/help/${prev.howToType}/${prev.slug}` })} className="hover:underline">
|
|
39
47
|
← {t("help.article.previous")}: {prev.title}
|
|
40
48
|
</Link>
|
|
41
49
|
) : (
|
|
42
50
|
<span />
|
|
43
51
|
)}
|
|
44
52
|
{next ? (
|
|
45
|
-
<Link href={
|
|
53
|
+
<Link href={generateUrl({ page: `/help/${next.howToType}/${next.slug}` })} className="hover:underline">
|
|
46
54
|
{t("help.article.next")}: {next.title} →
|
|
47
55
|
</Link>
|
|
48
56
|
) : (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useMemo, useState } from "react";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { useTranslations } from "next-intl";
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { HelpCircleIcon } from "lucide-react";
|
|
@@ -13,20 +13,29 @@ import {
|
|
|
13
13
|
SheetDescription,
|
|
14
14
|
} from "../../../shadcnui";
|
|
15
15
|
import { usePageUrlGenerator } from "../../../client";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import type { HelpArticle } from "../types/help-article.types";
|
|
16
|
+
import { HowToService } from "../../how-to/data/HowToService";
|
|
17
|
+
import type { HowToInterface } from "../../how-to/data/HowToInterface";
|
|
19
18
|
|
|
20
19
|
export function HelpHint({ contextKey }: { contextKey: string }) {
|
|
21
20
|
const t = useTranslations();
|
|
22
21
|
const generateUrl = usePageUrlGenerator();
|
|
23
|
-
const { manifest } = useHelp();
|
|
24
22
|
const [open, setOpen] = useState(false);
|
|
25
|
-
const [picked, setPicked] = useState<
|
|
23
|
+
const [picked, setPicked] = useState<HowToInterface | null>(null);
|
|
24
|
+
|
|
25
|
+
const [articles, setArticles] = useState<HowToInterface[]>([]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
let active = true;
|
|
28
|
+
HowToService.findPublished()
|
|
29
|
+
.then((list) => active && setArticles(list))
|
|
30
|
+
.catch(() => active && setArticles([]));
|
|
31
|
+
return () => {
|
|
32
|
+
active = false;
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
26
35
|
|
|
27
36
|
const matches = useMemo(
|
|
28
|
-
() =>
|
|
29
|
-
[
|
|
37
|
+
() => articles.filter((a) => a.contextualKeys.includes(contextKey) && !a.draft),
|
|
38
|
+
[articles, contextKey],
|
|
30
39
|
);
|
|
31
40
|
|
|
32
41
|
if (matches.length === 0) return null;
|
|
@@ -45,13 +54,13 @@ export function HelpHint({ contextKey }: { contextKey: string }) {
|
|
|
45
54
|
</SheetTrigger>
|
|
46
55
|
<SheetContent side="right" className="w-full max-w-md sm:max-w-lg">
|
|
47
56
|
<SheetHeader>
|
|
48
|
-
<SheetTitle>{active ? active.
|
|
57
|
+
<SheetTitle>{active ? active.name : t("help.hint.pickArticle")}</SheetTitle>
|
|
49
58
|
<SheetDescription>{active?.summary ?? ""}</SheetDescription>
|
|
50
59
|
</SheetHeader>
|
|
51
60
|
{active ? (
|
|
52
61
|
<div className="mt-4 space-y-3">
|
|
53
62
|
<p className="text-muted-foreground text-sm">{active.summary}</p>
|
|
54
|
-
<Link href={
|
|
63
|
+
<Link href={generateUrl({ page: `/help/${active.howToType}/${active.slug}` })} className="text-sm">
|
|
55
64
|
{t("help.hint.viewArticle")}
|
|
56
65
|
</Link>
|
|
57
66
|
</div>
|
|
@@ -64,7 +73,7 @@ export function HelpHint({ contextKey }: { contextKey: string }) {
|
|
|
64
73
|
onClick={() => setPicked(a)}
|
|
65
74
|
className="hover:bg-muted block w-full rounded px-2 py-1 text-left text-sm"
|
|
66
75
|
>
|
|
67
|
-
<div className="font-medium">{a.
|
|
76
|
+
<div className="font-medium">{a.name}</div>
|
|
68
77
|
<div className="text-muted-foreground text-xs">{a.summary}</div>
|
|
69
78
|
</button>
|
|
70
79
|
</li>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { useTranslations } from "next-intl";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
4
5
|
import { LifeBuoyIcon, ArrowUpRightIcon } from "lucide-react";
|
|
5
6
|
import { CommandItem } from "../../../shadcnui";
|
|
6
7
|
import { usePageUrlGenerator } from "../../../client";
|
|
7
|
-
import {
|
|
8
|
+
import { HowToService } from "../../how-to/data/HowToService";
|
|
9
|
+
import type { HowToInterface } from "../../how-to/data/HowToInterface";
|
|
8
10
|
|
|
9
11
|
export interface HelpSearchResultRowProps {
|
|
10
12
|
result: { id: string; name: string; entityType: string };
|
|
@@ -14,8 +16,16 @@ export interface HelpSearchResultRowProps {
|
|
|
14
16
|
export function HelpSearchResultRow({ result, onSelect }: HelpSearchResultRowProps) {
|
|
15
17
|
const t = useTranslations();
|
|
16
18
|
const generateUrl = usePageUrlGenerator();
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
+
const [article, setArticle] = useState<HowToInterface | null>(null);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
let active = true;
|
|
22
|
+
HowToService.findOne({ id: result.id })
|
|
23
|
+
.then((a) => active && setArticle(a))
|
|
24
|
+
.catch(() => active && setArticle(null));
|
|
25
|
+
return () => {
|
|
26
|
+
active = false;
|
|
27
|
+
};
|
|
28
|
+
}, [result.id]);
|
|
19
29
|
|
|
20
30
|
if (!article) {
|
|
21
31
|
return (
|
|
@@ -32,16 +42,16 @@ export function HelpSearchResultRow({ result, onSelect }: HelpSearchResultRowPro
|
|
|
32
42
|
return (
|
|
33
43
|
<CommandItem value={result.id} onSelect={onSelect} className="cursor-pointer p-0">
|
|
34
44
|
<Link
|
|
35
|
-
href={generateUrl({ page: `/help/${article.
|
|
45
|
+
href={generateUrl({ page: `/help/${article.howToType}/${article.slug}` })}
|
|
36
46
|
className="hover:bg-muted flex w-full items-center gap-3 rounded px-3 py-2"
|
|
37
47
|
>
|
|
38
48
|
<div className="bg-muted text-muted-foreground flex h-10 w-10 shrink-0 items-center justify-center rounded-full">
|
|
39
49
|
<LifeBuoyIcon className="h-5 w-5" />
|
|
40
50
|
</div>
|
|
41
51
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
42
|
-
<span className="text-foreground truncate text-sm font-semibold">{article.
|
|
52
|
+
<span className="text-foreground truncate text-sm font-semibold">{article.name}</span>
|
|
43
53
|
<span className="text-muted-foreground truncate text-xs">
|
|
44
|
-
{t(`help.modes.${article.
|
|
54
|
+
{t(`help.modes.${article.howToType}`)} · {article.summary}
|
|
45
55
|
</span>
|
|
46
56
|
</div>
|
|
47
57
|
<ArrowUpRightIcon className="text-muted-foreground h-5 w-5 shrink-0" />
|
|
@@ -1,24 +1,60 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
4
|
import { useTranslations } from "next-intl";
|
|
3
5
|
import Link from "next/link";
|
|
4
6
|
import { usePathname } from "next/navigation";
|
|
5
7
|
import { HELP_MODES } from "../types/help-article.types";
|
|
6
8
|
import { usePageUrlGenerator } from "../../../client";
|
|
7
|
-
import { useHelp } from "../contexts/HelpContext";
|
|
8
9
|
import { useHelpFilter } from "../hooks/useHelpFilter";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
10
|
+
import { HowToService } from "../../how-to/data/HowToService";
|
|
11
|
+
import type { HowToInterface } from "../../how-to/data/HowToInterface";
|
|
11
12
|
|
|
12
13
|
export function HelpSideNav() {
|
|
13
14
|
const t = useTranslations();
|
|
14
15
|
const generateUrl = usePageUrlGenerator();
|
|
15
16
|
const pathname = usePathname();
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const [articles, setArticles] = useState<HowToInterface[]>([]);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
let active = true;
|
|
21
|
+
HowToService.findPublished()
|
|
22
|
+
.then((list) => active && setArticles(list))
|
|
23
|
+
.catch(() => active && setArticles([]));
|
|
24
|
+
return () => {
|
|
25
|
+
active = false;
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
// useHelpFilter indexes by title/summary/tags — map HowTo getters into the shape it expects.
|
|
30
|
+
const indexable = useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
articles.map((a) => ({
|
|
33
|
+
id: a.id,
|
|
34
|
+
title: a.name,
|
|
35
|
+
summary: a.summary ?? "",
|
|
36
|
+
tags: a.tags,
|
|
37
|
+
mode: a.howToType ?? "",
|
|
38
|
+
slug: a.slug ?? "",
|
|
39
|
+
})),
|
|
40
|
+
[articles],
|
|
41
|
+
);
|
|
42
|
+
const { query, setQuery, filtered } = useHelpFilter(indexable as any);
|
|
20
43
|
const filtering = query.trim().length > 0;
|
|
21
44
|
|
|
45
|
+
const groups = useMemo(
|
|
46
|
+
() =>
|
|
47
|
+
HELP_MODES.map((mode) => ({
|
|
48
|
+
mode,
|
|
49
|
+
articles: articles
|
|
50
|
+
.filter((a) => a.howToType === mode && !a.draft)
|
|
51
|
+
.sort((x, y) => (x.order ?? 0) - (y.order ?? 0)),
|
|
52
|
+
})),
|
|
53
|
+
[articles],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const isActive = (mode: string, slug: string) => pathname.endsWith(`/help/${mode}/${slug}`);
|
|
57
|
+
|
|
22
58
|
return (
|
|
23
59
|
<nav aria-label="Help navigation" className="flex h-full flex-col gap-3 overflow-y-auto p-4">
|
|
24
60
|
<input
|
|
@@ -29,17 +65,17 @@ export function HelpSideNav() {
|
|
|
29
65
|
className="border-input bg-background w-full rounded-md border px-3 py-2 text-sm"
|
|
30
66
|
/>
|
|
31
67
|
{filtering ? (
|
|
32
|
-
filtered.length === 0 ? (
|
|
68
|
+
(filtered as any[]).length === 0 ? (
|
|
33
69
|
<p className="text-muted-foreground text-sm">{t("help.sideNav.noMatches")}</p>
|
|
34
70
|
) : (
|
|
35
71
|
<ul className="space-y-1">
|
|
36
|
-
{filtered.map((a) => (
|
|
72
|
+
{(filtered as any[]).map((a) => (
|
|
37
73
|
<li key={a.id}>
|
|
38
74
|
<Link
|
|
39
|
-
href={
|
|
75
|
+
href={generateUrl({ page: `/help/${a.mode}/${a.slug}` })}
|
|
40
76
|
className={
|
|
41
77
|
"hover:bg-muted block rounded px-2 py-1 text-sm " +
|
|
42
|
-
(
|
|
78
|
+
(isActive(a.mode, a.slug) ? "bg-muted font-medium" : "")
|
|
43
79
|
}
|
|
44
80
|
>
|
|
45
81
|
{a.title}
|
|
@@ -49,28 +85,27 @@ export function HelpSideNav() {
|
|
|
49
85
|
</ul>
|
|
50
86
|
)
|
|
51
87
|
) : (
|
|
52
|
-
|
|
53
|
-
const group = groups.find((g) => g.mode === mode)!;
|
|
88
|
+
groups.map((group) => {
|
|
54
89
|
if (group.articles.length === 0) return null;
|
|
55
90
|
return (
|
|
56
|
-
<div key={mode}>
|
|
91
|
+
<div key={group.mode}>
|
|
57
92
|
<Link
|
|
58
|
-
href={
|
|
93
|
+
href={generateUrl({ page: `/help/${group.mode}` })}
|
|
59
94
|
className="text-muted-foreground hover:text-foreground mb-1 block text-xs font-semibold tracking-wider uppercase"
|
|
60
95
|
>
|
|
61
|
-
{t(`help.modes.${mode}`)}
|
|
96
|
+
{t(`help.modes.${group.mode}`)}
|
|
62
97
|
</Link>
|
|
63
98
|
<ul className="space-y-0.5">
|
|
64
99
|
{group.articles.map((a) => (
|
|
65
100
|
<li key={a.id}>
|
|
66
101
|
<Link
|
|
67
|
-
href={
|
|
102
|
+
href={generateUrl({ page: `/help/${a.howToType}/${a.slug}` })}
|
|
68
103
|
className={
|
|
69
104
|
"hover:bg-muted block rounded px-2 py-1 text-sm " +
|
|
70
|
-
(
|
|
105
|
+
(isActive(a.howToType ?? "", a.slug ?? "") ? "bg-muted font-medium" : "")
|
|
71
106
|
}
|
|
72
107
|
>
|
|
73
|
-
{a.
|
|
108
|
+
{a.name}
|
|
74
109
|
</Link>
|
|
75
110
|
</li>
|
|
76
111
|
))}
|
|
@@ -17,7 +17,7 @@ vi.mock("../HelpAssistantSheet", () => ({
|
|
|
17
17
|
function setupConfig() {
|
|
18
18
|
configureJsonApi({
|
|
19
19
|
apiUrl: "http://localhost",
|
|
20
|
-
helpContent: {
|
|
20
|
+
helpContent: { brand: { label: "narr8" } },
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|