@endora-commerce/mod-blog 0.100.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.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/admin/api/blog-client.d.ts +70 -0
  4. package/dist/admin/api/blog-client.d.ts.map +1 -0
  5. package/dist/admin/api/blog-client.js +137 -0
  6. package/dist/admin/api/blog-client.js.map +1 -0
  7. package/dist/admin/components/CategoryTreeNode.d.ts +25 -0
  8. package/dist/admin/components/CategoryTreeNode.d.ts.map +1 -0
  9. package/dist/admin/components/CategoryTreeNode.js +14 -0
  10. package/dist/admin/components/CategoryTreeNode.js.map +1 -0
  11. package/dist/admin/components/PostStatusBadge.d.ts +7 -0
  12. package/dist/admin/components/PostStatusBadge.d.ts.map +1 -0
  13. package/dist/admin/components/PostStatusBadge.js +16 -0
  14. package/dist/admin/components/PostStatusBadge.js.map +1 -0
  15. package/dist/admin/components/RelatedPostsPicker.d.ts +11 -0
  16. package/dist/admin/components/RelatedPostsPicker.d.ts.map +1 -0
  17. package/dist/admin/components/RelatedPostsPicker.js +77 -0
  18. package/dist/admin/components/RelatedPostsPicker.js.map +1 -0
  19. package/dist/admin/components/RelatedProductsPicker.d.ts +9 -0
  20. package/dist/admin/components/RelatedProductsPicker.d.ts.map +1 -0
  21. package/dist/admin/components/RelatedProductsPicker.js +66 -0
  22. package/dist/admin/components/RelatedProductsPicker.js.map +1 -0
  23. package/dist/admin/components/TagPicker.d.ts +9 -0
  24. package/dist/admin/components/TagPicker.d.ts.map +1 -0
  25. package/dist/admin/components/TagPicker.js +66 -0
  26. package/dist/admin/components/TagPicker.js.map +1 -0
  27. package/dist/admin/index.d.ts +33 -0
  28. package/dist/admin/index.d.ts.map +1 -0
  29. package/dist/admin/index.js +114 -0
  30. package/dist/admin/index.js.map +1 -0
  31. package/dist/admin/pages/BlogCategoryEditor.d.ts +9 -0
  32. package/dist/admin/pages/BlogCategoryEditor.d.ts.map +1 -0
  33. package/dist/admin/pages/BlogCategoryEditor.js +196 -0
  34. package/dist/admin/pages/BlogCategoryEditor.js.map +1 -0
  35. package/dist/admin/pages/BlogCategoryTreePage.d.ts +9 -0
  36. package/dist/admin/pages/BlogCategoryTreePage.d.ts.map +1 -0
  37. package/dist/admin/pages/BlogCategoryTreePage.js +122 -0
  38. package/dist/admin/pages/BlogCategoryTreePage.js.map +1 -0
  39. package/dist/admin/pages/BlogPostEditor.d.ts +9 -0
  40. package/dist/admin/pages/BlogPostEditor.d.ts.map +1 -0
  41. package/dist/admin/pages/BlogPostEditor.js +314 -0
  42. package/dist/admin/pages/BlogPostEditor.js.map +1 -0
  43. package/dist/admin/pages/BlogPostListPage.d.ts +9 -0
  44. package/dist/admin/pages/BlogPostListPage.d.ts.map +1 -0
  45. package/dist/admin/pages/BlogPostListPage.js +75 -0
  46. package/dist/admin/pages/BlogPostListPage.js.map +1 -0
  47. package/dist/admin/pages/BlogTagListPage.d.ts +9 -0
  48. package/dist/admin/pages/BlogTagListPage.d.ts.map +1 -0
  49. package/dist/admin/pages/BlogTagListPage.js +145 -0
  50. package/dist/admin/pages/BlogTagListPage.js.map +1 -0
  51. package/dist/backend/entities/blog-category-language.entity.d.ts +11 -0
  52. package/dist/backend/entities/blog-category-language.entity.d.ts.map +1 -0
  53. package/dist/backend/entities/blog-category-language.entity.js +35 -0
  54. package/dist/backend/entities/blog-category-language.entity.js.map +1 -0
  55. package/dist/backend/entities/blog-category-sales-channel.entity.d.ts +17 -0
  56. package/dist/backend/entities/blog-category-sales-channel.entity.d.ts.map +1 -0
  57. package/dist/backend/entities/blog-category-sales-channel.entity.js +48 -0
  58. package/dist/backend/entities/blog-category-sales-channel.entity.js.map +1 -0
  59. package/dist/backend/entities/blog-category.entity.d.ts +32 -0
  60. package/dist/backend/entities/blog-category.entity.d.ts.map +1 -0
  61. package/dist/backend/entities/blog-category.entity.js +113 -0
  62. package/dist/backend/entities/blog-category.entity.js.map +1 -0
  63. package/dist/backend/entities/blog-post-category.entity.d.ts +10 -0
  64. package/dist/backend/entities/blog-post-category.entity.d.ts.map +1 -0
  65. package/dist/backend/entities/blog-post-category.entity.js +34 -0
  66. package/dist/backend/entities/blog-post-category.entity.js.map +1 -0
  67. package/dist/backend/entities/blog-post-language.entity.d.ts +9 -0
  68. package/dist/backend/entities/blog-post-language.entity.d.ts.map +1 -0
  69. package/dist/backend/entities/blog-post-language.entity.js +33 -0
  70. package/dist/backend/entities/blog-post-language.entity.js.map +1 -0
  71. package/dist/backend/entities/blog-post-related-post.entity.d.ts +15 -0
  72. package/dist/backend/entities/blog-post-related-post.entity.d.ts.map +1 -0
  73. package/dist/backend/entities/blog-post-related-post.entity.js +42 -0
  74. package/dist/backend/entities/blog-post-related-post.entity.js.map +1 -0
  75. package/dist/backend/entities/blog-post-related-product.entity.d.ts +14 -0
  76. package/dist/backend/entities/blog-post-related-product.entity.d.ts.map +1 -0
  77. package/dist/backend/entities/blog-post-related-product.entity.js +41 -0
  78. package/dist/backend/entities/blog-post-related-product.entity.js.map +1 -0
  79. package/dist/backend/entities/blog-post-sales-channel.entity.d.ts +16 -0
  80. package/dist/backend/entities/blog-post-sales-channel.entity.d.ts.map +1 -0
  81. package/dist/backend/entities/blog-post-sales-channel.entity.js +47 -0
  82. package/dist/backend/entities/blog-post-sales-channel.entity.js.map +1 -0
  83. package/dist/backend/entities/blog-post-tag.entity.d.ts +14 -0
  84. package/dist/backend/entities/blog-post-tag.entity.d.ts.map +1 -0
  85. package/dist/backend/entities/blog-post-tag.entity.js +41 -0
  86. package/dist/backend/entities/blog-post-tag.entity.js.map +1 -0
  87. package/dist/backend/entities/blog-post.entity.d.ts +31 -0
  88. package/dist/backend/entities/blog-post.entity.d.ts.map +1 -0
  89. package/dist/backend/entities/blog-post.entity.js +106 -0
  90. package/dist/backend/entities/blog-post.entity.js.map +1 -0
  91. package/dist/backend/entities/blog-tag.entity.d.ts +18 -0
  92. package/dist/backend/entities/blog-tag.entity.d.ts.map +1 -0
  93. package/dist/backend/entities/blog-tag.entity.js +66 -0
  94. package/dist/backend/entities/blog-tag.entity.js.map +1 -0
  95. package/dist/backend/index.d.ts +100 -0
  96. package/dist/backend/index.d.ts.map +1 -0
  97. package/dist/backend/index.js +182 -0
  98. package/dist/backend/index.js.map +1 -0
  99. package/dist/backend/routes.admin.d.ts +12 -0
  100. package/dist/backend/routes.admin.d.ts.map +1 -0
  101. package/dist/backend/routes.admin.js +148 -0
  102. package/dist/backend/routes.admin.js.map +1 -0
  103. package/dist/backend/routes.storefront.d.ts +6 -0
  104. package/dist/backend/routes.storefront.d.ts.map +1 -0
  105. package/dist/backend/routes.storefront.js +72 -0
  106. package/dist/backend/routes.storefront.js.map +1 -0
  107. package/dist/backend/services/blog-asset-references.d.ts +4 -0
  108. package/dist/backend/services/blog-asset-references.d.ts.map +1 -0
  109. package/dist/backend/services/blog-asset-references.js +79 -0
  110. package/dist/backend/services/blog-asset-references.js.map +1 -0
  111. package/dist/backend/services/blog-cache.d.ts +55 -0
  112. package/dist/backend/services/blog-cache.d.ts.map +1 -0
  113. package/dist/backend/services/blog-cache.js +111 -0
  114. package/dist/backend/services/blog-cache.js.map +1 -0
  115. package/dist/backend/services/blog-category-service.d.ts +53 -0
  116. package/dist/backend/services/blog-category-service.d.ts.map +1 -0
  117. package/dist/backend/services/blog-category-service.js +446 -0
  118. package/dist/backend/services/blog-category-service.js.map +1 -0
  119. package/dist/backend/services/blog-language-reference.d.ts +18 -0
  120. package/dist/backend/services/blog-language-reference.d.ts.map +1 -0
  121. package/dist/backend/services/blog-language-reference.js +43 -0
  122. package/dist/backend/services/blog-language-reference.js.map +1 -0
  123. package/dist/backend/services/blog-post-service.d.ts +79 -0
  124. package/dist/backend/services/blog-post-service.d.ts.map +1 -0
  125. package/dist/backend/services/blog-post-service.js +539 -0
  126. package/dist/backend/services/blog-post-service.js.map +1 -0
  127. package/dist/backend/services/blog-settings-resolver.d.ts +36 -0
  128. package/dist/backend/services/blog-settings-resolver.d.ts.map +1 -0
  129. package/dist/backend/services/blog-settings-resolver.js +68 -0
  130. package/dist/backend/services/blog-settings-resolver.js.map +1 -0
  131. package/dist/backend/services/blog-slug-collision.d.ts +35 -0
  132. package/dist/backend/services/blog-slug-collision.d.ts.map +1 -0
  133. package/dist/backend/services/blog-slug-collision.js +84 -0
  134. package/dist/backend/services/blog-slug-collision.js.map +1 -0
  135. package/dist/backend/services/blog-storefront-resolver.d.ts +107 -0
  136. package/dist/backend/services/blog-storefront-resolver.d.ts.map +1 -0
  137. package/dist/backend/services/blog-storefront-resolver.js +492 -0
  138. package/dist/backend/services/blog-storefront-resolver.js.map +1 -0
  139. package/dist/backend/services/blog-tag-service.d.ts +45 -0
  140. package/dist/backend/services/blog-tag-service.d.ts.map +1 -0
  141. package/dist/backend/services/blog-tag-service.js +198 -0
  142. package/dist/backend/services/blog-tag-service.js.map +1 -0
  143. package/dist/backend/services/seed-default-category.d.ts +25 -0
  144. package/dist/backend/services/seed-default-category.d.ts.map +1 -0
  145. package/dist/backend/services/seed-default-category.js +48 -0
  146. package/dist/backend/services/seed-default-category.js.map +1 -0
  147. package/dist/backend/services/seed-roles.d.ts +41 -0
  148. package/dist/backend/services/seed-roles.d.ts.map +1 -0
  149. package/dist/backend/services/seed-roles.js +98 -0
  150. package/dist/backend/services/seed-roles.js.map +1 -0
  151. package/dist/manifest.d.ts +226 -0
  152. package/dist/manifest.d.ts.map +1 -0
  153. package/dist/manifest.js +236 -0
  154. package/dist/manifest.js.map +1 -0
  155. package/dist/migrations/20260506T081055_blog_init.d.ts +33 -0
  156. package/dist/migrations/20260506T081055_blog_init.d.ts.map +1 -0
  157. package/dist/migrations/20260506T081055_blog_init.js +260 -0
  158. package/dist/migrations/20260506T081055_blog_init.js.map +1 -0
  159. package/dist/migrations/20260903T101744_blog_namespace_block_names.d.ts +6 -0
  160. package/dist/migrations/20260903T101744_blog_namespace_block_names.d.ts.map +1 -0
  161. package/dist/migrations/20260903T101744_blog_namespace_block_names.js +58 -0
  162. package/dist/migrations/20260903T101744_blog_namespace_block_names.js.map +1 -0
  163. package/dist/migrations/index.d.ts +34 -0
  164. package/dist/migrations/index.d.ts.map +1 -0
  165. package/dist/migrations/index.js +37 -0
  166. package/dist/migrations/index.js.map +1 -0
  167. package/docs/blog/index.md +199 -0
  168. package/i18n/en.json +166 -0
  169. package/i18n/pl.json +166 -0
  170. package/package.json +108 -0
  171. package/tailwind.css +14 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TagPicker.d.ts","sourceRoot":"","sources":["../../../src/admin/components/TagPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAYzE,UAAU,cAAc;IACtB,kDAAkD;IAClD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACpC;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,cAAc,GAAG,SAAS,CA2JxE"}
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { Alert, AlertDescription, Badge, Button, Input, Label } from '@endora-commerce/admin-kit/ui';
4
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
5
+ import { blogClient } from '../api/blog-client.js';
6
+ import { normalize } from '@endora-commerce/admin-kit/lib';
7
+ function pickName(name, fallback) {
8
+ if (!name)
9
+ return fallback;
10
+ return name['en-US'] ?? Object.values(name)[0] ?? fallback;
11
+ }
12
+ export function TagPicker({ value, onChange }) {
13
+ const t = useTranslation('blog');
14
+ const [allTags, setAllTags] = useState([]);
15
+ const [q, setQ] = useState('');
16
+ const [loading, setLoading] = useState(true);
17
+ const [error, setError] = useState(null);
18
+ useEffect(() => {
19
+ let live = true;
20
+ blogClient
21
+ .listTags({ perPage: 100 })
22
+ .then((res) => {
23
+ if (!live)
24
+ return;
25
+ setAllTags(res.data);
26
+ setLoading(false);
27
+ })
28
+ .catch((err) => {
29
+ if (!live)
30
+ return;
31
+ setError(err instanceof Error ? err.message : String(err));
32
+ setLoading(false);
33
+ });
34
+ return () => {
35
+ live = false;
36
+ };
37
+ }, []);
38
+ const tagsById = new Map(allTags.map((t) => [t.id, t]));
39
+ const needle = normalize(q);
40
+ const candidates = allTags.filter((tag) => {
41
+ if (value.includes(tag.id))
42
+ return false;
43
+ if (needle === '')
44
+ return true;
45
+ const haystack = `${normalize(pickName(tag.name, tag.code))} ${normalize(tag.code)}`;
46
+ return haystack.includes(needle);
47
+ });
48
+ const attach = useCallback((id) => onChange([...value, id]), [onChange, value]);
49
+ const detach = useCallback((id) => onChange(value.filter((existing) => existing !== id)), [onChange, value]);
50
+ const move = useCallback((id, dir) => {
51
+ const idx = value.indexOf(id);
52
+ if (idx < 0)
53
+ return;
54
+ const next = dir === 'up' ? idx - 1 : idx + 1;
55
+ if (next < 0 || next >= value.length)
56
+ return;
57
+ const out = [...value];
58
+ [out[idx], out[next]] = [out[next], out[idx]];
59
+ onChange(out);
60
+ }, [onChange, value]);
61
+ return (_jsxs("div", { className: "space-y-3", children: [error ? (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })) : null, _jsxs("div", { children: [_jsx(Label, { children: t('tagPicker.title', { count: value.length }) }), value.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('tagPicker.empty') })) : (_jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: value.map((id, idx) => {
62
+ const tag = tagsById.get(id);
63
+ return (_jsxs("span", { className: "inline-flex items-center gap-1 rounded border px-2 py-1 text-xs", children: [_jsx(Badge, { variant: "outline", className: "font-mono text-[10px]", children: tag?.code ?? id }), _jsx("span", { children: tag ? pickName(tag.name, tag.code) : t('common.unknown') }), _jsx(Button, { type: "button", variant: "outline", size: "sm", className: "ml-1 h-6 w-6 p-0", disabled: idx === 0, onClick: () => move(id, 'up'), "aria-label": t('tagPicker.moveUp'), children: "\u2191" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", className: "h-6 w-6 p-0", disabled: idx === value.length - 1, onClick: () => move(id, 'down'), "aria-label": t('tagPicker.moveDown'), children: "\u2193" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", className: "h-6 px-2", onClick: () => detach(id), children: "\u00D7" })] }, id));
64
+ }) }))] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "tag-picker-search", children: t('tagPicker.add') }), _jsx(Input, { id: "tag-picker-search", value: q, onChange: (event) => setQ(event.target.value), placeholder: t('tagPicker.searchPlaceholder') }), loading ? (_jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: t('tagPicker.loading') })) : candidates.length === 0 ? (_jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: q ? t('tagPicker.noMatches') : t('tagPicker.noCandidates') })) : (_jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: candidates.slice(0, 25).map((tag) => (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => attach(tag.id), children: [_jsx("span", { className: "mr-2 font-mono text-[10px]", children: tag.code }), pickName(tag.name, tag.code)] }, tag.id))) }))] })] }));
65
+ }
66
+ //# sourceMappingURL=TagPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TagPicker.js","sourceRoot":"","sources":["../../../src/admin/components/TagPicker.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `blog`'s admin surface — seven routes and three sidebar entries (feature 091,
3
+ * Phase 4 batch 16).
4
+ *
5
+ * **This is one of the two modules that close the drain.** `blog` and `cms` are
6
+ * the last owners in `backend/scripts/ledgers/admin-registrations.ts`, so the
7
+ * eighteen routes and seven nav entries they take out of `admin/src/App.tsx`
8
+ * and `admin/src/components/AppShell.tsx` are the last module-owned
9
+ * registrations those two files hold — SC-007.
10
+ *
11
+ * **What it drains: two keys, re-keyed rather than retired, and the difference
12
+ * is the batch's one judgement.** `BlogPostEditor` and `BlogCategoryEditor`
13
+ * each render `cms`' `PageBuilderEditor`; those are the two entries in
14
+ * `backend/scripts/ledgers/cross-module-imports/blog.ts`, and both endpoints of
15
+ * the reach are in this batch. `admin-component-contribution.md` Z1 decides the
16
+ * exit from the **signature** rather than from the schedule, and this batch
17
+ * re-read the file rather than inheriting the shard's answer: the props are
18
+ * `data` in and `onChange` back, so the **consumer** decides that the canvas
19
+ * appears — Z1 question 1, a published component, not a zone contribution. The
20
+ * publication is D-191's `./admin-ui` subpath on `cms`' own package, and Z11
21
+ * keeps the reach counted: a subpath is contract surface iff its emitted module
22
+ * exports no runtime binding, and `dist/admin-ui/index.js` exports a React
23
+ * component. So the coupling has a supported spelling and is not thereby
24
+ * uncounted, and the shard's own retiring condition — D-192's page-builder
25
+ * family growing a home for the whole builder — is unchanged and is not this
26
+ * merge request's.
27
+ *
28
+ * **This entry exports data and nothing else** (R2); every component is a
29
+ * dynamic-import factory (R6).
30
+ */
31
+ import type { AdminContributions } from '@endora-commerce/admin-kit/contributions';
32
+ export declare const contributions: AdminContributions;
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAoCnF,eAAO,MAAM,aAAa,EAAE,kBAiF3B,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * The code that opens every screen below.
3
+ *
4
+ * `packages/modules/blog/src/backend/routes.admin.ts` gates every `GET` with
5
+ * `blog.read` and every mutation with `blog.write`, and it is the read code
6
+ * that opens a screen — issue #232's rule, and the code all three hand-written
7
+ * sidebar rows carried.
8
+ *
9
+ * **The two `/new` routes take the write code instead**, which is batch 14's
10
+ * `/sales-channels/new` shape and is forced rather than chosen. This module's
11
+ * `new-post` palette action advertises `/blog/posts/new` under `blog.write`;
12
+ * declaring that route on the read code would make the palette advertise a
13
+ * screen the advertised code cannot open — Principle XVI item 2's exact
14
+ * prohibition, and a **regression** rather than a status quo, because the route
15
+ * is ungated in `App.tsx` today and a `blog.write` operator reaches it. A
16
+ * create form's only purpose is a write, so the write code is also the honest
17
+ * answer for what opens it.
18
+ *
19
+ * It costs the other half batch 14 paid: a link whose destination the
20
+ * operator's codes cannot open answers the admin's not-found page, which says
21
+ * nothing about permissions, so the create affordances are gated on the same
22
+ * code — the *New post* and *New category* buttons and the tree's *+ Child*,
23
+ * beside `cms`' three. Neither module gated anything client-side before this;
24
+ * `useAuth().hasPermission` is the kit's, and it is the predicate every other
25
+ * converted module already uses.
26
+ */
27
+ const READ_PERMISSION = 'blog.read';
28
+ /** The code the two create forms open on, and the one their saves enforce. */
29
+ const WRITE_PERMISSION = 'blog.write';
30
+ /** The module's landing route: the post list. */
31
+ const ROUTE_PATH = '/blog/posts';
32
+ export const contributions = {
33
+ routes: [
34
+ {
35
+ path: ROUTE_PATH,
36
+ component: () => import('./pages/BlogPostListPage.js'),
37
+ requiredPermission: READ_PERMISSION,
38
+ index: true,
39
+ },
40
+ {
41
+ // Declared before `/blog/posts/:id`, as `App.tsx` declared it. `<Routes>`
42
+ // ranks by specificity rather than by declaration order, so the static
43
+ // segment wins whatever the order; the ordering is kept anyway, so a
44
+ // reader does not have to know that to see why the editor route is safe
45
+ // here.
46
+ path: `${ROUTE_PATH}/new`,
47
+ component: () => import('./pages/BlogPostEditor.js'),
48
+ requiredPermission: WRITE_PERMISSION,
49
+ },
50
+ {
51
+ path: `${ROUTE_PATH}/:id`,
52
+ component: () => import('./pages/BlogPostEditor.js'),
53
+ requiredPermission: READ_PERMISSION,
54
+ },
55
+ {
56
+ path: '/blog/categories',
57
+ component: () => import('./pages/BlogCategoryTreePage.js'),
58
+ requiredPermission: READ_PERMISSION,
59
+ },
60
+ {
61
+ path: '/blog/categories/new',
62
+ component: () => import('./pages/BlogCategoryEditor.js'),
63
+ requiredPermission: WRITE_PERMISSION,
64
+ },
65
+ {
66
+ path: '/blog/categories/:id',
67
+ component: () => import('./pages/BlogCategoryEditor.js'),
68
+ requiredPermission: READ_PERMISSION,
69
+ },
70
+ {
71
+ path: '/blog/tags',
72
+ component: () => import('./pages/BlogTagListPage.js'),
73
+ requiredPermission: READ_PERMISSION,
74
+ },
75
+ ],
76
+ nav: [
77
+ {
78
+ to: ROUTE_PATH,
79
+ // Module-relative (R8), resolved in this module's own namespace out of
80
+ // `packages/modules/blog/i18n/`. It was `appShell.nav.blogPosts` in the
81
+ // shared `_i18n` bundle, one of the four shared files a module author had
82
+ // to edit.
83
+ labelKey: 'nav.blogPosts.label',
84
+ // The glyph `AppShell.tsx` rendered by hand, and already on
85
+ // `KnownIconNameSchema`.
86
+ icon: 'Newspaper',
87
+ section: 'content',
88
+ // Sixth, seventh and eighth of eight in the hand-written *Content* table,
89
+ // after the four `/cms/…` rows (100–400) and `megamenu`'s row, which has
90
+ // declared 500 since batch 8 with a comment saying it sits *"after the
91
+ // four `/cms/…` rows and before the three `/blog/…` ones"*. These three
92
+ // weights are what makes that comment true again.
93
+ weight: 600,
94
+ requiredPermission: READ_PERMISSION,
95
+ },
96
+ {
97
+ to: '/blog/categories',
98
+ labelKey: 'nav.blogCategories.label',
99
+ icon: 'Newspaper',
100
+ section: 'content',
101
+ weight: 700,
102
+ requiredPermission: READ_PERMISSION,
103
+ },
104
+ {
105
+ to: '/blog/tags',
106
+ labelKey: 'nav.blogTags.label',
107
+ icon: 'Newspaper',
108
+ section: 'content',
109
+ weight: 800,
110
+ requiredPermission: READ_PERMISSION,
111
+ },
112
+ ],
113
+ };
114
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC,8EAA8E;AAC9E,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAEtC,iDAAiD;AACjD,MAAM,UAAU,GAAG,aAAa,CAAC;AAEjC,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,MAAM,EAAE;QACN;YACE,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,6BAA6B,CAAC;YACtD,kBAAkB,EAAE,eAAe;YACnC,KAAK,EAAE,IAAI;SACZ;QACD;YACE,0EAA0E;YAC1E,uEAAuE;YACvE,qEAAqE;YACrE,wEAAwE;YACxE,QAAQ;YACR,IAAI,EAAE,GAAG,UAAU,MAAM;YACzB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,2BAA2B,CAAC;YACpD,kBAAkB,EAAE,gBAAgB;SACrC;QACD;YACE,IAAI,EAAE,GAAG,UAAU,MAAM;YACzB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,2BAA2B,CAAC;YACpD,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,IAAI,EAAE,kBAAkB;YACxB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,iCAAiC,CAAC;YAC1D,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,IAAI,EAAE,sBAAsB;YAC5B,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,+BAA+B,CAAC;YACxD,kBAAkB,EAAE,gBAAgB;SACrC;QACD;YACE,IAAI,EAAE,sBAAsB;YAC5B,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,+BAA+B,CAAC;YACxD,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,IAAI,EAAE,YAAY;YAClB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,4BAA4B,CAAC;YACrD,kBAAkB,EAAE,eAAe;SACpC;KACF;IACD,GAAG,EAAE;QACH;YACE,EAAE,EAAE,UAAU;YACd,uEAAuE;YACvE,wEAAwE;YACxE,0EAA0E;YAC1E,WAAW;YACX,QAAQ,EAAE,qBAAqB;YAC/B,4DAA4D;YAC5D,yBAAyB;YACzB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,SAAS;YAClB,0EAA0E;YAC1E,yEAAyE;YACzE,uEAAuE;YACvE,wEAAwE;YACxE,kDAAkD;YAClD,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,EAAE,EAAE,kBAAkB;YACtB,QAAQ,EAAE,0BAA0B;YACpC,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,eAAe;SACpC;QACD;YACE,EAAE,EAAE,YAAY;YAChB,QAAQ,EAAE,oBAAoB;YAC9B,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,eAAe;SACpC;KACF;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function BlogCategoryEditor(): ReactNode;
3
+ /**
4
+ * The registry loads a route component through a dynamic-import factory and
5
+ * reads its default export (feature 091, FR-013). The named export stays: it is
6
+ * the spelling this module's own code and its tests use.
7
+ */
8
+ export default BlogCategoryEditor;
9
+ //# sourceMappingURL=BlogCategoryEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogCategoryEditor.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/BlogCategoryEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA6ClF,wBAAgB,kBAAkB,IAAI,SAAS,CAkU9C;AAED;;;;GAIG;AACH,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,196 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
4
+ import { Alert, AlertDescription, Button, Card, CardContent, CardHeader, CardTitle, Input, Label, PageHeader } from '@endora-commerce/admin-kit/ui';
5
+ import { AssetFieldPicker, ContentLanguageTabs, ScopePicker } from '@endora-commerce/admin-kit/components';
6
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
7
+ import { PageBuilderEditor } from '@endora-commerce/mod-cms/admin-ui';
8
+ import { blogClient } from '../api/blog-client.js';
9
+ const blankForm = {
10
+ name: '',
11
+ slug: '',
12
+ enabled: true,
13
+ metaTitle: '',
14
+ metaDescription: '',
15
+ metaKeywords: '',
16
+ mainImageAssetId: '',
17
+ };
18
+ function descriptionFor(category, language) {
19
+ if (!category || !language)
20
+ return null;
21
+ const tree = category.description?.languages?.[language];
22
+ if (tree && typeof tree === 'object')
23
+ return tree;
24
+ return { root: { props: {} }, content: [] };
25
+ }
26
+ function pickValue(map, lang) {
27
+ if (!map)
28
+ return '';
29
+ return map[lang] ?? '';
30
+ }
31
+ export function BlogCategoryEditor() {
32
+ const t = useTranslation('blog');
33
+ const { id } = useParams();
34
+ const isNew = !id || id === 'new';
35
+ const [searchParams] = useSearchParams();
36
+ const initialParentId = searchParams.get('parentId');
37
+ const navigate = useNavigate();
38
+ const [category, setCategory] = useState(null);
39
+ const [parentId, setParentId] = useState(initialParentId);
40
+ const [form, setForm] = useState(blankForm);
41
+ const [scope, setScope] = useState({
42
+ salesChannelIds: [],
43
+ languages: ['en-US'],
44
+ });
45
+ const [activeLanguage, setActiveLanguage] = useState('en-US');
46
+ const [draftDescription, setDraftDescription] = useState(null);
47
+ const [error, setError] = useState(null);
48
+ const [info, setInfo] = useState(null);
49
+ const [saving, setSaving] = useState(false);
50
+ const reloadFormFromCategory = useCallback((loaded, language) => {
51
+ const lang = language ?? loaded.languages[0] ?? 'en-US';
52
+ setForm({
53
+ name: pickValue(loaded.name, lang) || pickValue(loaded.name, 'en-US'),
54
+ slug: loaded.slug,
55
+ enabled: loaded.enabled,
56
+ metaTitle: pickValue(loaded.metaTitle, lang),
57
+ metaDescription: pickValue(loaded.metaDescription, lang),
58
+ metaKeywords: pickValue(loaded.metaKeywords, lang),
59
+ mainImageAssetId: loaded.mainImageAssetId ?? '',
60
+ });
61
+ setParentId(loaded.parentId);
62
+ }, []);
63
+ const load = useCallback(async () => {
64
+ if (isNew || !id)
65
+ return;
66
+ setError(null);
67
+ const loaded = await blogClient.getCategory(id);
68
+ setCategory(loaded);
69
+ setScope({
70
+ salesChannelIds: loaded.salesChannelIds,
71
+ languages: loaded.languages,
72
+ });
73
+ const lang = loaded.languages[0] ?? 'en-US';
74
+ setActiveLanguage(lang);
75
+ reloadFormFromCategory(loaded, lang);
76
+ setDraftDescription(descriptionFor(loaded, lang));
77
+ }, [id, isNew, reloadFormFromCategory]);
78
+ useEffect(() => {
79
+ void load().catch((err) => setError(err instanceof Error ? err.message : String(err)));
80
+ }, [load]);
81
+ useEffect(() => {
82
+ if (category && activeLanguage) {
83
+ reloadFormFromCategory(category, activeLanguage);
84
+ setDraftDescription(descriptionFor(category, activeLanguage));
85
+ }
86
+ }, [category, activeLanguage, reloadFormFromCategory]);
87
+ const canSave = useMemo(() => {
88
+ return (form.name.trim().length > 0 &&
89
+ /^(?!tag$)[a-z0-9](?:[a-z0-9-]{0,158}[a-z0-9])?$/.test(form.slug) &&
90
+ scope.salesChannelIds.length > 0 &&
91
+ scope.languages.length > 0);
92
+ }, [form, scope]);
93
+ const onCreate = useCallback(async () => {
94
+ if (!canSave)
95
+ return;
96
+ setSaving(true);
97
+ setError(null);
98
+ setInfo(null);
99
+ const lang = activeLanguage ?? 'en-US';
100
+ try {
101
+ const created = await blogClient.createCategory({
102
+ parentId: parentId ?? null,
103
+ name: { [lang]: form.name },
104
+ slug: form.slug,
105
+ enabled: form.enabled,
106
+ salesChannelIds: scope.salesChannelIds,
107
+ languages: scope.languages,
108
+ mainImageAssetId: form.mainImageAssetId || null,
109
+ ...(form.metaTitle ? { metaTitle: { [lang]: form.metaTitle } } : {}),
110
+ ...(form.metaDescription ? { metaDescription: { [lang]: form.metaDescription } } : {}),
111
+ ...(form.metaKeywords ? { metaKeywords: { [lang]: form.metaKeywords } } : {}),
112
+ });
113
+ navigate(`/blog/categories/${created.id}`);
114
+ }
115
+ catch (err) {
116
+ setError(err instanceof Error ? err.message : String(err));
117
+ }
118
+ finally {
119
+ setSaving(false);
120
+ }
121
+ }, [activeLanguage, canSave, form, navigate, parentId, scope]);
122
+ const onSaveMetadata = useCallback(async () => {
123
+ if (!category || !canSave)
124
+ return;
125
+ setSaving(true);
126
+ setError(null);
127
+ setInfo(null);
128
+ const lang = activeLanguage ?? 'en-US';
129
+ try {
130
+ const updated = await blogClient.patchCategory(category.id, {
131
+ name: { ...category.name, [lang]: form.name },
132
+ slug: form.slug,
133
+ enabled: form.enabled,
134
+ salesChannelIds: scope.salesChannelIds,
135
+ languages: scope.languages,
136
+ mainImageAssetId: form.mainImageAssetId || null,
137
+ metaTitle: { ...(category.metaTitle ?? {}), [lang]: form.metaTitle },
138
+ metaDescription: {
139
+ ...(category.metaDescription ?? {}),
140
+ [lang]: form.metaDescription,
141
+ },
142
+ metaKeywords: {
143
+ ...(category.metaKeywords ?? {}),
144
+ [lang]: form.metaKeywords,
145
+ },
146
+ version: category.version,
147
+ });
148
+ setCategory(updated);
149
+ setInfo(t('messages.saved'));
150
+ }
151
+ catch (err) {
152
+ setError(err instanceof Error ? err.message : String(err));
153
+ }
154
+ finally {
155
+ setSaving(false);
156
+ }
157
+ }, [activeLanguage, canSave, category, form, scope]);
158
+ const onSaveDescription = useCallback(async () => {
159
+ if (!category || !activeLanguage || !draftDescription)
160
+ return;
161
+ setSaving(true);
162
+ setError(null);
163
+ setInfo(null);
164
+ try {
165
+ const nextLanguages = {
166
+ ...(category.description?.languages ?? {}),
167
+ [activeLanguage]: draftDescription,
168
+ };
169
+ const updated = await blogClient.putCategoryDescription(category.id, {
170
+ description: {
171
+ schema_version: category.description?.schema_version ?? 1,
172
+ languages: nextLanguages,
173
+ },
174
+ version: category.version,
175
+ });
176
+ setCategory(updated);
177
+ setInfo(t('messages.descriptionSaved'));
178
+ }
179
+ catch (err) {
180
+ setError(err instanceof Error ? err.message : String(err));
181
+ }
182
+ finally {
183
+ setSaving(false);
184
+ }
185
+ }, [activeLanguage, category, draftDescription]);
186
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(PageHeader, { title: isNew ? t('categoryEditor.title.new') : t('categoryEditor.title.edit'), description: category?.isSystem
187
+ ? t('categoryEditor.description.system')
188
+ : t('categoryEditor.description.default'), actions: _jsx(Button, { asChild: true, variant: "outline", children: _jsx(Link, { to: "/blog/categories", children: t('categoryEditor.backToTree') }) }) }), error ? (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })) : null, info ? (_jsx(Alert, { children: _jsx(AlertDescription, { children: info }) })) : null, _jsx(ScopePicker, { value: scope, onChange: setScope }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: t('sections.metadata') }) }), _jsxs(CardContent, { className: "grid gap-4", children: [_jsx(ContentLanguageTabs, { languages: scope.languages, activeLanguage: activeLanguage, onChange: setActiveLanguage }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "cat-name", children: t('fields.namePerLanguage') }), _jsx(Input, { id: "cat-name", value: form.name, onChange: (event) => setForm((f) => ({ ...f, name: event.target.value })), placeholder: t('categoryEditor.namePlaceholder') })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "cat-slug", children: t('fields.slug') }), _jsx(Input, { id: "cat-slug", value: form.slug, onChange: (event) => setForm((f) => ({ ...f, slug: event.target.value.toLowerCase() })), className: "font-mono", placeholder: t('categoryEditor.slugPlaceholder') })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "cat-meta-title", children: t('fields.metaTitlePerLanguage') }), _jsx(Input, { id: "cat-meta-title", value: form.metaTitle, onChange: (event) => setForm((f) => ({ ...f, metaTitle: event.target.value })) })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "cat-meta-description", children: t('fields.metaDescriptionPerLanguage') }), _jsx(Input, { id: "cat-meta-description", value: form.metaDescription, onChange: (event) => setForm((f) => ({ ...f, metaDescription: event.target.value })) })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "cat-meta-keywords", children: t('fields.metaKeywordsPerLanguage') }), _jsx(Input, { id: "cat-meta-keywords", value: form.metaKeywords, onChange: (event) => setForm((f) => ({ ...f, metaKeywords: event.target.value })) })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "cat-image", children: t('fields.mainImageAssetId') }), _jsx(AssetFieldPicker, { id: "cat-image", value: form.mainImageAssetId, onChange: (id) => setForm((f) => ({ ...f, mainImageAssetId: id })), acceptMimePrefix: "image/", allowUpload: true })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { id: "cat-enabled", type: "checkbox", checked: form.enabled, onChange: (event) => setForm((f) => ({ ...f, enabled: event.target.checked })) }), _jsx(Label, { htmlFor: "cat-enabled", children: t('fields.enabledStorefrontVisible') })] }), _jsx("div", { className: "flex justify-end gap-2", children: isNew ? (_jsx(Button, { type: "button", disabled: !canSave || saving, onClick: () => void onCreate(), children: saving ? t('common.saving') : t('common.create') })) : (_jsx(Button, { type: "button", disabled: !canSave || saving, onClick: () => void onSaveMetadata(), children: saving ? t('common.saving') : t('common.saveMetadata') })) })] })] }), !isNew && category && activeLanguage && draftDescription !== null ? (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between gap-2", children: [_jsx(CardTitle, { className: "text-base", children: t('categoryEditor.descriptionTitle', { language: activeLanguage }) }), _jsx(Button, { type: "button", size: "sm", disabled: saving, onClick: () => void onSaveDescription(), children: saving ? t('common.saving') : t('common.saveDescription') })] }), _jsx(CardContent, { children: _jsx(PageBuilderEditor, { data: draftDescription, onChange: setDraftDescription }) })] })) : null] }));
189
+ }
190
+ /**
191
+ * The registry loads a route component through a dynamic-import factory and
192
+ * reads its default export (feature 091, FR-013). The named export stays: it is
193
+ * the spelling this module's own code and its tests use.
194
+ */
195
+ export default BlogCategoryEditor;
196
+ //# sourceMappingURL=BlogCategoryEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogCategoryEditor.js","sourceRoot":"","sources":["../../../src/admin/pages/BlogCategoryEditor.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function BlogCategoryTreePage(): ReactNode;
3
+ /**
4
+ * The registry loads a route component through a dynamic-import factory and
5
+ * reads its default export (feature 091, FR-013). The named export stays: it is
6
+ * the spelling this module's own code and its tests use.
7
+ */
8
+ export default BlogCategoryTreePage;
9
+ //# sourceMappingURL=BlogCategoryTreePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogCategoryTreePage.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/BlogCategoryTreePage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA8BzE,wBAAgB,oBAAoB,IAAI,SAAS,CA0JhD;AAED;;;;GAIG;AACH,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,122 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { useNavigate } from 'react-router-dom';
4
+ import { Alert, AlertDescription, Button, Card, CardContent, CardHeader, CardTitle, PageHeader } from '@endora-commerce/admin-kit/ui';
5
+ import { useAuth } from '@endora-commerce/admin-kit/lib';
6
+ import { useTranslation } from '@endora-commerce/admin-kit/i18n';
7
+ import { blogClient } from '../api/blog-client.js';
8
+ import { CategoryTreeNode } from '../components/CategoryTreeNode.js';
9
+ function flatten(tree) {
10
+ const out = [];
11
+ const walk = (nodes, parentId, depth) => {
12
+ const total = nodes.length;
13
+ nodes.forEach((node, position) => {
14
+ out.push({ node, parentId, position, depth, siblingsTotal: total });
15
+ if (node.children.length > 0)
16
+ walk(node.children, node.id, depth + 1);
17
+ });
18
+ };
19
+ walk(tree, null, 0);
20
+ return out;
21
+ }
22
+ export function BlogCategoryTreePage() {
23
+ const t = useTranslation('blog');
24
+ const { hasPermission } = useAuth();
25
+ // The create route this screen links to takes the **write** code (feature
26
+ // 091, batch 16), so the button is gated on the same one: a link whose
27
+ // destination the operator's codes cannot open answers the admin's not-found
28
+ // page, which says nothing about permissions at all. That is batch 14's
29
+ // `/sales-channels/new` shape, applied to the five create entry points these
30
+ // two modules have.
31
+ const canWrite = hasPermission('blog.write');
32
+ const navigate = useNavigate();
33
+ const [tree, setTree] = useState([]);
34
+ const [loading, setLoading] = useState(true);
35
+ const [error, setError] = useState(null);
36
+ const [working, setWorking] = useState(false);
37
+ const load = useCallback(async () => {
38
+ setLoading(true);
39
+ setError(null);
40
+ try {
41
+ const out = await blogClient.getCategoryTree();
42
+ setTree(out.tree);
43
+ }
44
+ catch (err) {
45
+ setError(err instanceof Error ? err.message : String(err));
46
+ }
47
+ finally {
48
+ setLoading(false);
49
+ }
50
+ }, []);
51
+ useEffect(() => {
52
+ void load();
53
+ }, [load]);
54
+ const onMove = useCallback(async (target, direction, siblings) => {
55
+ const targetIdx = siblings.findIndex((s) => s.node.id === target.id);
56
+ if (targetIdx < 0)
57
+ return;
58
+ const swapIdx = direction === 'up' ? targetIdx - 1 : targetIdx + 1;
59
+ if (swapIdx < 0 || swapIdx >= siblings.length)
60
+ return;
61
+ // Two-move atomic swap inside the parent slot.
62
+ const moves = [
63
+ {
64
+ id: target.id,
65
+ parentId: siblings[targetIdx].parentId,
66
+ position: siblings[swapIdx].position,
67
+ },
68
+ {
69
+ id: siblings[swapIdx].node.id,
70
+ parentId: siblings[swapIdx].parentId,
71
+ position: siblings[targetIdx].position,
72
+ },
73
+ ];
74
+ setWorking(true);
75
+ setError(null);
76
+ try {
77
+ const out = await blogClient.applyCategoryTreeMoves(moves);
78
+ setTree(out.tree);
79
+ }
80
+ catch (err) {
81
+ setError(err instanceof Error ? err.message : String(err));
82
+ }
83
+ finally {
84
+ setWorking(false);
85
+ }
86
+ }, []);
87
+ const onDelete = useCallback(async (node) => {
88
+ if (!window.confirm(t('categoryTree.deleteConfirm', { slug: node.slug })))
89
+ return;
90
+ setWorking(true);
91
+ setError(null);
92
+ try {
93
+ await blogClient.deleteCategory(node.id, node.version);
94
+ await load();
95
+ }
96
+ catch (err) {
97
+ setError(err instanceof Error ? err.message : String(err));
98
+ }
99
+ finally {
100
+ setWorking(false);
101
+ }
102
+ }, [load]);
103
+ const onAddChild = useCallback((parentId) => {
104
+ navigate(`/blog/categories/new?parentId=${encodeURIComponent(parentId)}`);
105
+ }, [navigate]);
106
+ const flat = flatten(tree);
107
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(PageHeader, { title: t('categoryTree.title'), description: t('categoryTree.description'), actions: canWrite ? (_jsx(Button, { asChild: true, children: _jsx("a", { href: "/blog/categories/new", children: t('categoryTree.newCategory') }) })) : null }), error ? (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })) : null, _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: t('categoryTree.cardTitle') }) }), _jsx(CardContent, { children: loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('common.loading') })) : flat.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('categoryTree.empty') })) : (_jsx("div", { className: "rounded border", children: flat.map((entry) => {
108
+ // Build the same-parent siblings list once per render — needed
109
+ // by the up/down handlers to compute the swap target.
110
+ const siblings = flat.filter((s) => (s.parentId ?? null) === (entry.parentId ?? null) &&
111
+ s.depth === entry.depth);
112
+ const idx = siblings.findIndex((s) => s.node.id === entry.node.id);
113
+ return (_jsx(CategoryTreeNode, { node: entry.node, depth: entry.depth, canMoveUp: idx > 0 && !working, canMoveDown: idx < siblings.length - 1 && !working, onMoveUp: () => void onMove(entry.node, 'up', siblings), onMoveDown: () => void onMove(entry.node, 'down', siblings), onAddChild: onAddChild, canCreate: canWrite, onDelete: () => void onDelete(entry.node) }, entry.node.id));
114
+ }) })) })] })] }));
115
+ }
116
+ /**
117
+ * The registry loads a route component through a dynamic-import factory and
118
+ * reads its default export (feature 091, FR-013). The named export stays: it is
119
+ * the spelling this module's own code and its tests use.
120
+ */
121
+ export default BlogCategoryTreePage;
122
+ //# sourceMappingURL=BlogCategoryTreePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogCategoryTreePage.js","sourceRoot":"","sources":["../../../src/admin/pages/BlogCategoryTreePage.tsx"],"names":[],"mappings":"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"}