@c-rex/components 0.1.1 → 0.1.3

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.1",
3
+ "version": "0.1.3",
4
4
  "files": [
5
5
  "src"
6
6
  ],
@@ -56,11 +56,25 @@
56
56
  "./dialog-filter": {
57
57
  "types": "./src/dialog-filter.tsx",
58
58
  "import": "./src/dialog-filter.tsx"
59
+ },
60
+ "./check-article-lang": {
61
+ "types": "./src/check-article-lang.tsx",
62
+ "import": "./src/check-article-lang.tsx"
63
+ },
64
+ "./cookies": {
65
+ "types": "./src/cookies.tsx",
66
+ "import": "./src/cookies.tsx"
67
+ },
68
+ "./page-wrapper": {
69
+ "types": "./src/page-wrapper.tsx",
70
+ "import": "./src/page-wrapper.tsx"
59
71
  }
60
72
  },
61
73
  "scripts": {
62
74
  "storybook": "storybook dev -p 6006",
63
- "build-storybook": "storybook build"
75
+ "build-storybook": "storybook build",
76
+ "lint": "eslint .",
77
+ "lint:fix": "eslint . --fix"
64
78
  },
65
79
  "devDependencies": {
66
80
  "@c-rex/eslint-config": "*",
@@ -100,6 +114,7 @@
100
114
  "nuqs": "^2.4.3",
101
115
  "react": "^18",
102
116
  "react-dom": "^18",
117
+ "react-icons": "^5.5.0",
103
118
  "tailwindcss-animate": "^1.0.7"
104
119
  },
105
120
  "eslintConfig": {
package/src/blog-card.tsx CHANGED
@@ -1,4 +1,5 @@
1
1
  import Link from "next/link";
2
+ import React from "react";
2
3
  import { cn } from "@c-rex/utils";
3
4
  import { BlurImage } from "./blur-image";
4
5
  import { useTranslations } from "next-intl";
@@ -68,6 +69,7 @@ export const BlogCard = ({
68
69
  </p>
69
70
  )}
70
71
  </div>
72
+ {/*
71
73
  <div className="mt-4 flex items-center space-x-3">
72
74
  <div className="flex items-center -space-x-2">
73
75
  {data.authors && (
@@ -79,6 +81,7 @@ export const BlogCard = ({
79
81
  <p className="text-sm text-muted-foreground">{data.date}</p>
80
82
  )}
81
83
  </div>
84
+ */}
82
85
  </div>
83
86
  <Link href={data.slug} className="absolute inset-0" target="_blank">
84
87
  <span className="sr-only">{t("viewArticle")}</span>
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useState } from "react";
3
+ import React, { useState } from "react";
4
4
  import type { ComponentProps } from "react";
5
5
  import Image from "next/image";
6
6
  import { cn } from "@c-rex/utils";
@@ -0,0 +1,43 @@
1
+ "use client"
2
+
3
+ import { SidebarAvailableVersionsInterface } from "@c-rex/interfaces";
4
+ import { FC, useEffect, useRef } from "react";
5
+ import { toast } from "sonner"
6
+
7
+ interface Props {
8
+ availableVersions: SidebarAvailableVersionsInterface[]
9
+ contentLanguage: string
10
+ }
11
+
12
+ export const CheckArticleLangToast: FC<Props> = ({
13
+ availableVersions,
14
+ contentLanguage,
15
+ }) => {
16
+ const hasRun = useRef(false)
17
+
18
+ useEffect(() => {
19
+ if (hasRun.current) return
20
+ hasRun.current = true
21
+
22
+ const activeArticle = availableVersions.filter((item) => item.active)[0]
23
+ if (activeArticle == undefined || activeArticle.lang == contentLanguage) return
24
+
25
+ const articleAvailable = availableVersions.find((item) => item.lang === contentLanguage)
26
+ if (articleAvailable == undefined) return
27
+
28
+ showToast(articleAvailable.lang, articleAvailable.link)
29
+ }, [])
30
+
31
+ const showToast = (lang: string, link: string) => {
32
+ toast(`Read ${lang} version`, {
33
+ description: `This article is also available in ${lang}`,
34
+ action: {
35
+ label: `Open ${lang} version`,
36
+ onClick: () => window.location.href = link,
37
+ },
38
+ duration: 10000,
39
+ })
40
+ }
41
+
42
+ return null
43
+ }
@@ -0,0 +1,50 @@
1
+ "use client"
2
+
3
+ import { FC, useEffect } from "react";
4
+ import { CONTENT_LANG_KEY, UI_LANG_KEY, UI_LANG_OPTIONS } from "@c-rex/constants";
5
+ import { getCookieInFront } from "@c-rex/utils";
6
+ import { setCookie } from "@c-rex/utils/next-cookies";
7
+ import { ConfigInterface, LanguageAndCountries } from "@c-rex/interfaces";
8
+
9
+ type Props = {
10
+ configs: ConfigInterface;
11
+ availableLanguages: LanguageAndCountries[];
12
+ }
13
+ export const SetCookies: FC<Props> = ({ configs, availableLanguages }) => {
14
+ const manageUILanguage = async (configs: ConfigInterface): Promise<void> => {
15
+ const hasUILangCookie = await getCookieInFront(UI_LANG_KEY);
16
+
17
+ if (hasUILangCookie.value === null) {
18
+ const browserLang = navigator.language;
19
+
20
+ const locale = UI_LANG_OPTIONS.includes(browserLang.toLowerCase())
21
+ ? browserLang
22
+ : configs.languageSwitcher.default;
23
+
24
+ setCookie(UI_LANG_KEY, locale);
25
+ }
26
+ }
27
+
28
+ const manageContentLanguage = async (configs: ConfigInterface, availableLanguages: LanguageAndCountries[]): Promise<void> => {
29
+ const hasContentLangCookie = await getCookieInFront(CONTENT_LANG_KEY);
30
+
31
+ if (hasContentLangCookie.value === null) {
32
+ const browserLang = navigator.language;
33
+ const hasLang = availableLanguages.some((item) => item.value === browserLang);
34
+
35
+ const locale = hasLang
36
+ ? browserLang
37
+ : configs.languageSwitcher.default;
38
+
39
+
40
+ setCookie(CONTENT_LANG_KEY, locale);
41
+ }
42
+ }
43
+
44
+ useEffect(() => {
45
+ manageUILanguage(configs);
46
+ manageContentLanguage(configs, availableLanguages);
47
+ }, [])
48
+
49
+ return null;
50
+ };
@@ -11,7 +11,7 @@ import {
11
11
  } from "@c-rex/ui/dialog"
12
12
  import { Checkbox } from "@c-rex/ui/checkbox"
13
13
  import { Label } from "@c-rex/ui/label"
14
- import { useQueryState } from "nuqs"
14
+ import { parseAsInteger, parseAsString, useQueryStates } from "nuqs"
15
15
  import { useTranslations } from "next-intl"
16
16
 
17
17
  interface DialogFilterProps {
@@ -27,10 +27,13 @@ export const DialogFilter: FC<DialogFilterProps> = ({
27
27
  }) => {
28
28
  const t = useTranslations();
29
29
 
30
- const [_, setContentLanguage] = useQueryState('language', {
30
+ const [params, setParams] = useQueryStates({
31
+ language: parseAsString,
32
+ page: parseAsInteger,
33
+ }, {
31
34
  history: 'push',
32
35
  shallow: false,
33
- })
36
+ });
34
37
 
35
38
  const [languages, setLanguages] = useState(availableLanguages.map((item: any) => {
36
39
  const checked = startSelectedLanguages.includes(item.value)
@@ -50,7 +53,11 @@ export const DialogFilter: FC<DialogFilterProps> = ({
50
53
 
51
54
  const apply = () => {
52
55
  const selectedLanguages = languages.filter((item: any) => item.checked).map((item: any) => item.value).join(',')
53
- setContentLanguage(selectedLanguages)
56
+
57
+ setParams({
58
+ page: 1,
59
+ language: selectedLanguages,
60
+ });
54
61
  }
55
62
 
56
63
  return (
@@ -1,27 +1,42 @@
1
1
  'use client'
2
2
 
3
- import React, { FC, startTransition } from "react";
3
+ import React, { FC, startTransition, useEffect, useState } from "react";
4
4
  import { LanguageAndCountries } from "@c-rex/interfaces";
5
5
  import { SharedLanguageSwitch } from "./shared";
6
6
  import { setCookie } from "@c-rex/utils/next-cookies";
7
- import { CONTENT_LANG_KEY, DEFAULT_UI_LANG } from "@c-rex/constants";
7
+ import { CONTENT_LANG_KEY } from "@c-rex/constants";
8
8
  import { useQueryState } from "nuqs"
9
+ import { getCookieInFront } from "@c-rex/utils";
9
10
 
10
11
  interface ContentLanguageSwitchProps {
11
12
  availableLanguagesAndCountries: LanguageAndCountries[];
12
13
  }
13
14
  export const ContentLanguageSwitch: FC<ContentLanguageSwitchProps> = ({ availableLanguagesAndCountries }) => {
14
-
15
- const [contentLanguage, setContentLanguage] = useQueryState('language', {
15
+ const [selected, setSelected] = useState("");
16
+ const [queryLanguage, setContentLanguage] = useQueryState('language', {
16
17
  history: 'push',
17
18
  shallow: false,
18
- defaultValue: DEFAULT_UI_LANG
19
19
  })
20
20
 
21
+ useEffect(() => {
22
+ const get = async () => {
23
+ const uiLang = await getCookieInFront(CONTENT_LANG_KEY)
24
+
25
+ if (uiLang.value != null) {
26
+ setSelected(uiLang.value)
27
+ return
28
+ }
29
+ }
30
+ get()
31
+ }, [])
32
+
21
33
  const changeContentLanguage = (locale: string) => {
22
34
  startTransition(() => {
23
35
  setCookie(CONTENT_LANG_KEY, locale)
24
- setContentLanguage(locale)
36
+
37
+ if (queryLanguage != null) {
38
+ setContentLanguage(locale)
39
+ }
25
40
  });
26
41
  };
27
42
 
@@ -29,7 +44,7 @@ export const ContentLanguageSwitch: FC<ContentLanguageSwitchProps> = ({ availabl
29
44
  <SharedLanguageSwitch
30
45
  availableLanguagesAndCountries={availableLanguagesAndCountries}
31
46
  changeLanguage={changeContentLanguage}
32
- selected={contentLanguage}
47
+ selected={selected}
33
48
  />
34
49
  );
35
50
  };
@@ -23,8 +23,8 @@ export const SharedLanguageSwitch: FC<SharedLanguageSwitchProps> = ({ availableL
23
23
  {availableLanguagesAndCountries.map(item => {
24
24
  return (
25
25
  <DropdownMenuRadioItem key={item.value} value={item.value}>
26
- <span>{item.lang.toUpperCase()}</span>
27
26
  {getFlagIcon(item.country)}
27
+ <span>{item.lang.toUpperCase()}</span>
28
28
  </DropdownMenuRadioItem>
29
29
  )
30
30
  })}
@@ -1,26 +1,35 @@
1
1
  "use client"
2
2
 
3
3
  import React, { FC, startTransition, useEffect, useState } from "react";
4
- import { LanguageAndCountries } from "@c-rex/interfaces";
5
4
  import { SharedLanguageSwitch } from "./shared";
6
5
  import { setCookie } from "@c-rex/utils/next-cookies";
7
- import { DEFAULT_UI_LANG, UI_LANG_KEY } from "@c-rex/constants";
6
+ import { UI_LANG_KEY, UI_LANG_OPTIONS } from "@c-rex/constants";
7
+ import { getCookieInFront, getCountryCodeByLang } from "@c-rex/utils";
8
8
 
9
- interface UILanguageSwitchProps {
10
- availableLanguagesAndCountries: LanguageAndCountries[];
11
- }
12
- export const UILanguageSwitch: FC<UILanguageSwitchProps> = ({ availableLanguagesAndCountries }) => {
13
- const [selected, setSelected] = useState(DEFAULT_UI_LANG);
9
+ export const UILanguageSwitch: FC = () => {
10
+ const UILanguages = UI_LANG_OPTIONS.map((lang) => {
11
+
12
+ const langCode = lang.split("-")[0] as string;
13
+ return {
14
+ value: lang,
15
+ lang: langCode,
16
+ country: getCountryCodeByLang(langCode)
17
+ };
18
+ })
19
+
20
+ const [selected, setSelected] = useState("");
14
21
 
15
22
  useEffect(() => {
16
- const allCookies = document.cookie
17
- const uiLang = allCookies
18
- .split('; ')
19
- .find(row => row.startsWith(`${UI_LANG_KEY}=`))
20
- ?.split('=')[1]
21
- setSelected(uiLang as string)
22
- }, [])
23
+ const get = async () => {
24
+ const uiLang = await getCookieInFront(UI_LANG_KEY)
23
25
 
26
+ if (uiLang.value != null) {
27
+ setSelected(uiLang.value.toLowerCase())
28
+ return
29
+ }
30
+ }
31
+ get()
32
+ }, [])
24
33
 
25
34
  const setUILanguage = (locale: string) => {
26
35
  startTransition(() => {
@@ -30,7 +39,7 @@ export const UILanguageSwitch: FC<UILanguageSwitchProps> = ({ availableLanguages
30
39
 
31
40
  return (
32
41
  <SharedLanguageSwitch
33
- availableLanguagesAndCountries={availableLanguagesAndCountries}
42
+ availableLanguagesAndCountries={UILanguages}
34
43
  changeLanguage={setUILanguage}
35
44
  selected={selected}
36
45
  />
@@ -2,9 +2,6 @@ import React, { FC } from "react";
2
2
  import Link from "next/link";
3
3
  import Image from "next/image";
4
4
  import { SignOut, SignInBtn } from "./sign-in-out-btns";
5
- import { getServerSession } from "next-auth";
6
- import { getCookie } from '@c-rex/utils/next-cookies';
7
- import { ConfigInterface } from "@c-rex/interfaces";
8
5
  import {
9
6
  DropdownMenu,
10
7
  DropdownMenuContent,
@@ -16,43 +13,36 @@ import {
16
13
  DropdownMenuSubTrigger,
17
14
  DropdownMenuTrigger,
18
15
  } from "@c-rex/ui/dropdown-menu"
19
- import { SDK_CONFIG_KEY, UI_LANG_OPTIONS } from "@c-rex/constants";
20
- import { LanguageService } from "@c-rex/services";
21
- import { Settings } from "lucide-react";
16
+ import { Search, Settings } from "lucide-react";
22
17
  import { ContentLanguageSwitch } from "./language-switcher/content-language-switch";
23
18
  import { getTranslations } from "next-intl/server";
24
- import { getCountryCodeByLang } from "@c-rex/utils";
25
19
  import { UILanguageSwitch } from "./language-switcher/ui-language-switch";
20
+ import { Input } from "@c-rex/ui/input";
21
+ import { getServerSession } from "next-auth";
22
+ import { getConfigs } from "@c-rex/utils/next-cookies";
23
+ import { LanguageService } from "@c-rex/services";
24
+
26
25
 
27
26
  interface NavBarProps {
28
- scroll?: boolean;
29
- large?: boolean;
27
+ title: string;
30
28
  }
31
29
 
32
- export const NavBar: FC<NavBarProps> = async () => {
30
+ export const NavBar: FC<NavBarProps> = async ({ title }) => {
33
31
  const t = await getTranslations();
34
- const session = await getServerSession()
32
+ const configs = await getConfigs();
33
+ const languageService = new LanguageService(configs.languageSwitcher.endpoint);
34
+ const availableLanguagesAndCountries = await languageService.getLanguagesAndCountries();
35
35
 
36
- const jsonConfigs = await getCookie(SDK_CONFIG_KEY);
37
- if (!jsonConfigs) {
38
- return null;
36
+ let session: any;
37
+ if (configs.OIDC.user.enabled) {
38
+ session = await getServerSession();
39
39
  }
40
40
 
41
- const configs: ConfigInterface = JSON.parse(jsonConfigs);
42
- const service = new LanguageService(configs.languageSwitcher.endpoint);
43
- const contentLanguages = await service.getLanguagesAndCountries();
44
-
45
- const UILanguages = UI_LANG_OPTIONS.map((lang) => ({
46
- value: lang,
47
- lang: lang,
48
- country: getCountryCodeByLang(lang)
49
- }));
50
-
51
41
  return (
52
- <header className="sticky top-0 z-40 flex w-full bg-background/60 backdrop-blur-xl transition-all bg-transparent border-b justify-center py-4">
53
- <div className="container flex justify-between">
54
- <div className="flex gap-6 md:gap-10">
55
- <Link href="/" className="flex items-center space-x-1.5">
42
+ <header className="sticky top-0 z-40 flex w-full backdrop-blur-xl transition-all bg-transparent border-b justify-center py-4">
43
+ <div className="w-full px-4 flex justify-between">
44
+ <div className="flex">
45
+ <Link href="/" className="flex items-center justify-center space-x-1.5 w-60">
56
46
  <Image
57
47
  src="/img/logo.png"
58
48
  alt="C-rex Logo"
@@ -60,10 +50,20 @@ export const NavBar: FC<NavBarProps> = async () => {
60
50
  height={50}
61
51
  />
62
52
  </Link>
63
- {configs.projectName}
53
+
54
+ {title.length > 0 && (
55
+ <div className="flex items-center">
56
+ <h1 className="px-4 text-3xl font-bold tracking-tight text-balance">{title}</h1>
57
+ </div>
58
+ )}
64
59
  </div>
65
60
 
66
61
  <div className="flex items-center space-x-3">
62
+ <div className="flex items-center px-3 border rounded-full h-8">
63
+ <Search className="h-4 w-4 shrink-0 opacity-50" />
64
+ <Input variant="embedded" placeholder="Search" />
65
+ </div>
66
+
67
67
  {configs.OIDC.user.enabled && (
68
68
  <>
69
69
  {session ? (
@@ -82,7 +82,7 @@ export const NavBar: FC<NavBarProps> = async () => {
82
82
  <DropdownMenuTrigger>
83
83
  <Settings />
84
84
  </DropdownMenuTrigger>
85
- <DropdownMenuContent>
85
+ <DropdownMenuContent align="start" sideOffset={20} alignOffset={20}>
86
86
  <DropdownMenuLabel>{t("accountSettings.accountSettings")}</DropdownMenuLabel>
87
87
  <DropdownMenuSeparator />
88
88
 
@@ -94,7 +94,7 @@ export const NavBar: FC<NavBarProps> = async () => {
94
94
  <DropdownMenuPortal>
95
95
  <DropdownMenuSubContent>
96
96
  <ContentLanguageSwitch
97
- availableLanguagesAndCountries={contentLanguages}
97
+ availableLanguagesAndCountries={availableLanguagesAndCountries}
98
98
  />
99
99
  </DropdownMenuSubContent>
100
100
  </DropdownMenuPortal>
@@ -108,9 +108,7 @@ export const NavBar: FC<NavBarProps> = async () => {
108
108
 
109
109
  <DropdownMenuPortal>
110
110
  <DropdownMenuSubContent>
111
- <UILanguageSwitch
112
- availableLanguagesAndCountries={UILanguages}
113
- />
111
+ <UILanguageSwitch />
114
112
  </DropdownMenuSubContent>
115
113
  </DropdownMenuPortal>
116
114
  </DropdownMenuSub>
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import { NavBar } from './navbar/navbar';
3
+
4
+ type Props = {
5
+ children: React.ReactNode;
6
+ title: string;
7
+ }
8
+
9
+ export const PageWrapper = ({ children, title }: Props) => {
10
+ return (
11
+ <>
12
+ <NavBar title={title} />
13
+ {children}
14
+ </>
15
+ );
16
+ }
@@ -0,0 +1,65 @@
1
+ import React, { FC } from "react";
2
+ import {
3
+ Pagination as PaginationUI,
4
+ PaginationContent,
5
+ PaginationItem,
6
+ PaginationLink,
7
+ PaginationNext,
8
+ PaginationPrevious,
9
+ } from "@c-rex/ui/pagination"
10
+ import { parseAsInteger, useQueryState } from "nuqs";
11
+
12
+ interface PaginationProps {
13
+ totalPages: number;
14
+ currentPage: number;
15
+ }
16
+
17
+ export const Pagination: FC<PaginationProps> = ({ totalPages, currentPage }) => {
18
+ const disabledClass = "opacity-50 pointer-events-none";
19
+
20
+ const [_, setPage] = useQueryState('page',
21
+ parseAsInteger.withOptions({
22
+ history: 'push',
23
+ shallow: false,
24
+ })
25
+ )
26
+
27
+ return (
28
+ <PaginationUI className="py-4">
29
+ <PaginationContent>
30
+ <PaginationItem>
31
+ <PaginationPrevious
32
+ href="#"
33
+ className={currentPage === 1 ? disabledClass : ""}
34
+ onClick={() => setPage(currentPage - 1)}
35
+ />
36
+ </PaginationItem>
37
+
38
+ {Array.from({ length: totalPages }, (_, index) => index + 1).map((page) => (
39
+ <PaginationItem key={page}>
40
+ <PaginationLink
41
+ href="#"
42
+ isActive={page === currentPage}
43
+ onClick={() => setPage(page)}
44
+ >
45
+ {page}
46
+ </PaginationLink>
47
+ </PaginationItem>
48
+ ))}
49
+
50
+ {/*
51
+ <PaginationItem>
52
+ <PaginationEllipsis />
53
+ </PaginationItem>
54
+ */}
55
+ <PaginationItem>
56
+ <PaginationNext
57
+ href="#"
58
+ onClick={() => setPage(currentPage + 1)}
59
+ className={currentPage === totalPages ? disabledClass : ""}
60
+ />
61
+ </PaginationItem>
62
+ </PaginationContent>
63
+ </PaginationUI>
64
+ )
65
+ }
@@ -1,21 +1,36 @@
1
1
  import React, { FC } from "react";
2
- import { ConfigInterface, informationUnitsItems } from "@c-rex/interfaces";
2
+ import { ConfigInterface, DefaultPageInfo, informationUnitsResponseItems, } from "@c-rex/interfaces";
3
3
  import { Empty } from "./empty";
4
4
  import BlogView from './result-view/blog';
5
5
  import TableView from './result-view/table';
6
+ import { Pagination } from "./pagination";
6
7
 
7
8
  interface ResultListProps {
8
- items: informationUnitsItems[];
9
- configs: ConfigInterface
9
+ items: informationUnitsResponseItems[];
10
+ configs: ConfigInterface;
11
+ pagination: DefaultPageInfo;
10
12
  }
11
13
 
12
- export const ResultList: FC<ResultListProps> = ({ items, configs }) => {
13
- const ViewComponent = configs.resultViewStyle == "table" ? TableView : BlogView;
14
+ export const ResultList: FC<ResultListProps> = ({ items, configs, pagination }: ResultListProps) => {
15
+ const ViewComponent = configs.results.resultViewStyle == "table" ? TableView : BlogView;
14
16
 
15
17
  return (
16
18
  <>
17
19
  {
18
- items.length == 0 ? <Empty /> : <ViewComponent items={items} />
20
+ items.length == 0 ? (
21
+ <Empty />
22
+ ) : (
23
+ <>
24
+ <ViewComponent
25
+ items={items}
26
+ />
27
+
28
+ <Pagination
29
+ totalPages={pagination.pageCount}
30
+ currentPage={pagination.pageNumber}
31
+ />
32
+ </>
33
+ )
19
34
  }
20
35
  </>
21
36
  );
@@ -1,18 +1,12 @@
1
1
  import React, { FC } from "react";
2
2
  import { BlogCard } from "../blog-card";
3
- import { informationUnitsItems, Labels } from "@c-rex/interfaces";
3
+ import { informationUnitsResponseItems } from "@c-rex/interfaces";
4
4
 
5
5
  interface BlogViewProps {
6
- items: informationUnitsItems[];
6
+ items: informationUnitsResponseItems[];
7
7
  }
8
8
 
9
9
  const BlogView: FC<BlogViewProps> = ({ items }) => {
10
- const getTitle = (labels: Labels[]): string => {
11
- if (labels === undefined) {
12
- return "";
13
- }
14
- return labels.map((item) => item.value).join();
15
- };
16
10
 
17
11
  return (
18
12
  <div className="grid gap-8 md:grid-cols-2 md:gap-x-6 md:gap-y-10 xl:grid-cols-3">
@@ -21,14 +15,14 @@ const BlogView: FC<BlogViewProps> = ({ items }) => {
21
15
  <BlogCard
22
16
  key={index}
23
17
  data={{
24
- title: getTitle(item.labels),
18
+ title: item.title,
25
19
  blurDataURL: "/img/blog-post-1.webp",
26
20
  image: "/img/blog-post-1.webp",
27
- description: "item.shortDescription",
21
+ description: item.type,
28
22
  authors: "item.authors",
29
23
  _id: "item._id",
30
24
  date: "item.date",
31
- slug: `info/${item.shortId}`,
25
+ slug: item.link,
32
26
  }}
33
27
  />
34
28
  );
@@ -0,0 +1,39 @@
1
+ import React, { FC, JSX } from "react";
2
+ import {
3
+ DropdownMenu as DropdownMenuComp,
4
+ DropdownMenuContent,
5
+ DropdownMenuItem,
6
+ DropdownMenuTrigger,
7
+ } from "@c-rex/ui/dropdown-menu";
8
+
9
+ interface DropdownMenuProps {
10
+ items: {
11
+ format: string;
12
+ link: string;
13
+ }[];
14
+ icon: JSX.Element;
15
+ }
16
+
17
+ export const DropdownMenu: FC<DropdownMenuProps> = ({
18
+ items, icon
19
+ }) => {
20
+
21
+ return (
22
+ <DropdownMenuComp>
23
+ <DropdownMenuTrigger className="mx-2">
24
+ {icon}
25
+ </DropdownMenuTrigger>
26
+ <DropdownMenuContent>
27
+ {items.map((file, index) => {
28
+ return (
29
+ <DropdownMenuItem key={index}>
30
+ <a href={file.link} target="_blank" rel="noreferrer">
31
+ {file.format}
32
+ </a>
33
+ </DropdownMenuItem>
34
+ )
35
+ })}
36
+ </DropdownMenuContent>
37
+ </DropdownMenuComp>
38
+ );
39
+ };
@@ -1,4 +1,4 @@
1
- import React, { FC, ReactNode } from "react";
1
+ import React, { FC } from "react";
2
2
  import {
3
3
  Table,
4
4
  TableBody,
@@ -6,46 +6,74 @@ import {
6
6
  TableHeader,
7
7
  TableRow,
8
8
  } from "@c-rex/ui/table";
9
- import { informationUnitsItems, Labels } from "@c-rex/interfaces";
10
- import Link from "next/link";
9
+ import { informationUnitsResponseItems } from "@c-rex/interfaces";
11
10
  import { useTranslations } from "next-intl";
11
+ import { Ban, CloudDownload, CloudOff, Eye, EyeOff } from "lucide-react";
12
+ import { DropdownMenu } from "./dropdown-menu";
12
13
 
13
14
  interface TableViewProps {
14
- items: informationUnitsItems[];
15
+ items: informationUnitsResponseItems[];
15
16
  }
16
17
 
17
18
  const TableView: FC<TableViewProps> = ({ items }) => {
18
19
  const t = useTranslations("results")
19
20
 
20
- const getTitle = (labels: Labels[]): string => {
21
- return labels.map((item) => item.value).join();
22
- };
23
- const getIcons = (languages: string[]): string => {
24
- return languages.map((lang) => lang.split("-")[1]).join(",");
25
- };
26
-
27
21
  return (
28
22
  <div className="rounded-md border">
29
23
  <Table>
30
24
  <TableHeader>
31
25
  <TableRow>
32
26
  <TableCell>{t("title")}</TableCell>
27
+ <TableCell>{t("type")}</TableCell>
33
28
  <TableCell>{t("language")}</TableCell>
29
+ <TableCell>{t("files")}</TableCell>
34
30
  </TableRow>
35
31
  </TableHeader>
36
32
  <TableBody>
37
- {items.map((item: informationUnitsItems, index: number) => (
38
- <TableRow key={index}>
39
- <Link
40
- key={index}
41
- href={`info/${item.shortId}`}
42
- target="_blank"
43
- >
44
- <TableCell>{getTitle(item.labels)}</TableCell>
45
- </Link>
46
- <TableCell>{getIcons(item.languages)}</TableCell>
47
- </TableRow>
48
- ))}
33
+ {items.map((item, index) => {
34
+ const clazz = `h-12 c-rex_result_row c-rex_result_${item.type.toLowerCase()} ${item.disabled ? "c-rex_result_row_disabled" : ""}`
35
+
36
+ return (
37
+ <TableRow key={index} className={clazz}>
38
+ <TableCell className="h-12 c-rex_result_cell">
39
+ {item.disabled ? (item.title) : (<a href={item.link}>{item.title}</a>)}
40
+ </TableCell>
41
+ <TableCell>{item.type}</TableCell>
42
+ <TableCell>{item.language}</TableCell>
43
+ <TableCell>
44
+ {(item.disabled || (item.filesToDownload.length == 0 && item.filesToOpen.length == 0)) ? (
45
+ <button disabled className="mx-2">
46
+ <Ban />
47
+ </button>
48
+ ) : (
49
+ <>
50
+ {item.filesToDownload.length == 0 ? (
51
+ <button disabled className="mx-2">
52
+ <CloudOff />
53
+ </button>
54
+ ) : (
55
+ <DropdownMenu
56
+ items={item.filesToDownload}
57
+ icon={<CloudDownload />}
58
+ />
59
+ )}
60
+
61
+ {item.filesToOpen.length == 0 ? (
62
+ <button disabled className="mx-2">
63
+ <EyeOff />
64
+ </button>
65
+ ) : (
66
+ <DropdownMenu
67
+ items={item.filesToOpen}
68
+ icon={<Eye />}
69
+ />
70
+ )}
71
+ </>
72
+ )}
73
+ </TableCell>
74
+ </TableRow>
75
+ )
76
+ })}
49
77
  </TableBody>
50
78
  </Table>
51
79
  </div>
package/src/sidebar.tsx CHANGED
@@ -4,6 +4,7 @@ import React, { ComponentProps, JSX } from "react";
4
4
  import {
5
5
  Sidebar,
6
6
  SidebarContent,
7
+ SidebarFooter,
7
8
  SidebarGroup,
8
9
  SidebarGroupLabel,
9
10
  SidebarMenu,
@@ -12,20 +13,20 @@ import {
12
13
  SidebarMenuSub,
13
14
  SidebarMenuSubButton,
14
15
  SidebarMenuSubItem,
15
- SidebarRail
16
16
  } from "@c-rex/ui/sidebar";
17
17
  import { Skeleton } from "@c-rex/ui/skeleton";
18
- import { TreeOfContent } from "@c-rex/interfaces";
18
+ import { SidebarAvailableVersionsInterface, TreeOfContent } from "@c-rex/interfaces";
19
19
  import * as Flags from 'country-flag-icons/react/3x2';
20
20
  import { useTranslations } from "next-intl";
21
+ import { cn } from "@c-rex/utils";
21
22
 
22
23
  interface SidebarProps extends ComponentProps<typeof Sidebar> {
23
24
  data: TreeOfContent[];
24
25
  loading?: boolean;
25
- availableVersions: any
26
+ availableVersions: SidebarAvailableVersionsInterface[]
26
27
  }
27
28
 
28
- export function AppSidebar({ data, availableVersions, loading, ...props }: SidebarProps) {
29
+ export function AppSidebar({ data, availableVersions, loading, className, ...props }: SidebarProps) {
29
30
  const t = useTranslations();
30
31
 
31
32
  const getFlagIcon = (countryCode: string): JSX.Element | null => {
@@ -36,75 +37,90 @@ export function AppSidebar({ data, availableVersions, loading, ...props }: Sideb
36
37
  return <FlagComponent />;
37
38
  };
38
39
 
39
- return (
40
- <Sidebar collapsible="icon" {...props}>
41
- <SidebarContent>
42
- <SidebarGroup className="my-4">
43
- <SidebarGroupLabel>{t("tableOfContent")}:</SidebarGroupLabel>
44
- <SidebarMenu>
45
- {loading ? (
46
- <>
47
- <Skeleton className="w-auto h-10 mb-2" />
48
- <Skeleton className="w-auto h-10 mb-2" />
49
- <Skeleton className="w-auto h-10 mb-2 ml-8" />
50
- <Skeleton className="w-auto h-10 mb-2 ml-8" />
51
- <Skeleton className="w-auto h-10 mb-2 ml-8" />
52
- <Skeleton className="w-auto h-10 mb-2 ml-8" />
53
- <Skeleton className="w-auto h-10 mb-2" />
54
- </>
55
- ) : (
56
- <>
57
- {data.map((item) => (
58
- <SidebarMenuItem key={item.id}>
59
- <SidebarMenuButton asChild isActive={item.active}>
60
- <a href={item.link} title={item.label}>
61
- {item.label}
62
- </a>
63
- </SidebarMenuButton>
40
+ const tableOfContentGroup = (): JSX.Element | null => {
41
+ if (loading) {
42
+ return (
43
+ <>
44
+ <Skeleton className="w-auto h-10 mb-2" />
45
+ <Skeleton className="w-auto h-10 mb-2" />
46
+ <Skeleton className="w-auto h-10 mb-2 ml-8" />
47
+ <Skeleton className="w-auto h-10 mb-2 ml-8" />
48
+ <Skeleton className="w-auto h-10 mb-2 ml-8" />
49
+ <Skeleton className="w-auto h-10 mb-2 ml-8" />
50
+ <Skeleton className="w-auto h-10 mb-2" />
51
+ </>
52
+ )
53
+ }
54
+
55
+ if (data.length == 0) return null;
56
+
57
+ return (
58
+ <SidebarGroup>
59
+ <SidebarMenu>
60
+ {data.map((item) => (
61
+ <SidebarMenuItem key={item.id}>
62
+ <SidebarMenuButton asChild isActive={item.active}>
63
+ <a href={item.link} title={item.label}>
64
+ {item.label}
65
+ </a>
66
+ </SidebarMenuButton>
64
67
 
65
- {item.children?.length ? (
66
- <SidebarMenuSub>
67
- {item.children.map((item) => (
68
- <SidebarMenuSubItem key={item.label}>
69
- <SidebarMenuSubButton
70
- asChild
71
- isActive={item.active}
72
- >
73
- <a href={item.link} title={item.label}>
74
- {item.label}
75
- </a>
76
- </SidebarMenuSubButton>
77
- </SidebarMenuSubItem>
78
- ))}
79
- </SidebarMenuSub>
80
- ) : null}
81
- </SidebarMenuItem>
82
- ))}
83
- </>
84
- )}
85
- </SidebarMenu>
86
- </SidebarGroup>
68
+ {item.children?.length ? (
69
+ <SidebarMenuSub>
70
+ {item.children.map((item) => (
71
+ <SidebarMenuSubItem key={item.label}>
72
+ <SidebarMenuSubButton
73
+ asChild
74
+ isActive={item.active}
75
+ >
76
+ <a href={item.link} title={item.label}>
77
+ {item.label}
78
+ </a>
79
+ </SidebarMenuSubButton>
80
+ </SidebarMenuSubItem>
81
+ ))}
82
+ </SidebarMenuSub>
83
+ ) : null}
84
+ </SidebarMenuItem>
85
+ ))}
86
+ </SidebarMenu>
87
+ </SidebarGroup>
88
+ );
89
+ }
87
90
 
88
- <SidebarGroup>
89
- <SidebarGroupLabel>{t("availableIn")}:</SidebarGroupLabel>
90
- <SidebarMenu>
91
+ const availableVersionsGroup = (): JSX.Element | null => {
91
92
 
92
- {availableVersions.map((item: any) => {
93
- return (
93
+ if (availableVersions.length == 0) return null;
94
94
 
95
- <SidebarMenuItem key={item.shortId}>
96
- <SidebarMenuButton asChild isActive={item.active}>
97
- <a href={`../info/${item.shortId}`} title={item.lang}>
98
- {getFlagIcon(item.country)} {item.lang}
99
- </a>
100
- </SidebarMenuButton>
101
- </SidebarMenuItem>
102
- )
103
- })}
104
- </SidebarMenu>
105
- </SidebarGroup>
95
+ return (
96
+ <>
97
+ <SidebarGroupLabel>{t("availableIn")}:</SidebarGroupLabel>
98
+ <SidebarMenu>
99
+ {availableVersions.map((item) => {
100
+ return (
101
+ <SidebarMenuItem key={item.shortId}>
102
+ <SidebarMenuButton asChild isActive={item.active}>
103
+ <a href={item.link} title={item.lang}>
104
+ {getFlagIcon(item.country)} {item.lang}
105
+ </a>
106
+ </SidebarMenuButton>
107
+ </SidebarMenuItem>
108
+ )
109
+ })}
110
+ </SidebarMenu>
111
+ </>
112
+ )
113
+
114
+ }
115
+
116
+ return (
117
+ <Sidebar className={cn(className, "pt-20")} {...props}>
118
+ <SidebarContent>
119
+ {tableOfContentGroup()}
106
120
  </SidebarContent>
107
- <SidebarRail />
121
+ <SidebarFooter>
122
+ {availableVersionsGroup()}
123
+ </SidebarFooter>
108
124
  </Sidebar>
109
125
  );
110
126
  }