@c-rex/components 0.1.37 → 0.1.38
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 -18
- package/src/carousel/carousel.tsx +6 -5
- package/src/check-article-lang.tsx +8 -4
- package/src/favorites/bookmark-button.tsx +26 -11
- package/src/favorites/favorite-button.tsx +67 -21
- package/src/info/info-table.tsx +60 -38
- package/src/info/set-available-versions.tsx +19 -0
- package/src/navbar/language-switcher/content-language-switch.tsx +36 -36
- package/src/navbar/language-switcher/ui-language-switch.tsx +5 -6
- package/src/navbar/navbar.tsx +6 -2
- package/src/restriction-menu/restriction-menu.tsx +2 -3
- package/src/results/generic/table-result-list.tsx +1 -1
- package/src/results/utils.ts +9 -2
- package/src/stores/favorites-store.ts +21 -21
- package/src/stores/language-store.ts +2 -31
- package/src/article/article-action-bar.analysis.md +0 -15
- package/src/article/article-action-bar.stories.tsx +0 -15
- package/src/article/article-content.analysis.md +0 -15
- package/src/article/article-content.stories.tsx +0 -21
- package/src/autocomplete.analysis.md +0 -17
- package/src/breadcrumb.analysis.md +0 -15
- package/src/carousel/carousel.analysis.md +0 -17
- package/src/check-article-lang.analysis.md +0 -15
- package/src/directoryNodes/tree-of-content.analysis.md +0 -14
- package/src/directoryNodes/tree-of-content.stories.tsx +0 -22
- package/src/documents/result-list.analysis.md +0 -14
- package/src/documents/result-list.stories.tsx +0 -19
- package/src/favorites/bookmark-button.analysis.md +0 -17
- package/src/favorites/bookmark-button.stories.tsx +0 -19
- package/src/favorites/favorite-button.analysis.md +0 -18
- package/src/favorites/favorite-button.stories.tsx +0 -22
- package/src/icons/file-icon.analysis.md +0 -14
- package/src/icons/file-icon.stories.tsx +0 -19
- package/src/icons/flag-icon.analysis.md +0 -14
- package/src/icons/flag-icon.stories.tsx +0 -25
- package/src/icons/loading.analysis.md +0 -14
- package/src/icons/loading.stories.tsx +0 -21
- package/src/info/info-table.analysis.md +0 -15
- package/src/info/shared.analysis.md +0 -14
- package/src/info/stories/info-table.stories.tsx +0 -31
- package/src/info/stories/shared.stories.tsx +0 -24
- package/src/navbar/language-switcher/content-language-switch.analysis.md +0 -15
- package/src/navbar/language-switcher/shared.analysis.md +0 -14
- package/src/navbar/language-switcher/ui-language-switch.analysis.md +0 -15
- package/src/navbar/navbar.analysis.md +0 -14
- package/src/navbar/settings.analysis.md +0 -14
- package/src/navbar/sign-in-out-btns.analysis.md +0 -14
- package/src/navbar/stories/navbar.stories.tsx +0 -31
- package/src/navbar/stories/settings.stories.tsx +0 -15
- package/src/navbar/stories/sign-in-out-btns.stories.tsx +0 -15
- package/src/navbar/stories/user-menu.stories.tsx +0 -20
- package/src/navbar/user-menu.analysis.md +0 -14
- package/src/page-wrapper.analysis.md +0 -14
- package/src/render-article.analysis.md +0 -15
- package/src/renditions/file-download.analysis.md +0 -14
- package/src/renditions/file-download.stories.tsx +0 -19
- package/src/renditions/html.analysis.md +0 -17
- package/src/renditions/html.stories.tsx +0 -19
- package/src/renditions/image/container.analysis.md +0 -15
- package/src/renditions/image/container.stories.tsx +0 -19
- package/src/renditions/image/rendition.analysis.md +0 -14
- package/src/renditions/image/rendition.stories.tsx +0 -19
- package/src/restriction-menu/restriction-menu-container.analysis.md +0 -14
- package/src/restriction-menu/restriction-menu-item.analysis.md +0 -14
- package/src/restriction-menu/restriction-menu.analysis.md +0 -17
- package/src/results/analysis/cards.analysis.md +0 -14
- package/src/results/analysis/dialog-filter.analysis.md +0 -17
- package/src/results/analysis/empty.analysis.md +0 -14
- package/src/results/analysis/filter-navbar.analysis.md +0 -16
- package/src/results/analysis/pagination.analysis.md +0 -14
- package/src/results/analysis/table-with-images.analysis.md +0 -15
- package/src/results/analysis/table.analysis.md +0 -15
- package/src/results/filter-sidebar/index.analysis.md +0 -14
- package/src/results/generic/table-result-list.analysis.md +0 -15
- package/src/results/generic/table-result-list.stories.tsx +0 -21
- package/src/results/stories/cards.stories.tsx +0 -66
- package/src/results/stories/dialog-filter.stories.tsx +0 -20
- package/src/results/stories/empty.stories.tsx +0 -25
- package/src/results/stories/filter-navbar.stories.tsx +0 -19
- package/src/results/stories/filter-sidebar.stories.tsx +0 -20
- package/src/results/stories/pagination.stories.tsx +0 -24
- package/src/results/stories/table-with-images.stories.tsx +0 -19
- package/src/results/stories/table.stories.tsx +0 -78
- package/src/search-input.analysis.md +0 -15
- package/src/share-button.analysis.md +0 -19
- package/src/stories/autocomplete.stories.tsx +0 -20
- package/src/stories/breadcrumb.stories.tsx +0 -93
- package/src/stories/check-article-lang.stories.tsx +0 -22
- package/src/stories/render-article.stories.tsx +0 -19
- package/src/stories/search-input.stories.tsx +0 -21
- package/src/stories/share-button.stories.tsx +0 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c-rex/components",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"files": [
|
|
5
5
|
"src"
|
|
6
6
|
],
|
|
@@ -179,31 +179,19 @@
|
|
|
179
179
|
}
|
|
180
180
|
},
|
|
181
181
|
"scripts": {
|
|
182
|
-
"storybook": "storybook dev -p 6006",
|
|
183
|
-
"build-storybook": "storybook build",
|
|
184
182
|
"lint": "eslint .",
|
|
185
183
|
"lint:fix": "eslint . --fix"
|
|
186
184
|
},
|
|
187
185
|
"devDependencies": {
|
|
188
186
|
"@c-rex/eslint-config": "*",
|
|
189
187
|
"@c-rex/typescript-config": "*",
|
|
190
|
-
"@chromatic-com/storybook": "^3.2.6",
|
|
191
|
-
"@storybook/addon-essentials": "^8.6.12",
|
|
192
|
-
"@storybook/addon-onboarding": "^8.6.12",
|
|
193
|
-
"@storybook/blocks": "^8.6.12",
|
|
194
|
-
"@storybook/nextjs": "^8.6.12",
|
|
195
|
-
"@storybook/react": "^8.6.12",
|
|
196
|
-
"@storybook/test": "^8.6.12",
|
|
197
188
|
"@turbo/gen": "^2.4.4",
|
|
198
189
|
"@types/node": "^22.13.10",
|
|
199
190
|
"@types/react": "19.0.10",
|
|
200
191
|
"@types/react-dom": "19.0.4",
|
|
201
192
|
"autoprefixer": "^10.4.21",
|
|
202
193
|
"eslint": "^9.23.0",
|
|
203
|
-
"eslint-plugin-storybook": "^0.12.0",
|
|
204
194
|
"postcss": "^8.5.3",
|
|
205
|
-
"storybook": "^8.6.12",
|
|
206
|
-
"style-loader": "^4.0.0",
|
|
207
195
|
"tailwindcss": "^3.4.17",
|
|
208
196
|
"typescript": "latest"
|
|
209
197
|
},
|
|
@@ -226,10 +214,5 @@
|
|
|
226
214
|
"react-icons": "^5.5.0",
|
|
227
215
|
"tailwindcss-animate": "^1.0.7",
|
|
228
216
|
"zustand": "^5.0.8"
|
|
229
|
-
},
|
|
230
|
-
"eslintConfig": {
|
|
231
|
-
"extends": [
|
|
232
|
-
"plugin:storybook/recommended"
|
|
233
|
-
]
|
|
234
217
|
}
|
|
235
218
|
}
|
|
@@ -322,9 +322,10 @@ const DefaultRenderCarouselItem: FC<{
|
|
|
322
322
|
const itemType = getType(item.class);
|
|
323
323
|
const language = getLanguage(item.languages)
|
|
324
324
|
const countryCode = language.split("-")[1] || "";
|
|
325
|
+
const link = linkPattern.replace("{shortId}", item.shortId!);
|
|
325
326
|
|
|
326
327
|
return (
|
|
327
|
-
<
|
|
328
|
+
<Link href={link} className="group p-2 flex flex-1">
|
|
328
329
|
<Card className="p-4 flex-1 justify-between relative">
|
|
329
330
|
<Badge className="absolute -top-2 -right-2">{t(itemType.toLowerCase())}</Badge>
|
|
330
331
|
|
|
@@ -336,17 +337,17 @@ const DefaultRenderCarouselItem: FC<{
|
|
|
336
337
|
/>
|
|
337
338
|
)}
|
|
338
339
|
|
|
339
|
-
<
|
|
340
|
+
<span className="group-hover:underline text-lg font-semibold flex-1">
|
|
340
341
|
{title}
|
|
341
|
-
</
|
|
342
|
+
</span>
|
|
342
343
|
|
|
343
344
|
<div className="flex justify-between w-full">
|
|
344
|
-
<span className="w-8 block
|
|
345
|
+
<span className="w-8 block">
|
|
345
346
|
<Flag countryCode={countryCode} />
|
|
346
347
|
</span>
|
|
347
348
|
<span className="text-gray-400">{date || item.revision}</span>
|
|
348
349
|
</div>
|
|
349
350
|
</Card>
|
|
350
|
-
</
|
|
351
|
+
</Link>
|
|
351
352
|
);
|
|
352
353
|
};
|
|
@@ -14,11 +14,15 @@ interface Props {
|
|
|
14
14
|
export const CheckArticleLangToast: FC<Props> = ({ availableVersions }) => {
|
|
15
15
|
const t = useTranslations();
|
|
16
16
|
const { setAvailableVersions } = useAppConfig()
|
|
17
|
+
const searchLanguage = useSearchSettingsStore((state) => state.language);
|
|
17
18
|
|
|
18
19
|
useEffect(() => {
|
|
19
20
|
setAvailableVersions(availableVersions)
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
}, [availableVersions, setAvailableVersions])
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!searchLanguage) return;
|
|
25
|
+
const activeArticle = availableVersions.find((item) => item.active)
|
|
22
26
|
|
|
23
27
|
if (activeArticle == undefined || activeArticle.lang == searchLanguage) return
|
|
24
28
|
|
|
@@ -26,7 +30,7 @@ export const CheckArticleLangToast: FC<Props> = ({ availableVersions }) => {
|
|
|
26
30
|
if (articleAvailable != undefined) {
|
|
27
31
|
articleAvailableInToast(articleAvailable.lang, articleAvailable.link)
|
|
28
32
|
}
|
|
29
|
-
}, [])
|
|
33
|
+
}, [availableVersions, searchLanguage])
|
|
30
34
|
|
|
31
35
|
const articleAvailableInToast = (lang: string, link: string) => {
|
|
32
36
|
toast(t('toast.read', { lang }), {
|
|
@@ -40,4 +44,4 @@ export const CheckArticleLangToast: FC<Props> = ({ availableVersions }) => {
|
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
return null
|
|
43
|
-
}
|
|
47
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { ComponentProps, FC
|
|
4
|
-
import { Button
|
|
3
|
+
import { ComponentProps, FC } from "react";
|
|
4
|
+
import { Button } from "@c-rex/ui/button";
|
|
5
5
|
import { Trash } from "lucide-react";
|
|
6
6
|
import { cn } from "@c-rex/utils";
|
|
7
7
|
import Link from "next/link";
|
|
8
8
|
import { useFavoritesStore } from "../stores/favorites-store";
|
|
9
9
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@c-rex/ui/dialog";
|
|
10
|
+
import { useTranslations } from "next-intl";
|
|
10
11
|
import {
|
|
11
12
|
Table,
|
|
12
13
|
TableBody,
|
|
@@ -15,20 +16,22 @@ import {
|
|
|
15
16
|
} from "@c-rex/ui/table"
|
|
16
17
|
import { FaRegBookmark } from "react-icons/fa6";
|
|
17
18
|
|
|
19
|
+
const EMPTY_TOPICS: { id: string; label: string; color: string }[] = [];
|
|
20
|
+
|
|
18
21
|
type BookmarkProps = {
|
|
19
22
|
shortId: string;
|
|
23
|
+
linkPattern?: string;
|
|
20
24
|
triggerVariant?: ComponentProps<typeof Button>["variant"];
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
export const BookmarkButton: FC<BookmarkProps> = ({
|
|
24
28
|
shortId,
|
|
29
|
+
linkPattern = `/topics/{id}/pages`,
|
|
25
30
|
triggerVariant = "outline"
|
|
26
31
|
}) => {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
setMarkersList(useFavoritesStore.getState().documents[shortId]?.topics || []);
|
|
31
|
-
}, [shortId]);
|
|
32
|
+
const t = useTranslations("bookmarks");
|
|
33
|
+
const removeFavoriteTopic = useFavoritesStore((state) => state.unfavoriteTopic);
|
|
34
|
+
const markersList = useFavoritesStore((state) => state.documents[shortId]?.topics) ?? EMPTY_TOPICS;
|
|
32
35
|
return (
|
|
33
36
|
<Dialog>
|
|
34
37
|
<DialogTrigger asChild>
|
|
@@ -47,25 +50,37 @@ export const BookmarkButton: FC<BookmarkProps> = ({
|
|
|
47
50
|
</DialogTrigger>
|
|
48
51
|
<DialogContent>
|
|
49
52
|
<DialogHeader>
|
|
50
|
-
<DialogTitle>
|
|
53
|
+
<DialogTitle>{t("title")}</DialogTitle>
|
|
51
54
|
<DialogDescription>
|
|
52
|
-
|
|
55
|
+
{t("description")}
|
|
53
56
|
</DialogDescription>
|
|
54
57
|
</DialogHeader>
|
|
55
58
|
<Table>
|
|
56
59
|
<TableBody>
|
|
60
|
+
{markersList.length === 0 && (
|
|
61
|
+
<TableRow>
|
|
62
|
+
<TableCell colSpan={3} className="text-center">
|
|
63
|
+
{t("empty")}
|
|
64
|
+
</TableCell>
|
|
65
|
+
</TableRow>
|
|
66
|
+
)}
|
|
67
|
+
|
|
57
68
|
{markersList.map((item) => (
|
|
58
69
|
<TableRow key={item.id} className="min-h-12">
|
|
59
70
|
<TableCell>
|
|
60
71
|
<FaRegBookmark className={cn("w-5", `text-${item.color}`)} />
|
|
61
72
|
</TableCell>
|
|
62
73
|
<TableCell>
|
|
63
|
-
<Link href={
|
|
74
|
+
<Link href={linkPattern.replace("{id}", item.id)} className="hover:underline">
|
|
64
75
|
{item.label}
|
|
65
76
|
</Link>
|
|
66
77
|
</TableCell>
|
|
67
78
|
<TableCell>
|
|
68
|
-
<Button
|
|
79
|
+
<Button
|
|
80
|
+
variant="destructive"
|
|
81
|
+
size="icon"
|
|
82
|
+
onClick={() => removeFavoriteTopic(shortId, item.id)}
|
|
83
|
+
>
|
|
69
84
|
<Trash className="w-5 hover:text-red-600 cursor-pointer" />
|
|
70
85
|
</Button>
|
|
71
86
|
</TableCell>
|
|
@@ -6,6 +6,8 @@ import { FaStar, FaRegStar } from "react-icons/fa";
|
|
|
6
6
|
import { useFavoritesStore } from "../stores/favorites-store";
|
|
7
7
|
import { MARKER_COLORS, RESULT_TYPES } from "@c-rex/constants";
|
|
8
8
|
import { ResultTypes } from "@c-rex/types";
|
|
9
|
+
import { Loader2 } from "lucide-react";
|
|
10
|
+
import { toast } from "sonner";
|
|
9
11
|
|
|
10
12
|
export const FavoriteButton: FC<{ id: string, type: ResultTypes, label: string }> = ({ id, type, label }) => {
|
|
11
13
|
const addFavoriteTopic = useFavoritesStore((state) => state.favoriteTopic);
|
|
@@ -17,58 +19,102 @@ export const FavoriteButton: FC<{ id: string, type: ResultTypes, label: string }
|
|
|
17
19
|
const favoriteList = useFavoritesStore((state) => state.favorites);
|
|
18
20
|
const isFavorite = favoriteList.find((fav) => fav.id === id);
|
|
19
21
|
const [documentData, setDocumentData] = useState<{ id: string, label: string }>({ id, label });
|
|
22
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
20
23
|
|
|
21
24
|
useEffect(() => {
|
|
22
25
|
if (type === RESULT_TYPES.TOPIC) {
|
|
23
|
-
getTopicDocumentData(id)
|
|
26
|
+
void getTopicDocumentData(id).catch(() => {
|
|
27
|
+
// Lazy retry on user action.
|
|
28
|
+
});
|
|
24
29
|
}
|
|
25
|
-
}, []);
|
|
30
|
+
}, [id, type]);
|
|
26
31
|
|
|
27
|
-
const
|
|
32
|
+
const ensureDocumentData = async (topicId: string): Promise<{ id: string, label: string }> => {
|
|
33
|
+
if (type !== RESULT_TYPES.TOPIC) {
|
|
34
|
+
return { id, label };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (documentData.id !== topicId) {
|
|
38
|
+
return documentData;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return await getTopicDocumentData(topicId);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const addFavorite = async (targetId: string) => {
|
|
28
45
|
if (type === RESULT_TYPES.DOCUMENT) {
|
|
29
|
-
addFavoriteDocument(
|
|
46
|
+
addFavoriteDocument(targetId, label);
|
|
30
47
|
return;
|
|
31
48
|
}
|
|
32
49
|
|
|
33
|
-
const
|
|
50
|
+
const docData = await ensureDocumentData(targetId);
|
|
51
|
+
const length = favoriteDocumentList[docData.id]?.topics.length || 0;
|
|
34
52
|
const color = MARKER_COLORS[length] || MARKER_COLORS[MARKER_COLORS.length - 1] as string;
|
|
35
53
|
|
|
36
|
-
addFavoriteTopic(
|
|
37
|
-
}
|
|
54
|
+
addFavoriteTopic(docData.id, targetId, label, color);
|
|
55
|
+
};
|
|
38
56
|
|
|
39
|
-
const removeFavorite = (
|
|
57
|
+
const removeFavorite = async (targetId: string) => {
|
|
40
58
|
if (type === RESULT_TYPES.DOCUMENT) {
|
|
41
|
-
removeFavoriteDocument(
|
|
59
|
+
removeFavoriteDocument(targetId);
|
|
42
60
|
return;
|
|
43
61
|
}
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const getTopicDocumentData = async (topicId: string): Promise<void> => {
|
|
63
|
+
const docData = await ensureDocumentData(targetId);
|
|
64
|
+
removeFavoriteTopic(docData.id, targetId);
|
|
65
|
+
};
|
|
49
66
|
|
|
67
|
+
const getTopicDocumentData = async (topicId: string): Promise<{ id: string, label: string }> => {
|
|
50
68
|
const response = await fetch(`/api/information-units/document-by-topic-id?shortId=${topicId}`, {
|
|
51
69
|
method: "GET"
|
|
52
70
|
});
|
|
53
71
|
|
|
54
|
-
if (!response.ok)
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
throw new Error("Failed to fetch document by topic id");
|
|
74
|
+
}
|
|
55
75
|
|
|
56
76
|
const { documentId, label } = await response.json();
|
|
57
|
-
|
|
58
|
-
setDocumentData(
|
|
59
|
-
|
|
77
|
+
const data = { id: documentId, label };
|
|
78
|
+
setDocumentData(data);
|
|
79
|
+
return data;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleToggle = async () => {
|
|
83
|
+
if (isLoading) return;
|
|
84
|
+
setIsLoading(true);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
if (isFavorite) {
|
|
88
|
+
await removeFavorite(id);
|
|
89
|
+
} else {
|
|
90
|
+
await addFavorite(id);
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
toast.error("Não foi possível atualizar os favoritos.");
|
|
94
|
+
} finally {
|
|
95
|
+
setIsLoading(false);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
60
98
|
|
|
61
99
|
if (isFavorite) {
|
|
62
100
|
return (
|
|
63
|
-
<Button variant="ghost" size="icon" onClick={
|
|
64
|
-
|
|
101
|
+
<Button variant="ghost" size="icon" onClick={handleToggle} disabled={isLoading}>
|
|
102
|
+
{isLoading ? (
|
|
103
|
+
<Loader2 className="!h-5 !w-5 animate-spin" />
|
|
104
|
+
) : (
|
|
105
|
+
<FaStar className="!h-5 !w-5 color-primary" />
|
|
106
|
+
)}
|
|
65
107
|
</Button>
|
|
66
108
|
);
|
|
67
109
|
}
|
|
68
110
|
|
|
69
111
|
return (
|
|
70
|
-
<Button variant="ghost" size="icon" onClick={
|
|
71
|
-
|
|
112
|
+
<Button variant="ghost" size="icon" onClick={handleToggle} disabled={isLoading}>
|
|
113
|
+
{isLoading ? (
|
|
114
|
+
<Loader2 className="!h-5 !w-5 animate-spin" />
|
|
115
|
+
) : (
|
|
116
|
+
<FaRegStar className="!h-5 !w-5" />
|
|
117
|
+
)}
|
|
72
118
|
</Button>
|
|
73
119
|
)
|
|
74
120
|
}
|
package/src/info/info-table.tsx
CHANGED
|
@@ -20,40 +20,79 @@ import { Flag } from "../icons/flag-icon";
|
|
|
20
20
|
import { Button } from "@c-rex/ui/button";
|
|
21
21
|
import { FileIcon } from "../icons/file-icon";
|
|
22
22
|
import { BookmarkButton } from "../favorites/bookmark-button";
|
|
23
|
-
import {
|
|
23
|
+
import { informationUnitsGetAllServer } from "@c-rex/services/server-requests";
|
|
24
24
|
import { getTranslations } from "next-intl/server";
|
|
25
25
|
import { EN_LANG } from "@c-rex/constants";
|
|
26
26
|
import { getFileRenditions } from "../results/utils";
|
|
27
27
|
import Link from "next/link";
|
|
28
28
|
import { processDataToLabelValuePairs } from "@c-rex/utils";
|
|
29
|
+
import { AvailableVersionsInterface } from "@c-rex/interfaces";
|
|
30
|
+
import { SetAvailableVersions } from "./set-available-versions";
|
|
29
31
|
|
|
30
32
|
type Props = {
|
|
31
33
|
title: string;
|
|
32
|
-
|
|
34
|
+
linkPattern: string;
|
|
33
35
|
data: InformationUnitModel;
|
|
34
36
|
excludeKeys?: string[];
|
|
35
37
|
showBookmarkButton?: boolean;
|
|
38
|
+
saveVersionsInContext?: boolean;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
export const InfoTable: FC<Props> = async ({
|
|
39
42
|
title,
|
|
40
43
|
data,
|
|
41
|
-
|
|
44
|
+
linkPattern,
|
|
42
45
|
showBookmarkButton = false,
|
|
46
|
+
saveVersionsInContext = false
|
|
43
47
|
}) => {
|
|
44
|
-
const
|
|
48
|
+
const versionBaseShortId = data.versionOf?.shortId ?? null;
|
|
49
|
+
const [t, informationUnits] = await Promise.all([
|
|
50
|
+
getTranslations(),
|
|
51
|
+
versionBaseShortId
|
|
52
|
+
? informationUnitsGetAllServer({
|
|
53
|
+
Restrict: [`versionOf.shortId=${versionBaseShortId}`],
|
|
54
|
+
Fields: ["renditions", "class", "languages", "labels"],
|
|
55
|
+
})
|
|
56
|
+
: Promise.resolve({ items: [] }),
|
|
57
|
+
])
|
|
45
58
|
const files = getFileRenditions({ renditions: data.renditions! });
|
|
59
|
+
const versionsByShortId = new Map<string, { shortId: string; language: string }>();
|
|
60
|
+
|
|
61
|
+
if (data.shortId && data.languages?.[0]) {
|
|
62
|
+
versionsByShortId.set(data.shortId, {
|
|
63
|
+
shortId: data.shortId,
|
|
64
|
+
language: data.languages[0],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
informationUnits.items.forEach((item) => {
|
|
69
|
+
if (!item.shortId || !item.languages?.[0]) return;
|
|
70
|
+
versionsByShortId.set(item.shortId, {
|
|
71
|
+
shortId: item.shortId,
|
|
72
|
+
language: item.languages[0],
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const availableVersions = Array.from(versionsByShortId.values())
|
|
77
|
+
.map((item) => ({
|
|
78
|
+
shortId: item.shortId,
|
|
79
|
+
active: item.shortId === data.shortId,
|
|
80
|
+
lang: item.language,
|
|
81
|
+
country: item.language.split("-")[1] ?? item.language,
|
|
82
|
+
link: linkPattern.replace("{shortId}", item.shortId),
|
|
83
|
+
}))
|
|
84
|
+
.sort((a, b) => a.lang.localeCompare(b.lang)) as AvailableVersionsInterface[];
|
|
46
85
|
|
|
47
86
|
return (
|
|
48
87
|
<Card className="p-0 !pt-4">
|
|
49
88
|
<CardHeader>
|
|
50
89
|
<CardTitle className="text-lg flex justify-between items-end">
|
|
51
90
|
{title}
|
|
52
|
-
|
|
53
91
|
{showBookmarkButton && <BookmarkButton shortId={data.shortId!} />}
|
|
54
92
|
</CardTitle>
|
|
55
93
|
</CardHeader>
|
|
56
94
|
<CardContent className="space-y-3 !p-0">
|
|
95
|
+
{saveVersionsInContext && <SetAvailableVersions versions={availableVersions} />}
|
|
57
96
|
<Table>
|
|
58
97
|
<TableBody>
|
|
59
98
|
{processDataToLabelValuePairs(data).map((item) => (
|
|
@@ -67,39 +106,22 @@ export const InfoTable: FC<Props> = async ({
|
|
|
67
106
|
</TableRow>
|
|
68
107
|
))}
|
|
69
108
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<TableCell className="text-xs text-muted-foreground flex items-center gap-2 min-h-12">
|
|
87
|
-
{data.items.map((item) => {
|
|
88
|
-
const language = item.languages?.[0] ?? "NO LANGUAGE";
|
|
89
|
-
const country = language.split("-")[1]!;
|
|
90
|
-
return (
|
|
91
|
-
<span className="w-8 block border" key={item.shortId}>
|
|
92
|
-
<Link href={`/${linkPath}/${item.shortId}/content`} title={language}>
|
|
93
|
-
<Flag countryCode={country} />
|
|
94
|
-
</Link>
|
|
95
|
-
</span>
|
|
96
|
-
)
|
|
97
|
-
})}
|
|
98
|
-
</TableCell>
|
|
99
|
-
</TableRow>
|
|
100
|
-
)
|
|
101
|
-
}}
|
|
102
|
-
/>
|
|
109
|
+
<TableRow className="min-h-12">
|
|
110
|
+
<TableCell className="font-medium w-28 pl-4">
|
|
111
|
+
<h4 className="text-sm font-medium">{t("availableIn")}</h4>
|
|
112
|
+
</TableCell>
|
|
113
|
+
<TableCell className="text-xs text-muted-foreground flex items-center gap-2 min-h-12">
|
|
114
|
+
{availableVersions.map((item) => {
|
|
115
|
+
return (
|
|
116
|
+
<span className="w-8 block border" key={item.shortId}>
|
|
117
|
+
<Link href={item.link} title={item.lang}>
|
|
118
|
+
<Flag countryCode={item.country} />
|
|
119
|
+
</Link>
|
|
120
|
+
</span>
|
|
121
|
+
)
|
|
122
|
+
})}
|
|
123
|
+
</TableCell>
|
|
124
|
+
</TableRow>
|
|
103
125
|
|
|
104
126
|
{Object.keys(files).length > 0 && (
|
|
105
127
|
<TableRow className="min-h-12">
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { AvailableVersionsInterface } from "@c-rex/interfaces";
|
|
5
|
+
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
versions: AvailableVersionsInterface[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const SetAvailableVersions = ({ versions }: Props) => {
|
|
12
|
+
const { setAvailableVersions } = useAppConfig();
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setAvailableVersions(versions);
|
|
16
|
+
}, [setAvailableVersions, versions]);
|
|
17
|
+
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { startTransition } from "react";
|
|
3
|
+
import { startTransition, useCallback, useMemo } from "react";
|
|
4
4
|
import { SharedLanguageSwitch } from "./shared";
|
|
5
|
-
import { BLOG_TYPE_AND_LINK, DOCUMENTS_TYPE_AND_LINK, TOPICS_TYPE_AND_LINK } from "@c-rex/constants";
|
|
6
5
|
import { useQueryState } from "nuqs"
|
|
7
6
|
import { useAppConfig } from "@c-rex/contexts/config-provider";
|
|
8
7
|
import { useTranslations } from "next-intl";
|
|
@@ -16,26 +15,36 @@ type Props = {
|
|
|
16
15
|
|
|
17
16
|
export const ContentLanguageSwitch = ({ contentLangDefault }: Props) => {
|
|
18
17
|
const t = useTranslations();
|
|
19
|
-
const contentLang = useSearchSettingsStore
|
|
20
|
-
const updatePreferences = useSearchSettingsStore
|
|
21
|
-
|
|
18
|
+
const contentLang = useSearchSettingsStore((state) => state.language) || contentLangDefault;
|
|
19
|
+
const updatePreferences = useSearchSettingsStore((state) => state.updatePreferences);
|
|
20
|
+
const availableLanguages = useLanguageStore((state) => state.availableLanguages);
|
|
22
21
|
const { availableVersions } = useAppConfig()
|
|
23
22
|
|
|
24
|
-
const availableLanguagesAndCountries = () => {
|
|
25
|
-
const aux = useLanguageStore.getState().availableLanguages;
|
|
26
|
-
let result = aux.map(item => ({ ...item, link: "#" }))
|
|
27
|
-
if (availableVersions == null) return result
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
const normalizeLang = (lang: string) => lang.trim().toLowerCase();
|
|
25
|
+
|
|
26
|
+
const findVersionByLocale = useCallback((locale: string) => {
|
|
27
|
+
if (!availableVersions || availableVersions.length === 0) return undefined;
|
|
28
|
+
const normalizedLocale = normalizeLang(locale);
|
|
29
|
+
|
|
30
|
+
return availableVersions.find((item) => normalizeLang(item.lang) === normalizedLocale)
|
|
31
|
+
?? availableVersions.find((item) => {
|
|
32
|
+
const langCode = normalizeLang(item.lang).split("-")[0];
|
|
33
|
+
const localeCode = normalizedLocale.split("-")[0];
|
|
34
|
+
return langCode === localeCode;
|
|
35
|
+
});
|
|
36
|
+
}, [availableVersions]);
|
|
37
|
+
|
|
38
|
+
const availableLanguagesAndCountries = useMemo(() => {
|
|
39
|
+
return availableLanguages.map((item) => {
|
|
40
|
+
const availableVersion = findVersionByLocale(item.value);
|
|
31
41
|
return {
|
|
32
42
|
...item,
|
|
33
|
-
link: availableVersion?.link ?? "#"
|
|
34
|
-
}
|
|
35
|
-
})
|
|
43
|
+
link: availableVersion?.link ?? "#",
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}, [availableLanguages, availableVersions]);
|
|
36
47
|
|
|
37
|
-
return result;
|
|
38
|
-
}
|
|
39
48
|
const [queryLanguage, setContentLanguage] = useQueryState('language', {
|
|
40
49
|
history: 'push',
|
|
41
50
|
shallow: false,
|
|
@@ -44,34 +53,25 @@ export const ContentLanguageSwitch = ({ contentLangDefault }: Props) => {
|
|
|
44
53
|
const changeContentLanguage = (locale: string) => {
|
|
45
54
|
startTransition(() => {
|
|
46
55
|
updatePreferences({ language: locale })
|
|
47
|
-
|
|
48
56
|
if (queryLanguage != null) {
|
|
49
57
|
setContentLanguage(locale)
|
|
58
|
+
return;
|
|
50
59
|
}
|
|
51
60
|
|
|
52
|
-
//TODO en: needs to be fixed as it's not working
|
|
53
|
-
|
|
54
61
|
const currentPath = window.location.pathname;
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
const isArticlePage = currentPath.includes("/pages");
|
|
63
|
+
if (!isArticlePage) {
|
|
64
|
+
window.location.reload();
|
|
65
|
+
return;
|
|
66
|
+
};
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
const targetVersion = findVersionByLocale(locale);
|
|
69
|
+
if (targetVersion?.link) {
|
|
70
|
+
window.location.href = targetVersion.link;
|
|
63
71
|
return;
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
|
|
67
|
-
const filteredList = availableVersions.filter((item) => item.lang === locale)
|
|
68
|
-
|
|
69
|
-
if (filteredList.length > 0 && filteredList[0]) {
|
|
70
|
-
window.location.href = filteredList[0].link as string;
|
|
71
|
-
} else {
|
|
72
|
-
articleNotAvailableToast()
|
|
73
|
-
}
|
|
74
|
-
}
|
|
74
|
+
articleNotAvailableToast()
|
|
75
75
|
});
|
|
76
76
|
};
|
|
77
77
|
|
|
@@ -84,7 +84,7 @@ export const ContentLanguageSwitch = ({ contentLangDefault }: Props) => {
|
|
|
84
84
|
|
|
85
85
|
return (
|
|
86
86
|
<SharedLanguageSwitch
|
|
87
|
-
availableLanguagesAndCountries={availableLanguagesAndCountries
|
|
87
|
+
availableLanguagesAndCountries={availableLanguagesAndCountries}
|
|
88
88
|
changeLanguage={changeContentLanguage}
|
|
89
89
|
selected={contentLang}
|
|
90
90
|
/>
|
|
@@ -5,11 +5,12 @@ import { SharedLanguageSwitch } from "./shared";
|
|
|
5
5
|
import { UI_LANG_KEY, UI_LANG_OPTIONS } from "@c-rex/constants";
|
|
6
6
|
import { getCountryCodeByLang, } from "@c-rex/utils";
|
|
7
7
|
import { setCookie } from "@c-rex/utils/cookies";
|
|
8
|
-
import {
|
|
8
|
+
import { useLocale } from "next-intl";
|
|
9
|
+
import { useRouter } from "next/navigation";
|
|
9
10
|
|
|
10
11
|
export const UILanguageSwitch: FC = () => {
|
|
11
|
-
const uiLang =
|
|
12
|
-
const
|
|
12
|
+
const uiLang = useLocale().toLowerCase();
|
|
13
|
+
const router = useRouter();
|
|
13
14
|
const UILanguages = UI_LANG_OPTIONS.map((lang) => {
|
|
14
15
|
const langCode = lang.split("-")[0] as string;
|
|
15
16
|
return {
|
|
@@ -21,10 +22,8 @@ export const UILanguageSwitch: FC = () => {
|
|
|
21
22
|
|
|
22
23
|
const setUILanguage = (locale: string) => {
|
|
23
24
|
startTransition(() => {
|
|
24
|
-
setUiLang(locale)
|
|
25
25
|
setCookie(UI_LANG_KEY, locale, { httpOnly: false });
|
|
26
|
-
|
|
27
|
-
//window.location.reload()
|
|
26
|
+
router.refresh();
|
|
28
27
|
});
|
|
29
28
|
}
|
|
30
29
|
|
package/src/navbar/navbar.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FC } from "react";
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { SignInBtn } from "./sign-in-out-btns";
|
|
4
4
|
import { getServerSession } from "next-auth";
|
|
@@ -118,7 +118,11 @@ export const NavBar: FC<NavBarProps> = async ({
|
|
|
118
118
|
<div className="flex gap-2">
|
|
119
119
|
{willShowInput &&
|
|
120
120
|
<div className="hidden sm:flex flex-1 items-center px-3 border rounded-full h-8 c-rex-search-bar">
|
|
121
|
-
<SearchInput
|
|
121
|
+
<SearchInput
|
|
122
|
+
autocompleteType={autocompleteType}
|
|
123
|
+
onSelectPath={onSelectPath}
|
|
124
|
+
{...props}
|
|
125
|
+
/>
|
|
122
126
|
</div>
|
|
123
127
|
}
|
|
124
128
|
|