@c-rex/components 0.1.21 → 0.1.23
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 +78 -62
- package/src/article/article-action-bar.tsx +89 -0
- package/src/article/article-content.tsx +55 -0
- package/src/autocomplete.tsx +55 -50
- package/src/breadcrumb.tsx +3 -1
- package/src/directoryNodes/tree-of-content.tsx +49 -0
- package/src/{bookmark-button.tsx → favorites/bookmark-button.tsx} +12 -3
- package/src/{favorite-button.tsx → favorites/favorite-button.tsx} +1 -1
- package/src/generated/client-components.tsx +1350 -0
- package/src/generated/create-client-request.tsx +105 -0
- package/src/generated/create-server-request.tsx +61 -0
- package/src/generated/create-suggestions-request.tsx +56 -0
- package/src/generated/server-components.tsx +1056 -0
- package/src/generated/suggestions.tsx +299 -0
- package/src/info/bookmark.tsx +51 -0
- package/src/info/info-table.tsx +127 -60
- package/src/info/shared.tsx +1 -1
- package/src/navbar/language-switcher/shared.tsx +1 -1
- package/src/navbar/navbar.tsx +1 -1
- package/src/{stories → navbar/stories}/navbar.stories.tsx +1 -1
- package/src/page-wrapper.tsx +1 -1
- package/src/renditions/file-download.tsx +84 -0
- package/src/renditions/html.tsx +55 -0
- package/src/renditions/image/container.tsx +52 -0
- package/src/renditions/image/rendition.tsx +61 -0
- package/src/{dialog-filter.tsx → results/dialog-filter.tsx} +22 -23
- package/src/results/filter-navbar.tsx +241 -0
- package/src/results/filter-sidebar/index.tsx +125 -0
- package/src/results/filter-sidebar/utils.ts +164 -0
- package/src/{pagination.tsx → results/pagination.tsx} +12 -10
- package/src/results/result-container.tsx +70 -0
- package/src/{stories/blog-view.stories.tsx → results/stories/cards.stories.tsx} +1 -1
- package/src/{stories/table-view.stories.tsx → results/stories/table.stories.tsx} +1 -1
- package/src/results/table-with-images.tsx +140 -0
- package/src/{result-view → results}/table.tsx +1 -2
- package/src/results/utils.ts +67 -0
- package/src/{navbar/search-input.tsx → search-input.tsx} +9 -6
- package/src/share-button.tsx +49 -0
- package/src/stores/search-settings-store.ts +1 -1
- package/src/blur-image.tsx +0 -23
- package/src/left-sidebar.tsx +0 -90
- package/src/result-list.tsx +0 -43
- package/src/result-view/table-with-images.tsx +0 -199
- package/src/right-sidebar.tsx +0 -70
- package/src/search-modal.tsx +0 -140
- package/src/stories/blur-image.stories.tsx +0 -51
- package/src/stories/sidebar.stories.tsx +0 -94
- /package/src/{file-icon.tsx → icons/file-icon.tsx} +0 -0
- /package/src/{flag.tsx → icons/flag-icon.tsx} +0 -0
- /package/src/{loading.tsx → icons/loading.tsx} +0 -0
- /package/src/{result-view/blog.tsx → results/cards.tsx} +0 -0
- /package/src/{empty.tsx → results/empty.tsx} +0 -0
- /package/src/{stories → results/stories}/empty.stories.tsx +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { RenditionModel } from "@c-rex/interfaces";
|
|
3
|
+
import {
|
|
4
|
+
DropdownMenu,
|
|
5
|
+
DropdownMenuContent,
|
|
6
|
+
DropdownMenuItem,
|
|
7
|
+
DropdownMenuTrigger
|
|
8
|
+
} from "@c-rex/ui/dropdown-menu";
|
|
9
|
+
import { Button } from "@c-rex/ui/button";
|
|
10
|
+
import { FileIcon } from "../icons/file-icon";
|
|
11
|
+
import { CloudDownload, Eye } from "lucide-react";
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
interface OpenOrDownloadFileDropdown {
|
|
15
|
+
renditions: RenditionModel[] | null | undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const OpenOrDownloadFileDropdown: FC<OpenOrDownloadFileDropdown> = async ({ renditions }) => {
|
|
19
|
+
|
|
20
|
+
if (renditions == null || renditions.length == 0) return null;
|
|
21
|
+
|
|
22
|
+
const result: {
|
|
23
|
+
[key: string]: {
|
|
24
|
+
view: string;
|
|
25
|
+
download: string;
|
|
26
|
+
}
|
|
27
|
+
} = {}
|
|
28
|
+
|
|
29
|
+
renditions.forEach((item) => {
|
|
30
|
+
const key = item.format!
|
|
31
|
+
|
|
32
|
+
if (result[key] == undefined) {
|
|
33
|
+
result[key] = {
|
|
34
|
+
view: "",
|
|
35
|
+
download: ""
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const download = item.links?.filter((link) => link.rel == "download")[0]?.href
|
|
39
|
+
const view = item.links?.filter((link) => link.rel == "view")[0]?.href
|
|
40
|
+
|
|
41
|
+
if (download != null && download != undefined) {
|
|
42
|
+
result[key].download = download
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (view != null && view != undefined) {
|
|
46
|
+
result[key].view = view
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
{Object.keys(result).map((fileKey, idx) => {
|
|
53
|
+
const file = result[fileKey];
|
|
54
|
+
if (!file) return null;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<DropdownMenu key={idx}>
|
|
58
|
+
<DropdownMenuTrigger asChild>
|
|
59
|
+
<Button variant="ghost" size="icon">
|
|
60
|
+
<FileIcon extension={fileKey} />
|
|
61
|
+
</Button>
|
|
62
|
+
</DropdownMenuTrigger>
|
|
63
|
+
<DropdownMenuContent>
|
|
64
|
+
{file.view && (
|
|
65
|
+
<DropdownMenuItem asChild>
|
|
66
|
+
<a href={file.view} target="_blank" rel="noreferrer" className="flex items-center">
|
|
67
|
+
<Eye className="mr-2" /> Open
|
|
68
|
+
</a>
|
|
69
|
+
</DropdownMenuItem>
|
|
70
|
+
)}
|
|
71
|
+
{file.download && (
|
|
72
|
+
<DropdownMenuItem asChild>
|
|
73
|
+
<a href={file.download} target="_blank" rel="noreferrer" className="flex items-center">
|
|
74
|
+
<CloudDownload className="mr-2" /> Download
|
|
75
|
+
</a>
|
|
76
|
+
</DropdownMenuItem>
|
|
77
|
+
)}
|
|
78
|
+
</DropdownMenuContent>
|
|
79
|
+
</DropdownMenu>
|
|
80
|
+
);
|
|
81
|
+
})}
|
|
82
|
+
</>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import * as cheerio from "cheerio"
|
|
3
|
+
import { FragmentsGetAll } from "../generated/server-components";
|
|
4
|
+
|
|
5
|
+
interface HtmlRenditionProps {
|
|
6
|
+
htmlFormats?: string[]
|
|
7
|
+
shortId: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const HtmlRendition: FC<HtmlRenditionProps> = ({
|
|
11
|
+
shortId,
|
|
12
|
+
htmlFormats = ["application/xhtml+xml", "application/html", "text/html"]
|
|
13
|
+
}) => {
|
|
14
|
+
const empty = <div>No rendition available</div>;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<FragmentsGetAll
|
|
18
|
+
queryParams={{
|
|
19
|
+
Fields: ["titles", "renditions"],
|
|
20
|
+
Embed: ["renditions"],
|
|
21
|
+
PageSize: 1,
|
|
22
|
+
Links: true,
|
|
23
|
+
Restrict: [
|
|
24
|
+
`informationUnits=${shortId}`,
|
|
25
|
+
...htmlFormats.map(format => `renditions.format=${format}`)
|
|
26
|
+
],
|
|
27
|
+
|
|
28
|
+
}}
|
|
29
|
+
render={async (data, error) => {
|
|
30
|
+
if (error) {
|
|
31
|
+
return <div>Error loading content</div>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const renditions = data?.items?.[0]?.renditions;
|
|
35
|
+
|
|
36
|
+
if (renditions == null || renditions.length == 0) return empty;
|
|
37
|
+
if (renditions.length == 0 || renditions[0] == undefined || renditions[0].links == undefined) return empty;
|
|
38
|
+
|
|
39
|
+
const filteredLinks = renditions[0].links.filter((item) => item.rel == "view");
|
|
40
|
+
|
|
41
|
+
if (filteredLinks.length == 0 || filteredLinks[0] == undefined || filteredLinks[0].href == undefined) return empty;
|
|
42
|
+
|
|
43
|
+
const url = filteredLinks[0].href;
|
|
44
|
+
const html = await fetch(url).then(res => res.text());
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
const $ = cheerio.load(html)
|
|
48
|
+
|
|
49
|
+
const articleHtml = $("body").html() || ""
|
|
50
|
+
|
|
51
|
+
return <div dangerouslySetInnerHTML={{ __html: articleHtml }} />;
|
|
52
|
+
}}
|
|
53
|
+
/>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { FC } from "react";
|
|
4
|
+
import { FragmentsGetAllClient } from "../../generated/client-components";
|
|
5
|
+
import { ImageRendition } from "./rendition";
|
|
6
|
+
import { Skeleton } from "@c-rex/ui/skeleton";
|
|
7
|
+
|
|
8
|
+
interface ImageContainerProps {
|
|
9
|
+
itemShortId: string;
|
|
10
|
+
imageRestrictions?: string[];
|
|
11
|
+
imageFormats?: string[];
|
|
12
|
+
emptyImageStyle?: string;
|
|
13
|
+
imageStyle?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const ImageRenditionContainer: FC<ImageContainerProps> = ({
|
|
17
|
+
itemShortId,
|
|
18
|
+
imageRestrictions,
|
|
19
|
+
emptyImageStyle,
|
|
20
|
+
imageStyle,
|
|
21
|
+
imageFormats = ["image/svg+xml", "image/gif", "image/png", "image/jpg"],
|
|
22
|
+
}) => {
|
|
23
|
+
const newRestrictions = [
|
|
24
|
+
`informationUnits=${itemShortId}`,
|
|
25
|
+
//...imageFormats.map(format => `renditions.format=${format}`)
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<FragmentsGetAllClient
|
|
30
|
+
queryParams={{
|
|
31
|
+
Fields: ["titles", "renditions"],
|
|
32
|
+
Embed: ["renditions"],
|
|
33
|
+
PageSize: 1,
|
|
34
|
+
Links: true,
|
|
35
|
+
Restrict: newRestrictions,
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
{({ data, isLoading }) => (
|
|
39
|
+
isLoading ? (
|
|
40
|
+
<Skeleton className="w-full h-full" />
|
|
41
|
+
) : (
|
|
42
|
+
<ImageRendition
|
|
43
|
+
items={data?.items as any}
|
|
44
|
+
formats={imageFormats}
|
|
45
|
+
emptyImageStyle={emptyImageStyle}
|
|
46
|
+
imageStyle={imageStyle}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
)}
|
|
50
|
+
</FragmentsGetAllClient>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { FC } from "react";
|
|
4
|
+
import { ImageOff } from "lucide-react";
|
|
5
|
+
import { DocumentModel, ExternalProductGraphicModel, FragmentModel, InformationUnitModel, PackageModel, RenditionModel, TopicModel } from "@c-rex/interfaces";
|
|
6
|
+
import { cn } from "@c-rex/utils";
|
|
7
|
+
interface ImageRenditionProps {
|
|
8
|
+
items: (
|
|
9
|
+
DocumentModel |
|
|
10
|
+
ExternalProductGraphicModel |
|
|
11
|
+
FragmentModel |
|
|
12
|
+
InformationUnitModel |
|
|
13
|
+
PackageModel |
|
|
14
|
+
TopicModel
|
|
15
|
+
)[];
|
|
16
|
+
emptyImageStyle?: string;
|
|
17
|
+
imageStyle?: string;
|
|
18
|
+
formats: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const ImageRendition: FC<ImageRenditionProps> = ({ items, formats, emptyImageStyle, imageStyle }) => {
|
|
22
|
+
if (!items || items.length === 0) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
23
|
+
|
|
24
|
+
const item = items[0];
|
|
25
|
+
if (!item || item == undefined) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
26
|
+
|
|
27
|
+
const wanted = formats.map((f) => f.toLowerCase());
|
|
28
|
+
let rendition = null
|
|
29
|
+
const renditions = items[0]?.renditions as RenditionModel[] || [];
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
for (const fmt of wanted) {
|
|
33
|
+
|
|
34
|
+
const found = renditions.find((r) => r.format?.toLowerCase() === fmt);
|
|
35
|
+
|
|
36
|
+
if (found) {
|
|
37
|
+
rendition = found;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (rendition == null) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
43
|
+
|
|
44
|
+
const src =
|
|
45
|
+
rendition.links?.find((l) => l.rel === "view")?.href ??
|
|
46
|
+
rendition.links?.find((l) => l.rel === "download")?.href ??
|
|
47
|
+
rendition.links?.find((l) => l.rel === "resource")?.href ??
|
|
48
|
+
rendition.source ??
|
|
49
|
+
"";
|
|
50
|
+
|
|
51
|
+
if (!src) return <ImageOff className={cn(emptyImageStyle, "text-muted")} />;
|
|
52
|
+
|
|
53
|
+
const alt =
|
|
54
|
+
item.labels?.[0]?.value ??
|
|
55
|
+
item.titles?.[0]?.value ??
|
|
56
|
+
"Document image";
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<img src={src} alt={alt} loading="lazy" className={cn(imageStyle, "w-full")} />
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
1
3
|
import React, { FC, useEffect, useState } from "react"
|
|
2
4
|
import { Button } from "@c-rex/ui/button"
|
|
3
5
|
import {
|
|
@@ -17,9 +19,9 @@ import { useTranslations } from "next-intl"
|
|
|
17
19
|
import { LanguageAndCountries } from "@c-rex/interfaces"
|
|
18
20
|
import { WILD_CARD_OPTIONS, OPERATOR_OPTIONS } from "@c-rex/constants"
|
|
19
21
|
import { Switch } from "@c-rex/ui/switch"
|
|
20
|
-
import { useSearchSettingsStore } from "
|
|
22
|
+
import { useSearchSettingsStore } from "../stores/search-settings-store"
|
|
21
23
|
import { OperatorType, WildCardType } from "@c-rex/types"
|
|
22
|
-
import { useLanguageStore } from "
|
|
24
|
+
import { useLanguageStore } from "../stores/language-store"
|
|
23
25
|
|
|
24
26
|
interface DialogFilterProps {
|
|
25
27
|
trigger: React.ReactNode;
|
|
@@ -32,30 +34,31 @@ interface Languages {
|
|
|
32
34
|
|
|
33
35
|
export const DialogFilter: FC<DialogFilterProps> = ({ trigger }) => {
|
|
34
36
|
const t = useTranslations("filter");
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const savedOperator = useSearchSettingsStore((state) => state.operator)
|
|
38
|
-
const savedWildcard = useSearchSettingsStore((state) => state.wildcard)
|
|
39
|
-
const savedLanguages = useLanguageStore(state => state.contentLang);
|
|
37
|
+
const contentLang = useLanguageStore(state => state.contentLang);
|
|
38
|
+
const searchLanguages = useSearchSettingsStore(state => state.language);
|
|
40
39
|
const availableLanguagesAndCountries = useLanguageStore(state => state.availableLanguages);
|
|
41
40
|
|
|
42
|
-
const [like, setLike] = useState<boolean>(
|
|
43
|
-
const [operator, setOperator] = useState<string>(
|
|
44
|
-
const [wildcard, setWildcard] = useState<string>(
|
|
41
|
+
const [like, setLike] = useState<boolean>(useSearchSettingsStore.getState().like);
|
|
42
|
+
const [operator, setOperator] = useState<string>(useSearchSettingsStore.getState().operator);
|
|
43
|
+
const [wildcard, setWildcard] = useState<string>(useSearchSettingsStore.getState().wildcard);
|
|
44
|
+
const [languages, setLanguages] = useState<Languages[]>([]);
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const checked =
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const aux: Languages[] = availableLanguagesAndCountries.map((item: LanguageAndCountries) => {
|
|
48
|
+
const checked = searchLanguages.length === 0
|
|
49
|
+
? item.value === contentLang
|
|
50
|
+
: searchLanguages.includes(item.value);
|
|
49
51
|
|
|
50
52
|
return {
|
|
51
53
|
lang: item.lang,
|
|
52
54
|
value: item.value,
|
|
53
|
-
checked,
|
|
55
|
+
checked: checked,
|
|
54
56
|
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
setLanguages(aux);
|
|
60
|
+
}, [availableLanguagesAndCountries, contentLang]);
|
|
57
61
|
|
|
58
|
-
const [languages, setLanguages] = useState<Languages[]>(generateLanguagesList());
|
|
59
62
|
const [params, setParams] = useQueryStates({
|
|
60
63
|
search: parseAsString,
|
|
61
64
|
language: parseAsString,
|
|
@@ -99,10 +102,6 @@ export const DialogFilter: FC<DialogFilterProps> = ({ trigger }) => {
|
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
104
|
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
setLanguages(generateLanguagesList())
|
|
104
|
-
}, [availableLanguagesAndCountries])
|
|
105
|
-
|
|
106
105
|
return (
|
|
107
106
|
<Dialog>
|
|
108
107
|
<DialogTrigger asChild>
|
|
@@ -118,13 +117,13 @@ export const DialogFilter: FC<DialogFilterProps> = ({ trigger }) => {
|
|
|
118
117
|
{t("languages")}:
|
|
119
118
|
</Label>
|
|
120
119
|
<div className="flex items-center">
|
|
121
|
-
{languages?.map((item
|
|
120
|
+
{languages?.map((item, index) => {
|
|
122
121
|
return (
|
|
123
122
|
<div key={index} className="mr-4">
|
|
124
123
|
<Checkbox
|
|
125
124
|
id={`available-languages${item.lang}`}
|
|
126
125
|
className="mr-2"
|
|
127
|
-
{
|
|
126
|
+
checked={item.checked}
|
|
128
127
|
onCheckedChange={(checked: boolean) => onChangeCheckbox({
|
|
129
128
|
...item,
|
|
130
129
|
checked: checked,
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { FC, useMemo } from "react";
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import { parseAsBoolean, parseAsInteger, parseAsString, useQueryStates } from "nuqs";
|
|
5
|
+
import { Button } from "@c-rex/ui/button";
|
|
6
|
+
import { Funnel, X } from "lucide-react";
|
|
7
|
+
import { Badge } from "@c-rex/ui/badge";
|
|
8
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@c-rex/ui/tooltip";
|
|
9
|
+
import { OPERATOR_OPTIONS } from "@c-rex/constants";
|
|
10
|
+
import { Tags } from "@c-rex/interfaces";
|
|
11
|
+
import { memoizeFilteredTags } from "./filter-sidebar/utils";
|
|
12
|
+
|
|
13
|
+
type filterModel = {
|
|
14
|
+
key: string
|
|
15
|
+
name?: string
|
|
16
|
+
value: string
|
|
17
|
+
default?: string | boolean | null
|
|
18
|
+
removable: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type FilterNavbarProps = {
|
|
22
|
+
tags?: Tags
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const FilterNavbar: FC<FilterNavbarProps> = ({ tags }) => {
|
|
26
|
+
const t = useTranslations()
|
|
27
|
+
const [params, setParams] = useQueryStates({
|
|
28
|
+
language: parseAsString,
|
|
29
|
+
page: parseAsInteger,
|
|
30
|
+
wildcard: parseAsString,
|
|
31
|
+
operator: parseAsString,
|
|
32
|
+
packages: parseAsString,
|
|
33
|
+
filter: parseAsString,
|
|
34
|
+
like: parseAsBoolean,
|
|
35
|
+
search: {
|
|
36
|
+
defaultValue: "",
|
|
37
|
+
parse(value) {
|
|
38
|
+
return value
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}, {
|
|
42
|
+
history: 'push',
|
|
43
|
+
shallow: false,
|
|
44
|
+
});
|
|
45
|
+
const isMobile = false
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
const filteredTags = useMemo(() => {
|
|
49
|
+
return memoizeFilteredTags(tags, params.filter, params.packages);
|
|
50
|
+
}, [tags, params.filter, params.packages]);
|
|
51
|
+
|
|
52
|
+
const filters = useMemo(() => {
|
|
53
|
+
const filters: filterModel[] = [{
|
|
54
|
+
key: "operator",
|
|
55
|
+
name: t("filter.operator"),
|
|
56
|
+
value: `${params?.operator !== OPERATOR_OPTIONS.OR}`,
|
|
57
|
+
removable: false
|
|
58
|
+
}, {
|
|
59
|
+
key: "like",
|
|
60
|
+
name: t("filter.like"),
|
|
61
|
+
value: `${params.like}`,
|
|
62
|
+
removable: false
|
|
63
|
+
}, {
|
|
64
|
+
key: "wildcard",
|
|
65
|
+
name: t("filter.wildcard"),
|
|
66
|
+
value: params.wildcard as string,
|
|
67
|
+
removable: false,
|
|
68
|
+
}]
|
|
69
|
+
|
|
70
|
+
if (params.language !== null) {
|
|
71
|
+
const languages = params.language.split(",")
|
|
72
|
+
languages.forEach((item) => {
|
|
73
|
+
const aux = languages.filter(langItem => langItem !== item)
|
|
74
|
+
filters.push({
|
|
75
|
+
key: "language",
|
|
76
|
+
name: t(`languages`),
|
|
77
|
+
value: item.toUpperCase(),
|
|
78
|
+
removable: languages.length > 1,
|
|
79
|
+
default: aux.join(",")
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (params.filter !== null) {
|
|
85
|
+
const splittedParam = params.filter.split(",")
|
|
86
|
+
|
|
87
|
+
splittedParam.forEach((item, index) => {
|
|
88
|
+
const aux = item.split(".shortId=")
|
|
89
|
+
const name = aux[0] as string
|
|
90
|
+
const shortId = aux[1] as string
|
|
91
|
+
|
|
92
|
+
const defaultValue = [...splittedParam]
|
|
93
|
+
defaultValue.splice(index, 1)
|
|
94
|
+
|
|
95
|
+
if (!Object.keys(filteredTags).includes(name) || !filteredTags[name]) return;
|
|
96
|
+
|
|
97
|
+
const tag = filteredTags[name].find(el => el.shortId === shortId)
|
|
98
|
+
if (!tag) return;
|
|
99
|
+
|
|
100
|
+
const value = defaultValue.length == 0 ? null : defaultValue.join(",")
|
|
101
|
+
|
|
102
|
+
filters.push({
|
|
103
|
+
key: "filter",
|
|
104
|
+
name: t(`filter.tags.${name}`),
|
|
105
|
+
value: tag.label,
|
|
106
|
+
removable: true,
|
|
107
|
+
default: value
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (params.packages !== null) {
|
|
113
|
+
let label = params.packages
|
|
114
|
+
|
|
115
|
+
if (filteredTags["packages"]) {
|
|
116
|
+
const aux = filteredTags["packages"].find(el => el.shortId === params.packages)
|
|
117
|
+
|
|
118
|
+
if (aux) {
|
|
119
|
+
label = aux.label
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
filters.push({
|
|
124
|
+
key: "packages",
|
|
125
|
+
name: t("filter.tags.packages"),
|
|
126
|
+
value: label,
|
|
127
|
+
removable: true,
|
|
128
|
+
default: null
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Object.keys(params)
|
|
133
|
+
.filter(item => !["page", "search", "language", "operator", "like", "wildcard", "filter", "packages"].includes(item))
|
|
134
|
+
.forEach(item => {
|
|
135
|
+
if (params[item as keyof typeof params] === null || params[item as keyof typeof params] === undefined) return;
|
|
136
|
+
const value = params[item as keyof typeof params] as string
|
|
137
|
+
filters.push({ key: item, value: value, removable: true, default: null })
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
return filters
|
|
141
|
+
}, [filteredTags, params]);
|
|
142
|
+
|
|
143
|
+
if (filters == null || filters.length === 0) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div className="pb-4 flex justify-between">
|
|
149
|
+
|
|
150
|
+
<div className="flex flex-wrap gap-2">
|
|
151
|
+
<Button
|
|
152
|
+
size="sm"
|
|
153
|
+
variant="secondary"
|
|
154
|
+
//onClick={() => setOpen(true)}
|
|
155
|
+
className="md:hidden"
|
|
156
|
+
>
|
|
157
|
+
<Funnel className="h-2" />
|
|
158
|
+
</Button>
|
|
159
|
+
|
|
160
|
+
{filters.length > 0 && (
|
|
161
|
+
<>
|
|
162
|
+
{filters.slice(0, 1).map((item) => (
|
|
163
|
+
<Badge
|
|
164
|
+
key={`${item.key}-${item?.value}`}
|
|
165
|
+
variant="outline"
|
|
166
|
+
className="h-8"
|
|
167
|
+
>
|
|
168
|
+
{item?.name ? item.name : item.key}: {item.value}
|
|
169
|
+
|
|
170
|
+
{item.removable && (
|
|
171
|
+
<Button size="xs" variant="ghost" onClick={() => {
|
|
172
|
+
setParams({ [item.key]: item?.default })
|
|
173
|
+
}}>
|
|
174
|
+
<X className="h-2" />
|
|
175
|
+
</Button>
|
|
176
|
+
)}
|
|
177
|
+
</Badge>
|
|
178
|
+
))}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
{isMobile ? (
|
|
182
|
+
<TooltipProvider>
|
|
183
|
+
<Tooltip delayDuration={100}>
|
|
184
|
+
<TooltipTrigger>
|
|
185
|
+
<Badge
|
|
186
|
+
key={`grouped-filters`}
|
|
187
|
+
variant="outline"
|
|
188
|
+
className="h-8"
|
|
189
|
+
>
|
|
190
|
+
+{filters.length - 1} {t("filter.filters")}
|
|
191
|
+
</Badge>
|
|
192
|
+
|
|
193
|
+
</TooltipTrigger>
|
|
194
|
+
|
|
195
|
+
<TooltipContent>
|
|
196
|
+
{filters.slice(1).map((item) => {
|
|
197
|
+
const label = item?.name ? item.name : item.key
|
|
198
|
+
const returnString = `${label}: ${item.value}`;
|
|
199
|
+
return <div className="capitalize" key={returnString}>{returnString}</div>;
|
|
200
|
+
})}
|
|
201
|
+
</TooltipContent>
|
|
202
|
+
</Tooltip>
|
|
203
|
+
</TooltipProvider>
|
|
204
|
+
) : (
|
|
205
|
+
<>
|
|
206
|
+
{filters.slice(1).map((item) => (
|
|
207
|
+
<Badge
|
|
208
|
+
key={`${item.key}-${item?.value}`}
|
|
209
|
+
variant="outline"
|
|
210
|
+
className="h-8"
|
|
211
|
+
>
|
|
212
|
+
{item?.name ? item.name : item.key}: {item.value}
|
|
213
|
+
|
|
214
|
+
{item.removable && (
|
|
215
|
+
<Button size="xs" variant="ghost" onClick={() => {
|
|
216
|
+
setParams({ [item.key]: item?.default })
|
|
217
|
+
}}>
|
|
218
|
+
<X className="h-2" />
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
221
|
+
</Badge>
|
|
222
|
+
))}
|
|
223
|
+
</>
|
|
224
|
+
)}
|
|
225
|
+
</>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<Button
|
|
230
|
+
size="sm"
|
|
231
|
+
variant="secondary"
|
|
232
|
+
onClick={() => {
|
|
233
|
+
setParams({ filter: null, packages: null })
|
|
234
|
+
}}
|
|
235
|
+
>
|
|
236
|
+
{t("reset")}
|
|
237
|
+
</Button>
|
|
238
|
+
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
};
|