@c-rex/templates 0.1.9 → 0.1.10
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 +1 -1
- package/src/blog/article/page.tsx +86 -19
- package/src/blog/home/layout.tsx +54 -41
- package/src/blog/home/page.tsx +97 -258
- package/src/blog/{article/utils.ts → utils.ts} +14 -0
- package/src/doc/articles/documents.tsx +21 -7
- package/src/doc/articles/utils.ts +45 -36
- package/src/doc/articles/wrapper.tsx +82 -35
- package/src/doc/home/layout.tsx +8 -5
- package/src/doc/home/page.tsx +56 -43
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { JSX } from "react";
|
|
2
|
+
import { extractHtmlContent, getInfoWithCache, getPrimaryInfo } from "../utils";
|
|
3
|
+
import { BLOG_TYPE_AND_LINK, CONTENT_LANG_KEY } from "@c-rex/constants";
|
|
3
4
|
import { PageWrapper } from "@c-rex/components/page-wrapper";
|
|
4
5
|
import { Metadata } from "next";
|
|
5
6
|
import { RenderArticle } from "@c-rex/components/render-article";
|
|
6
|
-
import {
|
|
7
|
-
import { SidebarInset, SidebarProvider } from "@c-rex/ui/sidebar";
|
|
7
|
+
import { InfoCard } from "@c-rex/components/info-card";
|
|
8
8
|
import { CheckArticleLangToast } from "@c-rex/components/check-article-lang";
|
|
9
|
+
import { SidebarMenu, SidebarMenuSubButton, SidebarMenuSubItem } from "@c-rex/ui/sidebar";
|
|
10
|
+
import * as Flags from 'country-flag-icons/react/3x2';
|
|
11
|
+
import { getTranslations } from 'next-intl/server';
|
|
12
|
+
import { getConfigs } from "@c-rex/utils/next-cookies";
|
|
13
|
+
import { TopicsService } from "@c-rex/services";
|
|
14
|
+
import { cookies } from "next/headers";
|
|
15
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@c-rex/ui/card";
|
|
16
|
+
|
|
9
17
|
|
|
10
18
|
const infoCache = new Map<string, Awaited<ReturnType<typeof getPrimaryInfo>>>();
|
|
11
19
|
|
|
@@ -30,26 +38,85 @@ export const generateMetadata = async (
|
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
export const BlogPageTemplate = async ({ params }: { params: { id: string } }) => {
|
|
33
|
-
const
|
|
41
|
+
const config = getConfigs();
|
|
42
|
+
const service = new TopicsService();
|
|
43
|
+
const defaultLang = config.languageSwitcher.default;
|
|
44
|
+
const contentLang = cookies().get(CONTENT_LANG_KEY)?.value || defaultLang
|
|
45
|
+
|
|
46
|
+
const [articleList, articleInfo, t] = await Promise.all([
|
|
47
|
+
service.getList({
|
|
48
|
+
languages: [contentLang] as never[],
|
|
49
|
+
pageSize: 4,
|
|
50
|
+
}),
|
|
51
|
+
getInfoWithCache(params.id, BLOG_TYPE_AND_LINK, infoCache),
|
|
52
|
+
getTranslations()
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
const { htmlContent, articleLang, availableVersions } = articleInfo
|
|
34
56
|
const { articleHtml } = extractHtmlContent(htmlContent)
|
|
35
57
|
|
|
58
|
+
articleList.items = articleList.items.filter(item => item.shortId !== params.id)
|
|
59
|
+
|
|
36
60
|
return (
|
|
37
61
|
<PageWrapper title="" pageType="BLOG">
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
<CheckArticleLangToast availableVersions={availableVersions} />
|
|
63
|
+
|
|
64
|
+
<div className="container pt-4 flex flex-row gap-4">
|
|
65
|
+
<div className="flex-1">
|
|
66
|
+
<RenderArticle htmlContent={articleHtml} contentLang={articleLang} />
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div className="w-60 relative">
|
|
70
|
+
<div className="sticky top-24">
|
|
71
|
+
<Card className="mb-4">
|
|
72
|
+
|
|
73
|
+
<CardHeader>
|
|
74
|
+
<CardTitle className="text-lg">
|
|
75
|
+
{t("availableIn")}:
|
|
76
|
+
</CardTitle>
|
|
77
|
+
</CardHeader>
|
|
78
|
+
<CardContent className="space-y-3">
|
|
79
|
+
<SidebarMenu>
|
|
80
|
+
{availableVersions.map((item) => {
|
|
81
|
+
return (
|
|
82
|
+
<SidebarMenuSubItem key={item.shortId}>
|
|
83
|
+
<SidebarMenuSubButton
|
|
84
|
+
className="cursor-pointer"
|
|
85
|
+
isActive={item.active}
|
|
86
|
+
key={item.shortId}
|
|
87
|
+
href={item.link} title={item.lang}
|
|
88
|
+
>
|
|
89
|
+
{getFlagIcon(item.country)} {item.lang}
|
|
90
|
+
</SidebarMenuSubButton>
|
|
91
|
+
</SidebarMenuSubItem>
|
|
92
|
+
)
|
|
93
|
+
})}
|
|
94
|
+
</SidebarMenu>
|
|
95
|
+
</CardContent>
|
|
96
|
+
</Card>
|
|
97
|
+
|
|
98
|
+
<InfoCard
|
|
99
|
+
title={t("recentPosts")}
|
|
100
|
+
items={articleList.items.map(item => ({
|
|
101
|
+
label: item.title,
|
|
102
|
+
value: item.created,
|
|
103
|
+
link: item.link
|
|
104
|
+
}))}
|
|
105
|
+
/>
|
|
106
|
+
|
|
50
107
|
</div>
|
|
51
|
-
</
|
|
52
|
-
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
</div>
|
|
53
111
|
</PageWrapper>
|
|
54
112
|
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
const getFlagIcon = (countryCode: string): JSX.Element | null => {
|
|
117
|
+
type CountryCode = keyof typeof Flags;
|
|
118
|
+
const FlagComponent = Flags[countryCode as CountryCode];
|
|
119
|
+
if (!FlagComponent) return null;
|
|
120
|
+
|
|
121
|
+
return <FlagComponent />;
|
|
55
122
|
};
|
package/src/blog/home/layout.tsx
CHANGED
|
@@ -1,59 +1,72 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { InformationUnitsService } from "@c-rex/services";
|
|
1
|
+
import { DocumentTypesService, TopicsService } from "@c-rex/services";
|
|
3
2
|
import { HomePage } from "./page";
|
|
4
3
|
import { PageWrapper } from "@c-rex/components/page-wrapper";
|
|
5
4
|
import { WildCardType } from "@c-rex/types";
|
|
5
|
+
import { cookies } from "next/headers";
|
|
6
|
+
import { getConfigs } from "@c-rex/utils/next-cookies";
|
|
7
|
+
import { CONTENT_LANG_KEY } from "@c-rex/constants";
|
|
8
|
+
import { getTeaserInfo } from "../utils";
|
|
9
|
+
import { SearchProvider } from "@c-rex/contexts/search";
|
|
6
10
|
|
|
7
|
-
interface
|
|
8
|
-
searchParams
|
|
11
|
+
interface BlogHomeProps {
|
|
12
|
+
searchParams?: {
|
|
9
13
|
search?: string;
|
|
10
|
-
page
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
like: string;
|
|
15
|
-
packages?: string;
|
|
14
|
+
page?: string;
|
|
15
|
+
wildcard?: string;
|
|
16
|
+
operator?: string;
|
|
17
|
+
like?: string;
|
|
16
18
|
filter?: string;
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export const
|
|
21
|
-
const {
|
|
22
|
+
export const BlogHomeLayout = async ({ searchParams = {} }: BlogHomeProps) => {
|
|
23
|
+
const {
|
|
24
|
+
search = "",
|
|
25
|
+
page = "1",
|
|
26
|
+
wildcard = "",
|
|
27
|
+
operator = "",
|
|
28
|
+
filter = ""
|
|
29
|
+
} = searchParams;
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} as unknown as informationUnitsResponse;
|
|
30
|
-
|
|
31
|
-
if (search !== undefined) {
|
|
32
|
-
|
|
33
|
-
const filters: string[] = filter?.split(",") || []
|
|
34
|
-
const restrict: string[] = []
|
|
35
|
-
|
|
36
|
-
if (packages && packages.length > 0) {
|
|
37
|
-
restrict.push(`packages.shortId=${packages}`)
|
|
38
|
-
}
|
|
39
|
-
const service = new InformationUnitsService();
|
|
31
|
+
const config = getConfigs();
|
|
32
|
+
const service = new TopicsService();
|
|
33
|
+
const defaultLang = config.languageSwitcher.default;
|
|
34
|
+
const contentLang = cookies().get(CONTENT_LANG_KEY)?.value || defaultLang
|
|
35
|
+
const documentService = new DocumentTypesService();
|
|
36
|
+
const filters: string[] = filter.split(",") || []
|
|
40
37
|
|
|
41
|
-
|
|
38
|
+
const [data, tags] = await Promise.all([
|
|
39
|
+
service.getList({
|
|
42
40
|
queries: search,
|
|
43
|
-
page: Number(page),
|
|
44
|
-
fields: ["renditions", "class", "languages", "labels"],
|
|
45
|
-
languages:
|
|
41
|
+
page: Number(page || 1),
|
|
42
|
+
fields: ["renditions", "class", "languages", "labels", "created", "applicableForTypes"],
|
|
43
|
+
languages: [contentLang] as never[],
|
|
46
44
|
wildcard: wildcard as WildCardType,
|
|
47
|
-
restrict: restrict,
|
|
48
45
|
operator: operator,
|
|
49
|
-
|
|
50
|
-
filters
|
|
51
|
-
})
|
|
52
|
-
|
|
46
|
+
pageSize: 11,
|
|
47
|
+
filters,
|
|
48
|
+
}),
|
|
49
|
+
documentService.getLabels()
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
data.items = await Promise.all(data.items.map(async (item) => {
|
|
54
|
+
const response = await fetch(item.renditionUrl, { method: "GET" });
|
|
55
|
+
const text = await response.text();
|
|
56
|
+
const { imageSrc, desc } = getTeaserInfo(text);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
...item,
|
|
60
|
+
image: imageSrc,
|
|
61
|
+
description: desc
|
|
62
|
+
}
|
|
63
|
+
}))
|
|
53
64
|
|
|
54
65
|
return (
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
66
|
+
<SearchProvider>
|
|
67
|
+
<PageWrapper title="" pageType="HOME">
|
|
68
|
+
<HomePage data={data} tagsParam={tags} />
|
|
69
|
+
</PageWrapper>
|
|
70
|
+
</SearchProvider>
|
|
58
71
|
);
|
|
59
72
|
};
|
package/src/blog/home/page.tsx
CHANGED
|
@@ -1,58 +1,42 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import React, { FC, useEffect, useState } from "react";
|
|
4
|
-
import { Trash2, X } from "lucide-react"
|
|
3
|
+
import React, { FC, Fragment, useEffect, useState } from "react";
|
|
5
4
|
import { useTranslations } from 'next-intl'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { Button } from "@c-rex/ui/button";
|
|
9
|
-
import { Badge } from "@c-rex/ui/badge";
|
|
5
|
+
import { parseAsString, useQueryStates } from 'nuqs'
|
|
6
|
+
import { Card } from "@c-rex/ui/card";
|
|
10
7
|
import {
|
|
11
8
|
SidebarContent,
|
|
12
9
|
SidebarGroup,
|
|
13
|
-
SidebarGroupContent,
|
|
14
|
-
SidebarGroupLabel,
|
|
15
10
|
SidebarHeader,
|
|
16
11
|
SidebarMenu,
|
|
17
|
-
SidebarMenuSub,
|
|
18
12
|
SidebarMenuSubButton,
|
|
19
13
|
SidebarMenuSubItem
|
|
20
14
|
} from "@c-rex/ui/sidebar";
|
|
21
|
-
import {
|
|
22
|
-
import { DialogFilter } from "@c-rex/components/dialog-filter";
|
|
23
|
-
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
15
|
+
import { Pagination } from "@c-rex/components/pagination";
|
|
24
16
|
import { AutoComplete } from "@c-rex/components/autocomplete";
|
|
25
|
-
import {
|
|
26
|
-
import
|
|
17
|
+
import { Empty } from "@c-rex/components/empty";
|
|
18
|
+
import BlogView from "@c-rex/components/result-view/blog";
|
|
19
|
+
import { useSearchContext } from "@c-rex/contexts/search";
|
|
20
|
+
import { DefaultResponse, TopicsResponseItem } from "@c-rex/interfaces";
|
|
27
21
|
|
|
28
22
|
interface HomePageProps {
|
|
29
|
-
data:
|
|
23
|
+
data: DefaultResponse<TopicsResponseItem, null>;
|
|
24
|
+
tagsParam: tagsType[];
|
|
30
25
|
}
|
|
31
26
|
|
|
32
|
-
type
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
default?: string | boolean | null
|
|
37
|
-
removable: boolean
|
|
27
|
+
type tagsType = {
|
|
28
|
+
shortId: string;
|
|
29
|
+
label: string;
|
|
30
|
+
active?: boolean
|
|
38
31
|
}
|
|
39
32
|
|
|
40
|
-
export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
33
|
+
export const HomePage: FC<HomePageProps> = ({ data: dataAux, tagsParam }) => {
|
|
41
34
|
const t = useTranslations();
|
|
42
|
-
const {
|
|
43
|
-
|
|
44
|
-
const [
|
|
45
|
-
const [filters, setFilters] = useState<filterModel[] | null>(null)
|
|
46
|
-
const [disabled, setDisabled] = useState<boolean>(false)
|
|
47
|
-
const [loading, setLoading] = useState<boolean>(true)
|
|
35
|
+
const { setLoading } = useSearchContext();
|
|
36
|
+
const [tags, setTags] = useState<tagsType[]>(tagsParam);
|
|
37
|
+
const [data, setData] = useState<DefaultResponse<TopicsResponseItem, null>>(dataAux);
|
|
48
38
|
const [params, setParams] = useQueryStates({
|
|
49
|
-
language: parseAsString,
|
|
50
|
-
page: parseAsInteger,
|
|
51
|
-
wildcard: parseAsString,
|
|
52
|
-
operator: parseAsString,
|
|
53
|
-
packages: parseAsString,
|
|
54
39
|
filter: parseAsString,
|
|
55
|
-
like: parseAsBoolean,
|
|
56
40
|
search: {
|
|
57
41
|
defaultValue: "",
|
|
58
42
|
parse(value) {
|
|
@@ -65,258 +49,113 @@ export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
|
65
49
|
});
|
|
66
50
|
|
|
67
51
|
useEffect(() => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
generateFiltersObj()
|
|
71
|
-
} else {
|
|
72
|
-
setDisabled(true)
|
|
73
|
-
setFilters(null)
|
|
74
|
-
}
|
|
75
|
-
}, [params])
|
|
52
|
+
setLoading(false);
|
|
53
|
+
setData(dataAux);
|
|
76
54
|
|
|
77
|
-
|
|
78
|
-
const newTags = { ...data.tags }
|
|
55
|
+
}, [dataAux]);
|
|
79
56
|
|
|
80
|
-
if (params.filter !== null) {
|
|
81
|
-
const splittedParam = params.filter.split(",")
|
|
82
57
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const newTags = [
|
|
60
|
+
{
|
|
61
|
+
label: t("filter.all"),
|
|
62
|
+
shortId: "all",
|
|
63
|
+
active: true
|
|
64
|
+
},
|
|
65
|
+
...tagsParam.sort((a, b) => {
|
|
66
|
+
if (a.shortId < b.shortId) return -1;
|
|
67
|
+
if (a.shortId > b.shortId) return 1;
|
|
68
|
+
return 0;
|
|
69
|
+
})];
|
|
91
70
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
el.active = true
|
|
95
|
-
} else {
|
|
96
|
-
el.active = false
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
}
|
|
71
|
+
if (params.filter !== null) {
|
|
72
|
+
const shortId = params.filter.split("=")[1]
|
|
101
73
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
el.active = true
|
|
74
|
+
newTags.forEach((item) => {
|
|
75
|
+
if (item.shortId == shortId) {
|
|
76
|
+
item.active = true
|
|
106
77
|
} else {
|
|
107
|
-
|
|
78
|
+
item.active = false
|
|
108
79
|
}
|
|
109
80
|
})
|
|
110
81
|
}
|
|
111
82
|
|
|
112
83
|
setTags(newTags)
|
|
113
84
|
setLoading(false)
|
|
114
|
-
}, [
|
|
115
|
-
|
|
116
|
-
const generateFiltersObj = () => {
|
|
117
|
-
const filters: filterModel[] = [{
|
|
118
|
-
key: "operator",
|
|
119
|
-
name: t("filter.operator"),
|
|
120
|
-
value: `${params?.operator !== OPERATOR_OPTIONS.OR}`,
|
|
121
|
-
default: OPERATOR_OPTIONS.OR,
|
|
122
|
-
removable: params?.operator !== OPERATOR_OPTIONS.OR
|
|
123
|
-
}, {
|
|
124
|
-
key: "like",
|
|
125
|
-
name: t("filter.like"),
|
|
126
|
-
value: `${params.like}`,
|
|
127
|
-
default: false,
|
|
128
|
-
removable: params?.like as boolean
|
|
129
|
-
}, {
|
|
130
|
-
key: "wildcard",
|
|
131
|
-
value: params.wildcard as string,
|
|
132
|
-
default: WILD_CARD_OPTIONS.NONE,
|
|
133
|
-
removable: params?.wildcard !== WILD_CARD_OPTIONS.NONE
|
|
134
|
-
}]
|
|
135
|
-
|
|
136
|
-
const languages = params.language?.split(",")
|
|
137
|
-
languages?.forEach((item) => {
|
|
138
|
-
const aux = languages?.filter(langItem => langItem !== item)
|
|
139
|
-
filters.push({ key: "language", value: item, removable: languages.length > 1, default: aux.join(",") })
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
if (params.filter !== null) {
|
|
143
|
-
const splittedParam = params.filter.split(",")
|
|
144
|
-
|
|
145
|
-
splittedParam.forEach((item, index) => {
|
|
146
|
-
const aux = item.split(".shortId=")
|
|
147
|
-
const name = aux[0]
|
|
148
|
-
const shortId = aux[1]
|
|
149
|
-
|
|
150
|
-
const defaultValue = [...splittedParam]
|
|
151
|
-
defaultValue.splice(index, 1)
|
|
152
|
-
|
|
153
|
-
if (!tags[name]) return;
|
|
154
|
-
|
|
155
|
-
const tag = tags[name].find(el => el.shortId === shortId)
|
|
156
|
-
if (!tag) return;
|
|
157
|
-
|
|
158
|
-
const value = defaultValue.length == 0 ? null : defaultValue.join(",")
|
|
159
|
-
|
|
160
|
-
filters.push({ key: "filter", name: t(`filter.tags.${name}`), value: tag.label, removable: true, default: value })
|
|
161
|
-
})
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (params.packages !== null && tags["packages"]) {
|
|
165
|
-
const packageTag = tags["packages"]?.find(el => el.shortId === params.packages)
|
|
166
|
-
filters.push({
|
|
167
|
-
key: "packages",
|
|
168
|
-
name: t("filter.tags.packages"),
|
|
169
|
-
value: packageTag.label,
|
|
170
|
-
removable: true,
|
|
171
|
-
default: null
|
|
172
|
-
})
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
Object.keys(params)
|
|
176
|
-
.filter(item => !["page", "search", "language", "operator", "like", "wildcard", "filter", "packages"].includes(item))
|
|
177
|
-
.filter(item => params[item] != null)
|
|
178
|
-
.forEach(item => {
|
|
179
|
-
filters.push({ key: item, value: params[item], removable: true, default: null })
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
setFilters(filters)
|
|
183
|
-
}
|
|
85
|
+
}, [tagsParam])
|
|
184
86
|
|
|
185
|
-
const updateFilterParam = (
|
|
87
|
+
const updateFilterParam = (item: tagsType) => {
|
|
186
88
|
setLoading(true)
|
|
187
|
-
if (key === "packages") {
|
|
188
|
-
setParams({ packages: item.shortId })
|
|
189
|
-
return;
|
|
190
|
-
} else {
|
|
191
|
-
const value = `${key}.shortId=${item.shortId}`
|
|
192
|
-
let aux = value
|
|
193
|
-
|
|
194
|
-
if (params.filter != null) {
|
|
195
|
-
const splittedParam = params.filter.split(",")
|
|
196
|
-
const finalValue = [...splittedParam]
|
|
197
|
-
|
|
198
|
-
const hasParams = params.filter.includes(key)
|
|
199
|
-
|
|
200
|
-
if (hasParams) {
|
|
201
|
-
let mainIndex = -1
|
|
202
|
-
|
|
203
|
-
splittedParam.forEach((el, index) => {
|
|
204
|
-
if (el.includes(key)) {
|
|
205
|
-
mainIndex = index
|
|
206
|
-
}
|
|
207
|
-
})
|
|
208
|
-
finalValue[mainIndex] = value
|
|
209
|
-
} else {
|
|
210
|
-
finalValue.push(value)
|
|
211
|
-
}
|
|
212
89
|
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
setParams({ filter: aux })
|
|
90
|
+
if (item.shortId === "all") {
|
|
91
|
+
setParams({ filter: null })
|
|
92
|
+
return;
|
|
217
93
|
}
|
|
94
|
+
|
|
95
|
+
const value = `applicableForTypes.shortId=${item.shortId}`
|
|
96
|
+
setParams({ filter: value })
|
|
218
97
|
};
|
|
219
98
|
|
|
220
99
|
return (
|
|
221
|
-
<div className="container">
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
searchByPackage={false}
|
|
230
|
-
/>
|
|
231
|
-
</div>
|
|
232
|
-
<div className="col-span-12 sm:col-span-2 md:col-span-2">
|
|
233
|
-
<div className="flex justify-end">
|
|
234
|
-
<DialogFilter
|
|
235
|
-
setLoading={setLoading}
|
|
236
|
-
trigger={(
|
|
237
|
-
<Button variant="default" disabled={disabled}>{t("filter.filters")}</Button>
|
|
238
|
-
)}
|
|
100
|
+
<div className="container pt-6">
|
|
101
|
+
<div className="flex flex-row gap-6 pb-6">
|
|
102
|
+
<div className="flex-1">
|
|
103
|
+
<div className="pb-6">
|
|
104
|
+
<AutoComplete
|
|
105
|
+
embedded={false}
|
|
106
|
+
initialValue={params.search}
|
|
107
|
+
searchByPackage={false}
|
|
239
108
|
/>
|
|
240
109
|
</div>
|
|
241
|
-
</div>
|
|
242
|
-
</div>
|
|
243
110
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
{
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
<X className="h-2" />
|
|
257
|
-
</Button>
|
|
258
|
-
)}
|
|
259
|
-
</Badge>
|
|
260
|
-
))}
|
|
261
|
-
|
|
262
|
-
</div>
|
|
263
|
-
<Button
|
|
264
|
-
size="sm"
|
|
265
|
-
variant="outline"
|
|
266
|
-
disabled={params.filter === null}
|
|
267
|
-
onClick={() => {
|
|
268
|
-
setLoading(true)
|
|
269
|
-
setParams({ filter: null, packages: null })
|
|
270
|
-
}}
|
|
271
|
-
>
|
|
272
|
-
{t("filter.clearFilters")}
|
|
273
|
-
<Trash2 className="h-2" />
|
|
274
|
-
</Button>
|
|
111
|
+
{data?.items?.length == 0 ? (
|
|
112
|
+
<Empty />
|
|
113
|
+
) : (
|
|
114
|
+
<Fragment>
|
|
115
|
+
<BlogView items={data.items} />
|
|
116
|
+
|
|
117
|
+
<Pagination
|
|
118
|
+
totalPages={data.pageInfo.pageCount}
|
|
119
|
+
currentPage={data.pageInfo.pageNumber}
|
|
120
|
+
/>
|
|
121
|
+
</Fragment>
|
|
122
|
+
)}
|
|
275
123
|
</div>
|
|
276
|
-
)}
|
|
277
124
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
<
|
|
287
|
-
<
|
|
288
|
-
{t(`filter.tags.${key}`)}
|
|
289
|
-
</SidebarGroupLabel>
|
|
290
|
-
<SidebarGroupContent>
|
|
125
|
+
{tags.length > 0 && (
|
|
126
|
+
<div className="relative w-80">
|
|
127
|
+
<div className="sticky top-24">
|
|
128
|
+
|
|
129
|
+
<Card>
|
|
130
|
+
<SidebarHeader className="text-center font-bold">
|
|
131
|
+
{t("filter.categories")}
|
|
132
|
+
</SidebarHeader>
|
|
133
|
+
<SidebarContent>
|
|
134
|
+
<SidebarGroup className="py-0">
|
|
291
135
|
<SidebarMenu>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
136
|
+
|
|
137
|
+
{tags.map((tagItem) => (
|
|
138
|
+
|
|
139
|
+
<SidebarMenuSubItem key={tagItem.shortId}>
|
|
140
|
+
<SidebarMenuSubButton
|
|
141
|
+
className="cursor-pointer"
|
|
142
|
+
isActive={tagItem.active}
|
|
143
|
+
onClick={() => {
|
|
144
|
+
updateFilterParam(tagItem)
|
|
145
|
+
setLoading(true)
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
{tagItem.label}
|
|
149
|
+
</SidebarMenuSubButton>
|
|
150
|
+
</SidebarMenuSubItem>
|
|
151
|
+
))}
|
|
305
152
|
</SidebarMenu>
|
|
306
|
-
</
|
|
307
|
-
</
|
|
308
|
-
|
|
309
|
-
</
|
|
153
|
+
</SidebarGroup>
|
|
154
|
+
</SidebarContent>
|
|
155
|
+
</Card>
|
|
156
|
+
</div>
|
|
310
157
|
</div>
|
|
311
158
|
)}
|
|
312
|
-
|
|
313
|
-
<div className="flex-1">
|
|
314
|
-
<ResultList
|
|
315
|
-
configs={configs}
|
|
316
|
-
items={data.items}
|
|
317
|
-
pagination={data.pageInfo}
|
|
318
|
-
/>
|
|
319
|
-
</div>
|
|
320
159
|
</div>
|
|
321
160
|
</div>
|
|
322
161
|
);
|
|
@@ -139,4 +139,18 @@ export const extractHtmlContent = (htmlString: string) => {
|
|
|
139
139
|
metaTags,
|
|
140
140
|
articleHtml,
|
|
141
141
|
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const getTeaserInfo = (htmlString: string): {
|
|
145
|
+
imageSrc: string | null,
|
|
146
|
+
desc: string | null,
|
|
147
|
+
shortDesc: string | null
|
|
148
|
+
} => {
|
|
149
|
+
const $ = cheerio.load(htmlString)
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
imageSrc: $('.teaserfig img').attr('src') || null,
|
|
153
|
+
desc: $('.teaser-p').text() || null,
|
|
154
|
+
shortDesc: $('.shortdesc').text() || null,
|
|
155
|
+
}
|
|
142
156
|
}
|
|
@@ -1,12 +1,31 @@
|
|
|
1
|
-
import { getInfoWithCache, getPrimaryInfo } from "./utils";
|
|
1
|
+
import { extractHtmlContent, getInfoWithCache, getPrimaryInfo } from "./utils";
|
|
2
2
|
import { ArticleWrapper } from "./wrapper";
|
|
3
3
|
import { PageWrapper } from "@c-rex/components/page-wrapper";
|
|
4
4
|
import { DOCUMENTS_TYPE_AND_LINK } from "@c-rex/constants";
|
|
5
|
+
import { Metadata } from "next";
|
|
5
6
|
|
|
6
7
|
const infoCache = new Map<string, Awaited<ReturnType<typeof getPrimaryInfo>>>();
|
|
7
8
|
|
|
9
|
+
export const generateMetadata = async (
|
|
10
|
+
{ params }: { params: { id: string } }
|
|
11
|
+
): Promise<Metadata> => {
|
|
12
|
+
const { htmlContent, title } = await getInfoWithCache(params.id, DOCUMENTS_TYPE_AND_LINK, infoCache);
|
|
13
|
+
const { metaTags } = extractHtmlContent(htmlContent)
|
|
14
|
+
|
|
15
|
+
const other: Record<string, string[]> = {}
|
|
16
|
+
|
|
17
|
+
for (const { name, content } of metaTags) {
|
|
18
|
+
if (Object.keys(other).includes(name)) {
|
|
19
|
+
other[name]?.push(content)
|
|
20
|
+
} else {
|
|
21
|
+
other[name] = [content]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { ...other, title }
|
|
26
|
+
}
|
|
27
|
+
|
|
8
28
|
export const DocumentsPageTemplate = async ({ params }: { params: { id: string } }) => {
|
|
9
|
-
/*
|
|
10
29
|
const {
|
|
11
30
|
htmlContent,
|
|
12
31
|
title,
|
|
@@ -15,11 +34,6 @@ export const DocumentsPageTemplate = async ({ params }: { params: { id: string }
|
|
|
15
34
|
documentLang,
|
|
16
35
|
packageId
|
|
17
36
|
} = await getInfoWithCache(params.id, DOCUMENTS_TYPE_AND_LINK, infoCache);
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const { htmlContent, title, availableVersions, packageId, articleLang, documentLang } = await getPrimaryInfo(params.id, DOCUMENTS_TYPE_AND_LINK);
|
|
23
37
|
|
|
24
38
|
return (
|
|
25
39
|
<PageWrapper title={title} pageType="DOC">
|
|
@@ -1,83 +1,92 @@
|
|
|
1
1
|
import { DirectoryNodesService, InformationUnitsService, RenditionsService } from "@c-rex/services";
|
|
2
2
|
import { DOCUMENTS_TYPE_AND_LINK, TOPICS_TYPE_AND_LINK } from "@c-rex/constants";
|
|
3
|
-
import { DirectoryNodes, informationUnitsResponseItem, SidebarAvailableVersionsInterface } from "@c-rex/interfaces";
|
|
3
|
+
import { DirectoryNodes, informationUnitsItems, informationUnitsResponseItem, SidebarAvailableVersionsInterface } from "@c-rex/interfaces";
|
|
4
4
|
import * as cheerio from 'cheerio'
|
|
5
5
|
import { BLOG_TYPE_AND_LINK } from "@c-rex/constants";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
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<{
|
|
7
|
+
|
|
8
|
+
type ReturnType = {
|
|
14
9
|
htmlContent: string,
|
|
15
10
|
title: string,
|
|
16
11
|
articleLang: string,
|
|
17
12
|
packageId: string,
|
|
18
13
|
documentLang: string,
|
|
19
14
|
availableVersions: SidebarAvailableVersionsInterface[]
|
|
20
|
-
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* Get primary info for a given information unit
|
|
19
|
+
* @param id: string
|
|
20
|
+
* @param type: string
|
|
21
|
+
* @returns { htmlContent: string, title: string }
|
|
22
|
+
*/
|
|
23
|
+
export const getPrimaryInfo = async (id: string, type: string): Promise<ReturnType> => {
|
|
21
24
|
const renditionService = new RenditionsService();
|
|
22
25
|
const informationService = new InformationUnitsService();
|
|
26
|
+
const directoryNodeService = new DirectoryNodesService();
|
|
23
27
|
const informationUnitsItem = await informationService.getItem({ id });
|
|
24
28
|
|
|
25
29
|
let title = informationUnitsItem.labels[0]?.value
|
|
26
|
-
|
|
30
|
+
|
|
31
|
+
// const document: informationUnitsItems = null
|
|
27
32
|
let documentLang = ""
|
|
33
|
+
const promiseList: any = []
|
|
28
34
|
const articleLang = informationUnitsItem.languages[0];
|
|
29
35
|
|
|
30
|
-
const
|
|
36
|
+
const [versions, rootNode] = await Promise.all([
|
|
31
37
|
informationService.getList({
|
|
32
|
-
restrict: [`versionOf.shortId=${versionOf}`],
|
|
38
|
+
restrict: [`versionOf.shortId=${informationUnitsItem.versionOf.shortId}`],
|
|
33
39
|
fields: ["renditions", "class", "languages", "labels"],
|
|
34
40
|
}),
|
|
35
|
-
|
|
41
|
+
getRootNode(informationUnitsItem.directoryNodes)
|
|
42
|
+
])
|
|
43
|
+
const availableVersions = versions.items.map((item: informationUnitsResponseItem) => {
|
|
44
|
+
return {
|
|
45
|
+
shortId: item.shortId,
|
|
46
|
+
link: `/${type}/${item.shortId}`,
|
|
47
|
+
lang: item.language,
|
|
48
|
+
country: item.language.split("-")[1],
|
|
49
|
+
active: item.language === articleLang,
|
|
50
|
+
}
|
|
51
|
+
}).sort((a: SidebarAvailableVersionsInterface, b: SidebarAvailableVersionsInterface) => {
|
|
52
|
+
if (a.lang < b.lang) return -1;
|
|
53
|
+
if (a.lang > b.lang) return 1;
|
|
54
|
+
return 0;
|
|
55
|
+
}) as SidebarAvailableVersionsInterface[];
|
|
56
|
+
|
|
36
57
|
|
|
37
|
-
const rootNode = await getRootNode(informationUnitsItem.directoryNodes)
|
|
38
58
|
|
|
39
59
|
if (rootNode != null) {
|
|
40
60
|
title = rootNode.informationUnits[0]?.labels[0]?.value;
|
|
41
61
|
documentLang = rootNode.informationUnits[0]?.labels[0]?.language as string;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
const infoId = rootNode.informationUnits[0]?.shortId as string;
|
|
65
|
+
|
|
66
|
+
document = await informationService.getItem({ id: infoId });
|
|
67
|
+
|
|
42
68
|
}
|
|
43
69
|
|
|
44
70
|
if ([TOPICS_TYPE_AND_LINK, BLOG_TYPE_AND_LINK].includes(type)) {
|
|
45
|
-
promiseList.push(
|
|
46
|
-
renditionService.getHTMLRendition({ renditions: informationUnitsItem.renditions })
|
|
47
|
-
)
|
|
71
|
+
promiseList.push(renditionService.getHTMLRendition({ renditions: informationUnitsItem.renditions }))
|
|
48
72
|
|
|
49
73
|
} else if (rootNode != null && type == DOCUMENTS_TYPE_AND_LINK) {
|
|
50
74
|
|
|
51
|
-
const service = new DirectoryNodesService();
|
|
52
75
|
|
|
53
76
|
const directoryId = rootNode.childNodes[0]?.shortId as string;
|
|
54
77
|
|
|
55
|
-
const response = await
|
|
78
|
+
const response = await directoryNodeService.getItem(directoryId);
|
|
56
79
|
|
|
57
80
|
const infoId = response.informationUnits[0]?.shortId;
|
|
58
81
|
|
|
59
82
|
const childInformationUnit = await informationService.getItem({ id: infoId as string });
|
|
60
83
|
|
|
61
|
-
promiseList.push(
|
|
62
|
-
renditionService.getHTMLRendition({ renditions: childInformationUnit.renditions })
|
|
63
|
-
)
|
|
84
|
+
promiseList.push(renditionService.getHTMLRendition({ renditions: childInformationUnit.renditions }))
|
|
64
85
|
}
|
|
65
86
|
|
|
66
|
-
const [
|
|
87
|
+
const [htmlContent] = await Promise.all(promiseList)
|
|
88
|
+
|
|
67
89
|
|
|
68
|
-
const availableVersions = versions.items.map((item: informationUnitsResponseItem) => {
|
|
69
|
-
return {
|
|
70
|
-
shortId: item.shortId,
|
|
71
|
-
link: `/${type}/${item.shortId}`,
|
|
72
|
-
lang: item.language,
|
|
73
|
-
country: item.language.split("-")[1],
|
|
74
|
-
active: item.language === articleLang,
|
|
75
|
-
}
|
|
76
|
-
}).sort((a: SidebarAvailableVersionsInterface, b: SidebarAvailableVersionsInterface) => {
|
|
77
|
-
if (a.lang < b.lang) return -1;
|
|
78
|
-
if (a.lang > b.lang) return 1;
|
|
79
|
-
return 0;
|
|
80
|
-
}) as SidebarAvailableVersionsInterface[];
|
|
81
90
|
|
|
82
91
|
return {
|
|
83
92
|
htmlContent,
|
|
@@ -9,10 +9,12 @@ import { RenderArticle } from "@c-rex/components/render-article";
|
|
|
9
9
|
import { CloudDownload, Eye } from "lucide-react";
|
|
10
10
|
import { informationUnitsItems, SidebarAvailableVersionsInterface, TreeOfContent } from "@c-rex/interfaces";
|
|
11
11
|
import { DOCUMENTS_TYPE_AND_LINK, TOPICS_TYPE_AND_LINK } from "@c-rex/constants";
|
|
12
|
-
import { call, generateBreadcrumbItems, generateTreeOfContent, getFileRenditions } from "@c-rex/utils";
|
|
12
|
+
import { call, formatDateToLocale, generateBreadcrumbItems, generateTreeOfContent, getFileRenditions } from "@c-rex/utils";
|
|
13
13
|
import { DropdownMenu } from "@c-rex/components/dropdown-menu";
|
|
14
14
|
import { FileRenditionType } from "@c-rex/types";
|
|
15
15
|
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
16
|
+
import { InfoCard } from "@c-rex/components/info-card";
|
|
17
|
+
import { useTranslations } from "next-intl";
|
|
16
18
|
|
|
17
19
|
type Props = {
|
|
18
20
|
htmlContent: string,
|
|
@@ -24,13 +26,43 @@ type Props = {
|
|
|
24
26
|
availableVersions: SidebarAvailableVersionsInterface[],
|
|
25
27
|
packageId: string,
|
|
26
28
|
}
|
|
29
|
+
type DocumentsType = {
|
|
30
|
+
filesToDownload: {
|
|
31
|
+
format: string;
|
|
32
|
+
link: string;
|
|
33
|
+
}[],
|
|
34
|
+
filesToOpen: {
|
|
35
|
+
format: string,
|
|
36
|
+
link: string,
|
|
37
|
+
}[]
|
|
38
|
+
}
|
|
27
39
|
|
|
28
40
|
const loadArticleData = async (id: string, type: string, title: string) => {
|
|
29
41
|
const informationUnitsItem = await call<informationUnitsItems>('InformationUnitsService.getItem', { id: id });
|
|
30
42
|
const { rootNode, result: treeOfContent } = await generateTreeOfContent(informationUnitsItem.directoryNodes);
|
|
31
|
-
|
|
32
43
|
const articleLanguage = informationUnitsItem.languages[0]
|
|
33
44
|
|
|
45
|
+
const info: { label: string, value: string }[] = []
|
|
46
|
+
|
|
47
|
+
if (informationUnitsItem.created) {
|
|
48
|
+
info.push({
|
|
49
|
+
label: "createdAt",
|
|
50
|
+
value: formatDateToLocale(informationUnitsItem.created, articleLanguage)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (informationUnitsItem.revision) {
|
|
55
|
+
info.push({
|
|
56
|
+
label: "revision",
|
|
57
|
+
value: informationUnitsItem.revision
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
// add title or label. title priority
|
|
63
|
+
//add language (use flag icon)
|
|
64
|
+
|
|
65
|
+
|
|
34
66
|
let documents: {
|
|
35
67
|
filesToDownload: FileRenditionType[];
|
|
36
68
|
filesToOpen: FileRenditionType[];
|
|
@@ -67,26 +99,29 @@ const loadArticleData = async (id: string, type: string, title: string) => {
|
|
|
67
99
|
treeOfContent,
|
|
68
100
|
breadcrumbItems,
|
|
69
101
|
documents,
|
|
70
|
-
articleLanguage
|
|
102
|
+
articleLanguage,
|
|
103
|
+
info
|
|
71
104
|
}
|
|
72
105
|
};
|
|
73
106
|
|
|
74
|
-
export const ArticleWrapper = ({
|
|
75
|
-
|
|
107
|
+
export const ArticleWrapper = ({
|
|
108
|
+
htmlContent,
|
|
109
|
+
title,
|
|
110
|
+
id,
|
|
111
|
+
type,
|
|
112
|
+
documentLang,
|
|
113
|
+
articleLang,
|
|
114
|
+
availableVersions,
|
|
115
|
+
packageId
|
|
116
|
+
}: Props) => {
|
|
117
|
+
const t = useTranslations();
|
|
118
|
+
|
|
76
119
|
const { setPackageID, setArticleLang } = useAppConfig()
|
|
77
120
|
const [loading, setLoading] = useState<boolean>(true)
|
|
78
121
|
const [breadcrumbItems, setBreadcrumbItems] = useState<TreeOfContent[]>([])
|
|
79
122
|
const [treeOfContent, setTreeOfContent] = useState<TreeOfContent[]>([])
|
|
80
|
-
const [
|
|
81
|
-
|
|
82
|
-
format: string;
|
|
83
|
-
link: string;
|
|
84
|
-
}[],
|
|
85
|
-
filesToOpen: {
|
|
86
|
-
format: string,
|
|
87
|
-
link: string,
|
|
88
|
-
}[]
|
|
89
|
-
}>({
|
|
123
|
+
const [articleInfo, setArticleInfo] = useState<any>([])
|
|
124
|
+
const [documents, setDocuments] = useState<DocumentsType>({
|
|
90
125
|
filesToDownload: [],
|
|
91
126
|
filesToOpen: [],
|
|
92
127
|
})
|
|
@@ -100,11 +135,13 @@ export const ArticleWrapper = ({ htmlContent, title, id, type, documentLang, art
|
|
|
100
135
|
treeOfContent,
|
|
101
136
|
documents,
|
|
102
137
|
breadcrumbItems,
|
|
138
|
+
info
|
|
103
139
|
} = await loadArticleData(id, type, title)
|
|
104
140
|
|
|
105
141
|
setTreeOfContent(treeOfContent)
|
|
106
142
|
setDocuments(documents)
|
|
107
143
|
setBreadcrumbItems(breadcrumbItems)
|
|
144
|
+
setArticleInfo(info)
|
|
108
145
|
setLoading(false)
|
|
109
146
|
}
|
|
110
147
|
|
|
@@ -113,8 +150,6 @@ export const ArticleWrapper = ({ htmlContent, title, id, type, documentLang, art
|
|
|
113
150
|
|
|
114
151
|
return (
|
|
115
152
|
<SidebarProvider>
|
|
116
|
-
<title>{title}</title>
|
|
117
|
-
|
|
118
153
|
<CheckArticleLangToast availableVersions={availableVersions} />
|
|
119
154
|
|
|
120
155
|
<AppSidebar
|
|
@@ -124,27 +159,39 @@ export const ArticleWrapper = ({ htmlContent, title, id, type, documentLang, art
|
|
|
124
159
|
loading={loading}
|
|
125
160
|
/>
|
|
126
161
|
<SidebarInset>
|
|
127
|
-
<div className="
|
|
128
|
-
<
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
162
|
+
<div className="flex flex-row gap-4 p-4">
|
|
163
|
+
<div className="flex flex-1 flex-col">
|
|
164
|
+
<header className="flex h-12 shrink-0 items-center justify-between">
|
|
165
|
+
<Breadcrumb items={breadcrumbItems} loading={loading} lang={documentLang} />
|
|
166
|
+
|
|
167
|
+
<div className="flex">
|
|
168
|
+
{documents.filesToDownload.length > 0 && (
|
|
169
|
+
<DropdownMenu
|
|
170
|
+
items={documents.filesToDownload}
|
|
171
|
+
icon={<CloudDownload />}
|
|
172
|
+
/>
|
|
173
|
+
)}
|
|
174
|
+
{documents.filesToOpen.length > 0 && (
|
|
175
|
+
<DropdownMenu
|
|
176
|
+
items={documents.filesToOpen}
|
|
177
|
+
icon={<Eye />}
|
|
178
|
+
/>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
</header>
|
|
182
|
+
<RenderArticle htmlContent={htmlContent} contentLang={articleLang} />
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div className="w-60 relative">
|
|
186
|
+
<div className="sticky top-28 gap-4 flex flex-col">
|
|
187
|
+
{!loading && (
|
|
188
|
+
<InfoCard
|
|
189
|
+
title={t("about")}
|
|
190
|
+
items={articleInfo}
|
|
142
191
|
/>
|
|
143
192
|
)}
|
|
144
193
|
</div>
|
|
145
|
-
</
|
|
146
|
-
|
|
147
|
-
<RenderArticle htmlContent={htmlContent} contentLang={articleLang} />
|
|
194
|
+
</div>
|
|
148
195
|
</div>
|
|
149
196
|
</SidebarInset>
|
|
150
197
|
</SidebarProvider>
|
package/src/doc/home/layout.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { informationUnitsResponse } from "@c-rex/interfaces";
|
|
2
|
-
import { InformationUnitsService } from "@c-rex/services";
|
|
3
1
|
import { HomePage } from "./page";
|
|
4
2
|
import { PageWrapper } from "@c-rex/components/page-wrapper";
|
|
5
3
|
import { WildCardType } from "@c-rex/types";
|
|
4
|
+
import { InformationUnitsService } from "@c-rex/services";
|
|
5
|
+
import { informationUnitsResponse } from "@c-rex/interfaces";
|
|
6
|
+
import { SearchProvider } from "@c-rex/contexts/search";
|
|
6
7
|
|
|
7
8
|
interface HomeProps {
|
|
8
9
|
searchParams: {
|
|
@@ -52,8 +53,10 @@ export const HomeLayout = async ({ searchParams }: HomeProps) => {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
return (
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
56
|
+
<SearchProvider>
|
|
57
|
+
<PageWrapper title="" pageType="HOME">
|
|
58
|
+
<HomePage data={data} />
|
|
59
|
+
</PageWrapper>
|
|
60
|
+
</SearchProvider>
|
|
58
61
|
);
|
|
59
62
|
};
|
package/src/doc/home/page.tsx
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import React, { FC, useEffect, useState } from "react";
|
|
4
|
-
import { Trash2, X } from "lucide-react"
|
|
4
|
+
import { ChevronDown, Trash2, X } from "lucide-react"
|
|
5
5
|
import { useTranslations } from 'next-intl'
|
|
6
6
|
import { parseAsBoolean, parseAsInteger, parseAsString, useQueryStates } from 'nuqs'
|
|
7
7
|
import { informationUnitsResponse } from "@c-rex/interfaces";
|
|
8
8
|
import { Button } from "@c-rex/ui/button";
|
|
9
9
|
import { Badge } from "@c-rex/ui/badge";
|
|
10
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@c-rex/ui/collapsible";
|
|
10
11
|
import {
|
|
11
12
|
SidebarContent,
|
|
12
13
|
SidebarGroup,
|
|
@@ -20,10 +21,9 @@ import {
|
|
|
20
21
|
} from "@c-rex/ui/sidebar";
|
|
21
22
|
import { ResultList } from "@c-rex/components/result-list";
|
|
22
23
|
import { DialogFilter } from "@c-rex/components/dialog-filter";
|
|
23
|
-
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
24
24
|
import { AutoComplete } from "@c-rex/components/autocomplete";
|
|
25
|
-
import { OPERATOR_OPTIONS
|
|
26
|
-
import {
|
|
25
|
+
import { OPERATOR_OPTIONS } from "@c-rex/constants";
|
|
26
|
+
import { useSearchContext } from "@c-rex/contexts/search";
|
|
27
27
|
|
|
28
28
|
interface HomePageProps {
|
|
29
29
|
data: informationUnitsResponse;
|
|
@@ -39,12 +39,11 @@ type filterModel = {
|
|
|
39
39
|
|
|
40
40
|
export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
41
41
|
const t = useTranslations();
|
|
42
|
-
const {
|
|
42
|
+
const { setLoading } = useSearchContext();
|
|
43
43
|
|
|
44
44
|
const [tags, setTags] = useState<{ [key: string]: any[] }>(data.tags || {});
|
|
45
45
|
const [filters, setFilters] = useState<filterModel[] | null>(null)
|
|
46
46
|
const [disabled, setDisabled] = useState<boolean>(false)
|
|
47
|
-
const [loading, setLoading] = useState<boolean>(true)
|
|
48
47
|
const [params, setParams] = useQueryStates({
|
|
49
48
|
language: parseAsString,
|
|
50
49
|
page: parseAsInteger,
|
|
@@ -118,19 +117,16 @@ export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
|
118
117
|
key: "operator",
|
|
119
118
|
name: t("filter.operator"),
|
|
120
119
|
value: `${params?.operator !== OPERATOR_OPTIONS.OR}`,
|
|
121
|
-
|
|
122
|
-
removable: params?.operator !== OPERATOR_OPTIONS.OR
|
|
120
|
+
removable: false
|
|
123
121
|
}, {
|
|
124
122
|
key: "like",
|
|
125
123
|
name: t("filter.like"),
|
|
126
124
|
value: `${params.like}`,
|
|
127
|
-
|
|
128
|
-
removable: params?.like as boolean
|
|
125
|
+
removable: false
|
|
129
126
|
}, {
|
|
130
127
|
key: "wildcard",
|
|
131
128
|
value: params.wildcard as string,
|
|
132
|
-
|
|
133
|
-
removable: params?.wildcard !== WILD_CARD_OPTIONS.NONE
|
|
129
|
+
removable: false,
|
|
134
130
|
}]
|
|
135
131
|
|
|
136
132
|
const languages = params.language?.split(",")
|
|
@@ -161,8 +157,15 @@ export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
|
161
157
|
})
|
|
162
158
|
}
|
|
163
159
|
|
|
164
|
-
if (params.packages !== null
|
|
165
|
-
|
|
160
|
+
if (params.packages !== null) {
|
|
161
|
+
let packageTag = {
|
|
162
|
+
label: params.packages,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (tags["packages"]) {
|
|
166
|
+
packageTag = tags["packages"]?.find(el => el.shortId === params.packages)
|
|
167
|
+
}
|
|
168
|
+
|
|
166
169
|
filters.push({
|
|
167
170
|
key: "packages",
|
|
168
171
|
name: t("filter.tags.packages"),
|
|
@@ -220,8 +223,6 @@ export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
|
220
223
|
|
|
221
224
|
return (
|
|
222
225
|
<div className="container">
|
|
223
|
-
{loading && <Loading opacity={true} />}
|
|
224
|
-
|
|
225
226
|
<div className="grid grid-cols-12 gap-4 py-6">
|
|
226
227
|
<div className="col-span-12 sm:col-span-10 md:col-span-10">
|
|
227
228
|
<AutoComplete
|
|
@@ -233,7 +234,6 @@ export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
|
233
234
|
<div className="col-span-12 sm:col-span-2 md:col-span-2">
|
|
234
235
|
<div className="flex justify-end">
|
|
235
236
|
<DialogFilter
|
|
236
|
-
setLoading={setLoading}
|
|
237
237
|
trigger={(
|
|
238
238
|
<Button variant="default" disabled={disabled}>{t("filter.filters")}</Button>
|
|
239
239
|
)}
|
|
@@ -279,33 +279,47 @@ export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
|
279
279
|
<div className="flex flex-row gap-6 pb-6">
|
|
280
280
|
{data.items.length > 0 && (
|
|
281
281
|
<div className="w-80 bg-sidebar rounded-md border pb-4">
|
|
282
|
-
<SidebarHeader className="
|
|
283
|
-
|
|
282
|
+
<SidebarHeader className="!flex-row justify-center items-end font-bold">
|
|
283
|
+
{t("filter.filters")}
|
|
284
|
+
<span className="text-xs text-muted-foreground leading-5">
|
|
285
|
+
{data.pageInfo.totalItemCount} {t("results.results")}
|
|
286
|
+
</span>
|
|
284
287
|
</SidebarHeader>
|
|
285
|
-
<SidebarContent>
|
|
288
|
+
<SidebarContent className="!gap-0">
|
|
289
|
+
|
|
286
290
|
{Object.entries(tags).map(([key, value]: any) => (
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
291
|
+
|
|
292
|
+
<Collapsible defaultOpen key={key} className="py-0 group/collapsible">
|
|
293
|
+
<SidebarGroup>
|
|
294
|
+
|
|
295
|
+
<SidebarGroupLabel asChild className="hover:bg-sidebar-accent text-sidebar-accent-foreground text-sm font-bold">
|
|
296
|
+
<CollapsibleTrigger className="!h-9">
|
|
297
|
+
{t(`filter.tags.${key}`)}
|
|
298
|
+
<ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
|
|
299
|
+
</CollapsibleTrigger>
|
|
300
|
+
</SidebarGroupLabel>
|
|
301
|
+
|
|
302
|
+
<CollapsibleContent>
|
|
303
|
+
<SidebarGroupContent>
|
|
304
|
+
<SidebarMenu>
|
|
305
|
+
<SidebarMenuSub>
|
|
306
|
+
{value.map((item: any) => (
|
|
307
|
+
<SidebarMenuSubItem key={item.shortId}>
|
|
308
|
+
<SidebarMenuSubButton
|
|
309
|
+
className="cursor-pointer"
|
|
310
|
+
isActive={item.active}
|
|
311
|
+
onClick={() => updateFilterParam(key, item)}
|
|
312
|
+
>
|
|
313
|
+
{item.label} ({item.hits}/{item.total})
|
|
314
|
+
</SidebarMenuSubButton>
|
|
315
|
+
</SidebarMenuSubItem>
|
|
316
|
+
))}
|
|
317
|
+
</SidebarMenuSub>
|
|
318
|
+
</SidebarMenu>
|
|
319
|
+
</SidebarGroupContent>
|
|
320
|
+
</CollapsibleContent>
|
|
321
|
+
</SidebarGroup>
|
|
322
|
+
</Collapsible>
|
|
309
323
|
))}
|
|
310
324
|
</SidebarContent>
|
|
311
325
|
</div>
|
|
@@ -313,7 +327,6 @@ export const HomePage: FC<HomePageProps> = ({ data }) => {
|
|
|
313
327
|
|
|
314
328
|
<div className="flex-1">
|
|
315
329
|
<ResultList
|
|
316
|
-
configs={configs}
|
|
317
330
|
items={data.items}
|
|
318
331
|
pagination={data.pageInfo}
|
|
319
332
|
/>
|