@carlonicora/nextjs-jsonapi 1.108.0 → 1.110.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/{AssistantInterface-BYgI5z1-.d.mts → AssistantInterface-B1c8FhGA.d.mts} +2 -0
- package/dist/{AssistantInterface-DfDcz0gJ.d.ts → AssistantInterface-BBUHxOCd.d.ts} +2 -0
- package/dist/{AssistantMessageInterface-BpEhx2pC.d.ts → AssistantMessageInterface-BNzf1etU.d.ts} +16 -1
- package/dist/{AssistantMessageInterface-DJ3Me16Y.d.mts → AssistantMessageInterface-DrgHDjZ5.d.mts} +16 -1
- package/dist/{AuthComponent-B6DIk8Vf.d.ts → AuthComponent-Cd7lcYif.d.ts} +1 -1
- package/dist/{AuthComponent-BKI0ZbtD.d.mts → AuthComponent-DdxCFgUZ.d.mts} +1 -1
- package/dist/{BlockNoteEditor-2AXSTGGG.js → BlockNoteEditor-7OKW6HHN.js} +19 -19
- package/dist/{BlockNoteEditor-2AXSTGGG.js.map → BlockNoteEditor-7OKW6HHN.js.map} +1 -1
- package/dist/{BlockNoteEditor-XVIBGXHF.mjs → BlockNoteEditor-K7IIWM3S.mjs} +4 -4
- 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/{auth.interface-BBUgMZzs.d.ts → auth.interface-8b601idJ.d.ts} +1 -1
- package/dist/{auth.interface-XYEREOD6.d.mts → auth.interface-CXBF8Mhi.d.mts} +1 -1
- package/dist/billing/index.d.mts +41 -41
- package/dist/billing/index.d.ts +41 -41
- package/dist/billing/index.js +346 -346
- package/dist/billing/index.mjs +3 -3
- package/dist/{chunk-VLDLERJN.js → chunk-6XWW67F2.js} +29 -9
- package/dist/chunk-6XWW67F2.js.map +1 -0
- package/dist/{chunk-HC3JFN3C.js → chunk-AKXUNE3A.js} +1004 -862
- package/dist/chunk-AKXUNE3A.js.map +1 -0
- package/dist/{chunk-CFECWLHH.mjs → chunk-CWVYU4YZ.mjs} +23 -3
- package/dist/chunk-CWVYU4YZ.mjs.map +1 -0
- package/dist/{chunk-56XBGQGU.mjs → chunk-TXVVZLUQ.mjs} +1422 -1280
- package/dist/chunk-TXVVZLUQ.mjs.map +1 -0
- package/dist/{chunk-RXXZGPC3.js → chunk-U2HR57MY.js} +265 -113
- 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-N3NVIPSU.mjs → chunk-ZYPNVEHB.mjs} +235 -83
- package/dist/{chunk-N3NVIPSU.mjs.map → chunk-ZYPNVEHB.mjs.map} +1 -1
- package/dist/client/index.d.mts +9 -18
- package/dist/client/index.d.ts +9 -18
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.d.mts +421 -417
- package/dist/components/index.d.ts +421 -417
- package/dist/components/index.js +6 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +5 -3
- package/dist/{config-CLQynoaa.d.ts → config-BrkU_Ynz.d.ts} +4 -1
- package/dist/{config-k61pe_o2.d.mts → config-snd2ToEB.d.mts} +4 -1
- package/dist/contexts/index.d.mts +31 -20
- package/dist/contexts/index.d.ts +31 -20
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +75 -11
- package/dist/core/index.d.ts +75 -11
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/features/help/index.css +29 -0
- package/dist/features/help/index.css.map +1 -0
- package/dist/features/help/index.d.mts +74 -0
- package/dist/features/help/index.d.ts +74 -0
- package/dist/features/help/index.js +522 -0
- package/dist/features/help/index.js.map +1 -0
- package/dist/features/help/index.mjs +522 -0
- package/dist/features/help/index.mjs.map +1 -0
- package/dist/features/help/server.d.mts +33 -0
- package/dist/features/help/server.d.ts +33 -0
- package/dist/features/help/server.js +8 -0
- package/dist/features/help/server.js.map +1 -0
- package/dist/features/help/server.mjs +8 -0
- package/dist/features/help/server.mjs.map +1 -0
- 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 +10 -8
- package/dist/index.d.ts +10 -8
- package/dist/index.js +3 -3
- package/dist/index.mjs +2 -2
- package/dist/{notification.interface-aLEJbA_g.d.ts → notification.interface-C1T1C2ee.d.ts} +1 -100
- package/dist/{notification.interface-DLZGtV7Z.d.mts → notification.interface-DIxR23eS.d.mts} +1 -100
- package/dist/{s3.service-CVgLWaDc.d.mts → s3.service-0BTClOYO.d.mts} +2 -2
- package/dist/{s3.service-SLlX0Zbz.d.ts → s3.service-CT27Fm1s.d.ts} +2 -2
- package/dist/server/index.d.mts +4 -3
- package/dist/server/index.d.ts +4 -3
- 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/dist/types-CQSjy7et.d.mts +101 -0
- package/dist/types-DHOxe8rc.d.ts +101 -0
- package/dist/usePageUrlGenerator-tjq2mlDV.d.ts +14 -0
- package/dist/usePageUrlGenerator-uOnyJ6j2.d.mts +14 -0
- package/dist/{useSocket-BkxHHujj.d.mts → useSocket-B1fMIr17.d.mts} +1 -1
- package/dist/{useSocket-CMDjWFYm.d.ts → useSocket-BdJTBXKv.d.ts} +1 -1
- package/package.json +15 -1
- package/src/client/config.ts +9 -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 +28 -0
- package/src/features/assistant/contexts/AssistantContext.tsx +35 -19
- package/src/features/assistant/contexts/__tests__/AssistantContext.spec.tsx +66 -6
- package/src/features/assistant/data/Assistant.ts +2 -0
- package/src/features/assistant/data/AssistantInterface.ts +2 -0
- package/src/features/assistant/data/AssistantService.ts +18 -8
- package/src/features/assistant-message/components/parts/MessageSourcesContainer.tsx +4 -0
- package/src/features/assistant-message/components/parts/MessageSourcesPanel.tsx +6 -4
- package/src/features/assistant-message/components/parts/tabs/ContentsTab.tsx +5 -1
- package/src/features/assistant-message/components/parts/tabs/ReferencesTab.tsx +3 -1
- package/src/features/assistant-message/data/AssistantMessage.ts +27 -1
- package/src/features/assistant-message/data/AssistantMessageInterface.ts +1 -0
- package/src/features/assistant-message/data/__tests__/AssistantMessage.spec.ts +7 -3
- package/src/features/help/components/HelpArticleBody.tsx +62 -0
- package/src/features/help/components/HelpAskAi.tsx +36 -0
- package/src/features/help/components/HelpAssistantSheet.tsx +53 -0
- package/src/features/help/components/HelpHeader.tsx +40 -0
- package/src/features/help/components/HelpHint.tsx +86 -0
- package/src/features/help/components/HelpSearchResultRow.tsx +61 -0
- package/src/features/help/components/HelpSideNav.tsx +119 -0
- package/src/features/help/components/HelpTOC.tsx +49 -0
- package/src/features/help/components/__tests__/HelpAskAi.spec.tsx +68 -0
- package/src/features/help/components/__tests__/HelpAssistantSheet.spec.tsx +36 -0
- package/src/features/help/components/__tests__/HelpHint.spec.tsx +50 -0
- package/src/features/help/components/__tests__/HelpSearchResultRow.spec.tsx +58 -0
- package/src/features/help/components/__tests__/HelpSideNav.spec.tsx +50 -0
- package/src/features/help/contexts/HelpContext.spec.tsx +26 -0
- package/src/features/help/contexts/HelpContext.tsx +24 -0
- package/src/features/help/hooks/useHelp.ts +1 -0
- package/src/features/help/hooks/useHelpFilter.ts +27 -0
- package/src/features/help/i18n-keys.ts +34 -0
- package/src/features/help/index.ts +24 -0
- package/src/features/help/interfaces/help-content-config.interface.ts +12 -0
- package/src/features/help/server-entry.ts +11 -0
- package/src/features/help/types/help-article.types.ts +35 -0
- package/src/features/help/utils/articleUrl.ts +13 -0
- 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 +64 -3
- package/src/features/how-to/data/HowToInterface.ts +14 -0
- package/src/features/how-to/data/HowToService.ts +61 -0
- package/src/index.ts +4 -0
- package/dist/chunk-56XBGQGU.mjs.map +0 -1
- package/dist/chunk-CFECWLHH.mjs.map +0 -1
- package/dist/chunk-HC3JFN3C.js.map +0 -1
- package/dist/chunk-RXXZGPC3.js.map +0 -1
- package/dist/chunk-VLDLERJN.js.map +0 -1
- /package/dist/{BlockNoteEditor-XVIBGXHF.mjs.map → BlockNoteEditor-K7IIWM3S.mjs.map} +0 -0
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
4
|
import { PlusIcon, SearchIcon, XIcon } from "lucide-react";
|
|
5
5
|
import { useTranslations } from "next-intl";
|
|
6
|
-
import { ReactNode, useCallback, useMemo, useState } from "react";
|
|
6
|
+
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { useForm } from "react-hook-form";
|
|
8
8
|
import { v4 } from "uuid";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
|
|
11
|
-
import { EditorSheet, FormInput } from "../../../../components";
|
|
11
|
+
import { EditorSheet, FormInput, FormSelect, FormTextarea, FormCheckbox } from "../../../../components";
|
|
12
12
|
import { BlockNoteEditorContainer } from "../../../../components";
|
|
13
13
|
import { ModuleRegistry, Modules } from "../../../../core";
|
|
14
14
|
import { useI18nRouter } from "../../../../hooks";
|
|
@@ -27,6 +27,9 @@ import {
|
|
|
27
27
|
import { HowTo } from "../../data/HowTo";
|
|
28
28
|
import { HowToInput, HowToInterface } from "../../data/HowToInterface";
|
|
29
29
|
import { HowToService } from "../../data/HowToService";
|
|
30
|
+
import HowToMultiSelector from "./HowToMultiSelector";
|
|
31
|
+
|
|
32
|
+
const HOW_TO_TYPES = ["tutorial", "how-to", "reference", "explanation"] as const;
|
|
30
33
|
|
|
31
34
|
function PageSelector({
|
|
32
35
|
value,
|
|
@@ -140,6 +143,14 @@ function HowToEditorInternal({
|
|
|
140
143
|
name: z.string().min(1, { message: t(`howto.fields.name.error`) }),
|
|
141
144
|
description: z.any(),
|
|
142
145
|
pages: z.array(z.string()),
|
|
146
|
+
howToType: z.enum(HOW_TO_TYPES),
|
|
147
|
+
slug: z.string().min(1, { message: t(`howto.fields.slug.error`) }),
|
|
148
|
+
order: z.number().int().min(0),
|
|
149
|
+
summary: z.string().optional(),
|
|
150
|
+
tags: z.string().optional(),
|
|
151
|
+
contextualKeys: z.string().optional(),
|
|
152
|
+
draft: z.boolean().optional(),
|
|
153
|
+
relatedArticles: z.array(z.object({ id: z.string().uuid(), name: z.string() })).optional(),
|
|
143
154
|
}),
|
|
144
155
|
[t],
|
|
145
156
|
);
|
|
@@ -150,6 +161,14 @@ function HowToEditorInternal({
|
|
|
150
161
|
name: howTo?.name || "",
|
|
151
162
|
description: howTo?.description || [],
|
|
152
163
|
pages: HowTo.parsePagesFromString(howTo?.pages),
|
|
164
|
+
howToType: (howTo?.howToType as (typeof HOW_TO_TYPES)[number]) ?? "how-to",
|
|
165
|
+
slug: howTo?.slug ?? "",
|
|
166
|
+
order: howTo?.order ?? 0,
|
|
167
|
+
summary: howTo?.summary ?? "",
|
|
168
|
+
tags: (howTo?.tags ?? []).join(", "),
|
|
169
|
+
contextualKeys: (howTo?.contextualKeys ?? []).join(", "),
|
|
170
|
+
draft: howTo?.draft ?? false,
|
|
171
|
+
relatedArticles: [] as { id: string; name: string }[],
|
|
153
172
|
}),
|
|
154
173
|
[howTo],
|
|
155
174
|
);
|
|
@@ -159,6 +178,26 @@ function HowToEditorInternal({
|
|
|
159
178
|
defaultValues: getDefaultValues(),
|
|
160
179
|
});
|
|
161
180
|
|
|
181
|
+
const initialRelatedIds = useRef<string[]>([]);
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
if (!howTo?.howToType || !howTo?.slug) return;
|
|
184
|
+
let active = true;
|
|
185
|
+
HowToService.findRelated({ howToType: howTo.howToType, slug: howTo.slug })
|
|
186
|
+
.then((list) => {
|
|
187
|
+
if (!active) return;
|
|
188
|
+
initialRelatedIds.current = list.map((r) => r.id);
|
|
189
|
+
form.setValue(
|
|
190
|
+
"relatedArticles",
|
|
191
|
+
list.map((r) => ({ id: r.id, name: r.name })),
|
|
192
|
+
{ shouldDirty: false },
|
|
193
|
+
);
|
|
194
|
+
})
|
|
195
|
+
.catch(() => {});
|
|
196
|
+
return () => {
|
|
197
|
+
active = false;
|
|
198
|
+
};
|
|
199
|
+
}, [howTo, form]);
|
|
200
|
+
|
|
162
201
|
const handleDescriptionChange = useCallback(
|
|
163
202
|
(content: any) => {
|
|
164
203
|
form.setValue("description", content, { shouldDirty: true });
|
|
@@ -191,17 +230,40 @@ function HowToEditorInternal({
|
|
|
191
230
|
propagateChanges={propagateChanges}
|
|
192
231
|
size="lg"
|
|
193
232
|
onSubmit={async (values) => {
|
|
233
|
+
const toList = (s?: string) =>
|
|
234
|
+
(s ?? "")
|
|
235
|
+
.split(",")
|
|
236
|
+
.map((x) => x.trim())
|
|
237
|
+
.filter(Boolean);
|
|
238
|
+
|
|
194
239
|
const payload: HowToInput = {
|
|
195
240
|
id: values.id,
|
|
196
241
|
name: values.name,
|
|
197
242
|
authorId: "",
|
|
198
243
|
description: values.description,
|
|
199
244
|
pages: HowTo.serializePagesToString(values.pages),
|
|
245
|
+
howToType: values.howToType,
|
|
246
|
+
slug: values.slug,
|
|
247
|
+
order: values.order,
|
|
248
|
+
summary: values.summary,
|
|
249
|
+
tags: toList(values.tags),
|
|
250
|
+
contextualKeys: toList(values.contextualKeys),
|
|
251
|
+
draft: values.draft ?? false,
|
|
200
252
|
};
|
|
201
253
|
|
|
202
|
-
const
|
|
254
|
+
const saved = howTo ? await HowToService.update(payload) : await HowToService.create(payload);
|
|
203
255
|
|
|
204
|
-
|
|
256
|
+
const desired = new Set((values.relatedArticles ?? []).map((r) => r.id));
|
|
257
|
+
const current = new Set(initialRelatedIds.current);
|
|
258
|
+
for (const relatedId of desired) {
|
|
259
|
+
if (!current.has(relatedId)) await HowToService.addRelated({ howToId: values.id, relatedId });
|
|
260
|
+
}
|
|
261
|
+
for (const relatedId of current) {
|
|
262
|
+
if (!desired.has(relatedId)) await HowToService.removeRelated({ howToId: values.id, relatedId });
|
|
263
|
+
}
|
|
264
|
+
initialRelatedIds.current = Array.from(desired);
|
|
265
|
+
|
|
266
|
+
return saved;
|
|
205
267
|
}}
|
|
206
268
|
onReset={() => {
|
|
207
269
|
return getDefaultValues();
|
|
@@ -222,6 +284,28 @@ function HowToEditorInternal({
|
|
|
222
284
|
placeholder={t(`howto.fields.name.placeholder`)}
|
|
223
285
|
isRequired
|
|
224
286
|
/>
|
|
287
|
+
<FormSelect
|
|
288
|
+
form={form}
|
|
289
|
+
id="howToType"
|
|
290
|
+
name={t(`howto.fields.howToType.label`)}
|
|
291
|
+
placeholder={t(`howto.fields.howToType.placeholder`)}
|
|
292
|
+
values={HOW_TO_TYPES.map((m) => ({ id: m, text: t(`howto.howToType.${m}` as any) }))}
|
|
293
|
+
isRequired
|
|
294
|
+
/>
|
|
295
|
+
<FormInput
|
|
296
|
+
form={form}
|
|
297
|
+
id="slug"
|
|
298
|
+
name={t(`howto.fields.slug.label`)}
|
|
299
|
+
placeholder={t(`howto.fields.slug.placeholder`)}
|
|
300
|
+
isRequired
|
|
301
|
+
/>
|
|
302
|
+
<FormInput form={form} id="order" name={t(`howto.fields.order.label`)} type="number" />
|
|
303
|
+
<FormTextarea
|
|
304
|
+
form={form}
|
|
305
|
+
id="summary"
|
|
306
|
+
name={t(`howto.fields.summary.label`)}
|
|
307
|
+
placeholder={t(`howto.fields.summary.placeholder`)}
|
|
308
|
+
/>
|
|
225
309
|
<div className="space-y-2">
|
|
226
310
|
<Label>{t(`howto.fields.description.label`)}</Label>
|
|
227
311
|
<div className="max-h-80 overflow-y-auto rounded-md border">
|
|
@@ -260,6 +344,26 @@ function HowToEditorInternal({
|
|
|
260
344
|
</Button>
|
|
261
345
|
</div>
|
|
262
346
|
</div>
|
|
347
|
+
<FormInput
|
|
348
|
+
form={form}
|
|
349
|
+
id="tags"
|
|
350
|
+
name={t(`howto.fields.tags.label`)}
|
|
351
|
+
placeholder={t(`howto.fields.tags.placeholder`)}
|
|
352
|
+
/>
|
|
353
|
+
<FormInput
|
|
354
|
+
form={form}
|
|
355
|
+
id="contextualKeys"
|
|
356
|
+
name={t(`howto.fields.contextualKeys.label`)}
|
|
357
|
+
placeholder={t(`howto.fields.contextualKeys.placeholder`)}
|
|
358
|
+
/>
|
|
359
|
+
<HowToMultiSelector
|
|
360
|
+
id="relatedArticles"
|
|
361
|
+
form={form}
|
|
362
|
+
currentHowTo={howTo}
|
|
363
|
+
label={t(`howto.fields.relatedArticles.label`)}
|
|
364
|
+
placeholder={t(`howto.fields.relatedArticles.placeholder`)}
|
|
365
|
+
/>
|
|
366
|
+
<FormCheckbox form={form} id="draft" name={t(`howto.fields.draft.label`)} />
|
|
263
367
|
</div>
|
|
264
368
|
</EditorSheet>
|
|
265
369
|
);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { beforeAll, describe, it, expect } from "vitest";
|
|
2
|
+
import { ApiRequestDataTypeInterface } from "../../../core/interfaces/ApiRequestDataTypeInterface";
|
|
3
|
+
import { ModuleRegistry } from "../../../core/registry/ModuleRegistry";
|
|
4
|
+
import { HowTo } from "./HowTo";
|
|
5
|
+
|
|
6
|
+
// createJsonApi reads Modules.HowTo.name (resolved lazily via ModuleRegistry),
|
|
7
|
+
// so the module must be registered before the model serialises — mirrors the
|
|
8
|
+
// AssistantMessage.spec setup convention in this package.
|
|
9
|
+
const howToModule: ApiRequestDataTypeInterface = {
|
|
10
|
+
name: "howtos",
|
|
11
|
+
model: HowTo,
|
|
12
|
+
} as any;
|
|
13
|
+
|
|
14
|
+
beforeAll(() => {
|
|
15
|
+
ModuleRegistry.register("HowTo", howToModule);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("HowTo.createJsonApi", () => {
|
|
19
|
+
it("serialises the new attributes", () => {
|
|
20
|
+
const res = new HowTo().createJsonApi({
|
|
21
|
+
id: "11111111-1111-4111-8111-111111111111",
|
|
22
|
+
name: "Add an NPC",
|
|
23
|
+
authorId: "",
|
|
24
|
+
description: [{ type: "paragraph", content: [] }],
|
|
25
|
+
howToType: "how-to",
|
|
26
|
+
slug: "add-an-npc",
|
|
27
|
+
order: 30,
|
|
28
|
+
summary: "Create a Non-Player Character.",
|
|
29
|
+
tags: ["world"],
|
|
30
|
+
contextualKeys: ["npc.editor"],
|
|
31
|
+
draft: false,
|
|
32
|
+
});
|
|
33
|
+
const a = res.data.attributes;
|
|
34
|
+
expect(a.howToType).toBe("how-to");
|
|
35
|
+
expect(a.slug).toBe("add-an-npc");
|
|
36
|
+
expect(a.order).toBe(30);
|
|
37
|
+
expect(a.summary).toBe("Create a Non-Player Character.");
|
|
38
|
+
expect(a.tags).toEqual(["world"]);
|
|
39
|
+
expect(a.contextualKeys).toEqual(["npc.editor"]);
|
|
40
|
+
expect(a.draft).toBe(false);
|
|
41
|
+
expect(typeof a.description).toBe("string"); // existing: BlockNote JSON stringified
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -5,6 +5,13 @@ import { HowToInput, HowToInterface } from "./HowToInterface";
|
|
|
5
5
|
export class HowTo extends Content implements HowToInterface {
|
|
6
6
|
private _description?: any;
|
|
7
7
|
private _pages?: string;
|
|
8
|
+
private _howToType?: string;
|
|
9
|
+
private _slug?: string;
|
|
10
|
+
private _order?: number;
|
|
11
|
+
private _summary?: string;
|
|
12
|
+
private _tags?: string[];
|
|
13
|
+
private _contextualKeys?: string[];
|
|
14
|
+
private _draft?: boolean;
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
17
|
* Parse pages from backend JSON string (handles legacy single string + JSON array)
|
|
@@ -36,13 +43,60 @@ export class HowTo extends Content implements HowToInterface {
|
|
|
36
43
|
return this._pages;
|
|
37
44
|
}
|
|
38
45
|
|
|
46
|
+
get howToType(): string | undefined {
|
|
47
|
+
return this._howToType;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get slug(): string | undefined {
|
|
51
|
+
return this._slug;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get order(): number | undefined {
|
|
55
|
+
return this._order;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get summary(): string | undefined {
|
|
59
|
+
return this._summary;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get tags(): string[] {
|
|
63
|
+
return this._tags ?? [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get contextualKeys(): string[] {
|
|
67
|
+
return this._contextualKeys ?? [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get draft(): boolean {
|
|
71
|
+
return this._draft ?? false;
|
|
72
|
+
}
|
|
73
|
+
|
|
39
74
|
rehydrate(data: JsonApiHydratedDataInterface): this {
|
|
40
75
|
super.rehydrate(data);
|
|
41
76
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
77
|
+
const rawDescription = data.jsonApi.attributes.description;
|
|
78
|
+
if (rawDescription === undefined || rawDescription === null || rawDescription === "") {
|
|
79
|
+
this._description = undefined;
|
|
80
|
+
} else if (typeof rawDescription === "string") {
|
|
81
|
+
try {
|
|
82
|
+
this._description = JSON.parse(rawDescription);
|
|
83
|
+
} catch {
|
|
84
|
+
// Help-content HowTos store plain-text summaries; admin-created HowTos
|
|
85
|
+
// store JSON-stringified BlockNote docs. Fall back to the raw string
|
|
86
|
+
// rather than crashing rehydration for the plain-text case.
|
|
87
|
+
this._description = rawDescription;
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
this._description = rawDescription;
|
|
91
|
+
}
|
|
45
92
|
this._pages = data.jsonApi.attributes.pages;
|
|
93
|
+
this._howToType = data.jsonApi.attributes.howToType;
|
|
94
|
+
this._slug = data.jsonApi.attributes.slug;
|
|
95
|
+
this._order = data.jsonApi.attributes.order;
|
|
96
|
+
this._summary = data.jsonApi.attributes.summary;
|
|
97
|
+
this._tags = data.jsonApi.attributes.tags ?? [];
|
|
98
|
+
this._contextualKeys = data.jsonApi.attributes.contextualKeys ?? [];
|
|
99
|
+
this._draft = data.jsonApi.attributes.draft ?? false;
|
|
46
100
|
|
|
47
101
|
return this;
|
|
48
102
|
}
|
|
@@ -63,6 +117,13 @@ export class HowTo extends Content implements HowToInterface {
|
|
|
63
117
|
|
|
64
118
|
if (data.description !== undefined) response.data.attributes.description = JSON.stringify(data.description);
|
|
65
119
|
if (data.pages !== undefined) response.data.attributes.pages = data.pages;
|
|
120
|
+
if (data.howToType !== undefined) response.data.attributes.howToType = data.howToType;
|
|
121
|
+
if (data.slug !== undefined) response.data.attributes.slug = data.slug;
|
|
122
|
+
if (data.order !== undefined) response.data.attributes.order = data.order;
|
|
123
|
+
if (data.summary !== undefined) response.data.attributes.summary = data.summary;
|
|
124
|
+
if (data.tags !== undefined) response.data.attributes.tags = data.tags;
|
|
125
|
+
if (data.contextualKeys !== undefined) response.data.attributes.contextualKeys = data.contextualKeys;
|
|
126
|
+
if (data.draft !== undefined) response.data.attributes.draft = data.draft;
|
|
66
127
|
|
|
67
128
|
return response;
|
|
68
129
|
}
|
|
@@ -3,9 +3,23 @@ import { ContentInput, ContentInterface } from "../../content/data/content.inter
|
|
|
3
3
|
export type HowToInput = ContentInput & {
|
|
4
4
|
description?: any;
|
|
5
5
|
pages?: string | undefined | null;
|
|
6
|
+
howToType?: string;
|
|
7
|
+
slug?: string;
|
|
8
|
+
order?: number;
|
|
9
|
+
summary?: string;
|
|
10
|
+
tags?: string[];
|
|
11
|
+
contextualKeys?: string[];
|
|
12
|
+
draft?: boolean;
|
|
6
13
|
};
|
|
7
14
|
|
|
8
15
|
export interface HowToInterface extends ContentInterface {
|
|
9
16
|
get description(): any;
|
|
10
17
|
get pages(): string | undefined;
|
|
18
|
+
get howToType(): string | undefined;
|
|
19
|
+
get slug(): string | undefined;
|
|
20
|
+
get order(): number | undefined;
|
|
21
|
+
get summary(): string | undefined;
|
|
22
|
+
get tags(): string[];
|
|
23
|
+
get contextualKeys(): string[];
|
|
24
|
+
get draft(): boolean;
|
|
11
25
|
}
|
|
@@ -58,4 +58,65 @@ export class HowToService extends AbstractService {
|
|
|
58
58
|
endpoint: new EndpointCreator({ endpoint: Modules.HowTo, id: params.howToId }).generate(),
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
+
|
|
62
|
+
static async findPublished(params: { howToType?: string } = {}): Promise<HowToInterface[]> {
|
|
63
|
+
const endpoint = new EndpointCreator({ endpoint: `public/${Modules.HowTo.name}` });
|
|
64
|
+
if (params.howToType) endpoint.addAdditionalParam("type", params.howToType);
|
|
65
|
+
return this.callApi({
|
|
66
|
+
type: Modules.HowTo,
|
|
67
|
+
method: HttpMethod.GET,
|
|
68
|
+
endpoint: endpoint.generate(),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static async findPublishedArticle(params: { howToType: string; slug: string }): Promise<HowToInterface> {
|
|
73
|
+
return this.callApi<HowToInterface>({
|
|
74
|
+
type: Modules.HowTo,
|
|
75
|
+
method: HttpMethod.GET,
|
|
76
|
+
endpoint: new EndpointCreator({
|
|
77
|
+
endpoint: `public/${Modules.HowTo.name}`,
|
|
78
|
+
id: params.howToType,
|
|
79
|
+
childEndpoint: params.slug,
|
|
80
|
+
}).generate(),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static async findRelated(params: { howToType: string; slug: string }): Promise<HowToInterface[]> {
|
|
85
|
+
return this.callApi({
|
|
86
|
+
type: Modules.HowTo,
|
|
87
|
+
method: HttpMethod.GET,
|
|
88
|
+
endpoint: new EndpointCreator({
|
|
89
|
+
endpoint: `public/${Modules.HowTo.name}`,
|
|
90
|
+
id: params.howToType,
|
|
91
|
+
childEndpoint: params.slug,
|
|
92
|
+
childId: "related",
|
|
93
|
+
}).generate(),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static async addRelated(params: { howToId: string; relatedId: string }): Promise<HowToInterface> {
|
|
98
|
+
return this.callApi<HowToInterface>({
|
|
99
|
+
type: Modules.HowTo,
|
|
100
|
+
method: HttpMethod.POST,
|
|
101
|
+
endpoint: new EndpointCreator({
|
|
102
|
+
endpoint: Modules.HowTo,
|
|
103
|
+
id: params.howToId,
|
|
104
|
+
childEndpoint: "related",
|
|
105
|
+
childId: params.relatedId,
|
|
106
|
+
}).generate(),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static async removeRelated(params: { howToId: string; relatedId: string }): Promise<void> {
|
|
111
|
+
await this.callApi({
|
|
112
|
+
type: Modules.HowTo,
|
|
113
|
+
method: HttpMethod.DELETE,
|
|
114
|
+
endpoint: new EndpointCreator({
|
|
115
|
+
endpoint: Modules.HowTo,
|
|
116
|
+
id: params.howToId,
|
|
117
|
+
childEndpoint: "related",
|
|
118
|
+
childId: params.relatedId,
|
|
119
|
+
}).generate(),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
61
122
|
}
|
package/src/index.ts
CHANGED
|
@@ -57,3 +57,7 @@ export * from "./features/rbac/rbac.module";
|
|
|
57
57
|
// Assistant + AssistantMessage feature data
|
|
58
58
|
export * from "./features/assistant/data";
|
|
59
59
|
export * from "./features/assistant-message/data";
|
|
60
|
+
|
|
61
|
+
// Help feature: NOT re-exported from the main barrel. Consumers must import from
|
|
62
|
+
// the dedicated `@carlonicora/nextjs-jsonapi/help` subpath to keep the help-feature
|
|
63
|
+
// surface (client contexts + server-only utilities) out of the main client bundle.
|