@carlonicora/nextjs-jsonapi 1.0.5 → 1.1.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 (111) hide show
  1. package/LICENSE +675 -0
  2. package/dist/{AbstractService-B2n_JdiC.d.mts → AbstractService-BsY6W3Ej.d.mts} +1 -1
  3. package/dist/{AbstractService-DtQTYovo.d.ts → AbstractService-wLid8dB0.d.ts} +1 -1
  4. package/dist/AuthComponent-hxOPs9o8.d.mts +11 -0
  5. package/dist/AuthComponent-hxOPs9o8.d.ts +11 -0
  6. package/dist/{BlockNoteEditor-BLVXQPXV.mjs → BlockNoteEditor-6TBRDBCF.mjs} +7 -7
  7. package/dist/{BlockNoteEditor-ZTDHULFT.js → BlockNoteEditor-BCPDRNLK.js} +17 -17
  8. package/dist/{BlockNoteEditor-ZTDHULFT.js.map → BlockNoteEditor-BCPDRNLK.js.map} +1 -1
  9. package/dist/{chunk-6GKHCVF6.js → chunk-32HM6MDD.js} +1 -1
  10. package/dist/chunk-32HM6MDD.js.map +1 -0
  11. package/dist/{chunk-A5DDIABK.js → chunk-EZK3H6EJ.js} +206 -206
  12. package/dist/{chunk-A5DDIABK.js.map → chunk-EZK3H6EJ.js.map} +1 -1
  13. package/dist/{chunk-WEC4YMOS.js → chunk-FPMPTR7S.js} +778 -590
  14. package/dist/chunk-FPMPTR7S.js.map +1 -0
  15. package/dist/{chunk-RZO2LOW4.js → chunk-FYRFMABS.js} +181 -183
  16. package/dist/chunk-FYRFMABS.js.map +1 -0
  17. package/dist/{chunk-DD3KISNB.mjs → chunk-HAG77QBV.mjs} +1 -1
  18. package/dist/chunk-HAG77QBV.mjs.map +1 -0
  19. package/dist/{chunk-2LM6LCJW.mjs → chunk-JLN6UWII.mjs} +41 -2
  20. package/dist/chunk-JLN6UWII.mjs.map +1 -0
  21. package/dist/{chunk-JC3WJK65.js → chunk-LJRD4SRV.js} +41 -2
  22. package/dist/chunk-LJRD4SRV.js.map +1 -0
  23. package/dist/{chunk-3APORDYP.mjs → chunk-LUHQILAI.mjs} +700 -512
  24. package/dist/chunk-LUHQILAI.mjs.map +1 -0
  25. package/dist/{chunk-7C5RAEBO.mjs → chunk-MA2L2PL2.mjs} +2 -4
  26. package/dist/chunk-MA2L2PL2.mjs.map +1 -0
  27. package/dist/{chunk-PYF2U6WG.mjs → chunk-NPPQNSXN.mjs} +11 -1
  28. package/dist/{chunk-PYF2U6WG.mjs.map → chunk-NPPQNSXN.mjs.map} +1 -1
  29. package/dist/{chunk-HMHGLXWC.js → chunk-WZJDLITG.js} +11 -1
  30. package/dist/chunk-WZJDLITG.js.map +1 -0
  31. package/dist/{chunk-IWFGEPAA.mjs → chunk-X53MF5ZN.mjs} +2 -2
  32. package/dist/client/index.d.mts +8 -4
  33. package/dist/client/index.d.ts +8 -4
  34. package/dist/client/index.js +9 -7
  35. package/dist/client/index.js.map +1 -1
  36. package/dist/client/index.mjs +8 -6
  37. package/dist/components/index.d.mts +20 -3
  38. package/dist/components/index.d.ts +20 -3
  39. package/dist/components/index.js +13 -7
  40. package/dist/components/index.js.map +1 -1
  41. package/dist/components/index.mjs +12 -6
  42. package/dist/{user.fields-CbdObSmS.d.mts → content.fields-cHPdM8GJ.d.mts} +10 -1
  43. package/dist/{user.fields-CbdObSmS.d.ts → content.fields-cHPdM8GJ.d.ts} +10 -1
  44. package/dist/{AuthComponent-m6Qp4Hz6.d.ts → content.interface-C_PGZMuy.d.ts} +1 -11
  45. package/dist/{AuthComponent-CPLvEerw.d.mts → content.interface-D_WS6CrB.d.mts} +1 -11
  46. package/dist/contexts/index.d.mts +2 -1
  47. package/dist/contexts/index.d.ts +2 -1
  48. package/dist/contexts/index.js +7 -7
  49. package/dist/contexts/index.mjs +6 -6
  50. package/dist/core/index.d.mts +6 -3
  51. package/dist/core/index.d.ts +6 -3
  52. package/dist/core/index.js +2 -2
  53. package/dist/core/index.mjs +1 -1
  54. package/dist/features/index.d.mts +7 -17
  55. package/dist/features/index.d.ts +7 -17
  56. package/dist/features/index.js +4 -4
  57. package/dist/features/index.mjs +3 -3
  58. package/dist/hooks/index.d.mts +4 -4
  59. package/dist/hooks/index.d.ts +4 -4
  60. package/dist/hooks/index.js +7 -7
  61. package/dist/hooks/index.mjs +6 -6
  62. package/dist/index.d.mts +4 -3
  63. package/dist/index.d.ts +4 -3
  64. package/dist/index.js +3 -3
  65. package/dist/index.mjs +2 -2
  66. package/dist/permissions/index.d.mts +3 -2
  67. package/dist/permissions/index.d.ts +3 -2
  68. package/dist/permissions/index.js +2 -2
  69. package/dist/permissions/index.mjs +1 -1
  70. package/dist/server/index.d.mts +2 -1
  71. package/dist/server/index.d.ts +2 -1
  72. package/dist/server/index.js +2 -2
  73. package/dist/server/index.mjs +1 -1
  74. package/dist/shadcnui/index.js +5 -3
  75. package/dist/shadcnui/index.js.map +1 -1
  76. package/dist/shadcnui/index.mjs +4 -2
  77. package/dist/{types-BUAlgqqh.d.ts → types-B2QRyqyK.d.ts} +1 -1
  78. package/dist/{types-iVdVY7ba.d.mts → types-CgvNmxTd.d.mts} +1 -1
  79. package/dist/{types-Bl61ob-7.d.mts → types-t2PyXhDu.d.mts} +4 -0
  80. package/dist/{types-Bl61ob-7.d.ts → types-t2PyXhDu.d.ts} +4 -0
  81. package/dist/utils/index.d.mts +27 -3
  82. package/dist/utils/index.d.ts +27 -3
  83. package/dist/utils/index.js +16 -2
  84. package/dist/utils/index.js.map +1 -1
  85. package/dist/utils/index.mjs +15 -1
  86. package/package.json +1 -1
  87. package/src/client/index.ts +3 -1
  88. package/src/components/index.ts +1 -0
  89. package/src/core/registry/ModuleRegistry.ts +16 -1
  90. package/src/features/content/components/index.ts +1 -0
  91. package/src/features/content/components/lists/ContentsList.tsx +67 -0
  92. package/src/features/content/components/lists/ContentsListById.tsx +30 -0
  93. package/src/features/content/components/lists/RelevantContentsList.tsx +30 -0
  94. package/src/features/content/components/lists/index.ts +3 -0
  95. package/src/features/content/data/content.fields.ts +0 -2
  96. package/src/features/content/hooks/index.ts +1 -0
  97. package/src/features/content/hooks/useContentTableStructure.tsx +132 -0
  98. package/src/permissions/types.ts +3 -0
  99. package/src/utils/icons.tsx +40 -0
  100. package/src/utils/index.ts +1 -0
  101. package/dist/chunk-2LM6LCJW.mjs.map +0 -1
  102. package/dist/chunk-3APORDYP.mjs.map +0 -1
  103. package/dist/chunk-6GKHCVF6.js.map +0 -1
  104. package/dist/chunk-7C5RAEBO.mjs.map +0 -1
  105. package/dist/chunk-DD3KISNB.mjs.map +0 -1
  106. package/dist/chunk-HMHGLXWC.js.map +0 -1
  107. package/dist/chunk-JC3WJK65.js.map +0 -1
  108. package/dist/chunk-RZO2LOW4.js.map +0 -1
  109. package/dist/chunk-WEC4YMOS.js.map +0 -1
  110. /package/dist/{BlockNoteEditor-BLVXQPXV.mjs.map → BlockNoteEditor-6TBRDBCF.mjs.map} +0 -0
  111. /package/dist/{chunk-IWFGEPAA.mjs.map → chunk-X53MF5ZN.mjs.map} +0 -0
@@ -1,10 +1,12 @@
1
1
  import { ClassValue } from 'clsx';
2
2
  export { ClassValue } from 'clsx';
3
3
  import * as React from 'react';
4
- import { ReactElement } from 'react';
5
- import { M as ModuleWithPermissions, a as Action } from '../types-Bl61ob-7.js';
4
+ import { ReactElement, ReactNode } from 'react';
5
+ import { M as ModuleWithPermissions, a as Action } from '../types-t2PyXhDu.js';
6
6
  import { z } from 'zod';
7
7
  import { PartialBlock } from '@blocknote/core';
8
+ import { LucideIcon } from 'lucide-react';
9
+ import { A as ApiDataInterface } from '../ApiDataInterface-DPP8s46n.js';
8
10
 
9
11
  declare function cn(...inputs: ClassValue[]): string;
10
12
 
@@ -197,4 +199,26 @@ declare class BlockNoteWordDiffRendererUtil {
197
199
  };
198
200
  }
199
201
 
200
- export { type BlockDiffOptions, BlockNoteDiffUtil, BlockNoteWordDiffRendererUtil, type DiffBlock, type DiffResult, type EntityObject, type FormatOption, TableOptions, type UserObject, type WordDiff, cn, composeRefs, entityObjectSchema, exists, formatDate, getTableComponents, getTableOptions, useComposedRefs, useIsMobile, userObjectSchema };
202
+ declare const getIconByModule: (params: {
203
+ module: ModuleWithPermissions;
204
+ className?: string;
205
+ }) => ReactNode;
206
+ declare const getIcon: (params: {
207
+ element: ApiDataInterface;
208
+ className?: string;
209
+ }) => ReactNode;
210
+ declare const getIconByModuleName: (params: {
211
+ name: string;
212
+ className?: string;
213
+ }) => ReactNode;
214
+ declare const getLucideIcon: (params: {
215
+ element: ApiDataInterface;
216
+ }) => LucideIcon | null;
217
+ declare const getLucideIconByModule: (params: {
218
+ module: ModuleWithPermissions;
219
+ }) => LucideIcon | null;
220
+ declare const getLucideIconByModuleName: (params: {
221
+ name: string;
222
+ }) => LucideIcon | null;
223
+
224
+ export { type BlockDiffOptions, BlockNoteDiffUtil, BlockNoteWordDiffRendererUtil, type DiffBlock, type DiffResult, type EntityObject, type FormatOption, TableOptions, type UserObject, type WordDiff, cn, composeRefs, entityObjectSchema, exists, formatDate, getIcon, getIconByModule, getIconByModuleName, getLucideIcon, getLucideIconByModule, getLucideIconByModuleName, getTableComponents, getTableOptions, useComposedRefs, useIsMobile, userObjectSchema };
@@ -12,7 +12,15 @@
12
12
 
13
13
 
14
14
 
15
- var _chunkJC3WJK65js = require('../chunk-JC3WJK65.js');
15
+
16
+
17
+
18
+
19
+
20
+
21
+ var _chunkLJRD4SRVjs = require('../chunk-LJRD4SRV.js');
22
+ require('../chunk-WZJDLITG.js');
23
+ require('../chunk-AGAJMJ4T.js');
16
24
  require('../chunk-7QVYU63E.js');
17
25
 
18
26
 
@@ -28,5 +36,11 @@ require('../chunk-7QVYU63E.js');
28
36
 
29
37
 
30
38
 
31
- exports.BlockNoteDiffUtil = _chunkJC3WJK65js.BlockNoteDiffUtil; exports.BlockNoteWordDiffRendererUtil = _chunkJC3WJK65js.BlockNoteWordDiffRendererUtil; exports.TableOptions = _chunkJC3WJK65js.TableOptions; exports.cn = _chunkJC3WJK65js.cn; exports.composeRefs = _chunkJC3WJK65js.composeRefs; exports.entityObjectSchema = _chunkJC3WJK65js.entityObjectSchema; exports.exists = _chunkJC3WJK65js.exists; exports.formatDate = _chunkJC3WJK65js.formatDate; exports.getTableComponents = _chunkJC3WJK65js.getTableComponents; exports.getTableOptions = _chunkJC3WJK65js.getTableOptions; exports.useComposedRefs = _chunkJC3WJK65js.useComposedRefs; exports.useIsMobile = _chunkJC3WJK65js.useIsMobile; exports.userObjectSchema = _chunkJC3WJK65js.userObjectSchema;
39
+
40
+
41
+
42
+
43
+
44
+
45
+ exports.BlockNoteDiffUtil = _chunkLJRD4SRVjs.BlockNoteDiffUtil; exports.BlockNoteWordDiffRendererUtil = _chunkLJRD4SRVjs.BlockNoteWordDiffRendererUtil; exports.TableOptions = _chunkLJRD4SRVjs.TableOptions; exports.cn = _chunkLJRD4SRVjs.cn; exports.composeRefs = _chunkLJRD4SRVjs.composeRefs; exports.entityObjectSchema = _chunkLJRD4SRVjs.entityObjectSchema; exports.exists = _chunkLJRD4SRVjs.exists; exports.formatDate = _chunkLJRD4SRVjs.formatDate; exports.getIcon = _chunkLJRD4SRVjs.getIcon; exports.getIconByModule = _chunkLJRD4SRVjs.getIconByModule; exports.getIconByModuleName = _chunkLJRD4SRVjs.getIconByModuleName; exports.getLucideIcon = _chunkLJRD4SRVjs.getLucideIcon; exports.getLucideIconByModule = _chunkLJRD4SRVjs.getLucideIconByModule; exports.getLucideIconByModuleName = _chunkLJRD4SRVjs.getLucideIconByModuleName; exports.getTableComponents = _chunkLJRD4SRVjs.getTableComponents; exports.getTableOptions = _chunkLJRD4SRVjs.getTableOptions; exports.useComposedRefs = _chunkLJRD4SRVjs.useComposedRefs; exports.useIsMobile = _chunkLJRD4SRVjs.useIsMobile; exports.userObjectSchema = _chunkLJRD4SRVjs.userObjectSchema;
32
46
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/utils/index.js"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,uDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,6uBAAC","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/utils/index.js"}
1
+ {"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/utils/index.js"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,uDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,ymCAAC","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/utils/index.js"}
@@ -7,12 +7,20 @@ import {
7
7
  entityObjectSchema,
8
8
  exists,
9
9
  formatDate,
10
+ getIcon,
11
+ getIconByModule,
12
+ getIconByModuleName,
13
+ getLucideIcon,
14
+ getLucideIconByModule,
15
+ getLucideIconByModuleName,
10
16
  getTableComponents,
11
17
  getTableOptions,
12
18
  useComposedRefs,
13
19
  useIsMobile,
14
20
  userObjectSchema
15
- } from "../chunk-2LM6LCJW.mjs";
21
+ } from "../chunk-JLN6UWII.mjs";
22
+ import "../chunk-NPPQNSXN.mjs";
23
+ import "../chunk-RBIVEH2K.mjs";
16
24
  import "../chunk-PAWJFY3S.mjs";
17
25
  export {
18
26
  BlockNoteDiffUtil,
@@ -23,6 +31,12 @@ export {
23
31
  entityObjectSchema,
24
32
  exists,
25
33
  formatDate,
34
+ getIcon,
35
+ getIconByModule,
36
+ getIconByModuleName,
37
+ getLucideIcon,
38
+ getLucideIconByModule,
39
+ getLucideIconByModuleName,
26
40
  getTableComponents,
27
41
  getTableOptions,
28
42
  useComposedRefs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carlonicora/nextjs-jsonapi",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "description": "Next.js JSON:API client with server/client support and caching",
5
5
  "author": "Carlo Nicora",
6
6
  "license": "GPL-3.0-or-later",
@@ -10,15 +10,17 @@ export * from "./hooks";
10
10
  export * from "./request";
11
11
  export * from "./token";
12
12
 
13
- import { useCompanyTableStructure } from "../features/company/hooks";
14
13
  // Table generator registration (must be in client-only context)
14
+ import { useCompanyTableStructure } from "../features/company/hooks";
15
15
  import { useRoleTableStructure } from "../features/role/hooks";
16
16
  import { useUserTableStructure } from "../features/user/hooks";
17
17
  import { registerTableGenerator } from "../hooks";
18
18
 
19
+ export * from "../features/content/hooks";
19
20
  export * from "../features/role/hooks";
20
21
  export * from "../features/user/hooks";
21
22
 
22
23
  registerTableGenerator("roles", useRoleTableStructure);
23
24
  registerTableGenerator("users", useUserTableStructure);
24
25
  registerTableGenerator("companies", useCompanyTableStructure);
26
+ // Note: Content registration moved to app-level to support app-specific cellTopic
@@ -10,6 +10,7 @@ export * from "./tables";
10
10
 
11
11
  export * from "../features/auth/components";
12
12
  export * from "../features/company/components";
13
+ export * from "../features/content/components";
13
14
  export * from "../features/feature/components";
14
15
  export * from "../features/notification/components";
15
16
  export * from "../features/role/components";
@@ -49,6 +49,15 @@ class ModuleRegistryClass {
49
49
  }
50
50
  throw new Error(`Module not found: ${moduleName}`);
51
51
  }
52
+
53
+ findByModelName(modelName: string): ModuleWithPermissions {
54
+ // Direct lookup by registry key (e.g., "Article", "Document")
55
+ const module = this._modules.get(modelName);
56
+ if (!module) {
57
+ throw new Error(`Module not found for model: ${modelName}`);
58
+ }
59
+ return module as ModuleWithPermissions;
60
+ }
52
61
  }
53
62
 
54
63
  export const ModuleRegistry = new ModuleRegistryClass();
@@ -59,6 +68,12 @@ export const Modules = new Proxy({} as ModuleDefinitions, {
59
68
  if (prop === "findByName") {
60
69
  return (name: string) => ModuleRegistry.findByName(name);
61
70
  }
71
+ if (prop === "findByModelName") {
72
+ return (name: string) => ModuleRegistry.findByModelName(name);
73
+ }
62
74
  return ModuleRegistry.get(prop as keyof ModuleDefinitions);
63
75
  },
64
- }) as ModuleDefinitions & { findByName: (name: string) => ModuleWithPermissions };
76
+ }) as ModuleDefinitions & {
77
+ findByName: (name: string) => ModuleWithPermissions;
78
+ findByModelName: (name: string) => ModuleWithPermissions;
79
+ };
@@ -0,0 +1 @@
1
+ export * from "./lists";
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "next-intl";
4
+ import { Modules } from "../../../../core";
5
+ import { usePageUrlGenerator } from "../../../../hooks";
6
+ import { HoverCard, HoverCardContent, HoverCardTrigger, Link } from "../../../../shadcnui";
7
+ import { getIconByModule } from "../../../../utils";
8
+ import { ContributorsList } from "../../../user/components";
9
+ import { ContentInterface } from "../../data";
10
+
11
+ type ContentsListProps = {
12
+ contentList: ContentInterface[];
13
+ };
14
+
15
+ export function ContentsList({ contentList }: ContentsListProps) {
16
+ const t = useTranslations();
17
+
18
+ return (
19
+ <div className="flex min-h-0 w-full flex-col overflow-y-auto">
20
+ <h2 className="text-xl font-semibold">{t(`foundations.content.news`)}</h2>
21
+ <div className="flex flex-col">
22
+ {contentList.map((content) => (
23
+ <ContentsListElement content={content} key={content.id} />
24
+ ))}
25
+ </div>
26
+ </div>
27
+ );
28
+ }
29
+
30
+ type ContentsListElementProps = {
31
+ content: ContentInterface;
32
+ };
33
+
34
+ function ContentsListElement({ content }: ContentsListElementProps) {
35
+ const generateUrl = usePageUrlGenerator();
36
+
37
+ const contentModule = content.contentType ? Modules.findByModelName(content.contentType) : undefined;
38
+ const link = contentModule ? generateUrl({ page: contentModule, id: content.id }) : "#";
39
+
40
+ return (
41
+ <div className="hover:bg-muted flex w-full flex-col gap-y-2 border-b p-2 py-4">
42
+ <div className="flex w-full justify-between gap-x-2">
43
+ <HoverCard>
44
+ <HoverCardTrigger asChild>
45
+ <Link href={link} className="flex w-full items-center justify-start gap-2 font-semibold">
46
+ {contentModule && getIconByModule({ module: contentModule, className: "h-4 w-4" })}
47
+ {content.name}
48
+ </Link>
49
+ </HoverCardTrigger>
50
+ <HoverCardContent className="flex max-h-96 w-96 flex-col gap-y-4 overflow-y-auto">
51
+ <Link href={link} className="font-semibold">
52
+ {content.name}
53
+ </Link>
54
+ <div className="text-xs">{content.abstract}</div>
55
+ </HoverCardContent>
56
+ </HoverCard>
57
+ <ContributorsList content={content} />
58
+ </div>
59
+ {/* <div className="text-muted-foreground text-xs">{content.tldr}</div>
60
+ {content.topics.length > 0 && (
61
+ <div className="flex w-full items-center justify-between">
62
+ <TopicBadgesList topics={content.topics} limit={1} />
63
+ </div>
64
+ )} */}
65
+ </div>
66
+ );
67
+ }
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "next-intl";
4
+ import { ContentListTable } from "../../../../components";
5
+ import { Modules } from "../../../../core";
6
+ import { DataListRetriever, useDataListRetriever } from "../../../../hooks";
7
+ import { ContentFields, ContentInterface, ContentService } from "../../data";
8
+
9
+ type ContentsListByIdProps = {
10
+ contentIds: string[];
11
+ };
12
+
13
+ export function ContentsListById({ contentIds }: ContentsListByIdProps) {
14
+ const t = useTranslations();
15
+
16
+ const data: DataListRetriever<ContentInterface> = useDataListRetriever({
17
+ module: Modules.Content,
18
+ retriever: (params) => ContentService.findMany(params),
19
+ retrieverParams: { contentIds: contentIds },
20
+ });
21
+
22
+ return (
23
+ <ContentListTable
24
+ data={data}
25
+ fields={[ContentFields.name, ContentFields.authors, ContentFields.updatedAt]}
26
+ tableGeneratorType={Modules.Content}
27
+ title={t(`generic.relevant`)}
28
+ />
29
+ );
30
+ }
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ import { useTranslations } from "next-intl";
4
+ import { ContentListTable } from "../../../../components";
5
+ import { Modules } from "../../../../core";
6
+ import { DataListRetriever, useDataListRetriever } from "../../../../hooks";
7
+ import { ContentFields, ContentInterface, ContentService } from "../../data";
8
+
9
+ type RelevantContentsListProps = {
10
+ id: string;
11
+ };
12
+
13
+ export function RelevantContentsList({ id }: RelevantContentsListProps) {
14
+ const t = useTranslations();
15
+
16
+ const data: DataListRetriever<ContentInterface> = useDataListRetriever({
17
+ module: Modules.Content,
18
+ retriever: (params) => ContentService.findRelevant(params),
19
+ retrieverParams: { id: id },
20
+ });
21
+
22
+ return (
23
+ <ContentListTable
24
+ data={data}
25
+ fields={[ContentFields.name, ContentFields.authors, ContentFields.relevance, ContentFields.updatedAt]}
26
+ tableGeneratorType={Modules.Content}
27
+ title={t(`generic.relevant`)}
28
+ />
29
+ );
30
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./ContentsList";
2
+ export * from "./ContentsListById";
3
+ export * from "./RelevantContentsList";
@@ -5,8 +5,6 @@ export enum ContentFields {
5
5
  relevance = "relevance",
6
6
 
7
7
  authors = "authors",
8
- topic = "topic",
9
- expertise = "expertise",
10
8
 
11
9
  createdAt = "createdAt",
12
10
  updatedAt = "updatedAt",
@@ -0,0 +1 @@
1
+ export * from "./useContentTableStructure";
@@ -0,0 +1,132 @@
1
+ "use client";
2
+
3
+ import { ColumnDef } from "@tanstack/react-table";
4
+ import { useTranslations } from "next-intl";
5
+ import { useMemo } from "react";
6
+ import { cellDate, cellId, ContributorsList } from "../../../components";
7
+ import { Modules } from "../../../core";
8
+ import { TableContent, usePageUrlGenerator, UseTableStructureHook } from "../../../hooks";
9
+ import { Link, Tooltip, TooltipContent, TooltipTrigger } from "../../../shadcnui";
10
+ import { getIconByModule } from "../../../utils";
11
+ import { ContentFields, ContentInterface } from "../data";
12
+
13
+ export const useContentTableStructure = <U extends string = ContentFields>(
14
+ params: Parameters<UseTableStructureHook<ContentInterface, U>>[0],
15
+ ): ReturnType<UseTableStructureHook<ContentInterface, U>> => {
16
+ const t = useTranslations();
17
+ const generateUrl = usePageUrlGenerator();
18
+
19
+ const tableData = useMemo(() => {
20
+ return params.data.map((content: ContentInterface) => {
21
+ const entry: TableContent<ContentInterface> = {
22
+ jsonApiData: content,
23
+ };
24
+ entry[ContentFields.contentId] = content.id;
25
+ params.fields.forEach((field) => {
26
+ entry[field as string] = content[field as keyof ContentInterface];
27
+ });
28
+ return entry;
29
+ });
30
+ }, [params.data, params.fields]);
31
+
32
+ const fieldColumnMap: Partial<Record<string, () => any>> = {
33
+ [ContentFields.contentId]: () =>
34
+ cellId({
35
+ name: "contentId",
36
+ checkedIds: params.checkedIds,
37
+ toggleId: params.toggleId,
38
+ }),
39
+ [ContentFields.name]: () => ({
40
+ id: "name",
41
+ accessorKey: "name",
42
+ header: t(`foundations.content.fields.name.label`),
43
+ cell: ({ row }: { row: TableContent<ContentInterface> }) => {
44
+ const content: ContentInterface = row.original.jsonApiData;
45
+
46
+ const contentModule = content.contentType ? Modules.findByModelName(content.contentType) : undefined;
47
+ const link = contentModule ? generateUrl({ page: contentModule, id: content.id }) : "#";
48
+
49
+ return (
50
+ <Tooltip>
51
+ <TooltipTrigger className="flex items-center justify-start space-x-2">
52
+ <>
53
+ {contentModule && getIconByModule({ module: contentModule, className: "h-4 w-4" })}
54
+ <Link href={link}>{content.name}</Link>
55
+ </>
56
+ </TooltipTrigger>
57
+ <TooltipContent>{content.tldr}</TooltipContent>
58
+ </Tooltip>
59
+ );
60
+ },
61
+ enableSorting: false,
62
+ enableHiding: false,
63
+ }),
64
+ [ContentFields.relevance]: () => ({
65
+ id: "relevance",
66
+ accessorKey: "relevance",
67
+ header: t(`generic.relevance`),
68
+ cell: ({ row }: { row: TableContent<ContentInterface> }) => {
69
+ const content: ContentInterface = row.original.jsonApiData;
70
+
71
+ if (!content.relevance) return <></>;
72
+
73
+ const response = `${content.relevance.toFixed(0)}%`;
74
+
75
+ return (
76
+ <div className="relative flex h-5 w-20 items-center justify-center overflow-hidden rounded border text-center">
77
+ <div
78
+ className={`bg-accent absolute top-0 left-0 h-full opacity-${Math.round(content.relevance)}`}
79
+ style={{ width: `${content.relevance}%` }}
80
+ ></div>
81
+ <span
82
+ className={`relative text-xs ${content.relevance < 40 ? "text-muted-foreground" : "text-accent-foreground font-semibold"}`}
83
+ >
84
+ {response}
85
+ </span>
86
+ </div>
87
+ );
88
+ },
89
+ enableSorting: false,
90
+ enableHiding: false,
91
+ }),
92
+ [ContentFields.authors]: () => ({
93
+ id: "authors",
94
+ accessorKey: "authors",
95
+ header: t(`generic.relationships.author.label`),
96
+ cell: ({ row }: { row: TableContent<ContentInterface> }) => {
97
+ const content: ContentInterface = row.original.jsonApiData;
98
+ return <ContributorsList content={content} />;
99
+ },
100
+ enableSorting: false,
101
+ enableHiding: false,
102
+ }),
103
+ [ContentFields.createdAt]: () =>
104
+ cellDate({
105
+ name: "createdAt",
106
+ title: t(`generic.date.create`),
107
+ }),
108
+ [ContentFields.updatedAt]: () =>
109
+ cellDate({
110
+ name: "updatedAt",
111
+ title: t(`generic.date.update`),
112
+ }),
113
+ };
114
+
115
+ const columns = useMemo(() => {
116
+ return params.fields
117
+ .map((field) => {
118
+ // First check local fieldColumnMap
119
+ const localHandler = fieldColumnMap[field];
120
+ if (localHandler) return localHandler();
121
+
122
+ // Fallback to customCells from context
123
+ const customHandler = params.context?.customCells?.[field];
124
+ if (customHandler) return customHandler({ t });
125
+
126
+ return undefined;
127
+ })
128
+ .filter((col) => col !== undefined) as ColumnDef<TableContent<ContentInterface>>[];
129
+ }, [params.fields, fieldColumnMap, t, generateUrl, params.context?.customCells]);
130
+
131
+ return useMemo(() => ({ data: tableData, columns: columns }), [tableData, columns]);
132
+ };
@@ -1,3 +1,4 @@
1
+ import { LucideIcon } from "lucide-react";
1
2
  import { ApiRequestDataTypeInterface } from "../core/interfaces/ApiRequestDataTypeInterface";
2
3
  import { FieldSelector } from "../core/fields/FieldSelector";
3
4
 
@@ -80,6 +81,7 @@ export type ModuleWithPermissions = ApiRequestDataTypeInterface & {
80
81
  pageUrl?: string;
81
82
  feature?: string;
82
83
  moduleId?: string;
84
+ icon?: LucideIcon;
83
85
  inclusions?: Record<
84
86
  string,
85
87
  {
@@ -99,6 +101,7 @@ export type ModuleFactory = (params: {
99
101
  model: any;
100
102
  feature?: string;
101
103
  moduleId?: string;
104
+ icon?: LucideIcon;
102
105
  inclusions?: Record<
103
106
  string,
104
107
  {
@@ -0,0 +1,40 @@
1
+ import { LucideIcon } from "lucide-react";
2
+ import { ReactNode } from "react";
3
+ import { ApiDataInterface, Modules } from "../core";
4
+ import { ContentInterface } from "../features/content/data/content.interface";
5
+ import { ModuleWithPermissions } from "../permissions";
6
+ import { cn } from "./cn";
7
+
8
+ export const getIconByModule = (params: { module: ModuleWithPermissions; className?: string }): ReactNode => {
9
+ const IconComponent = getLucideIconByModule({ module: params.module });
10
+ if (!IconComponent) return null;
11
+ return <IconComponent className={cn(``, params.className)} />;
12
+ };
13
+
14
+ export const getIcon = (params: { element: ApiDataInterface; className?: string }): ReactNode => {
15
+ const IconComponent = getLucideIcon({ element: params.element });
16
+ if (!IconComponent) return null;
17
+ return <IconComponent className={cn(``, params.className)} />;
18
+ };
19
+
20
+ export const getIconByModuleName = (params: { name: string; className?: string }): ReactNode => {
21
+ return getIconByModule({ module: Modules.findByModelName(params.name), className: params.className });
22
+ };
23
+
24
+ export const getLucideIcon = (params: { element: ApiDataInterface }): LucideIcon | null => {
25
+ if (params.element.type === "contents") {
26
+ const contentType = (params.element as ContentInterface).contentType;
27
+ if (!contentType) return null;
28
+ return getLucideIconByModule({ module: Modules.findByModelName(contentType) });
29
+ }
30
+
31
+ return getLucideIconByModule({ module: Modules.findByName(params.element.type) });
32
+ };
33
+
34
+ export const getLucideIconByModule = (params: { module: ModuleWithPermissions }): LucideIcon | null => {
35
+ return params.module.icon ?? null;
36
+ };
37
+
38
+ export const getLucideIconByModuleName = (params: { name: string }): LucideIcon | null => {
39
+ return getLucideIconByModule({ module: Modules.findByModelName(params.name) });
40
+ };
@@ -13,3 +13,4 @@ export { entityObjectSchema, userObjectSchema, type EntityObject, type UserObjec
13
13
 
14
14
  export * from "./blocknote-diff.util";
15
15
  export * from "./blocknote-word-diff-renderer.util";
16
+ export * from "./icons";