@c-rex/components 0.1.2 → 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 +18 -1
- package/src/autocomplete.tsx +28 -31
- package/src/blog-card.tsx +38 -41
- package/src/blur-image.tsx +1 -1
- package/src/breadcrumb.tsx +3 -2
- package/src/check-article-lang.tsx +41 -0
- package/src/dialog-filter.tsx +11 -4
- package/src/navbar/language-switcher/content-language-switch.tsx +15 -14
- package/src/navbar/language-switcher/shared.tsx +1 -1
- package/src/navbar/language-switcher/ui-language-switch.tsx +20 -21
- package/src/navbar/navbar.tsx +26 -37
- package/src/page-wrapper.tsx +16 -0
- package/src/pagination.tsx +65 -0
- package/src/render-article.tsx +16 -0
- package/src/result-list.tsx +19 -54
- package/src/result-view/blog.tsx +5 -19
- package/src/result-view/dropdown-menu.tsx +39 -0
- package/src/result-view/table.tsx +45 -21
- package/src/sidebar.tsx +19 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c-rex/components",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"files": [
|
|
5
5
|
"src"
|
|
6
6
|
],
|
|
@@ -49,6 +49,10 @@
|
|
|
49
49
|
"types": "./src/result-view/table.tsx",
|
|
50
50
|
"import": "./src/result-view/table.tsx"
|
|
51
51
|
},
|
|
52
|
+
"./dropdown-menu": {
|
|
53
|
+
"types": "./src/result-view/dropdown-menu.tsx",
|
|
54
|
+
"import": "./src/result-view/dropdown-menu.tsx"
|
|
55
|
+
},
|
|
52
56
|
"./sidebar": {
|
|
53
57
|
"types": "./src/sidebar.tsx",
|
|
54
58
|
"import": "./src/sidebar.tsx"
|
|
@@ -56,6 +60,18 @@
|
|
|
56
60
|
"./dialog-filter": {
|
|
57
61
|
"types": "./src/dialog-filter.tsx",
|
|
58
62
|
"import": "./src/dialog-filter.tsx"
|
|
63
|
+
},
|
|
64
|
+
"./check-article-lang": {
|
|
65
|
+
"types": "./src/check-article-lang.tsx",
|
|
66
|
+
"import": "./src/check-article-lang.tsx"
|
|
67
|
+
},
|
|
68
|
+
"./render-article": {
|
|
69
|
+
"types": "./src/render-article.tsx",
|
|
70
|
+
"import": "./src/render-article.tsx"
|
|
71
|
+
},
|
|
72
|
+
"./page-wrapper": {
|
|
73
|
+
"types": "./src/page-wrapper.tsx",
|
|
74
|
+
"import": "./src/page-wrapper.tsx"
|
|
59
75
|
}
|
|
60
76
|
},
|
|
61
77
|
"scripts": {
|
|
@@ -102,6 +118,7 @@
|
|
|
102
118
|
"nuqs": "^2.4.3",
|
|
103
119
|
"react": "^18",
|
|
104
120
|
"react-dom": "^18",
|
|
121
|
+
"react-icons": "^5.5.0",
|
|
105
122
|
"tailwindcss-animate": "^1.0.7"
|
|
106
123
|
},
|
|
107
124
|
"eslintConfig": {
|
package/src/autocomplete.tsx
CHANGED
|
@@ -31,6 +31,10 @@ export const AutoComplete = ({
|
|
|
31
31
|
const [suggestions, setSuggestions] = useState<string[]>([]);
|
|
32
32
|
const [open, setOpen] = useState(false);
|
|
33
33
|
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
setQuery(initialValue);
|
|
36
|
+
}, [initialValue]);
|
|
37
|
+
|
|
34
38
|
useEffect(() => {
|
|
35
39
|
const debounceFetch = setTimeout(() => {
|
|
36
40
|
if (query) {
|
|
@@ -56,7 +60,7 @@ export const AutoComplete = ({
|
|
|
56
60
|
onMouseDown={() => setOpen(false)}
|
|
57
61
|
onFocus={() => setOpen(true)}
|
|
58
62
|
>
|
|
59
|
-
<Input />
|
|
63
|
+
<Input value={query} />
|
|
60
64
|
</CommandPrimitive.Input>
|
|
61
65
|
</PopoverAnchor>
|
|
62
66
|
|
|
@@ -75,36 +79,29 @@ export const AutoComplete = ({
|
|
|
75
79
|
}}
|
|
76
80
|
className="w-[--radix-popover-trigger-width] p-0"
|
|
77
81
|
>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
</CommandItem>
|
|
102
|
-
))}
|
|
103
|
-
</CommandGroup>
|
|
104
|
-
) : (
|
|
105
|
-
<CommandEmpty>No suggestions.</CommandEmpty>
|
|
106
|
-
)}
|
|
107
|
-
</CommandList>
|
|
82
|
+
{query.length > 0 && (
|
|
83
|
+
<CommandList>
|
|
84
|
+
{suggestions.length > 0 ? (
|
|
85
|
+
<CommandGroup>
|
|
86
|
+
{suggestions.map((option) => (
|
|
87
|
+
<CommandItem
|
|
88
|
+
key={option}
|
|
89
|
+
value={option}
|
|
90
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
91
|
+
onSelect={(inputValue) => {
|
|
92
|
+
setOpen(false);
|
|
93
|
+
onSelect(inputValue);
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
{option}
|
|
97
|
+
</CommandItem>
|
|
98
|
+
))}
|
|
99
|
+
</CommandGroup>
|
|
100
|
+
) : (
|
|
101
|
+
<CommandEmpty>No suggestions.</CommandEmpty>
|
|
102
|
+
)}
|
|
103
|
+
</CommandList>
|
|
104
|
+
)}
|
|
108
105
|
</PopoverContent>
|
|
109
106
|
</Command>
|
|
110
107
|
</Popover>
|
package/src/blog-card.tsx
CHANGED
|
@@ -1,58 +1,52 @@
|
|
|
1
1
|
import Link from "next/link";
|
|
2
|
-
import React from "react";
|
|
3
2
|
import { cn } from "@c-rex/utils";
|
|
4
3
|
import { BlurImage } from "./blur-image";
|
|
5
4
|
import { useTranslations } from "next-intl";
|
|
5
|
+
import { informationUnitsResponseItem } from "@c-rex/interfaces";
|
|
6
6
|
|
|
7
7
|
interface BlogCardProp {
|
|
8
|
-
|
|
9
|
-
title: string;
|
|
10
|
-
blurDataURL: string;
|
|
11
|
-
image: string;
|
|
12
|
-
description: string;
|
|
13
|
-
authors: string;
|
|
14
|
-
_id: string;
|
|
15
|
-
date: string;
|
|
16
|
-
slug: string;
|
|
17
|
-
};
|
|
8
|
+
item: informationUnitsResponseItem;
|
|
18
9
|
priority?: boolean;
|
|
19
10
|
horizontal?: boolean;
|
|
20
11
|
}
|
|
21
12
|
|
|
22
13
|
export const BlogCard = ({
|
|
23
|
-
|
|
14
|
+
item,
|
|
24
15
|
priority,
|
|
25
16
|
horizontal = false,
|
|
26
17
|
}: BlogCardProp) => {
|
|
27
18
|
const t = useTranslations("results")
|
|
19
|
+
const image = "/img/blog-post-1.webp"
|
|
20
|
+
const blurDataURL = "/img/blog-post-1.webp"
|
|
28
21
|
|
|
29
22
|
return (
|
|
30
23
|
<article
|
|
31
24
|
className={cn(
|
|
32
|
-
"group relative",
|
|
25
|
+
"group relative", `c-rex_result_${item.type.toLowerCase()}`,
|
|
33
26
|
horizontal
|
|
34
27
|
? "grid grid-cols-1 gap-3 md:grid-cols-2 md:gap-6"
|
|
35
28
|
: "flex flex-col space-y-2",
|
|
29
|
+
item.disabled ? "c-rex_result_item_disabled" : ""
|
|
36
30
|
)}
|
|
37
31
|
>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
32
|
+
|
|
33
|
+
<div className="w-full overflow-hidden rounded-xl border">
|
|
34
|
+
<BlurImage
|
|
35
|
+
alt={item.title}
|
|
36
|
+
blurDataURL={blurDataURL}
|
|
37
|
+
className={cn(
|
|
38
|
+
"size-full object-cover object-center",
|
|
39
|
+
horizontal ? "lg:h-72" : null,
|
|
40
|
+
)}
|
|
41
|
+
width={800}
|
|
42
|
+
height={400}
|
|
43
|
+
priority={priority}
|
|
44
|
+
placeholder="blur"
|
|
45
|
+
src={image}
|
|
46
|
+
sizes="(max-width: 768px) 750px, 600px"
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
56
50
|
<div
|
|
57
51
|
className={cn(
|
|
58
52
|
"flex flex-1 flex-col",
|
|
@@ -61,31 +55,34 @@ export const BlogCard = ({
|
|
|
61
55
|
>
|
|
62
56
|
<div className="w-full">
|
|
63
57
|
<h2 className="my-1.5 line-clamp-2 font-heading text-2xl">
|
|
64
|
-
{
|
|
58
|
+
{item.title}
|
|
65
59
|
</h2>
|
|
66
|
-
{
|
|
60
|
+
{item.type && (
|
|
67
61
|
<p className="line-clamp-2 text-muted-foreground">
|
|
68
|
-
{
|
|
62
|
+
{item.type}
|
|
69
63
|
</p>
|
|
70
64
|
)}
|
|
71
65
|
</div>
|
|
72
66
|
{/*
|
|
73
67
|
<div className="mt-4 flex items-center space-x-3">
|
|
74
68
|
<div className="flex items-center -space-x-2">
|
|
75
|
-
{
|
|
76
|
-
<p className="text-sm text-muted-foreground">{
|
|
69
|
+
{item.authors && (
|
|
70
|
+
<p className="text-sm text-muted-foreground">{item.authors}</p>
|
|
77
71
|
)}
|
|
78
72
|
</div>
|
|
79
73
|
|
|
80
|
-
{
|
|
81
|
-
<p className="text-sm text-muted-foreground">{
|
|
74
|
+
{item.date && (
|
|
75
|
+
<p className="text-sm text-muted-foreground">{item.date}</p>
|
|
82
76
|
)}
|
|
83
77
|
</div>
|
|
84
78
|
*/}
|
|
85
79
|
</div>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
|
|
81
|
+
{!item.disabled && (
|
|
82
|
+
<Link href={item.link} className="absolute inset-0" target="_blank">
|
|
83
|
+
<span className="sr-only">{t("viewArticle")}</span>
|
|
84
|
+
</Link>
|
|
85
|
+
)}
|
|
89
86
|
</article>
|
|
90
87
|
);
|
|
91
88
|
};
|
package/src/blur-image.tsx
CHANGED
package/src/breadcrumb.tsx
CHANGED
|
@@ -13,10 +13,11 @@ import { useTranslations } from 'next-intl';
|
|
|
13
13
|
|
|
14
14
|
interface BreadcrumbProps {
|
|
15
15
|
items: TreeOfContent[];
|
|
16
|
+
lang: string;
|
|
16
17
|
loading?: boolean;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading }) => {
|
|
20
|
+
export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading, lang }) => {
|
|
20
21
|
const t = useTranslations("breadcrumbs");
|
|
21
22
|
if (!items) {
|
|
22
23
|
return null;
|
|
@@ -61,7 +62,7 @@ export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading }) => {
|
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
return (
|
|
64
|
-
<BreadcrumbComponent>
|
|
65
|
+
<BreadcrumbComponent lang={lang}>
|
|
65
66
|
<BreadcrumbList>
|
|
66
67
|
{newItemList.map((item, index) => {
|
|
67
68
|
const isLast = index === newItemList.length - 1;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { FC, useEffect, useRef } from "react";
|
|
4
|
+
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
5
|
+
import { SidebarAvailableVersionsInterface } from "@c-rex/interfaces";
|
|
6
|
+
import { toast } from "sonner"
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
availableVersions: SidebarAvailableVersionsInterface[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const CheckArticleLangToast: FC<Props> = ({ availableVersions }) => {
|
|
13
|
+
const { contentLang } = useAppConfig()
|
|
14
|
+
const hasRun = useRef(false)
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (hasRun.current) return
|
|
18
|
+
hasRun.current = true
|
|
19
|
+
|
|
20
|
+
const activeArticle = availableVersions.filter((item) => item.active)[0]
|
|
21
|
+
if (activeArticle == undefined || activeArticle.lang == contentLang) return
|
|
22
|
+
|
|
23
|
+
const articleAvailable = availableVersions.find((item) => item.lang === contentLang)
|
|
24
|
+
if (articleAvailable == undefined) return
|
|
25
|
+
|
|
26
|
+
showToast(articleAvailable.lang, articleAvailable.link)
|
|
27
|
+
}, [])
|
|
28
|
+
|
|
29
|
+
const showToast = (lang: string, link: string) => {
|
|
30
|
+
toast(`Read ${lang} version`, {
|
|
31
|
+
description: `This article is also available in ${lang}`,
|
|
32
|
+
action: {
|
|
33
|
+
label: `Open ${lang} version`,
|
|
34
|
+
onClick: () => window.location.href = link,
|
|
35
|
+
},
|
|
36
|
+
duration: 10000,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return null
|
|
41
|
+
}
|
package/src/dialog-filter.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "@c-rex/ui/dialog"
|
|
12
12
|
import { Checkbox } from "@c-rex/ui/checkbox"
|
|
13
13
|
import { Label } from "@c-rex/ui/label"
|
|
14
|
-
import {
|
|
14
|
+
import { parseAsInteger, parseAsString, useQueryStates } from "nuqs"
|
|
15
15
|
import { useTranslations } from "next-intl"
|
|
16
16
|
|
|
17
17
|
interface DialogFilterProps {
|
|
@@ -27,10 +27,13 @@ export const DialogFilter: FC<DialogFilterProps> = ({
|
|
|
27
27
|
}) => {
|
|
28
28
|
const t = useTranslations();
|
|
29
29
|
|
|
30
|
-
const [
|
|
30
|
+
const [params, setParams] = useQueryStates({
|
|
31
|
+
language: parseAsString,
|
|
32
|
+
page: parseAsInteger,
|
|
33
|
+
}, {
|
|
31
34
|
history: 'push',
|
|
32
35
|
shallow: false,
|
|
33
|
-
})
|
|
36
|
+
});
|
|
34
37
|
|
|
35
38
|
const [languages, setLanguages] = useState(availableLanguages.map((item: any) => {
|
|
36
39
|
const checked = startSelectedLanguages.includes(item.value)
|
|
@@ -50,7 +53,11 @@ export const DialogFilter: FC<DialogFilterProps> = ({
|
|
|
50
53
|
|
|
51
54
|
const apply = () => {
|
|
52
55
|
const selectedLanguages = languages.filter((item: any) => item.checked).map((item: any) => item.value).join(',')
|
|
53
|
-
|
|
56
|
+
|
|
57
|
+
setParams({
|
|
58
|
+
page: 1,
|
|
59
|
+
language: selectedLanguages,
|
|
60
|
+
});
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
return (
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
"use client"
|
|
2
2
|
|
|
3
|
-
import React, {
|
|
4
|
-
import { LanguageAndCountries } from "@c-rex/interfaces";
|
|
3
|
+
import React, { startTransition } from "react";
|
|
5
4
|
import { SharedLanguageSwitch } from "./shared";
|
|
6
|
-
import { setCookie } from "@c-rex/utils
|
|
7
|
-
import { CONTENT_LANG_KEY
|
|
5
|
+
import { getFromCookieString, setCookie } from "@c-rex/utils";
|
|
6
|
+
import { CONTENT_LANG_KEY } from "@c-rex/constants";
|
|
8
7
|
import { useQueryState } from "nuqs"
|
|
8
|
+
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
availableLanguagesAndCountries
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const [contentLanguage, setContentLanguage] = useQueryState('language', {
|
|
10
|
+
export const ContentLanguageSwitch = () => {
|
|
11
|
+
const { availableLanguagesAndCountries, setContentLang } = useAppConfig()
|
|
12
|
+
const contentLang = getFromCookieString(document.cookie, CONTENT_LANG_KEY)
|
|
13
|
+
const [queryLanguage, setContentLanguage] = useQueryState('language', {
|
|
16
14
|
history: 'push',
|
|
17
15
|
shallow: false,
|
|
18
|
-
defaultValue: DEFAULT_UI_LANG
|
|
19
16
|
})
|
|
20
17
|
|
|
21
18
|
const changeContentLanguage = (locale: string) => {
|
|
22
19
|
startTransition(() => {
|
|
23
20
|
setCookie(CONTENT_LANG_KEY, locale)
|
|
24
|
-
|
|
21
|
+
setContentLang(locale)
|
|
22
|
+
|
|
23
|
+
if (queryLanguage != null) {
|
|
24
|
+
setContentLanguage(locale)
|
|
25
|
+
}
|
|
25
26
|
});
|
|
26
27
|
};
|
|
27
28
|
|
|
@@ -29,7 +30,7 @@ export const ContentLanguageSwitch: FC<ContentLanguageSwitchProps> = ({ availabl
|
|
|
29
30
|
<SharedLanguageSwitch
|
|
30
31
|
availableLanguagesAndCountries={availableLanguagesAndCountries}
|
|
31
32
|
changeLanguage={changeContentLanguage}
|
|
32
|
-
selected={
|
|
33
|
+
selected={contentLang}
|
|
33
34
|
/>
|
|
34
35
|
);
|
|
35
36
|
};
|
|
@@ -23,8 +23,8 @@ export const SharedLanguageSwitch: FC<SharedLanguageSwitchProps> = ({ availableL
|
|
|
23
23
|
{availableLanguagesAndCountries.map(item => {
|
|
24
24
|
return (
|
|
25
25
|
<DropdownMenuRadioItem key={item.value} value={item.value}>
|
|
26
|
-
<span>{item.lang.toUpperCase()}</span>
|
|
27
26
|
{getFlagIcon(item.country)}
|
|
27
|
+
<span>{item.lang.toUpperCase()}</span>
|
|
28
28
|
</DropdownMenuRadioItem>
|
|
29
29
|
)
|
|
30
30
|
})}
|
|
@@ -1,38 +1,37 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import React, { FC, startTransition
|
|
4
|
-
import { LanguageAndCountries } from "@c-rex/interfaces";
|
|
3
|
+
import React, { FC, startTransition } from "react";
|
|
5
4
|
import { SharedLanguageSwitch } from "./shared";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
interface UILanguageSwitchProps {
|
|
10
|
-
availableLanguagesAndCountries: LanguageAndCountries[];
|
|
11
|
-
}
|
|
12
|
-
export const UILanguageSwitch: FC<UILanguageSwitchProps> = ({ availableLanguagesAndCountries }) => {
|
|
13
|
-
const [selected, setSelected] = useState(DEFAULT_UI_LANG);
|
|
14
|
-
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
const allCookies = document.cookie
|
|
17
|
-
const uiLang = allCookies
|
|
18
|
-
.split('; ')
|
|
19
|
-
.find(row => row.startsWith(`${UI_LANG_KEY}=`))
|
|
20
|
-
?.split('=')[1]
|
|
21
|
-
setSelected(uiLang as string)
|
|
22
|
-
}, [])
|
|
5
|
+
import { UI_LANG_KEY, UI_LANG_OPTIONS } from "@c-rex/constants";
|
|
6
|
+
import { getCountryCodeByLang, getFromCookieString, setCookie } from "@c-rex/utils";
|
|
7
|
+
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
23
8
|
|
|
9
|
+
export const UILanguageSwitch: FC = () => {
|
|
10
|
+
const { setUiLang } = useAppConfig()
|
|
11
|
+
const uiLang = getFromCookieString(document.cookie, UI_LANG_KEY).toLowerCase()
|
|
12
|
+
const UILanguages = UI_LANG_OPTIONS.map((lang) => {
|
|
13
|
+
const langCode = lang.split("-")[0] as string;
|
|
14
|
+
return {
|
|
15
|
+
value: lang,
|
|
16
|
+
lang: langCode,
|
|
17
|
+
country: getCountryCodeByLang(langCode)
|
|
18
|
+
};
|
|
19
|
+
})
|
|
24
20
|
|
|
25
21
|
const setUILanguage = (locale: string) => {
|
|
26
22
|
startTransition(() => {
|
|
27
23
|
setCookie(UI_LANG_KEY, locale)
|
|
24
|
+
setUiLang(locale)
|
|
25
|
+
|
|
26
|
+
window.location.reload()
|
|
28
27
|
});
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
return (
|
|
32
31
|
<SharedLanguageSwitch
|
|
33
|
-
availableLanguagesAndCountries={
|
|
32
|
+
availableLanguagesAndCountries={UILanguages}
|
|
34
33
|
changeLanguage={setUILanguage}
|
|
35
|
-
selected={
|
|
34
|
+
selected={uiLang}
|
|
36
35
|
/>
|
|
37
36
|
);
|
|
38
37
|
};
|
package/src/navbar/navbar.tsx
CHANGED
|
@@ -2,9 +2,6 @@ import React, { FC } from "react";
|
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import Image from "next/image";
|
|
4
4
|
import { SignOut, SignInBtn } from "./sign-in-out-btns";
|
|
5
|
-
import { getServerSession } from "next-auth";
|
|
6
|
-
import { getCookie } from '@c-rex/utils/next-cookies';
|
|
7
|
-
import { ConfigInterface } from "@c-rex/interfaces";
|
|
8
5
|
import {
|
|
9
6
|
DropdownMenu,
|
|
10
7
|
DropdownMenuContent,
|
|
@@ -16,47 +13,31 @@ import {
|
|
|
16
13
|
DropdownMenuSubTrigger,
|
|
17
14
|
DropdownMenuTrigger,
|
|
18
15
|
} from "@c-rex/ui/dropdown-menu"
|
|
19
|
-
import { SDK_CONFIG_KEY, UI_LANG_OPTIONS } from "@c-rex/constants";
|
|
20
|
-
import { LanguageService } from "@c-rex/services";
|
|
21
16
|
import { Settings } from "lucide-react";
|
|
22
17
|
import { ContentLanguageSwitch } from "./language-switcher/content-language-switch";
|
|
23
18
|
import { getTranslations } from "next-intl/server";
|
|
24
|
-
import { getCountryCodeByLang } from "@c-rex/utils";
|
|
25
19
|
import { UILanguageSwitch } from "./language-switcher/ui-language-switch";
|
|
20
|
+
import { getServerSession } from "next-auth";
|
|
21
|
+
import { getConfigs } from "@c-rex/utils/next-cookies";
|
|
26
22
|
|
|
27
23
|
interface NavBarProps {
|
|
28
|
-
|
|
29
|
-
large?: boolean;
|
|
24
|
+
title: string;
|
|
30
25
|
}
|
|
31
26
|
|
|
32
|
-
export const NavBar: FC<NavBarProps> = async () => {
|
|
27
|
+
export const NavBar: FC<NavBarProps> = async ({ title }) => {
|
|
33
28
|
const t = await getTranslations();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const jsonConfigs = await getCookie(SDK_CONFIG_KEY);
|
|
37
|
-
if (!jsonConfigs) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const configs: ConfigInterface = JSON.parse(jsonConfigs);
|
|
42
|
-
const service = new LanguageService(configs.languageSwitcher.endpoint);
|
|
43
|
-
const contentLanguages = await service.getLanguagesAndCountries();
|
|
29
|
+
const configs = getConfigs();
|
|
44
30
|
|
|
31
|
+
let session: any;
|
|
45
32
|
if (configs.OIDC.user.enabled) {
|
|
46
33
|
session = await getServerSession();
|
|
47
34
|
}
|
|
48
35
|
|
|
49
|
-
const UILanguages = UI_LANG_OPTIONS.map((lang) => ({
|
|
50
|
-
value: lang,
|
|
51
|
-
lang: lang,
|
|
52
|
-
country: getCountryCodeByLang(lang)
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
36
|
return (
|
|
56
|
-
<header className="sticky top-0 z-40 flex w-full
|
|
57
|
-
<div className="
|
|
58
|
-
<div className="flex
|
|
59
|
-
<Link href="/" className="flex items-center space-x-1.5">
|
|
37
|
+
<header className="sticky top-0 z-40 flex w-full backdrop-blur-xl transition-all bg-transparent border-b justify-center py-4">
|
|
38
|
+
<div className="w-full px-4 flex justify-between">
|
|
39
|
+
<div className="flex">
|
|
40
|
+
<Link href="/" className="flex items-center justify-center space-x-1.5 w-60">
|
|
60
41
|
<Image
|
|
61
42
|
src="/img/logo.png"
|
|
62
43
|
alt="C-rex Logo"
|
|
@@ -64,10 +45,22 @@ export const NavBar: FC<NavBarProps> = async () => {
|
|
|
64
45
|
height={50}
|
|
65
46
|
/>
|
|
66
47
|
</Link>
|
|
67
|
-
|
|
48
|
+
|
|
49
|
+
{title.length > 0 && (
|
|
50
|
+
<div className="flex items-center">
|
|
51
|
+
<h1 className="px-4 text-3xl font-bold tracking-tight text-balance">{title}</h1>
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
68
54
|
</div>
|
|
69
55
|
|
|
70
56
|
<div className="flex items-center space-x-3">
|
|
57
|
+
{/*
|
|
58
|
+
<div className="flex items-center px-3 border rounded-full h-8">
|
|
59
|
+
<Search className="h-4 w-4 shrink-0 opacity-50" />
|
|
60
|
+
<Input variant="embedded" placeholder="Search" />
|
|
61
|
+
</div>
|
|
62
|
+
*/}
|
|
63
|
+
|
|
71
64
|
{configs.OIDC.user.enabled && (
|
|
72
65
|
<>
|
|
73
66
|
{session ? (
|
|
@@ -86,7 +79,7 @@ export const NavBar: FC<NavBarProps> = async () => {
|
|
|
86
79
|
<DropdownMenuTrigger>
|
|
87
80
|
<Settings />
|
|
88
81
|
</DropdownMenuTrigger>
|
|
89
|
-
<DropdownMenuContent>
|
|
82
|
+
<DropdownMenuContent align="start" sideOffset={20} alignOffset={20}>
|
|
90
83
|
<DropdownMenuLabel>{t("accountSettings.accountSettings")}</DropdownMenuLabel>
|
|
91
84
|
<DropdownMenuSeparator />
|
|
92
85
|
|
|
@@ -97,9 +90,7 @@ export const NavBar: FC<NavBarProps> = async () => {
|
|
|
97
90
|
|
|
98
91
|
<DropdownMenuPortal>
|
|
99
92
|
<DropdownMenuSubContent>
|
|
100
|
-
<ContentLanguageSwitch
|
|
101
|
-
availableLanguagesAndCountries={contentLanguages}
|
|
102
|
-
/>
|
|
93
|
+
<ContentLanguageSwitch />
|
|
103
94
|
</DropdownMenuSubContent>
|
|
104
95
|
</DropdownMenuPortal>
|
|
105
96
|
</DropdownMenuSub>
|
|
@@ -112,9 +103,7 @@ export const NavBar: FC<NavBarProps> = async () => {
|
|
|
112
103
|
|
|
113
104
|
<DropdownMenuPortal>
|
|
114
105
|
<DropdownMenuSubContent>
|
|
115
|
-
<UILanguageSwitch
|
|
116
|
-
availableLanguagesAndCountries={UILanguages}
|
|
117
|
-
/>
|
|
106
|
+
<UILanguageSwitch />
|
|
118
107
|
</DropdownMenuSubContent>
|
|
119
108
|
</DropdownMenuPortal>
|
|
120
109
|
</DropdownMenuSub>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { NavBar } from './navbar/navbar';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
title: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const PageWrapper = ({ children, title }: Props) => {
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
<NavBar title={title} />
|
|
13
|
+
{children}
|
|
14
|
+
</>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { FC } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Pagination as PaginationUI,
|
|
4
|
+
PaginationContent,
|
|
5
|
+
PaginationItem,
|
|
6
|
+
PaginationLink,
|
|
7
|
+
PaginationNext,
|
|
8
|
+
PaginationPrevious,
|
|
9
|
+
} from "@c-rex/ui/pagination"
|
|
10
|
+
import { parseAsInteger, useQueryState } from "nuqs";
|
|
11
|
+
|
|
12
|
+
interface PaginationProps {
|
|
13
|
+
totalPages: number;
|
|
14
|
+
currentPage: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Pagination: FC<PaginationProps> = ({ totalPages, currentPage }) => {
|
|
18
|
+
const disabledClass = "opacity-50 pointer-events-none";
|
|
19
|
+
|
|
20
|
+
const [_, setPage] = useQueryState('page',
|
|
21
|
+
parseAsInteger.withOptions({
|
|
22
|
+
history: 'push',
|
|
23
|
+
shallow: false,
|
|
24
|
+
})
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<PaginationUI className="py-4">
|
|
29
|
+
<PaginationContent>
|
|
30
|
+
<PaginationItem>
|
|
31
|
+
<PaginationPrevious
|
|
32
|
+
href="#"
|
|
33
|
+
className={currentPage === 1 ? disabledClass : ""}
|
|
34
|
+
onClick={() => setPage(currentPage - 1)}
|
|
35
|
+
/>
|
|
36
|
+
</PaginationItem>
|
|
37
|
+
|
|
38
|
+
{Array.from({ length: totalPages }, (_, index) => index + 1).map((page) => (
|
|
39
|
+
<PaginationItem key={page}>
|
|
40
|
+
<PaginationLink
|
|
41
|
+
href="#"
|
|
42
|
+
isActive={page === currentPage}
|
|
43
|
+
onClick={() => setPage(page)}
|
|
44
|
+
>
|
|
45
|
+
{page}
|
|
46
|
+
</PaginationLink>
|
|
47
|
+
</PaginationItem>
|
|
48
|
+
))}
|
|
49
|
+
|
|
50
|
+
{/*
|
|
51
|
+
<PaginationItem>
|
|
52
|
+
<PaginationEllipsis />
|
|
53
|
+
</PaginationItem>
|
|
54
|
+
*/}
|
|
55
|
+
<PaginationItem>
|
|
56
|
+
<PaginationNext
|
|
57
|
+
href="#"
|
|
58
|
+
onClick={() => setPage(currentPage + 1)}
|
|
59
|
+
className={currentPage === totalPages ? disabledClass : ""}
|
|
60
|
+
/>
|
|
61
|
+
</PaginationItem>
|
|
62
|
+
</PaginationContent>
|
|
63
|
+
</PaginationUI>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
type Props = {
|
|
4
|
+
htmlContent: string,
|
|
5
|
+
contentLang: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const RenderArticle = ({ htmlContent, contentLang }: Props) => {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
lang={contentLang}
|
|
12
|
+
className="pb-4"
|
|
13
|
+
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
package/src/result-list.tsx
CHANGED
|
@@ -1,72 +1,37 @@
|
|
|
1
1
|
import React, { FC } from "react";
|
|
2
|
-
import { ConfigInterface,
|
|
2
|
+
import { ConfigInterface, DefaultPageInfo, informationUnitsResponseItem, } from "@c-rex/interfaces";
|
|
3
3
|
import { Empty } from "./empty";
|
|
4
4
|
import BlogView from './result-view/blog';
|
|
5
5
|
import TableView from './result-view/table';
|
|
6
|
-
import {
|
|
7
|
-
import { DEFAULT_UI_LANG, RESULT_TYPES } from "@c-rex/constants";
|
|
6
|
+
import { Pagination } from "./pagination";
|
|
8
7
|
|
|
9
8
|
interface ResultListProps {
|
|
10
|
-
items:
|
|
11
|
-
configs: ConfigInterface
|
|
9
|
+
items: informationUnitsResponseItem[];
|
|
10
|
+
configs: ConfigInterface;
|
|
11
|
+
pagination: DefaultPageInfo;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export const ResultList: FC<ResultListProps> = ({ items, configs }) => {
|
|
15
|
-
const
|
|
16
|
-
const ViewComponent = configs.resultViewStyle == "table" ? TableView : BlogView;
|
|
17
|
-
|
|
18
|
-
const getTitle = (labels: Labels[]): string => {
|
|
19
|
-
if (labels === undefined) {
|
|
20
|
-
return "";
|
|
21
|
-
}
|
|
22
|
-
return labels.map((item) => item.value).join();
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const getType = (labels: Labels[]): { localeLabel: string, comparableLabel: string } => {
|
|
26
|
-
const localeLabel = labels.find((item) => item.language === locale);
|
|
27
|
-
const comparableLabel = labels.find((item) => item.language === DEFAULT_UI_LANG);
|
|
28
|
-
|
|
29
|
-
if (!localeLabel || !comparableLabel) {
|
|
30
|
-
return {
|
|
31
|
-
localeLabel: "",
|
|
32
|
-
comparableLabel: ""
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
localeLabel: localeLabel.value,
|
|
38
|
-
comparableLabel: comparableLabel.value
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const handleByType = (type: string, shortId: string) => {
|
|
43
|
-
switch (type) {
|
|
44
|
-
case RESULT_TYPES.TOPIC:
|
|
45
|
-
window.location.href = `/topics/${shortId}`;
|
|
46
|
-
break;
|
|
47
|
-
case RESULT_TYPES.DOCUMENT:
|
|
48
|
-
//should check if its a pdf or not
|
|
49
|
-
// if pdf then open in new tab
|
|
50
|
-
// else download the file
|
|
51
|
-
window.open(`/documents/${shortId}`, '_blank');
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
14
|
+
export const ResultList: FC<ResultListProps> = ({ items, configs, pagination }: ResultListProps) => {
|
|
15
|
+
const ViewComponent = configs.results.resultViewStyle == "table" ? TableView : BlogView;
|
|
55
16
|
|
|
56
17
|
return (
|
|
57
|
-
|
|
18
|
+
<>
|
|
58
19
|
{
|
|
59
20
|
items.length == 0 ? (
|
|
60
21
|
<Empty />
|
|
61
22
|
) : (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
23
|
+
<>
|
|
24
|
+
<ViewComponent
|
|
25
|
+
items={items}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<Pagination
|
|
29
|
+
totalPages={pagination.pageCount}
|
|
30
|
+
currentPage={pagination.pageNumber}
|
|
31
|
+
/>
|
|
32
|
+
</>
|
|
68
33
|
)
|
|
69
34
|
}
|
|
70
|
-
|
|
35
|
+
</>
|
|
71
36
|
);
|
|
72
37
|
};
|
package/src/result-view/blog.tsx
CHANGED
|
@@ -1,32 +1,18 @@
|
|
|
1
1
|
import React, { FC } from "react";
|
|
2
2
|
import { BlogCard } from "../blog-card";
|
|
3
|
-
import {
|
|
3
|
+
import { informationUnitsResponseItem } from "@c-rex/interfaces";
|
|
4
4
|
|
|
5
5
|
interface BlogViewProps {
|
|
6
|
-
items:
|
|
7
|
-
getType: (labels: Labels[]) => { localeLabel: string, comparableLabel: string };
|
|
8
|
-
getTitle: (labels: Labels[]) => string;
|
|
6
|
+
items: informationUnitsResponseItem[];
|
|
9
7
|
}
|
|
10
8
|
|
|
11
|
-
const BlogView: FC<BlogViewProps> = ({ items
|
|
12
|
-
|
|
9
|
+
const BlogView: FC<BlogViewProps> = ({ items }) => {
|
|
13
10
|
return (
|
|
14
11
|
<div className="grid gap-8 md:grid-cols-2 md:gap-x-6 md:gap-y-10 xl:grid-cols-3">
|
|
15
12
|
{items.map((item, index) => {
|
|
13
|
+
|
|
16
14
|
return (
|
|
17
|
-
<BlogCard
|
|
18
|
-
key={index}
|
|
19
|
-
data={{
|
|
20
|
-
title: getTitle(item.labels),
|
|
21
|
-
blurDataURL: "/img/blog-post-1.webp",
|
|
22
|
-
image: "/img/blog-post-1.webp",
|
|
23
|
-
description: getType(item.class.labels).localeLabel,
|
|
24
|
-
authors: "item.authors",
|
|
25
|
-
_id: "item._id",
|
|
26
|
-
date: "item.date",
|
|
27
|
-
slug: `info/${item.shortId}`,
|
|
28
|
-
}}
|
|
29
|
-
/>
|
|
15
|
+
<BlogCard key={index} item={item} />
|
|
30
16
|
);
|
|
31
17
|
})}
|
|
32
18
|
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, { FC, JSX } from "react";
|
|
2
|
+
import {
|
|
3
|
+
DropdownMenu as DropdownMenuComp,
|
|
4
|
+
DropdownMenuContent,
|
|
5
|
+
DropdownMenuItem,
|
|
6
|
+
DropdownMenuTrigger,
|
|
7
|
+
} from "@c-rex/ui/dropdown-menu";
|
|
8
|
+
|
|
9
|
+
interface DropdownMenuProps {
|
|
10
|
+
items: {
|
|
11
|
+
format: string;
|
|
12
|
+
link: string;
|
|
13
|
+
}[];
|
|
14
|
+
icon: JSX.Element;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const DropdownMenu: FC<DropdownMenuProps> = ({
|
|
18
|
+
items, icon
|
|
19
|
+
}) => {
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<DropdownMenuComp>
|
|
23
|
+
<DropdownMenuTrigger className="mx-2">
|
|
24
|
+
{icon}
|
|
25
|
+
</DropdownMenuTrigger>
|
|
26
|
+
<DropdownMenuContent>
|
|
27
|
+
{items.map((file, index) => {
|
|
28
|
+
return (
|
|
29
|
+
<DropdownMenuItem key={index}>
|
|
30
|
+
<a href={file.link} target="_blank" rel="noreferrer">
|
|
31
|
+
{file.format}
|
|
32
|
+
</a>
|
|
33
|
+
</DropdownMenuItem>
|
|
34
|
+
)
|
|
35
|
+
})}
|
|
36
|
+
</DropdownMenuContent>
|
|
37
|
+
</DropdownMenuComp>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FC } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Table,
|
|
4
4
|
TableBody,
|
|
@@ -6,19 +6,17 @@ import {
|
|
|
6
6
|
TableHeader,
|
|
7
7
|
TableRow,
|
|
8
8
|
} from "@c-rex/ui/table";
|
|
9
|
-
import {
|
|
9
|
+
import { informationUnitsResponseItem } from "@c-rex/interfaces";
|
|
10
10
|
import { useTranslations } from "next-intl";
|
|
11
|
+
import { Ban, CloudDownload, CloudOff, Eye, EyeOff } from "lucide-react";
|
|
12
|
+
import { DropdownMenu } from "./dropdown-menu";
|
|
11
13
|
|
|
12
14
|
interface TableViewProps {
|
|
13
|
-
items:
|
|
14
|
-
getType: (labels: Labels[]) => { localeLabel: string, comparableLabel: string };
|
|
15
|
-
getTitle: (labels: Labels[]) => string;
|
|
16
|
-
handleByType: any;
|
|
15
|
+
items: informationUnitsResponseItem[];
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
const TableView: FC<TableViewProps> = ({ items
|
|
18
|
+
const TableView: FC<TableViewProps> = ({ items }) => {
|
|
20
19
|
const t = useTranslations("results")
|
|
21
|
-
|
|
22
20
|
return (
|
|
23
21
|
<div className="rounded-md border">
|
|
24
22
|
<Table>
|
|
@@ -27,25 +25,51 @@ const TableView: FC<TableViewProps> = ({ items, getType, getTitle, handleByType
|
|
|
27
25
|
<TableCell>{t("title")}</TableCell>
|
|
28
26
|
<TableCell>{t("type")}</TableCell>
|
|
29
27
|
<TableCell>{t("language")}</TableCell>
|
|
28
|
+
<TableCell>{t("files")}</TableCell>
|
|
30
29
|
</TableRow>
|
|
31
30
|
</TableHeader>
|
|
32
31
|
<TableBody>
|
|
33
|
-
{items.map((item
|
|
34
|
-
const
|
|
35
|
-
let lang = "";
|
|
36
|
-
|
|
37
|
-
if (item.languages.length > 0) {
|
|
38
|
-
lang = item.languages[0] as string;
|
|
39
|
-
lang = lang.split("-")[0] as string;
|
|
40
|
-
}
|
|
32
|
+
{items.map((item, index) => {
|
|
33
|
+
const clazz = `h-12 c-rex_result_row c-rex_result_${item.type.toLowerCase()} ${item.disabled ? "c-rex_result_item_disabled" : ""}`
|
|
41
34
|
|
|
42
35
|
return (
|
|
43
|
-
<TableRow key={index}>
|
|
44
|
-
<TableCell className="
|
|
45
|
-
{
|
|
36
|
+
<TableRow key={index} className={clazz}>
|
|
37
|
+
<TableCell className="h-12 c-rex_result_cell">
|
|
38
|
+
{item.disabled ? (item.title) : (<a href={item.link}>{item.title}</a>)}
|
|
39
|
+
</TableCell>
|
|
40
|
+
<TableCell>{item.type}</TableCell>
|
|
41
|
+
<TableCell>{item.language}</TableCell>
|
|
42
|
+
<TableCell>
|
|
43
|
+
{(item.disabled || (item.filesToDownload.length == 0 && item.filesToOpen.length == 0)) ? (
|
|
44
|
+
<button disabled className="mx-2">
|
|
45
|
+
<Ban />
|
|
46
|
+
</button>
|
|
47
|
+
) : (
|
|
48
|
+
<>
|
|
49
|
+
{item.filesToDownload.length == 0 ? (
|
|
50
|
+
<button disabled className="mx-2">
|
|
51
|
+
<CloudOff />
|
|
52
|
+
</button>
|
|
53
|
+
) : (
|
|
54
|
+
<DropdownMenu
|
|
55
|
+
items={item.filesToDownload}
|
|
56
|
+
icon={<CloudDownload />}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
|
|
60
|
+
{item.filesToOpen.length == 0 ? (
|
|
61
|
+
<button disabled className="mx-2">
|
|
62
|
+
<EyeOff />
|
|
63
|
+
</button>
|
|
64
|
+
) : (
|
|
65
|
+
<DropdownMenu
|
|
66
|
+
items={item.filesToOpen}
|
|
67
|
+
icon={<Eye />}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
</>
|
|
71
|
+
)}
|
|
46
72
|
</TableCell>
|
|
47
|
-
<TableCell>{type.localeLabel}</TableCell>
|
|
48
|
-
<TableCell>{lang.toUpperCase()}</TableCell>
|
|
49
73
|
</TableRow>
|
|
50
74
|
)
|
|
51
75
|
})}
|
package/src/sidebar.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import React, { ComponentProps, JSX } from "react";
|
|
|
4
4
|
import {
|
|
5
5
|
Sidebar,
|
|
6
6
|
SidebarContent,
|
|
7
|
+
SidebarFooter,
|
|
7
8
|
SidebarGroup,
|
|
8
9
|
SidebarGroupLabel,
|
|
9
10
|
SidebarMenu,
|
|
@@ -12,12 +13,12 @@ import {
|
|
|
12
13
|
SidebarMenuSub,
|
|
13
14
|
SidebarMenuSubButton,
|
|
14
15
|
SidebarMenuSubItem,
|
|
15
|
-
SidebarRail
|
|
16
16
|
} from "@c-rex/ui/sidebar";
|
|
17
17
|
import { Skeleton } from "@c-rex/ui/skeleton";
|
|
18
18
|
import { SidebarAvailableVersionsInterface, TreeOfContent } from "@c-rex/interfaces";
|
|
19
19
|
import * as Flags from 'country-flag-icons/react/3x2';
|
|
20
20
|
import { useTranslations } from "next-intl";
|
|
21
|
+
import { cn } from "@c-rex/utils";
|
|
21
22
|
|
|
22
23
|
interface SidebarProps extends ComponentProps<typeof Sidebar> {
|
|
23
24
|
data: TreeOfContent[];
|
|
@@ -25,7 +26,7 @@ interface SidebarProps extends ComponentProps<typeof Sidebar> {
|
|
|
25
26
|
availableVersions: SidebarAvailableVersionsInterface[]
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export function AppSidebar({ data, availableVersions, loading, ...props }: SidebarProps) {
|
|
29
|
+
export function AppSidebar({ data, availableVersions, loading, className, lang, ...props }: SidebarProps) {
|
|
29
30
|
const t = useTranslations();
|
|
30
31
|
|
|
31
32
|
const getFlagIcon = (countryCode: string): JSX.Element | null => {
|
|
@@ -39,7 +40,7 @@ export function AppSidebar({ data, availableVersions, loading, ...props }: Sideb
|
|
|
39
40
|
const tableOfContentGroup = (): JSX.Element | null => {
|
|
40
41
|
if (loading) {
|
|
41
42
|
return (
|
|
42
|
-
|
|
43
|
+
<SidebarGroup className="pt-4">
|
|
43
44
|
<Skeleton className="w-auto h-10 mb-2" />
|
|
44
45
|
<Skeleton className="w-auto h-10 mb-2" />
|
|
45
46
|
<Skeleton className="w-auto h-10 mb-2 ml-8" />
|
|
@@ -47,15 +48,20 @@ export function AppSidebar({ data, availableVersions, loading, ...props }: Sideb
|
|
|
47
48
|
<Skeleton className="w-auto h-10 mb-2 ml-8" />
|
|
48
49
|
<Skeleton className="w-auto h-10 mb-2 ml-8" />
|
|
49
50
|
<Skeleton className="w-auto h-10 mb-2" />
|
|
50
|
-
|
|
51
|
+
</SidebarGroup>
|
|
51
52
|
)
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
if (data.length == 0) return null;
|
|
55
56
|
|
|
57
|
+
const langProp = {} as any
|
|
58
|
+
|
|
59
|
+
if (lang) {
|
|
60
|
+
langProp.lang = lang
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
return (
|
|
57
|
-
<SidebarGroup
|
|
58
|
-
<SidebarGroupLabel>{t("tableOfContent")}:</SidebarGroupLabel>
|
|
64
|
+
<SidebarGroup {...langProp}>
|
|
59
65
|
<SidebarMenu>
|
|
60
66
|
{data.map((item) => (
|
|
61
67
|
<SidebarMenuItem key={item.id}>
|
|
@@ -93,10 +99,10 @@ export function AppSidebar({ data, availableVersions, loading, ...props }: Sideb
|
|
|
93
99
|
if (availableVersions.length == 0) return null;
|
|
94
100
|
|
|
95
101
|
return (
|
|
96
|
-
|
|
102
|
+
<>
|
|
97
103
|
<SidebarGroupLabel>{t("availableIn")}:</SidebarGroupLabel>
|
|
98
104
|
<SidebarMenu>
|
|
99
|
-
{availableVersions.map((item
|
|
105
|
+
{availableVersions.map((item) => {
|
|
100
106
|
return (
|
|
101
107
|
<SidebarMenuItem key={item.shortId}>
|
|
102
108
|
<SidebarMenuButton asChild isActive={item.active}>
|
|
@@ -108,18 +114,19 @@ export function AppSidebar({ data, availableVersions, loading, ...props }: Sideb
|
|
|
108
114
|
)
|
|
109
115
|
})}
|
|
110
116
|
</SidebarMenu>
|
|
111
|
-
|
|
117
|
+
</>
|
|
112
118
|
)
|
|
113
119
|
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
return (
|
|
117
|
-
<Sidebar
|
|
123
|
+
<Sidebar className={cn(className, "pt-20")} {...props}>
|
|
118
124
|
<SidebarContent>
|
|
119
125
|
{tableOfContentGroup()}
|
|
120
|
-
{availableVersionsGroup()}
|
|
121
126
|
</SidebarContent>
|
|
122
|
-
<
|
|
127
|
+
<SidebarFooter>
|
|
128
|
+
{availableVersionsGroup()}
|
|
129
|
+
</SidebarFooter>
|
|
123
130
|
</Sidebar>
|
|
124
131
|
);
|
|
125
132
|
}
|