@c-rex/templates 0.1.3 → 0.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-rex/templates",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src"
@@ -18,9 +18,13 @@
18
18
  "types": "./src/home/page.tsx",
19
19
  "import": "./src/home/page.tsx"
20
20
  },
21
- "./articles/info/page": {
22
- "types": "./src/articles/info/page.tsx",
23
- "import": "./src/articles/info/page.tsx"
21
+ "./articles/topics/page": {
22
+ "types": "./src/articles/topics/page.tsx",
23
+ "import": "./src/articles/topics/page.tsx"
24
+ },
25
+ "./articles/blog/page": {
26
+ "types": "./src/articles/blog/page.tsx",
27
+ "import": "./src/articles/blog/page.tsx"
24
28
  },
25
29
  "./articles/documents/page": {
26
30
  "types": "./src/articles/documents/page.tsx",
@@ -52,13 +56,14 @@
52
56
  "@c-rex/services": "*",
53
57
  "@c-rex/ui": "*",
54
58
  "@c-rex/utils": "*",
59
+ "cheerio": "^1.1.0",
55
60
  "i18next": "^25.1.3",
56
61
  "next": "^14",
57
62
  "next-intl": "^4.1.0",
58
63
  "nuqs": "^2.4.3",
59
64
  "react": "^18",
60
- "sonner": "^2.0.5",
61
65
  "react-dom": "^18",
62
- "react-i18next": "^15.5.1"
66
+ "react-i18next": "^15.5.1",
67
+ "sonner": "^2.0.5"
63
68
  }
64
69
  }
@@ -0,0 +1,69 @@
1
+ import React from "react";
2
+ import { getPrimaryInfo } from "../utils";
3
+ import { TOPICS_TYPE_AND_LINK } from "@c-rex/constants";
4
+ import { PageWrapper } from "@c-rex/components/page-wrapper";
5
+ import { Breadcrumb } from "@c-rex/components/breadcrumb";
6
+ import * as cheerio from 'cheerio'
7
+ import { Metadata } from "next";
8
+ import { RenderArticle } from "@c-rex/components/render-article";
9
+
10
+ const extractHtmlContent = (htmlString: string) => {
11
+ const $ = cheerio.load(htmlString)
12
+
13
+ const metaTags = $('meta').map((_, el) => {
14
+ const name = $(el).attr('name')
15
+ const content = $(el).attr('content')
16
+ return name && content ? { name, content } : null
17
+ }).get().filter(Boolean)
18
+
19
+ const articleHtml = $('body').html() || ''
20
+
21
+ return {
22
+ metaTags,
23
+ articleHtml,
24
+ }
25
+ }
26
+
27
+ export const generateMetadata = async (
28
+ { params }: { params: { id: string } }
29
+ ): Promise<Metadata> => {
30
+ const { htmlContent, title } = await getPrimaryInfo(params.id, TOPICS_TYPE_AND_LINK);
31
+ const { metaTags } = extractHtmlContent(htmlContent)
32
+
33
+ const other: Record<string, string[]> = {}
34
+
35
+ for (const { name, content } of metaTags) {
36
+ if (Object.keys(other).includes(name)) {
37
+ other[name]?.push(content)
38
+ } else {
39
+ other[name] = [content]
40
+ }
41
+ }
42
+
43
+ return { ...other, title }
44
+ }
45
+
46
+ export const BlogPageTemplate = async ({ params }: { params: { id: string } }) => {
47
+ const { htmlContent, title, lang } = await getPrimaryInfo(params.id, TOPICS_TYPE_AND_LINK);
48
+ const { articleHtml } = extractHtmlContent(htmlContent)
49
+
50
+ const breadcrumbItems = [{
51
+ link: "/",
52
+ label: title,
53
+ id: "home",
54
+ active: false,
55
+ children: [],
56
+ }]
57
+
58
+ return (
59
+ <PageWrapper title="">
60
+ <div className="container flex flex-col">
61
+ <header className="flex h-16 shrink-0 items-center justify-between">
62
+ <Breadcrumb items={breadcrumbItems} lang={lang} />
63
+ </header>
64
+
65
+ <RenderArticle htmlContent={articleHtml} contentLang={lang} />
66
+ </div>
67
+ </PageWrapper>
68
+ );
69
+ };
@@ -1,8 +1,21 @@
1
1
  import React from "react";
2
+ import { getPrimaryInfo } from "../utils";
2
3
  import { ArticleWrapper } from "../wrapper";
4
+ import { PageWrapper } from "@c-rex/components/page-wrapper";
5
+ import { DOCUMENTS_TYPE_AND_LINK } from "@c-rex/constants";
6
+
7
+ export const DocumentsPageTemplate = async ({ params }: { params: { id: string } }) => {
8
+ const { htmlContent, title, lang } = await getPrimaryInfo(params.id, DOCUMENTS_TYPE_AND_LINK);
3
9
 
4
- export const DocumentsPageTemplate = ({ params }: { params: { id: string } }) => {
5
10
  return (
6
- <ArticleWrapper id={params.id} type="documents" />
11
+ <PageWrapper title={title}>
12
+ <ArticleWrapper
13
+ title={title}
14
+ htmlContent={htmlContent}
15
+ lang={lang}
16
+ id={params.id}
17
+ type={DOCUMENTS_TYPE_AND_LINK}
18
+ />
19
+ </PageWrapper>
7
20
  );
8
21
  };
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { getPrimaryInfo } from "../utils";
3
+ import { TOPICS_TYPE_AND_LINK } from "@c-rex/constants";
4
+ import { ArticleWrapper } from "../wrapper";
5
+ import { PageWrapper } from "@c-rex/components/page-wrapper";
6
+
7
+ export const TopicsPageTemplate = async ({ params }: { params: { id: string } }) => {
8
+ const { htmlContent, title, lang } = await getPrimaryInfo(params.id, TOPICS_TYPE_AND_LINK);
9
+
10
+ return (
11
+ <PageWrapper title={title}>
12
+ <ArticleWrapper
13
+ title={title}
14
+ lang={lang}
15
+ htmlContent={htmlContent}
16
+ id={params.id}
17
+ type={TOPICS_TYPE_AND_LINK}
18
+ />
19
+ </PageWrapper>
20
+ );
21
+ };
@@ -0,0 +1,75 @@
1
+ "use server"
2
+
3
+ import { DirectoryNodesService, InformationUnitsService, RenditionsService } from "@c-rex/services";
4
+ import { DOCUMENTS_TYPE_AND_LINK, TOPICS_TYPE_AND_LINK } from "@c-rex/constants";
5
+ import { DirectoryNodes } from "@c-rex/interfaces";
6
+
7
+ /*
8
+ * Get primary info for a given information unit
9
+ * @param id: string
10
+ * @param type: string
11
+ * @returns { htmlContent: string, title: string }
12
+ */
13
+ export const getPrimaryInfo = async (id: string, type: string): Promise<{
14
+ htmlContent: string,
15
+ title: string,
16
+ lang: string
17
+ }> => {
18
+ const renditionService = new RenditionsService();
19
+ const informationService = new InformationUnitsService();
20
+ const informationUnitsItem = await informationService.getItem({ id });
21
+
22
+ let title = informationUnitsItem.labels[0]?.value
23
+ const lang = informationUnitsItem.languages[0];
24
+ const rootNode = await getRootNode(informationUnitsItem.directoryNodes);
25
+ let htmlContent = ""
26
+
27
+ if (rootNode != null) {
28
+ title = rootNode.informationUnits[0]?.labels[0]?.value;
29
+ }
30
+
31
+ if (type == TOPICS_TYPE_AND_LINK) {
32
+ htmlContent = await renditionService.getHTMLRendition({ renditions: informationUnitsItem.renditions });
33
+ } else if (rootNode != null && type == DOCUMENTS_TYPE_AND_LINK) {
34
+ const directoryId = rootNode.childNodes[0]?.shortId as string;
35
+ const service = new DirectoryNodesService();
36
+ const response = await service.getItem(directoryId);
37
+ const infoId = response.informationUnits[0]?.shortId;
38
+
39
+ const childInformationUnit = await informationService.getItem({ id: infoId as string });
40
+ htmlContent = await renditionService.getHTMLRendition({ renditions: childInformationUnit.renditions });
41
+ }
42
+
43
+ return {
44
+ htmlContent,
45
+ title: title as string,
46
+ lang: lang as string,
47
+ }
48
+ }
49
+
50
+ export const getRootNode = async (directoryNodes: DirectoryNodes[]): Promise<DirectoryNodes | null> => {
51
+ const service = new DirectoryNodesService();
52
+
53
+ if (directoryNodes.length == 0 || directoryNodes[0] == undefined) {
54
+ return null;
55
+ }
56
+
57
+ let id = directoryNodes[0].shortId;
58
+ let response = await service.getItem(id);
59
+
60
+ while (response.parents != undefined) {
61
+
62
+ const hasInfo = (response.informationUnits != undefined) && (response.informationUnits[0] != undefined);
63
+ const hasLabel = (response.labels != undefined) && (response.labels[0] != undefined);
64
+ const hasParent = (response.parents != undefined) && (response.parents[0] != undefined);
65
+
66
+ if (!hasInfo || !hasLabel || !hasParent) {
67
+ return null;
68
+ }
69
+
70
+ id = response.parents[0]?.shortId as string;
71
+ response = await service.getItem(id);
72
+ }
73
+
74
+ return response;
75
+ };
@@ -1,73 +1,169 @@
1
- import React from "react";
1
+ "use client"
2
+
3
+ import React, { useEffect, useState } from "react";
2
4
  import { SidebarInset, SidebarProvider } from "@c-rex/ui/sidebar";
3
5
  import { AppSidebar } from "@c-rex/components/sidebar";
4
6
  import { CheckArticleLangToast } from "@c-rex/components/check-article-lang";
5
- import { loadArticleData } from "@c-rex/utils";
6
- import { getCookieFromBack } from "@c-rex/utils/next-cookies";
7
- import { CONTENT_LANG_KEY } from "@c-rex/constants";
8
- import { PageWrapper } from "@c-rex/components/page-wrapper";
9
7
  import { Breadcrumb } from "@c-rex/components/breadcrumb";
10
- import { DropdownMenu } from "../../../components/src/result-view/dropdown-menu";
8
+ import { RenderArticle } from "@c-rex/components/render-article";
11
9
  import { CloudDownload, Eye } from "lucide-react";
10
+ import { informationUnitsItems, informationUnitsResponse, SidebarAvailableVersionsInterface, TreeOfContent } from "@c-rex/interfaces";
11
+ import { DOCUMENTS_TYPE_AND_LINK, TOPICS_TYPE_AND_LINK } from "@c-rex/constants";
12
+ import { call, generateBreadcrumbItems, generateTreeOfContent } from "@c-rex/utils";
13
+ import { DropdownMenu } from "@c-rex/components/dropdown-menu";
14
+ import { FileRenditionType } from "@c-rex/types";
12
15
 
13
16
  type Props = {
17
+ htmlContent: string,
18
+ title: string,
14
19
  id: string,
15
20
  type: string,
21
+ lang: string,
22
+ }
23
+ type filesRenditions = {
24
+ filesToDownload: FileRenditionType[],
25
+ filesToOpen: FileRenditionType[]
16
26
  }
17
- export const ArticleWrapper = async ({ id, type }: Props) => {
27
+ const loadArticleData = async (id: string, type: string, title: string) => {
28
+ const informationUnitsItem = await call<informationUnitsItems>('InformationUnitsService.getItem', { id: id });
18
29
 
19
- const contentLanguage = getCookieFromBack(CONTENT_LANG_KEY);
20
- const {
21
- availableVersions,
22
- title,
30
+ const { rootNode, result: treeOfContent } = await generateTreeOfContent(informationUnitsItem.directoryNodes);
31
+
32
+ const articleLanguage = informationUnitsItem.languages[0]
33
+ const versionOf = informationUnitsItem.versionOf.shortId
34
+
35
+ const versions = await call<informationUnitsResponse>("InformationUnitsService.getList", {
36
+ filters: [`versionOf.shortId=${versionOf}`],
37
+ fields: ["renditions", "class", "languages", "labels"],
38
+ })
39
+ const availableVersions = versions.items.map((item) => {
40
+ return {
41
+ shortId: item.shortId,
42
+ link: `/${type}/${item.shortId}`,
43
+ lang: item.language,
44
+ country: item.language.split("-")[1],
45
+ active: item.language === articleLanguage,
46
+ }
47
+ }).sort((a, b) => {
48
+ if (a.lang < b.lang) return -1;
49
+ if (a.lang > b.lang) return 1;
50
+ return 0;
51
+ }) as SidebarAvailableVersionsInterface[];
52
+
53
+ let documents = await call<filesRenditions>("RenditionsService.getFileRenditions", { renditions: informationUnitsItem.renditions });
54
+ let rootNodeInfoID = "";
55
+ let breadcrumbItems: TreeOfContent[] = [];
56
+
57
+ if (rootNode != null) {
58
+ rootNodeInfoID = rootNode.informationUnits[0]?.shortId as string;
59
+
60
+ const childInformationUnit = await call<informationUnitsItems>('InformationUnitsService.getItem', { id: rootNodeInfoID });
61
+ documents = await call<filesRenditions>("RenditionsService.getFileRenditions", { renditions: childInformationUnit.renditions });
62
+ }
63
+
64
+ if (type == TOPICS_TYPE_AND_LINK) {
65
+ breadcrumbItems = generateBreadcrumbItems(treeOfContent);
66
+ } else if (rootNode != null && type == DOCUMENTS_TYPE_AND_LINK) {
67
+ if ((treeOfContent !== null && treeOfContent !== undefined) && (treeOfContent[0] !== undefined)) {
68
+ treeOfContent[0].active = true;
69
+ }
70
+
71
+ breadcrumbItems = [{
72
+ link: "/",
73
+ label: title,
74
+ id: "title",
75
+ active: false,
76
+ children: [],
77
+ }]
78
+ }
79
+
80
+ return {
23
81
  treeOfContent,
24
- documents,
25
82
  breadcrumbItems,
26
- htmlContent
27
- } = await loadArticleData(id, type)
83
+ availableVersions,
84
+ documents,
85
+ articleLanguage
86
+ }
87
+ };
88
+
89
+ export const ArticleWrapper = ({ htmlContent, title, id, type, lang }: Props) => {
90
+ const [loading, setLoading] = useState<boolean>(true)
91
+ const [availableVersions, setAvailableVersions] = useState<SidebarAvailableVersionsInterface[]>([])
92
+
93
+
94
+ //set available versions on confgi context so will be possible to get the link when change contant language
95
+ const [breadcrumbItems, setBreadcrumbItems] = useState<TreeOfContent[]>([])
96
+ const [treeOfContent, setTreeOfContent] = useState<TreeOfContent[]>([])
97
+ const [documents, setDocuments] = useState<{
98
+ filesToDownload: {
99
+ format: string;
100
+ link: string;
101
+ }[],
102
+ filesToOpen: {
103
+ format: string,
104
+ link: string,
105
+ }[]
106
+ }>({
107
+ filesToDownload: [],
108
+ filesToOpen: [],
109
+ })
110
+
111
+ useEffect(() => {
112
+ const fetchData = async () => {
113
+
114
+ const {
115
+ availableVersions,
116
+ treeOfContent,
117
+ documents,
118
+ breadcrumbItems,
119
+ } = await loadArticleData(id, type, title)
120
+
121
+ setAvailableVersions(availableVersions)
122
+ setTreeOfContent(treeOfContent)
123
+ setDocuments(documents)
124
+ setBreadcrumbItems(breadcrumbItems)
125
+ setLoading(false)
126
+ }
127
+
128
+ fetchData();
129
+ }, [])
28
130
 
29
131
  return (
30
- <PageWrapper title={title}>
31
- <SidebarProvider>
32
- <title>{title}</title>
33
-
34
- <CheckArticleLangToast
35
- availableVersions={availableVersions}
36
- contentLanguage={contentLanguage}
37
- />
38
-
39
- <AppSidebar
40
- data={treeOfContent}
41
- availableVersions={availableVersions}
42
- />
43
- <SidebarInset>
44
- <div className="container flex flex-col">
45
- <header className="flex h-16 shrink-0 items-center justify-between">
46
- <Breadcrumb items={breadcrumbItems} />
47
- <div className="flex">
48
-
49
- {documents.filesToDownload.length > 0 && (
50
- <DropdownMenu
51
- items={documents.filesToDownload}
52
- icon={<CloudDownload />}
53
- />
54
- )}
55
- {documents.filesToOpen.length > 0 && (
56
- <DropdownMenu
57
- items={documents.filesToOpen}
58
- icon={<Eye />}
59
- />
60
- )}
61
- </div>
62
- </header>
63
-
64
- <div
65
- className="pb-4"
66
- dangerouslySetInnerHTML={{ __html: htmlContent }}
67
- />
68
- </div>
69
- </SidebarInset>
70
- </SidebarProvider>
71
- </PageWrapper>
132
+ <SidebarProvider>
133
+ <title>{title}</title>
134
+
135
+ <CheckArticleLangToast availableVersions={availableVersions} />
136
+
137
+ <AppSidebar
138
+ lang={lang}
139
+ data={treeOfContent}
140
+ availableVersions={availableVersions}
141
+ loading={loading}
142
+ />
143
+ <SidebarInset>
144
+ <div className="container flex flex-col">
145
+ <header className="flex h-16 shrink-0 items-center justify-between">
146
+ <Breadcrumb items={breadcrumbItems} loading={loading} lang={lang} />
147
+
148
+ <div className="flex">
149
+ {documents.filesToDownload.length > 0 && (
150
+ <DropdownMenu
151
+ items={documents.filesToDownload}
152
+ icon={<CloudDownload />}
153
+ />
154
+ )}
155
+ {documents.filesToOpen.length > 0 && (
156
+ <DropdownMenu
157
+ items={documents.filesToOpen}
158
+ icon={<Eye />}
159
+ />
160
+ )}
161
+ </div>
162
+ </header>
163
+
164
+ <RenderArticle htmlContent={htmlContent} contentLang={lang} />
165
+ </div>
166
+ </SidebarInset>
167
+ </SidebarProvider>
72
168
  );
73
169
  };
@@ -1,7 +1,6 @@
1
1
  import React from "react";
2
2
  import { informationUnitsResponse } from "@c-rex/interfaces";
3
- import { InformationUnitsService, LanguageService } from "@c-rex/services";
4
- import { getConfigs } from "@c-rex/utils/next-cookies";
3
+ import { InformationUnitsService } from "@c-rex/services";
5
4
  import { HomePage } from "./page";
6
5
  import { PageWrapper } from "@c-rex/components/page-wrapper";
7
6
 
@@ -16,10 +15,6 @@ interface HomeProps {
16
15
  export const HomeLayout = async ({ searchParams }: HomeProps) => {
17
16
  const { search, page, language } = searchParams;
18
17
 
19
- const configs = await getConfigs();
20
- const languageService = new LanguageService(configs.languageSwitcher.endpoint);
21
- const availableLanguagesAndCountries = await languageService.getLanguagesAndCountries();
22
-
23
18
  let data = {
24
19
  items: [],
25
20
  pageInfo: {
@@ -54,9 +49,7 @@ export const HomeLayout = async ({ searchParams }: HomeProps) => {
54
49
  <PageWrapper title="">
55
50
  <HomePage
56
51
  data={data}
57
- configs={configs}
58
52
  selectedLanguages={selectedLanguages}
59
- availableLanguages={availableLanguagesAndCountries}
60
53
  />
61
54
  </PageWrapper>
62
55
  );
package/src/home/page.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import React, { FC } from "react";
4
- import { ConfigInterface, informationUnitsResponse, LanguageAndCountries } from "@c-rex/interfaces";
4
+ import { informationUnitsResponse } from "@c-rex/interfaces";
5
5
  import { call } from "@c-rex/utils";
6
6
  import { Button } from "@c-rex/ui/button";
7
7
  import { AutoComplete } from "@c-rex/components/autocomplete";
@@ -9,17 +9,17 @@ import { ResultList } from "@c-rex/components/result-list";
9
9
  import { useTranslations } from 'next-intl'
10
10
  import { parseAsInteger, parseAsString, useQueryStates } from 'nuqs'
11
11
  import { DialogFilter } from "@c-rex/components/dialog-filter";
12
- import { getCookieInFront } from "@c-rex/utils";
12
+ import { getCookie } from "@c-rex/utils";
13
13
  import { CONTENT_LANG_KEY } from "@c-rex/constants";
14
+ import { useAppConfig } from "@c-rex/contexts/config-provider";
14
15
 
15
16
  interface HomePageProps {
16
17
  data: informationUnitsResponse;
17
18
  selectedLanguages: string[];
18
- availableLanguages: LanguageAndCountries[];
19
- configs: ConfigInterface
20
19
  }
21
20
 
22
- export const HomePage: FC<HomePageProps> = ({ data, selectedLanguages, availableLanguages, configs }) => {
21
+ export const HomePage: FC<HomePageProps> = ({ data, selectedLanguages }) => {
22
+ const { configs, availableLanguagesAndCountries: availableLanguages, contentLang } = useAppConfig()
23
23
  const t = useTranslations();
24
24
  const [params, setParams] = useQueryStates({
25
25
  search: {
@@ -39,12 +39,12 @@ export const HomePage: FC<HomePageProps> = ({ data, selectedLanguages, available
39
39
  const { search } = params;
40
40
 
41
41
  const onSearch = (value: string): Promise<string[]> => {
42
- return call('InformationUnitsService.getSuggestions', { query: value });
42
+ return call("InformationUnitsService.getSuggestions", { query: value, language: contentLang });
43
43
  }
44
44
 
45
45
  const onSelect = (value: string) => {
46
46
  //show loading
47
- getCookieInFront(CONTENT_LANG_KEY)
47
+ getCookie(CONTENT_LANG_KEY)
48
48
  .then((cookie) => cookie.value)
49
49
  .then((cookie) => {
50
50
  setParams({
package/src/layout.tsx CHANGED
@@ -2,9 +2,7 @@ import React from "react";
2
2
  import { NextIntlClientProvider } from 'next-intl';
3
3
  import { getLocale } from "next-intl/server";
4
4
  import { Toaster } from "@c-rex/ui/sonner"
5
- import { LanguageService } from "@c-rex/services";
6
- import { getConfigs } from "@c-rex/utils/next-cookies";
7
- import { SetCookies } from "@c-rex/components/cookies";
5
+ import { AppConfigProvider } from "@c-rex/contexts/config-provider";
8
6
  import { NuqsAdapter } from 'nuqs/adapters/next/app'
9
7
 
10
8
  interface DefaultRootLayoutProps {
@@ -13,9 +11,6 @@ interface DefaultRootLayoutProps {
13
11
 
14
12
  export const DefaultRootLayout = async ({ children }: DefaultRootLayoutProps) => {
15
13
  const locale = await getLocale();
16
- const configs = await getConfigs();
17
- const languageService = new LanguageService(configs.languageSwitcher.endpoint);
18
- const availableLanguages = await languageService.getLanguagesAndCountries();
19
14
 
20
15
  return (
21
16
  <NuqsAdapter>
@@ -24,9 +19,10 @@ export const DefaultRootLayout = async ({ children }: DefaultRootLayoutProps) =>
24
19
  <body className="min-h-screen bg-background font-sans antialiased">
25
20
  <div className="items-center flex flex-col">
26
21
  <NextIntlClientProvider>
27
- <Toaster />
28
- <SetCookies configs={configs} availableLanguages={availableLanguages} />
29
- {children}
22
+ <AppConfigProvider>
23
+ <Toaster />
24
+ {children}
25
+ </AppConfigProvider>
30
26
  </NextIntlClientProvider>
31
27
  </div>
32
28
  </body>
@@ -1,9 +0,0 @@
1
- import React from "react";
2
- import { ArticleWrapper } from "../wrapper";
3
-
4
- export const InfoPageTemplate = ({ params }: { params: { id: string } }) => {
5
-
6
- return (
7
- <ArticleWrapper id={params.id} type="topics" />
8
- );
9
- };