@c-rex/components 0.1.14 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-rex/components",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "files": [
5
5
  "src"
6
6
  ],
@@ -25,6 +25,10 @@
25
25
  "types": "./src/breadcrumb.tsx",
26
26
  "import": "./src/breadcrumb.tsx"
27
27
  },
28
+ "./settings-menu": {
29
+ "types": "./src/navbar/settings.tsx",
30
+ "import": "./src/navbar/settings.tsx"
31
+ },
28
32
  "./flag": {
29
33
  "types": "./src/flag.tsx",
30
34
  "import": "./src/flag.tsx"
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  import { useEffect, useRef, useState } from "react";
2
4
  import { Input } from "@c-rex/ui/input";
3
5
  import { call, generateQueryParams } from "@c-rex/utils";
@@ -1,4 +1,4 @@
1
- import React, { FC, useState } from "react"
1
+ import React, { FC, useEffect, useState } from "react"
2
2
  import { Button } from "@c-rex/ui/button"
3
3
  import {
4
4
  Dialog,
@@ -35,17 +35,18 @@ export const DialogFilter: FC<DialogFilterProps> = ({ trigger }) => {
35
35
  const t = useTranslations("filter");
36
36
  const { setLoading } = useSearchContext();
37
37
 
38
- const savedLike = useSearchSettingsStore.getState().like;
39
- const savedOperator = useSearchSettingsStore.getState().operator;
40
- const savedWildcard = useSearchSettingsStore.getState().wildcard;
41
- const savedLanguages = useLanguageStore.getState().contentLang;
42
- const availableLanguagesAndCountries = useLanguageStore.getState().availableLanguages;
38
+ const savedLike = useSearchSettingsStore((state) => state.like)
39
+ const savedOperator = useSearchSettingsStore((state) => state.operator)
40
+ const savedWildcard = useSearchSettingsStore((state) => state.wildcard)
41
+ const savedLanguages = useLanguageStore(state => state.contentLang);
42
+ const availableLanguagesAndCountries = useLanguageStore(state => state.availableLanguages);
43
43
 
44
44
  const [like, setLike] = useState<boolean>(savedLike);
45
45
  const [operator, setOperator] = useState<string>(savedOperator);
46
46
  const [wildcard, setWildcard] = useState<string>(savedWildcard);
47
- const [languages, setLanguages] = useState<Languages[]>(
48
- availableLanguagesAndCountries?.map((item: LanguageAndCountries) => {
47
+
48
+ const generateLanguagesList = (): Languages[] => {
49
+ return availableLanguagesAndCountries?.map((item: LanguageAndCountries) => {
49
50
  const checked = savedLanguages.includes(item.value)
50
51
 
51
52
  return {
@@ -54,8 +55,9 @@ export const DialogFilter: FC<DialogFilterProps> = ({ trigger }) => {
54
55
  checked,
55
56
  }
56
57
  })
57
- );
58
+ }
58
59
 
60
+ const [languages, setLanguages] = useState<Languages[]>(generateLanguagesList());
59
61
  const [params, setParams] = useQueryStates({
60
62
  search: parseAsString,
61
63
  language: parseAsString,
@@ -100,6 +102,10 @@ export const DialogFilter: FC<DialogFilterProps> = ({ trigger }) => {
100
102
  });
101
103
  }
102
104
 
105
+ useEffect(() => {
106
+ setLanguages(generateLanguagesList())
107
+ }, [availableLanguagesAndCountries])
108
+
103
109
  return (
104
110
  <Dialog>
105
111
  <DialogTrigger asChild>
@@ -0,0 +1,9 @@
1
+ import { PiFilePdf } from "react-icons/pi";
2
+
3
+ export const FileIcon = ({ extension }: { extension: string }) => {
4
+ const IconsToFileExtension: Record<string, React.ReactNode> = {
5
+ "application/pdf": <PiFilePdf className="!h-5 !w-5 text-primary" />,
6
+ };
7
+
8
+ return IconsToFileExtension[extension] || null;
9
+ };
@@ -1,3 +1,5 @@
1
+ "use client"
2
+
1
3
  import React, { FC } from "react";
2
4
  import { Card, CardContent, CardHeader, CardTitle } from "@c-rex/ui/card";
3
5
  import { useTranslations } from "next-intl";
@@ -1,5 +1,5 @@
1
1
  import React, { FC } from "react";
2
- import { articleInfoItemType, DocumentsType } from "@c-rex/types";
2
+ import { articleInfoItemType, DocumentsType, Favorite } from "@c-rex/types";
3
3
  import { Card, CardContent, CardHeader, CardTitle } from "@c-rex/ui/card";
4
4
  import {
5
5
  Table,
@@ -15,25 +15,23 @@ import {
15
15
  DropdownMenuItem,
16
16
  DropdownMenuTrigger,
17
17
  } from "@c-rex/ui/dropdown-menu";
18
- import { CloudDownload, Eye } from "lucide-react";
18
+ import { Bookmark, CloudDownload, Eye } from "lucide-react";
19
19
 
20
- import { FaFilePdf } from "react-icons/fa6";
21
20
  import { AvailableVersionsInterface } from "@c-rex/interfaces";
22
21
  import { Flag } from "../flag";
22
+ import { Button } from "@c-rex/ui/button";
23
23
  import { useLanguageStore } from "../stores/language-store";
24
+ import { FileIcon } from "../file-icon";
24
25
 
25
26
  type Props = {
26
27
  title: string;
27
28
  files?: DocumentsType
28
29
  items: articleInfoItemType[]
29
30
  availableVersions?: AvailableVersionsInterface[]
31
+ markersList?: Favorite[]
30
32
  }
31
33
 
32
- const IconsToFileExtension: Record<string, React.ReactNode> = {
33
- "application/pdf": <FaFilePdf className="h-6 w-6" />,
34
- };
35
-
36
- export const InfoTable: FC<Props> = ({ title, items, files, availableVersions }) => {
34
+ export const InfoTable: FC<Props> = ({ title, items, files, availableVersions, markersList }) => {
37
35
  const t = useTranslations();
38
36
  const uiLang = useLanguageStore(state => state.uiLang);
39
37
  const newItems = filteredItems(items)
@@ -75,18 +73,20 @@ export const InfoTable: FC<Props> = ({ title, items, files, availableVersions })
75
73
 
76
74
  return (
77
75
  <DropdownMenu key={index}>
78
- <DropdownMenuTrigger className="mx-2">
79
- {IconsToFileExtension[item]}
76
+ <DropdownMenuTrigger className="mr-2" asChild >
77
+ <Button variant="outline" size="icon" >
78
+ <FileIcon extension={item} />
79
+ </Button>
80
80
  </DropdownMenuTrigger>
81
81
  <DropdownMenuContent>
82
82
  <DropdownMenuItem>
83
83
  <a href={files[item].view} target="_blank" rel="noreferrer" className="flex items-center">
84
- <Eye className="mr-2 " /> Open
84
+ <Eye className="mr-2" /> Open
85
85
  </a>
86
86
  </DropdownMenuItem>
87
87
  <DropdownMenuItem>
88
88
  <a href={files[item].download} target="_blank" rel="noreferrer" className="flex items-center">
89
- <CloudDownload className="mr-2 " /> Download
89
+ <CloudDownload className="mr-2" /> Download
90
90
  </a>
91
91
  </DropdownMenuItem>
92
92
 
@@ -118,9 +118,43 @@ export const InfoTable: FC<Props> = ({ title, items, files, availableVersions })
118
118
  </TableRow>
119
119
  )}
120
120
 
121
+ {markersList && markersList.length > 0 && (
122
+ <TableRow className="min-h-12">
123
+ <TableCell className="font-medium w-28 pl-4">
124
+ <h4 className="text-sm font-medium">Bookmarks</h4>
125
+ </TableCell>
126
+ <TableCell className="text-xs text-muted-foreground flex items-center gap-2 min-h-12">
127
+ <DropdownMenu>
128
+ <DropdownMenuTrigger className="mr-2" asChild>
129
+ <Button variant="outline" size="icon" className="relative">
130
+ <Bookmark className="text-primary" />
131
+
132
+ <span
133
+ className="absolute -top-[10px] -right-[10px] min-w-5 min-h-5 bg-primary text-white rounded-full"
134
+ >{markersList.length}</span>
135
+ </Button>
136
+ </DropdownMenuTrigger>
137
+ <DropdownMenuContent>
138
+
139
+ {markersList.map((marker) => (
140
+ <DropdownMenuItem key={marker.id}>
141
+ <a href={`./${marker.id}`} target="_blank" rel="noreferrer" className="flex items-center">
142
+ {marker.label}
143
+ </a>
144
+ </DropdownMenuItem>
145
+ ))}
146
+
147
+ </DropdownMenuContent>
148
+ </DropdownMenu>
149
+
150
+ </TableCell>
151
+ </TableRow>
152
+ )}
153
+
154
+
121
155
  </TableBody>
122
156
  </Table>
123
157
  </CardContent>
124
- </Card>
158
+ </Card >
125
159
  );
126
160
  }
@@ -2,10 +2,10 @@ 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";
5
- import { getConfigs } from "@c-rex/utils/next-cookies";
6
5
  import { SettingsMenu } from "./settings";
7
6
  import { UserMenu } from "./user-menu";
8
7
  import { SearchInput } from "./search-input";
8
+ import { getClientConfig, getServerConfig } from "@c-rex/core";
9
9
 
10
10
  interface NavBarProps {
11
11
  title: string;
@@ -14,10 +14,11 @@ interface NavBarProps {
14
14
  }
15
15
 
16
16
  export const NavBar: FC<NavBarProps> = async ({ title, showInput, showPkgFilter }) => {
17
- const configs = await getConfigs();
17
+ const clientConfigs = getClientConfig();
18
+ const serverConfigs = getServerConfig();
18
19
 
19
20
  let session: any;
20
- if (configs.OIDC.user.enabled) {
21
+ if (clientConfigs.OIDC.userEnabled) {
21
22
  session = await getServerSession();
22
23
  }
23
24
 
@@ -42,17 +43,17 @@ export const NavBar: FC<NavBarProps> = async ({ title, showInput, showPkgFilter
42
43
  <div className="flex items-center gap-2 absolute sm:static top-4 h-14 sm:h-min right-4">
43
44
  <SearchInput showInput={showInput} showPkgFilter={showPkgFilter} placedOn="NAVBAR" />
44
45
 
45
- {configs.OIDC.user.enabled && (
46
+ {clientConfigs.OIDC.userEnabled && (
46
47
  <>
47
48
  {session ? (
48
- <UserMenu session={session} />
49
+ <UserMenu session={session} OIDCEndPoint={serverConfigs.OIDC.user.issuer} />
49
50
  ) : (
50
51
  <SignInBtn />
51
52
  )}
52
53
  </>
53
54
  )}
54
55
 
55
- {configs.languageSwitcher.enabled && (
56
+ {clientConfigs.languageSwitcher.enabled && (
56
57
  <SettingsMenu />
57
58
  )}
58
59
  </div>
@@ -23,7 +23,7 @@ export const SearchInput: FC<Props> = ({ showInput, showPkgFilter, placedOn = "N
23
23
  placedOn === "NAVBAR" ? "hidden lg:flex" : "sm:flex-none sm:w-60 md:w-72 lg:w-full lg:hidden flex",
24
24
  "flex-1 items-center px-3 border rounded-full h-8 c-rex-search-bar"
25
25
  )}>
26
- <Search className="h-4 w-4 shrink-0 opacity-50" />
26
+ <Search className="shrink-0 opacity-50" />
27
27
 
28
28
  <AutoComplete
29
29
  initialValue=""
@@ -1,21 +1,35 @@
1
1
  "use client"
2
2
 
3
- import React from "react";
3
+ import React, { useState } from "react";
4
4
  import { signIn } from "next-auth/react";
5
5
  import { Button } from "@c-rex/ui/button";
6
6
  import { useTranslations } from "next-intl";
7
7
 
8
8
  export const SignInBtn = () => {
9
9
  const t = useTranslations("user")
10
+ const [isLoading, setIsLoading] = useState<boolean>(false)
11
+
12
+ const handleSignIn = () => {
13
+ setIsLoading(true);
14
+ signIn("crex", { prompt: "login", callbackUrl: "/" });
15
+ }
16
+
10
17
  return (
11
18
  <Button
12
19
  className="gap-2 px-5 md:flex"
13
20
  variant="default"
14
21
  size="sm"
15
22
  rounded="full"
16
- onClick={() => signIn("crex", { callbackUrl: "/" })}
23
+ disabled={isLoading}
24
+ onClick={handleSignIn}
17
25
  >
18
- <span>{t("signIn")}</span>
26
+ {isLoading ? (
27
+ <div className="flex items-center justify-center py-4">
28
+ <div className="animate-spin rounded-full h-5 w-5 border-2 border-gray-300 border-t-gray-950" />
29
+ </div>
30
+ ) : (
31
+ <span>{t("signIn")}</span>
32
+ )}
19
33
  </Button>
20
34
  );
21
35
  }
@@ -11,19 +11,17 @@ import {
11
11
  } from "@c-rex/ui/dropdown-menu"
12
12
  import { CircleUser } from "lucide-react";
13
13
  import { useTranslations } from "next-intl";
14
- import { useAppConfig } from "@c-rex/contexts/config-provider";
15
14
  import { signOut } from "next-auth/react";
16
15
 
17
16
 
18
17
  interface Props {
19
18
  session: any
19
+ OIDCEndPoint: string
20
20
  }
21
21
 
22
- export const UserMenu: FC<Props> = ({ session }) => {
23
- const t = useTranslations();
24
- const { configs } = useAppConfig();
25
22
 
26
- const endpoint = configs.OIDC.user.issuer.split("oidc/")[0];
23
+ export const UserMenu: FC<Props> = ({ session, OIDCEndPoint }) => {
24
+ const t = useTranslations();
27
25
 
28
26
  return (
29
27
  <DropdownMenu>
@@ -38,18 +36,21 @@ export const UserMenu: FC<Props> = ({ session }) => {
38
36
  <DropdownMenuSeparator />
39
37
  <DropdownMenuGroup>
40
38
  <DropdownMenuItem>
41
- <a href={`${endpoint}oidc/Profile`} target="_blank" rel="noreferrer">
39
+ <a href={`${OIDCEndPoint}oidc/Profile`} target="_blank" rel="noreferrer">
42
40
  Profile settings
43
41
  </a>
44
42
  </DropdownMenuItem>
45
43
 
46
44
  <DropdownMenuItem>
47
- <a href={`${endpoint}oidc/Profile/ChangePassword`} target="_blank" rel="noreferrer">
45
+ <a href={`${OIDCEndPoint}oidc/Profile/ChangePassword`} target="_blank" rel="noreferrer">
48
46
  Change password
49
47
  </a>
50
48
  </DropdownMenuItem>
51
49
 
52
- <DropdownMenuItem onClick={() => signOut()} className='text-red-500 focus:text-red-500'>
50
+ <DropdownMenuItem
51
+ onClick={() => signOut({ callbackUrl: "/api/auth/logout" })}
52
+ className='text-red-500 focus:text-red-500'
53
+ >
53
54
  {t("user.signOut")}
54
55
  </DropdownMenuItem>
55
56
  </DropdownMenuGroup>
@@ -61,7 +61,7 @@ export const RenderArticle = ({ htmlContent, contentLang }: Props) => {
61
61
  }, [highlightedContent, registerContainer]);
62
62
 
63
63
  return (
64
- <main ref={containerRef} lang={contentLang} className="pb-4">
64
+ <main ref={containerRef} lang={contentLang} className="pb-4 min-h-[70vh]">
65
65
  {highlightedContent}
66
66
  </main>
67
67
  );
@@ -1,10 +1,12 @@
1
- import React, { FC } from "react";
1
+ import React, { FC, ReactNode } from "react";
2
2
  import { DefaultPageInfo, informationUnitsResponseItem, TopicsResponseItem, } 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
6
  import { Pagination } from "./pagination";
7
7
  import { useAppConfig } from "@c-rex/contexts/config-provider";
8
+ import { ResultViewStyles } from "@c-rex/types";
9
+ import TableWithImageView from "./result-view/table-with-images";
8
10
 
9
11
  interface ResultListProps {
10
12
  items: informationUnitsResponseItem[] | TopicsResponseItem[];
@@ -14,32 +16,28 @@ interface ResultListProps {
14
16
  export const ResultList: FC<ResultListProps> = ({ items, pagination }: ResultListProps) => {
15
17
  const { configs } = useAppConfig()
16
18
 
17
- const isTableView = configs.results.resultViewStyle === "table";
19
+ const listComponent: Record<ResultViewStyles, ReactNode> = {
20
+ "cards": <BlogView items={items as TopicsResponseItem[]} />,
21
+ "table": <TableView items={items as informationUnitsResponseItem[]} />,
22
+ "table-with-images": <TableWithImageView items={items as informationUnitsResponseItem[]} />,
23
+ }
24
+
25
+ if (!(configs.results.resultViewStyle in listComponent)) {
26
+ throw new Error(`Unsupported result view style: ${configs.results.resultViewStyle}`);
27
+ }
28
+
29
+ if (items.length === 0) {
30
+ return <Empty />;
31
+ }
18
32
 
19
33
  return (
20
34
  <>
21
- {
22
- items.length === 0 ? (
23
- <Empty />
24
- ) : (
25
- <>
26
- {isTableView ? (
27
- <TableView
28
- items={items as informationUnitsResponseItem[]}
29
- />
30
- ) : (
31
- <BlogView
32
- items={items as TopicsResponseItem[]}
33
- />
34
- )}
35
+ {listComponent[configs.results.resultViewStyle]}
35
36
 
36
- <Pagination
37
- totalPages={pagination.pageCount}
38
- currentPage={pagination.pageNumber}
39
- />
40
- </>
41
- )
42
- }
37
+ <Pagination
38
+ totalPages={pagination.pageCount}
39
+ currentPage={pagination.pageNumber}
40
+ />
43
41
  </>
44
42
  );
45
43
  };
@@ -50,6 +50,7 @@ const BlogView: FC<BlogViewProps> = ({ items }) => {
50
50
  style={{
51
51
  width: "100%", height: index == 0 ? "auto" : "190px"
52
52
  }}
53
+ loading={index == 0 ? "eager" : "lazy"}
53
54
  onLoad={() => setLoading(false)}
54
55
  onError={() => setLoading(false)}
55
56
  />
@@ -0,0 +1,174 @@
1
+ import { FC, useEffect, useState } from "react";
2
+ import { informationUnitsResponseItem } from "@c-rex/interfaces";
3
+ import { CloudDownload, Eye, FileStack, ImageOff } from "lucide-react";
4
+ import { FaFilePdf } from "react-icons/fa6";
5
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@c-rex/ui/dropdown-menu";
6
+ import { cn } from "@c-rex/utils";
7
+ import { useQueryState } from "nuqs";
8
+ import { Skeleton } from "@c-rex/ui/skeleton";
9
+ import { FavoriteButton } from "../favorite-button";
10
+ import { Button } from "@c-rex/ui/button";
11
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@c-rex/ui/tooltip";
12
+ import { ResultTypes } from "@c-rex/types";
13
+
14
+ interface TableWithImageViewProps {
15
+ items: informationUnitsResponseItem[];
16
+ }
17
+
18
+ const IconsToFileExtension: Record<string, React.ReactNode> = {
19
+ "application/pdf": <FaFilePdf className="h-5 w-5" />,
20
+ };
21
+
22
+ const Image: FC<{ id: string }> = ({ id }) => {
23
+ const [imageUrl, setImageUrl] = useState("");
24
+ const [loading, setLoading] = useState(true);
25
+ const [error, setError] = useState(false);
26
+
27
+ useEffect(() => {
28
+ let ignore = false;
29
+
30
+ async function fetchImage() {
31
+ try {
32
+ const res = await fetch(`https://picsum.photos/v2/list?limit=1`);
33
+
34
+ {/*
35
+ TODO: talvez vamos receber mais de uma imagem, mas por enquanto pegamos apenas a primeira na lista
36
+ */}
37
+
38
+
39
+ const data = await res.json();
40
+ if (!ignore) {
41
+ setImageUrl(data[0].download_url);
42
+ setLoading(false);
43
+ }
44
+ } catch (err) {
45
+ setError(true);
46
+ console.error("Error loading image", err);
47
+ }
48
+ }
49
+
50
+ fetchImage();
51
+ return () => {
52
+ ignore = true
53
+ };
54
+ }, [id]);
55
+
56
+ if (error) {
57
+ return <ImageOff className="h-16 w-16 text-muted" />;
58
+ }
59
+
60
+ return (
61
+ <div className="h-16 w-16 flex items-center">
62
+ {loading ? (
63
+ <Skeleton className="h-16 w-16" />
64
+ ) : (
65
+ <img
66
+ src={imageUrl}
67
+ loading="lazy"
68
+ onLoad={() => setLoading(false)}
69
+ />
70
+ )}
71
+ </div>
72
+ );
73
+ }
74
+
75
+ const TableWithImageView: FC<TableWithImageViewProps> = ({ items, params }) => {
76
+ const [query] = useQueryState("search");
77
+
78
+ return (
79
+ <div className="mb-6">
80
+ {items.map((item, index) => (
81
+ <div
82
+ className={cn(
83
+ "min-h-12 flex flex-wrap items-center border px-4 py-2 gap-2 rounded mb-2",
84
+ `c-rex-result-item c-rex-result-${item.type.toLowerCase()}`,
85
+ item.disabled && "c-rex-result-item-disabled",
86
+ )}
87
+ key={index}
88
+ >
89
+
90
+ <div className="h-16 w-16 flex items-center">
91
+ <Image id={item.shortId} {...params} />
92
+ </div>
93
+
94
+ <div className="flex-1 p-2 flex flex-col">
95
+ <span className=" text-sm text-muted-foreground">
96
+ {item.language.split("-")[0]?.toUpperCase()}
97
+ </span>
98
+
99
+ <span className="text-lg font-medium">
100
+ {item.disabled ? (
101
+ item.title
102
+ ) : (
103
+ <a className="hover:underline" href={`${item.link}?q=${query}`}>{item.title}</a>
104
+ )}
105
+ </span>
106
+
107
+ <span className="text-sm">
108
+ {item.type}
109
+ </span>
110
+ <span className="text-sm">
111
+ short description here {/*
112
+ TODO: get abstract if has the value key on it. Same behavior on class props
113
+
114
+ abstract and description are not being returned by the API yet
115
+ */}
116
+ </span>
117
+ </div>
118
+
119
+ <div className="flex flex-col p-2 ml-auto justify-center">
120
+ <span className="text-end text-sm text-muted-foreground mb-2">
121
+ {item.revision}
122
+ </span>
123
+ <div className="flex gap-2">
124
+ {Object.keys(item.files).map((fileKey, index) => {
125
+ if (!item.files[fileKey]) return null
126
+
127
+ return (
128
+ <DropdownMenu key={index}>
129
+ <DropdownMenuTrigger>
130
+ <Button variant="ghost" size="icon" rounded="full">
131
+ {IconsToFileExtension[fileKey]}
132
+ </Button>
133
+ </DropdownMenuTrigger>
134
+ <DropdownMenuContent>
135
+ <DropdownMenuItem>
136
+ <a href={item.files[fileKey].view} target="_blank" rel="noreferrer" className="flex items-center">
137
+ <Eye className="mr-2" /> Open {/*TODO: use i18n functions*/}
138
+ </a>
139
+ </DropdownMenuItem>
140
+ <DropdownMenuItem>
141
+ <a href={item.files[fileKey].download} target="_blank" rel="noreferrer" className="flex items-center">
142
+ <CloudDownload className="mr-2" /> Download {/*TODO: use i18n functions*/}
143
+ </a>
144
+ </DropdownMenuItem>
145
+ </DropdownMenuContent>
146
+ </DropdownMenu>
147
+ )
148
+ })}
149
+
150
+ <FavoriteButton id={item.shortId} type={item.type as ResultTypes} label={item.title} />
151
+
152
+ {item.multipleVersions.length > 1 && (
153
+ <Tooltip>
154
+ <TooltipTrigger asChild>
155
+ <Button variant="ghost" size="icon" rounded="full">
156
+ <FileStack /> {JSON.stringify(item.multipleVersions)}
157
+ </Button>
158
+ </TooltipTrigger>
159
+ <TooltipContent>
160
+
161
+ Available in: {item.multipleVersions}
162
+ </TooltipContent>
163
+ </Tooltip>
164
+ )}
165
+ </div>
166
+
167
+ </div>
168
+ </div>
169
+ ))}
170
+ </div>
171
+ );
172
+ };
173
+
174
+ export default TableWithImageView;
@@ -79,12 +79,12 @@ const TableView: FC<TableViewProps> = ({ items }) => {
79
79
  <DropdownMenuContent>
80
80
  <DropdownMenuItem>
81
81
  <a href={item.files[fileKey].view} target="_blank" rel="noreferrer" className="flex items-center">
82
- <Eye className="mr-2 " /> Open
82
+ <Eye className="mr-2" /> Open {/*TODO: use i18n functions*/}
83
83
  </a>
84
84
  </DropdownMenuItem>
85
85
  <DropdownMenuItem>
86
86
  <a href={item.files[fileKey].download} target="_blank" rel="noreferrer" className="flex items-center">
87
- <CloudDownload className="mr-2 " /> Download
87
+ <CloudDownload className="mr-2" /> Download {/*TODO: use i18n functions*/}
88
88
  </a>
89
89
  </DropdownMenuItem>
90
90
  </DropdownMenuContent>
@@ -2,6 +2,7 @@ import { UI_LANG_KEY } from "@c-rex/constants";
2
2
  import { AVAILABLE_CONTENT_LANG_KEY } from "@c-rex/constants";
3
3
  import { CONTENT_LANG_KEY } from "@c-rex/constants";
4
4
  import { LanguageAndCountries } from "@c-rex/interfaces";
5
+ import { getCookie, setCookie } from "@c-rex/utils";
5
6
  import { create } from "zustand";
6
7
 
7
8
  type LanguageStoreType = {
@@ -14,33 +15,21 @@ type LanguageStoreType = {
14
15
  hydrate: () => void;
15
16
  };
16
17
 
17
- function setCookie(name: string, value: string, days = 365) {
18
- const expires = new Date(Date.now() + days * 864e5).toUTCString();
19
- document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/`;
20
- }
21
-
22
- function getCookie(name: string): string {
23
- return document.cookie
24
- .split('; ')
25
- .find(row => row.startsWith(name + '='))
26
- ?.split('=')[1] || '';
27
- }
28
-
29
18
  export const useLanguageStore = create<LanguageStoreType>((set) => ({
30
19
  contentLang: "",
31
20
  uiLang: "",
32
21
  availableLanguages: [],
33
22
  setContentLang: (v) => {
34
23
  set({ contentLang: v });
35
- setCookie(CONTENT_LANG_KEY, v);
24
+ setCookie(CONTENT_LANG_KEY, v, 7);
36
25
  },
37
26
  setUiLang: (v) => {
38
27
  set({ uiLang: v });
39
- setCookie(UI_LANG_KEY, v);
28
+ setCookie(UI_LANG_KEY, v, 7);
40
29
  },
41
30
  setAvailableLanguages: (list) => {
42
31
  set({ availableLanguages: list });
43
- setCookie(AVAILABLE_CONTENT_LANG_KEY, JSON.stringify(list));
32
+ setCookie(AVAILABLE_CONTENT_LANG_KEY, JSON.stringify(list), 7);
44
33
  },
45
34
  hydrate: () => {
46
35
  set({
@@ -12,7 +12,7 @@ type SearchSettingsStore = {
12
12
 
13
13
  export const useSearchSettingsStore = create<SearchSettingsStore>()(
14
14
  persist((set) => ({
15
- language: [],
15
+ language: [] as string[],
16
16
  wildcard: "BOTH",
17
17
  operator: "OR",
18
18
  like: false,
@@ -1,39 +0,0 @@
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
- };