@c-rex/components 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-rex/components",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "files": [
5
5
  "src"
6
6
  ],
@@ -25,13 +25,21 @@
25
25
  "types": "./src/breadcrumb.tsx",
26
26
  "import": "./src/breadcrumb.tsx"
27
27
  },
28
+ "./flag": {
29
+ "types": "./src/flag.tsx",
30
+ "import": "./src/flag.tsx"
31
+ },
28
32
  "./pagination": {
29
33
  "types": "./src/pagination.tsx",
30
34
  "import": "./src/pagination.tsx"
31
35
  },
36
+ "./info-table": {
37
+ "types": "./src/info/info-table.tsx",
38
+ "import": "./src/info/info-table.tsx"
39
+ },
32
40
  "./info-card": {
33
- "types": "./src/info-card.tsx",
34
- "import": "./src/info-card.tsx"
41
+ "types": "./src/info/info-card.tsx",
42
+ "import": "./src/info/info-card.tsx"
35
43
  },
36
44
  "./empty": {
37
45
  "types": "./src/empty.tsx",
@@ -61,9 +69,13 @@
61
69
  "types": "./src/result-view/dropdown-menu.tsx",
62
70
  "import": "./src/result-view/dropdown-menu.tsx"
63
71
  },
64
- "./sidebar": {
65
- "types": "./src/sidebar.tsx",
66
- "import": "./src/sidebar.tsx"
72
+ "./left-sidebar": {
73
+ "types": "./src/left-sidebar.tsx",
74
+ "import": "./src/left-sidebar.tsx"
75
+ },
76
+ "./right-sidebar": {
77
+ "types": "./src/right-sidebar.tsx",
78
+ "import": "./src/right-sidebar.tsx"
67
79
  },
68
80
  "./dialog-filter": {
69
81
  "types": "./src/dialog-filter.tsx",
@@ -81,7 +81,7 @@ export const AutoComplete = ({ initialValue, embedded, searchByPackage }: Props)
81
81
  }, [query]);
82
82
 
83
83
  return (
84
- <div className="relative" ref={containerRef}>
84
+ <div className="relative flex-1" ref={containerRef}>
85
85
  <Input
86
86
  variant={embedded ? "embedded" : undefined}
87
87
  type="text"
@@ -1,15 +1,21 @@
1
1
  import React, { FC, Fragment, ReactNode } from "react";
2
2
  import {
3
3
  Breadcrumb as BreadcrumbComponent,
4
+ BreadcrumbEllipsis,
4
5
  BreadcrumbItem,
5
6
  BreadcrumbLink,
6
7
  BreadcrumbList,
7
8
  BreadcrumbPage,
8
9
  BreadcrumbSeparator
9
10
  } from "@c-rex/ui/breadcrumb";
11
+ import { Button } from "@c-rex/ui/button";
12
+ import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from "@c-rex/ui/drawer";
10
13
  import { Skeleton } from "@c-rex/ui/skeleton";
11
14
  import { TreeOfContent } from "@c-rex/interfaces";
12
15
  import { useTranslations } from 'next-intl';
16
+ import Link from "next/link"
17
+ import { useBreakpoint } from "@c-rex/ui/hooks"
18
+ import { DEVICE_OPTIONS } from "@c-rex/constants";
13
19
 
14
20
  interface BreadcrumbProps {
15
21
  items: TreeOfContent[];
@@ -19,9 +25,12 @@ interface BreadcrumbProps {
19
25
 
20
26
  export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading, lang }) => {
21
27
  const t = useTranslations("breadcrumbs");
22
- if (!items) {
23
- return null;
24
- }
28
+ const device = useBreakpoint();
29
+ const isMobile = (device === DEVICE_OPTIONS.MOBILE || device === DEVICE_OPTIONS.TABLET);
30
+
31
+ if (!items) return null
32
+
33
+ const lastItem = items[items.length - 1] as TreeOfContent;
25
34
 
26
35
  if (loading) {
27
36
  return (
@@ -43,15 +52,49 @@ export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading, lang }) => {
43
52
  );
44
53
  }
45
54
 
46
- const homeItem: TreeOfContent = {
47
- link: "/",
48
- label: t("home"),
49
- id: "home",
50
- active: false,
51
- children: [],
52
- };
55
+ const renderDrawer = () => {
56
+ if (items.length < 2) return null
57
+
58
+ return (
59
+ <>
60
+ <BreadcrumbItem>
61
+ <Drawer>
62
+ <DrawerTrigger asChild>
63
+ <BreadcrumbEllipsis className="h-4 w-4" />
64
+ </DrawerTrigger>
65
+ <DrawerContent>
66
+ <DrawerHeader className="text-left">
67
+ <DrawerTitle>Navigate to</DrawerTitle>
68
+ <DrawerDescription>
69
+ Select a page to navigate to.
70
+ </DrawerDescription>
71
+ </DrawerHeader>
72
+
73
+ <div className="grid gap-1 px-4">
74
+ {items.slice(0, -1).map((item, index) => (
75
+ <Link
76
+ key={index}
77
+ href={item.link}
78
+ className="py-1 text-sm"
79
+ >
80
+ {item.label}
81
+ </Link>
82
+ ))}
83
+ </div>
53
84
 
54
- const newItemList: TreeOfContent[] = [homeItem, ...items];
85
+ <DrawerFooter className="pt-4">
86
+ <DrawerClose asChild>
87
+ <Button variant="outline">Close</Button>
88
+ </DrawerClose>
89
+ </DrawerFooter>
90
+ </DrawerContent>
91
+ </Drawer>
92
+ </BreadcrumbItem>
93
+ <BreadcrumbSeparator />
94
+ </>
95
+
96
+ );
97
+ };
55
98
 
56
99
  const renderLink = (showLink: boolean, item: TreeOfContent): ReactNode => {
57
100
  if (showLink) {
@@ -62,22 +105,37 @@ export const Breadcrumb: FC<BreadcrumbProps> = ({ items, loading, lang }) => {
62
105
  };
63
106
 
64
107
  return (
65
- <BreadcrumbComponent lang={lang}>
108
+ <BreadcrumbComponent lang={lang} className="hidden sm:block">
66
109
  <BreadcrumbList>
67
- {newItemList.map((item, index) => {
68
- const isLast = index === newItemList.length - 1;
69
-
70
- return (
71
- <Fragment key={`${item.label}-fragment`}>
72
- <BreadcrumbItem key={`${item.label}-item`}>
73
- {renderLink(!isLast, item)}
74
- </BreadcrumbItem>
75
- {!isLast && (
76
- <BreadcrumbSeparator key={`${item.label}-separator`} />
77
- )}
78
- </Fragment>
79
- );
80
- })}
110
+ <BreadcrumbItem>
111
+ <BreadcrumbLink asChild>
112
+ <BreadcrumbLink href="/">{t("home")}</BreadcrumbLink>
113
+ </BreadcrumbLink>
114
+ </BreadcrumbItem>
115
+ <BreadcrumbSeparator />
116
+
117
+ {isMobile ? renderDrawer() : (
118
+ <>
119
+ {items.slice(0, -1).map((item, index) => {
120
+ const isLast = index === items.length - 1;
121
+
122
+ return (
123
+ <Fragment key={`${item.label}-fragment`}>
124
+ <BreadcrumbItem key={`${item.label}-item`} className={index.toString()}>
125
+ {renderLink(!isLast, item)}
126
+ </BreadcrumbItem>
127
+ {!isLast && (
128
+ <BreadcrumbSeparator key={`${item.label}-separator`} />
129
+ )}
130
+ </Fragment>
131
+ );
132
+ })}
133
+ </>
134
+ )}
135
+
136
+ <BreadcrumbPage>
137
+ {renderLink(false, lastItem)}
138
+ </BreadcrumbPage>
81
139
  </BreadcrumbList>
82
140
  </BreadcrumbComponent>
83
141
  );
@@ -2,12 +2,12 @@
2
2
 
3
3
  import { FC, useEffect } from "react";
4
4
  import { useAppConfig } from "@c-rex/contexts/config-provider";
5
- import { SidebarAvailableVersionsInterface } from "@c-rex/interfaces";
5
+ import { AvailableVersionsInterface } from "@c-rex/interfaces";
6
6
  import { toast } from "sonner"
7
7
  import { useTranslations } from "next-intl"
8
8
 
9
9
  interface Props {
10
- availableVersions: SidebarAvailableVersionsInterface[]
10
+ availableVersions: AvailableVersionsInterface[]
11
11
  }
12
12
 
13
13
  export const CheckArticleLangToast: FC<Props> = ({ availableVersions }) => {
@@ -113,7 +113,7 @@ export const DialogFilter: FC<DialogFilterProps> = ({ trigger }) => {
113
113
  </DialogTrigger>
114
114
  <DialogContent>
115
115
  <DialogHeader>
116
- <DialogTitle>{t("filters")}</DialogTitle>
116
+ <DialogTitle>{t("searchSettings")}</DialogTitle>
117
117
  </DialogHeader>
118
118
 
119
119
  <div className="grid grid-cols-2 items-center pt-2">
package/src/flag.tsx ADDED
@@ -0,0 +1,15 @@
1
+ import React, { FC } from "react";
2
+ import * as Flags from 'country-flag-icons/react/3x2';
3
+
4
+ interface FlagProps {
5
+ countryCode: string;
6
+ }
7
+ export const Flag: FC<FlagProps> = ({ countryCode }) => {
8
+
9
+ type CountryCode = keyof typeof Flags;
10
+ const FlagComponent = Flags[countryCode as CountryCode];
11
+
12
+ if (!FlagComponent) return null;
13
+
14
+ return <FlagComponent />;
15
+ };
@@ -1,6 +1,9 @@
1
1
  import { Card, CardContent, CardHeader, CardTitle } from "@c-rex/ui/card";
2
+ import { getFromCookieString } from "@c-rex/utils";
2
3
  import { useTranslations } from "next-intl";
3
4
  import React, { FC } from "react";
5
+ import { filteredItems, renderValue } from "./shared";
6
+ import { UI_LANG_KEY } from "@c-rex/constants";
4
7
 
5
8
  type Props = {
6
9
  title: string;
@@ -12,6 +15,8 @@ type Props = {
12
15
  }
13
16
  export const InfoCard: FC<Props> = ({ title, items }) => {
14
17
  const t = useTranslations();
18
+ const uiLang = getFromCookieString(document.cookie, UI_LANG_KEY)
19
+ const newItems = filteredItems(items)
15
20
 
16
21
  return (
17
22
  <Card>
@@ -19,7 +24,7 @@ export const InfoCard: FC<Props> = ({ title, items }) => {
19
24
  <CardTitle className="text-lg">{title}</CardTitle>
20
25
  </CardHeader>
21
26
  <CardContent className="space-y-3">
22
- {items.map((item, index) => (
27
+ {newItems.map((item, index) => (
23
28
  <div key={index} className="border-b border-blog-divider last:border-0 pb-3 last:pb-0">
24
29
  {item.link ? (
25
30
  <a href={item.link}>
@@ -29,7 +34,7 @@ export const InfoCard: FC<Props> = ({ title, items }) => {
29
34
  <h4 className="text-sm font-medium mb-1">{t(item.label)}</h4>
30
35
  )}
31
36
 
32
- <span className="text-xs text-muted-foreground">{item.value}</span>
37
+ <span className="text-xs text-muted-foreground">{renderValue(item, uiLang)}</span>
33
38
  </div>
34
39
  ))}
35
40
  </CardContent>
@@ -0,0 +1,127 @@
1
+ import React, { FC } from "react";
2
+ import { articleInfoItemType, DocumentsType } from "@c-rex/types";
3
+ import { Card, CardContent, CardHeader, CardTitle } from "@c-rex/ui/card";
4
+ import {
5
+ Table,
6
+ TableBody,
7
+ TableCell,
8
+ TableRow,
9
+ } from "@c-rex/ui/table"
10
+ import { useTranslations } from "next-intl";
11
+ import { getFromCookieString } from "@c-rex/utils";
12
+ import { UI_LANG_KEY } from "@c-rex/constants";
13
+ import { filteredItems, renderValue } from "./shared";
14
+ import {
15
+ DropdownMenu,
16
+ DropdownMenuContent,
17
+ DropdownMenuItem,
18
+ DropdownMenuTrigger,
19
+ } from "@c-rex/ui/dropdown-menu";
20
+ import { CloudDownload, Eye } from "lucide-react";
21
+
22
+ import { FaFilePdf } from "react-icons/fa6";
23
+ import { AvailableVersionsInterface } from "@c-rex/interfaces";
24
+ import { Flag } from "../flag";
25
+
26
+ type Props = {
27
+ title: string;
28
+ files?: DocumentsType
29
+ items: articleInfoItemType[]
30
+ availableVersions?: AvailableVersionsInterface[]
31
+ }
32
+
33
+ const IconsToFileExtension: Record<string, React.ReactNode> = {
34
+ "application/pdf": <FaFilePdf className="h-6 w-6" />,
35
+ };
36
+
37
+ export const InfoTable: FC<Props> = ({ title, items, files, availableVersions }) => {
38
+ const t = useTranslations();
39
+ const uiLang = getFromCookieString(document.cookie, UI_LANG_KEY)
40
+ const newItems = filteredItems(items)
41
+
42
+ return (
43
+ <Card className="p-0 !pt-4">
44
+ <CardHeader>
45
+ <CardTitle className="text-lg">{title}</CardTitle>
46
+ </CardHeader>
47
+ <CardContent className="space-y-3 !p-0">
48
+ <Table>
49
+ <TableBody>
50
+ {newItems.map((item, index) => (
51
+ <TableRow key={index} className="h-12">
52
+ <TableCell className="font-medium w-28 pl-4">
53
+ {item.link ? (
54
+ <a href={item.link} className="underline">
55
+ <h4 className="text-sm font-medium">{t(item.label)}</h4>
56
+ </a>
57
+ ) : (
58
+ <h4 className="text-sm font-medium">{t(item.label)}</h4>
59
+ )}
60
+ </TableCell>
61
+ <TableCell className="text-xs text-muted-foreground flex items-center gap-2 h-12">
62
+ {renderValue(item, uiLang)}
63
+ </TableCell>
64
+ </TableRow>
65
+ ))}
66
+
67
+ {files && (
68
+ <TableRow className="h-12">
69
+ <TableCell className="font-medium w-28 pl-4">
70
+ <h4 className="text-sm font-medium">{t("files")}</h4>
71
+ </TableCell>
72
+ <TableCell className="text-xs text-muted-foreground flex items-center gap-2 h-12">
73
+
74
+ {Object.keys(files).map((item, index) => {
75
+ if (!files[item]) return null
76
+
77
+ return (
78
+ <DropdownMenu key={index}>
79
+ <DropdownMenuTrigger className="mx-2">
80
+ {IconsToFileExtension[item]}
81
+ </DropdownMenuTrigger>
82
+ <DropdownMenuContent>
83
+ <DropdownMenuItem>
84
+ <a href={files[item].view} target="_blank" rel="noreferrer" className="flex items-center">
85
+ <Eye className="mr-2 " /> Open
86
+ </a>
87
+ </DropdownMenuItem>
88
+ <DropdownMenuItem>
89
+ <a href={files[item].download} target="_blank" rel="noreferrer" className="flex items-center">
90
+ <CloudDownload className="mr-2 " /> Download
91
+ </a>
92
+ </DropdownMenuItem>
93
+
94
+ </DropdownMenuContent>
95
+ </DropdownMenu>
96
+ )
97
+ })}
98
+
99
+ </TableCell>
100
+ </TableRow>
101
+ )}
102
+
103
+ {availableVersions && (
104
+ <TableRow className="h-12">
105
+ <TableCell className="font-medium w-28 pl-4">
106
+ <h4 className="text-sm font-medium">{t("availableIn")}</h4>
107
+ </TableCell>
108
+ <TableCell className="text-xs text-muted-foreground flex items-center gap-2 h-12">
109
+ {availableVersions.map((item) => {
110
+ return (
111
+ <span className="w-8 block border" key={item.shortId}>
112
+ <a href={item.link} title={item.lang}>
113
+ <Flag countryCode={item.country} />
114
+ </a>
115
+ </span>
116
+ )
117
+ })}
118
+ </TableCell>
119
+ </TableRow>
120
+ )}
121
+
122
+ </TableBody>
123
+ </Table>
124
+ </CardContent>
125
+ </Card>
126
+ );
127
+ }
@@ -0,0 +1,32 @@
1
+ import React, { ReactNode } from "react";
2
+ import { articleInfoItemType } from "@c-rex/types";
3
+ import { formatDateToLocale } from "@c-rex/utils";
4
+ import { Flag } from "../flag";
5
+
6
+ const isDate = (value: string): boolean => {
7
+ return (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value))
8
+ }
9
+ const isLanguage = (label: string): boolean => {
10
+ return label === "language" || label === "languages";
11
+ }
12
+
13
+ export const renderValue = (item: articleInfoItemType, uiLang: string): ReactNode => {
14
+ if (isDate(item.value)) return formatDateToLocale(item.value, uiLang);
15
+
16
+ if (isLanguage(item.label)) {
17
+ const countryCode = item.value.split("-")[1] || item.value;
18
+ return (
19
+ <span className="w-8 block">
20
+ <Flag countryCode={countryCode} />
21
+ </span>
22
+
23
+ );
24
+ }
25
+
26
+ return item.value;
27
+ }
28
+
29
+ export const filteredItems = (items: articleInfoItemType[]): articleInfoItemType[] => {
30
+ const hasTitle = items.some(item => item.label === "titles");
31
+ return items.filter(item => (hasTitle ? !(item.label == "labels") : true));
32
+ }
@@ -4,9 +4,7 @@ import React, { ComponentProps, JSX } from "react";
4
4
  import {
5
5
  Sidebar,
6
6
  SidebarContent,
7
- SidebarFooter,
8
7
  SidebarGroup,
9
- SidebarGroupLabel,
10
8
  SidebarMenu,
11
9
  SidebarMenuButton,
12
10
  SidebarMenuItem,
@@ -15,28 +13,18 @@ import {
15
13
  SidebarMenuSubItem,
16
14
  } from "@c-rex/ui/sidebar";
17
15
  import { Skeleton } from "@c-rex/ui/skeleton";
18
- import { SidebarAvailableVersionsInterface, TreeOfContent } from "@c-rex/interfaces";
19
- import * as Flags from 'country-flag-icons/react/3x2';
16
+ import { TreeOfContent } from "@c-rex/interfaces";
20
17
  import { useTranslations } from "next-intl";
21
18
  import { cn } from "@c-rex/utils";
22
19
 
23
20
  interface SidebarProps extends ComponentProps<typeof Sidebar> {
24
21
  data: TreeOfContent[];
25
22
  loading?: boolean;
26
- availableVersions: SidebarAvailableVersionsInterface[]
27
23
  }
28
24
 
29
- export function AppSidebar({ data, availableVersions, loading, className, lang, ...props }: SidebarProps) {
25
+ export function LeftSidebar({ data, loading, className, lang, ...props }: SidebarProps) {
30
26
  const t = useTranslations();
31
27
 
32
- const getFlagIcon = (countryCode: string): JSX.Element | null => {
33
- type CountryCode = keyof typeof Flags;
34
- const FlagComponent = Flags[countryCode as CountryCode];
35
- if (!FlagComponent) return null;
36
-
37
- return <FlagComponent />;
38
- };
39
-
40
28
  const tableOfContentGroup = (): JSX.Element | null => {
41
29
  if (loading) {
42
30
  return (
@@ -94,39 +82,11 @@ export function AppSidebar({ data, availableVersions, loading, className, lang,
94
82
  );
95
83
  }
96
84
 
97
- const availableVersionsGroup = (): JSX.Element | null => {
98
-
99
- if (availableVersions.length == 0) return null;
100
-
101
- return (
102
- <>
103
- <SidebarGroupLabel>{t("availableIn")}:</SidebarGroupLabel>
104
- <SidebarMenu>
105
- {availableVersions.map((item) => {
106
- return (
107
- <SidebarMenuItem key={item.shortId}>
108
- <SidebarMenuButton asChild isActive={item.active}>
109
- <a href={item.link} title={item.lang}>
110
- {getFlagIcon(item.country)} {item.lang}
111
- </a>
112
- </SidebarMenuButton>
113
- </SidebarMenuItem>
114
- )
115
- })}
116
- </SidebarMenu>
117
- </>
118
- )
119
-
120
- }
121
-
122
85
  return (
123
- <Sidebar className={cn(className, "pt-20")} {...props}>
86
+ <Sidebar className={cn(className)} {...props}>
124
87
  <SidebarContent>
125
88
  {tableOfContentGroup()}
126
89
  </SidebarContent>
127
- <SidebarFooter>
128
- {availableVersionsGroup()}
129
- </SidebarFooter>
130
90
  </Sidebar>
131
91
  );
132
92
  }
@@ -1,7 +1,7 @@
1
- import React, { FC, JSX } from "react";
2
- import * as Flags from 'country-flag-icons/react/3x2';
1
+ import React, { FC } from "react";
3
2
  import { LanguageAndCountries } from "@c-rex/interfaces";
4
3
  import { DropdownMenuRadioGroup, DropdownMenuRadioItem } from "@c-rex/ui/dropdown-menu";
4
+ import { Flag } from "../../flag";
5
5
 
6
6
  interface SharedLanguageSwitchProps {
7
7
  availableLanguagesAndCountries: (LanguageAndCountries & { link?: string })[];
@@ -10,14 +10,6 @@ interface SharedLanguageSwitchProps {
10
10
  }
11
11
  export const SharedLanguageSwitch: FC<SharedLanguageSwitchProps> = ({ availableLanguagesAndCountries, changeLanguage, selected }) => {
12
12
 
13
- const getFlagIcon = (countryCode: string): JSX.Element | null => {
14
- type CountryCode = keyof typeof Flags;
15
- const FlagComponent = Flags[countryCode as CountryCode];
16
- if (!FlagComponent) return null;
17
-
18
- return <FlagComponent />;
19
- };
20
-
21
13
  return (
22
14
  <DropdownMenuRadioGroup value={selected}>
23
15
  {availableLanguagesAndCountries.map(item => {
@@ -31,7 +23,7 @@ export const SharedLanguageSwitch: FC<SharedLanguageSwitchProps> = ({ availableL
31
23
  changeLanguage(item.value)
32
24
  }}
33
25
  >
34
- {getFlagIcon(item.country)}
26
+ <Flag countryCode={item.country} />
35
27
  <span>{item.lang.toUpperCase()}</span>
36
28
  </DropdownMenuRadioItem>
37
29
  )
@@ -1,6 +1,5 @@
1
1
  import { FC } from "react";
2
2
  import Link from "next/link";
3
- import Image from "next/image";
4
3
  import { SignInBtn } from "./sign-in-out-btns";
5
4
  import { getServerSession } from "next-auth";
6
5
  import { getConfigs } from "@c-rex/utils/next-cookies";
@@ -26,24 +25,24 @@ export const NavBar: FC<NavBarProps> = async ({ title, showInput, showPkgFilter
26
25
  <header className="sticky top-0 z-40 flex w-full backdrop-blur-xl transition-all bg-transparent border-b justify-center py-4">
27
26
  <div className="w-full px-4 flex justify-between">
28
27
  <div className="flex">
29
- <Link href="/" className="flex items-center justify-center space-x-1.5 w-60">
30
- <Image
28
+ <Link href="/" className="flex items-center
29
+ space-x-1.5 w-36 sm:w-60">
30
+ <img
31
31
  src="/img/logo.png"
32
32
  alt="C-rex Logo"
33
- width={88}
34
- height={50}
33
+ className="h-12"
35
34
  />
36
35
  </Link>
37
36
 
38
37
  {title.length > 0 && (
39
38
  <div className="flex items-center">
40
- <h1 className="px-4 text-3xl font-bold tracking-tight text-balance">{title}</h1>
39
+ <h1 className="sm:px-4 text-xl sm:text-2xl md:text-3xl font-bold tracking-tight text-balance">{title}</h1>
41
40
  </div>
42
41
  )}
43
42
  </div>
44
43
 
45
44
  <div className="flex items-center space-x-3">
46
- <SearchInput showInput={showInput} showPkgFilter={showPkgFilter} />
45
+ <SearchInput showInput={showInput} showPkgFilter={showPkgFilter} placedOn="NAVBAR" />
47
46
 
48
47
  {configs.OIDC.user.enabled && (
49
48
  <>
@@ -4,20 +4,25 @@ import React, { FC, useState } from "react";
4
4
  import { FileCheck, FileX, Search } from "lucide-react";
5
5
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@c-rex/ui/tooltip";
6
6
  import { AutoComplete } from "../autocomplete";
7
+ import { cn } from "@c-rex/utils";
7
8
 
9
+ type PlacedOn = "NAVBAR" | "BODY"
8
10
  type Props = {
9
11
  showInput: boolean
10
12
  showPkgFilter: boolean
13
+ placedOn?: PlacedOn
11
14
  }
12
- export const SearchInput: FC<Props> = ({ showInput, showPkgFilter }) => {
15
+
16
+ export const SearchInput: FC<Props> = ({ showInput, showPkgFilter, placedOn = "NAVBAR" }) => {
13
17
  const [checked, setChecked] = useState<boolean>(true);
14
18
 
15
- if (!showInput) {
16
- return null;
17
- }
19
+ if (!showInput) return null
18
20
 
19
21
  return (
20
- <div className="flex items-center px-3 border rounded-full h-8">
22
+ <div className={cn(
23
+ placedOn === "NAVBAR" ? "hidden lg:flex" : "sm:flex-none sm:w-60 md:w-72 lg:w-full lg:hidden flex",
24
+ "flex-1 items-center px-3 border rounded-full h-8"
25
+ )}>
21
26
  <Search className="h-4 w-4 shrink-0 opacity-50" />
22
27
 
23
28
  <AutoComplete
@@ -20,7 +20,7 @@ export const SettingsMenu: FC = () => {
20
20
 
21
21
  return (
22
22
  <DropdownMenu>
23
- <DropdownMenuTrigger>
23
+ <DropdownMenuTrigger className="block">
24
24
  <Settings />
25
25
  </DropdownMenuTrigger>
26
26
  <DropdownMenuContent align="end" sideOffset={10} alignOffset={0}>
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import React from "react";
4
- import { signOut, signIn } from "next-auth/react";
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
 
@@ -19,18 +19,3 @@ export const SignInBtn = () => {
19
19
  </Button>
20
20
  );
21
21
  }
22
-
23
- export const SignOut = () => {
24
- const t = useTranslations("user")
25
- return (
26
- <Button
27
- className="gap-2 px-5 md:flex"
28
- variant="default"
29
- size="sm"
30
- rounded="full"
31
- onClick={() => signOut()}
32
- >
33
- <span>{t("signOut")}</span>
34
- </Button>
35
- );
36
- }
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { NavBar } from './navbar/navbar';
3
+ import { MultiSidebarProvider } from "@c-rex/ui/sidebar";
3
4
 
4
5
  type Props = {
5
6
  children: React.ReactNode;
@@ -12,13 +13,13 @@ export const PageWrapper = ({ children, title, pageType }: Props) => {
12
13
  const showPkgFilter = pageType === "DOC"
13
14
 
14
15
  return (
15
- <>
16
+ <MultiSidebarProvider>
16
17
  <NavBar
17
18
  title={title}
18
19
  showInput={showInput}
19
20
  showPkgFilter={showPkgFilter}
20
21
  />
21
22
  {children}
22
- </>
23
+ </MultiSidebarProvider>
23
24
  );
24
25
  }
@@ -1,5 +1,5 @@
1
1
  import React, { FC } from "react";
2
- import { DefaultPageInfo, informationUnitsResponseItem, } from "@c-rex/interfaces";
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';
@@ -7,25 +7,31 @@ import { Pagination } from "./pagination";
7
7
  import { useAppConfig } from "@c-rex/contexts/config-provider";
8
8
 
9
9
  interface ResultListProps {
10
- items: informationUnitsResponseItem[];
10
+ items: informationUnitsResponseItem[] | TopicsResponseItem[];
11
11
  pagination: DefaultPageInfo;
12
12
  }
13
13
 
14
14
  export const ResultList: FC<ResultListProps> = ({ items, pagination }: ResultListProps) => {
15
15
  const { configs } = useAppConfig()
16
16
 
17
- const ViewComponent = configs.results.resultViewStyle == "table" ? TableView : BlogView;
17
+ const isTableView = configs.results.resultViewStyle === "table";
18
18
 
19
19
  return (
20
20
  <>
21
21
  {
22
- items.length == 0 ? (
22
+ items.length === 0 ? (
23
23
  <Empty />
24
24
  ) : (
25
25
  <>
26
- <ViewComponent
27
- items={items}
28
- />
26
+ {isTableView ? (
27
+ <TableView
28
+ items={items as informationUnitsResponseItem[]}
29
+ />
30
+ ) : (
31
+ <BlogView
32
+ items={items as TopicsResponseItem[]}
33
+ />
34
+ )}
29
35
 
30
36
  <Pagination
31
37
  totalPages={pagination.pageCount}
@@ -1,80 +1,100 @@
1
1
  import { FC } from "react";
2
- import {
3
- Table,
4
- TableBody,
5
- TableCell,
6
- TableHeader,
7
- TableRow,
8
- } from "@c-rex/ui/table";
9
2
  import { informationUnitsResponseItem } from "@c-rex/interfaces";
10
3
  import { useTranslations } from "next-intl";
11
- import { Ban, CloudDownload, CloudOff, Eye, EyeOff } from "lucide-react";
12
- import { DropdownMenu } from "./dropdown-menu";
4
+ import { Ban, CloudDownload, Eye } from "lucide-react";
5
+ import { FaFilePdf } from "react-icons/fa6";
6
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@c-rex/ui/dropdown-menu";
7
+ import { cn } from "@c-rex/utils";
8
+ import { Flag } from "../flag";
9
+ import { Badge } from "@c-rex/ui/badge";
13
10
 
14
11
  interface TableViewProps {
15
12
  items: informationUnitsResponseItem[];
16
13
  }
17
14
 
15
+
16
+ const IconsToFileExtension: Record<string, React.ReactNode> = {
17
+ "application/pdf": <FaFilePdf className="h-6 w-6" />,
18
+ };
19
+
18
20
  const TableView: FC<TableViewProps> = ({ items }) => {
19
21
  const t = useTranslations("results")
22
+
20
23
  return (
21
- <div className="rounded-md border">
22
- <Table>
23
- <TableHeader>
24
- <TableRow>
25
- <TableCell>{t("title")}</TableCell>
26
- <TableCell>{t("type")}</TableCell>
27
- <TableCell>{t("language")}</TableCell>
28
- <TableCell>{t("files")}</TableCell>
29
- </TableRow>
30
- </TableHeader>
31
- <TableBody>
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" : ""}`
24
+ <div className="rounded-md border mb-6 last:border-b-0">
25
+ <div className="font-bold text-sm p-2 border-b items-center flex-wrap hidden md:flex">
26
+ <div className="w-1/2 md:w-2/5 p-2">{t("title")}</div>
27
+ <div className="w-1/2 md:w-1/5 p-2 flex justify-center">{t("language")}</div>
28
+ <div className="w-1/2 md:w-1/5 p-2">{t("type")}</div>
29
+ <div className="w-1/2 md:w-1/5 p-2 flex justify-center">{t("files")}</div>
30
+ </div>
31
+
32
+ {items.map((item, index) => (
33
+ <div
34
+ className={cn(
35
+ "min-h-12 c-rex_result_row flex flex-wrap items-center",
36
+ `c-rex_result_${item.type.toLowerCase()}`,
37
+ item.disabled && "c-rex_result_item_disabled",
38
+ "border-b"
39
+ )}
40
+ key={index}
41
+ >
42
+ <div className="w-4/5 md:w-2/5 p-2">
43
+ {item.disabled ? (item.title) : (<a href={item.link}>{item.title}</a>)}
44
+ </div>
45
+
46
+ <div className="w-1/5 md:w-1/5 flex justify-center p-2">
47
+ <span className="w-8 inline-block">
48
+ {item.language.split("-").length > 1 && (
49
+ <Flag countryCode={item.language.split("-")[1] as string} />
50
+ )}
51
+ </span>
52
+ </div>
53
+
54
+ <div className="w-4/5 md:w-1/5 p-2">
55
+ <Badge variant="secondary">
56
+ {item.type}
57
+ </Badge>
58
+ </div>
59
+
60
+ <div className="w-1/5 flex justify-center p-2">
61
+ {(item.disabled || (Object.keys(item.files).length === 0)) ? (
62
+ <Ban />
63
+ ) : (
64
+ <>
65
+ {Object.keys(item.files).map((fileKey, index) => {
66
+
67
+ if (!item.files[fileKey]) {
68
+ return null
69
+ }
70
+
71
+ return (
72
+ <DropdownMenu key={index}>
73
+ <DropdownMenuTrigger className="mx-2">
74
+ {IconsToFileExtension[fileKey]}
75
+ </DropdownMenuTrigger>
76
+ <DropdownMenuContent>
77
+ <DropdownMenuItem>
78
+ <a href={item.files[fileKey].view} target="_blank" rel="noreferrer" className="flex items-center">
79
+ <Eye className="mr-2 " /> Open
80
+ </a>
81
+ </DropdownMenuItem>
82
+ <DropdownMenuItem>
83
+ <a href={item.files[fileKey].download} target="_blank" rel="noreferrer" className="flex items-center">
84
+ <CloudDownload className="mr-2 " /> Download
85
+ </a>
86
+ </DropdownMenuItem>
87
+ </DropdownMenuContent>
88
+ </DropdownMenu>
89
+ )
90
+ })}
91
+ </>
92
+ )}
34
93
 
35
- return (
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
- )}
94
+ </div>
59
95
 
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
- )}
72
- </TableCell>
73
- </TableRow>
74
- )
75
- })}
76
- </TableBody>
77
- </Table>
96
+ </div>
97
+ ))}
78
98
  </div>
79
99
  );
80
100
  };
@@ -0,0 +1,52 @@
1
+ "use client";
2
+
3
+ import React, { ComponentProps } from "react";
4
+ import {
5
+ Sidebar,
6
+ SidebarContent,
7
+ } from "@c-rex/ui/sidebar";
8
+ import { useTranslations } from "next-intl";
9
+ import { cn } from "@c-rex/utils";
10
+ import { articleInfoItemType, DocumentsType } from "@c-rex/types";
11
+ import { InfoTable } from "./info/info-table";
12
+ import { AvailableVersionsInterface } from "@c-rex/interfaces";
13
+
14
+ interface SidebarProps extends ComponentProps<typeof Sidebar> {
15
+ articleInfo: articleInfoItemType[],
16
+ documentInfo: articleInfoItemType[],
17
+ files: DocumentsType,
18
+ availableVersions?: AvailableVersionsInterface[]
19
+
20
+ }
21
+
22
+ export function RightSidebar({ articleInfo, documentInfo, className, files, availableVersions, ...props }: SidebarProps) {
23
+ const t = useTranslations();
24
+
25
+ return (
26
+ <Sidebar className={cn(className)} {...props} side="right">
27
+ <SidebarContent className="p-2">
28
+ {(articleInfo.length > 0 || documentInfo.length > 0) && (
29
+ <div className=" gap-4 flex flex-col">
30
+
31
+ {documentInfo.length > 0 && (
32
+ <InfoTable
33
+ title={t("aboutDocument")}
34
+ items={documentInfo}
35
+ files={files}
36
+ />
37
+ )}
38
+
39
+ {articleInfo.length > 0 && (
40
+ <InfoTable
41
+ title={t("aboutArticle")}
42
+ items={articleInfo}
43
+ availableVersions={availableVersions}
44
+ />
45
+ )}
46
+
47
+ </div>
48
+ )}
49
+ </SidebarContent>
50
+ </Sidebar>
51
+ );
52
+ }