,
- init(): void;
-}
-
-export type { AuthStore };
diff --git a/src/auth/hoc/withAuth.tsx b/src/auth/hoc/withAuth.tsx
deleted file mode 100644
index 242699f..0000000
--- a/src/auth/hoc/withAuth.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React, { useEffect } from "react";
-/* -------------------------------------------------------------------------- */
-/* Types */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* Misc */
-/* -------------------------------------------------------------------------- */
-import { useAuthViewModel } from "auth/controller/useAuthViewModel";
-import { useAuthStore } from "auth/data/authSlice";
-/* -------------------------------------------------------------------------- */
-/* Helpers */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import AppLoader from "components/parts/Loader";
-import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
-import AuthFailure from "pages/AuthFailure";
-/* -------------------------------------------------------------------------- */
-/* Authentication logic HOC component */
-/* -------------------------------------------------------------------------- */
-
-const withAuth = (
- Component: React.ComponentType
,
- componentName = Component.displayName ?? Component.name
-): {
- (props: P): JSX.Element;
- displayName: string;
-} => {
- function WithAuthComponent(props: P) {
- const store = useAuthStore();
- const [searchParams] = useSearchParams();
- const navigate = useNavigate();
- const location = useLocation();
- const { init, isLoading, isFailed, isLogedIn, authenticate } =
- useAuthViewModel(store);
- const code = searchParams.get("code");
- useEffect(() => {
- if(isFailed) {
- return;
- }
- if (code) {
- authenticate(code);
- navigate(location.pathname, {
- replace: true,
- });
- return;
- }
- if (!isLogedIn) {
- init();
- navigate(location.pathname, {
- replace: true,
- });
- }
- }, [isFailed, isLogedIn, code, authenticate, init, navigate, location]);
-
- if (isLoading) {
- return ;
- }
-
- if (!isLogedIn) {
- return ;
- }
-
- return ;
- }
-
- WithAuthComponent.displayName = `withAuth(${componentName})`;
-
- return WithAuthComponent;
-};
-
-export { withAuth };
diff --git a/src/auth/useCases/authInitUseCase.ts b/src/auth/useCases/authInitUseCase.ts
deleted file mode 100644
index a88575f..0000000
--- a/src/auth/useCases/authInitUseCase.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { AuthStore } from "auth/domain/authStore";
-
-type AuthInitStore = Pick;
-
-const authInitUseCase = (store: AuthInitStore) => {
- store.init();
-}
-
-export {authInitUseCase};
\ No newline at end of file
diff --git a/src/auth/useCases/authenticationUseCase.ts b/src/auth/useCases/authenticationUseCase.ts
deleted file mode 100644
index 6ed7e63..0000000
--- a/src/auth/useCases/authenticationUseCase.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { AuthStore } from "auth/domain/authStore";
-
-type AuthenticationStore = Pick
-
-const authenticationUseCase = (store: AuthenticationStore, code: string) => {
- store.authenticate(code);
-};
-
-export { authenticationUseCase };
diff --git a/src/auth/useCases/signoutUseCase.ts b/src/auth/useCases/signoutUseCase.ts
deleted file mode 100644
index fdeb14a..0000000
--- a/src/auth/useCases/signoutUseCase.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { AuthStore } from "auth/domain/authStore";
-import { is } from "immer/dist/internal";
-
-type LogoutStore = Pick;
-
-const signOutUseCase = (store: LogoutStore) => {
- store.signOut();
-};
-
-export { signOutUseCase as signoutUseCase };
diff --git a/src/components/AcceptCookies.tsx b/src/components/AcceptCookies.tsx
new file mode 100755
index 0000000..2c6301d
--- /dev/null
+++ b/src/components/AcceptCookies.tsx
@@ -0,0 +1,41 @@
+import React from "react";
+import { Button } from "./Button/Button";
+import Typography from "./typography/Typography";
+
+type AcceptCookiesProps = {
+ onClickAccept?: () => void;
+ onClickCustomise?: () => void;
+};
+
+export function AcceptCookies({
+ onClickAccept,
+ onClickCustomise,
+}: AcceptCookiesProps) {
+ return (
+
+
+
+ By clicking “Accept All Cookies”, you agree to the storing of cookies
+ on your device to enhance site navigation, analyze site usage, and
+ assist in our marketing efforts.
+
+
+
+
+ Customize settings
+
+
+ Accept all cookies
+
+
+
+ );
+}
diff --git a/src/components/Article/Article.tsx b/src/components/Article/Article.tsx
new file mode 100755
index 0000000..ded1eb1
--- /dev/null
+++ b/src/components/Article/Article.tsx
@@ -0,0 +1,76 @@
+import React, { useState } from "react";
+/* -------------------------------------------------------------------------- */
+/* imports Article parts */
+/* -------------------------------------------------------------------------- */
+import { ArticleTitle } from "./ArticleParts/ArticleTitle";
+import { ArticleBreadcumbs } from "./ArticleParts/ArticleBreadcumbs";
+import { ArticleAuthors } from "./ArticleParts/ArticleAuthors";
+import { ArticleKeywords } from "./ArticleParts/ArticleKeywords";
+import { ArticleInteractionButtons } from "./ArticleParts/InteractionButtons/ArticleInteractionButtons";
+import { ArticleDescription } from "./ArticleParts/ArticleDescription";
+import { ArticleSubscriptionsButtons } from "./ArticleParts/ArticleSubscriptionsButton";
+import classNames from "classnames";
+
+/**
+ * Reduces a sequence of names to initials.
+ * @param {String} name Space Delimited sequence of names.
+ * @param {String} sep A period separating the initials.
+ * @param {String} trail A period ending the initials.
+ * @param {String} hyph A hypen separating double names.
+ * @return {String} Properly formatted initials.
+ */
+type ArticleTileExtentions = {
+ Title?: {
+ children?: string;
+ className?: string;
+ };
+ Breadcumbs?: {
+ children?: string[];
+ highlightLAstChild?: boolean;
+ };
+ Authors?: {
+ children: React.ReactNode;
+ className?: string;
+ emphasis?: "low" | "high";
+ };
+ Keywords?: {
+ children?: React.ReactNode;
+ className?: string;
+ emphasis?: "low" | "high";
+ };
+ Description?: {
+ children?: React.ReactNode;
+ emphasis?: "low" | "high";
+ isShowing?: boolean;
+ };
+ InteractionButtons?: {
+ children?: React.ReactNode;
+ className?: string;
+ emphasis?: "high" | "low";
+ };
+ SubscriptionButtons?: {
+ className?: string;
+ };
+};
+
+type ArticleTileProps = {
+ className?: string;
+ children?: React.ReactNode;
+};
+
+export function Article({
+ className,
+ children,
+}: ArticleTileProps & ArticleTileExtentions) {
+ const [isShowing, setIsShowing] = useState(false);
+
+ return {children}
;
+}
+
+Article.Title = ArticleTitle;
+Article.Breadcumbs = ArticleBreadcumbs;
+Article.Authors = ArticleAuthors;
+Article.Keywords = ArticleKeywords;
+Article.InteractionButtons = ArticleInteractionButtons;
+Article.Description = ArticleDescription;
+Article.SubscriptionsButtons = ArticleSubscriptionsButtons;
diff --git a/src/components/Article/ArticleParts/ArticleAuthors.tsx b/src/components/Article/ArticleParts/ArticleAuthors.tsx
new file mode 100755
index 0000000..3b749b8
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleAuthors.tsx
@@ -0,0 +1,53 @@
+import React, { useMemo } from "react";
+import { RouterLink } from "components/typography/RouterLink";
+import Typography from "components/typography/Typography";
+import classNames from "classnames";
+import { SVGUser } from "components/icons";
+
+type AuthorsProps = {
+ children: React.ReactNode;
+ className?: string;
+ emphasis?: "low" | "high";
+ linkTo?: string;
+};
+
+export function ArticleAuthors({
+ children,
+
+ className,
+ emphasis = "high",
+ linkTo = "#",
+}: AuthorsProps) {
+ const authors = React.Children.map(children, (author, i) => {
+ return (
+
+
+ {author}
+ {i != React.Children.count(children) - 1 ? "," : null}
+
+
+ );
+ });
+
+ return (
+
+ );
+}
+
+ArticleAuthors.displayName = "ArticleAuthors";
diff --git a/src/components/Article/ArticleParts/ArticleBreadcumbs.tsx b/src/components/Article/ArticleParts/ArticleBreadcumbs.tsx
new file mode 100755
index 0000000..ad451c1
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleBreadcumbs.tsx
@@ -0,0 +1,33 @@
+import Breadcrumbs from "components/breadcrumbs";
+import Logo from "components/Logo";
+import classNames from "classnames";
+
+type ArticleBreadcumbsProps = {
+ emphasis?: "high" | "low";
+ children?: string[];
+ className?: string;
+};
+
+export function ArticleBreadcumbs({
+ children,
+ emphasis = "high",
+ className, //Emphasis high uses when we display article page
+}: ArticleBreadcumbsProps) {
+ return (
+
+ {emphasis === "high" ? : null}
+ {children}
+
+ );
+}
+
+ArticleBreadcumbs.displayName = "ArticleBreadcumbs";
diff --git a/src/components/Article/ArticleParts/ArticleDescription.tsx b/src/components/Article/ArticleParts/ArticleDescription.tsx
new file mode 100755
index 0000000..01a2912
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleDescription.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+import Typography from "components/typography/Typography";
+import { Transition } from "@headlessui/react";
+import classNames from "classnames";
+
+type ArticleDescriptionProps = {
+ children?: React.ReactNode;
+ emphasis?: "low" | "high";
+ isShowing?: boolean;
+ className?: string;
+};
+
+export function ArticleDescription({
+ children,
+ className,
+ emphasis = "high",
+ isShowing = false,
+}: ArticleDescriptionProps) {
+ return emphasis === "low" ? (
+
+
+ {children}
+
+
+ ) : (
+
+ {children}
+
+ );
+}
+
+ArticleDescription.displayName = "ArticleDescription";
diff --git a/src/components/Article/ArticleParts/ArticleKeywords.tsx b/src/components/Article/ArticleParts/ArticleKeywords.tsx
new file mode 100755
index 0000000..5cc1ef8
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleKeywords.tsx
@@ -0,0 +1,61 @@
+import React from "react";
+import Typography from "components/typography/Typography";
+import { SVGKey } from "components/icons";
+import { RouterLink } from "components/typography/RouterLink";
+import classNames from "classnames";
+
+type KeywordsProps = {
+ children?: React.ReactNode;
+ className?: string;
+ emphasis?: "low" | "high";
+ linkTo?: string;
+};
+
+export function ArticleKeywords({
+ children,
+ className,
+ emphasis = "high",
+ linkTo = "#",
+}: KeywordsProps) {
+ const keywords = React.Children.map(children, (keyword, i) => {
+ return (
+
+
+
+ {keyword}
+ {i != React.Children.count(children) - 1 && emphasis === "low"
+ ? ","
+ : null}
+
+
+
+ );
+ });
+ return (
+
+ {emphasis === "low" ? (
+
+ ) : null}
+
+ {keywords}
+
+
+ );
+}
+
+ArticleKeywords.displayName = "ArticleKeywords";
diff --git a/src/components/Article/ArticleParts/ArticleSubscriptionsButton.tsx b/src/components/Article/ArticleParts/ArticleSubscriptionsButton.tsx
new file mode 100755
index 0000000..24c9c8b
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleSubscriptionsButton.tsx
@@ -0,0 +1,39 @@
+import React, { useState } from "react";
+import { Button } from "components/Button/Button";
+import {
+ SVGFavoriteFilled,
+ SVGFavoriteOutlined,
+ SVGFolder,
+} from "components/icons";
+
+const subscriptionStyles = "fill-gray-500 stroke-gray-500 w-6";
+
+type ArticleSubscriptionsButtonsProps = {
+ className?: string;
+ favorite?: boolean;
+ //Todo create tracking subscriptions and onClick props
+} & Omit, "">;
+
+export function ArticleSubscriptionsButtons({
+ className,
+ favorite = false,
+}: ArticleSubscriptionsButtonsProps) {
+ return (
+
+
+
+
+
+
+ {}}>
+
+ {!favorite ? (
+
+ ) : (
+
+ )}
+
+
+
+ );
+}
diff --git a/src/components/Article/ArticleParts/ArticleTitle.tsx b/src/components/Article/ArticleParts/ArticleTitle.tsx
new file mode 100755
index 0000000..939d848
--- /dev/null
+++ b/src/components/Article/ArticleParts/ArticleTitle.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import Typography from "components/typography/Typography";
+import { RouterLink } from "components/typography/RouterLink";
+type ArticleTitleProps = {
+ className?: string;
+ children?: string;
+ linkTo?: string;
+};
+
+export function ArticleTitle({
+ className,
+ children,
+ linkTo = "#",
+}: ArticleTitleProps) {
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+ArticleTitle.displayName = "ArticleTitle";
diff --git a/src/components/Article/ArticleParts/InteractionButtons/ArticleInteractionButtons.tsx b/src/components/Article/ArticleParts/InteractionButtons/ArticleInteractionButtons.tsx
new file mode 100755
index 0000000..1de4324
--- /dev/null
+++ b/src/components/Article/ArticleParts/InteractionButtons/ArticleInteractionButtons.tsx
@@ -0,0 +1,133 @@
+import React from "react";
+import { Button } from "components/Button/Button";
+import Typography from "components/typography/Typography";
+import {
+ SVGArrowDown,
+ SVGArrowUp,
+ SVGCite,
+ SVGFiletext,
+ SVGDownload,
+ SVGShare,
+ SVGFolder,
+} from "components/icons";
+import classNames from "classnames";
+import { Transition } from "@headlessui/react";
+import Link from "components/typography/Link";
+import { useTranslation } from "react-i18next";
+import { ShareButton } from "./ArticleShareButton";
+
+const interactionButtonsStore = [
+ {
+ icon: ,
+ title: "Download",
+ buttonEmphasis: "low",
+ iconClassName: "w-6 fill-gray-900 stroke-gray-900",
+ },
+ {
+ icon: ,
+ title: "Cite",
+ buttonEmphasis: "low",
+ iconClassName: "w-6 fill-gray-900 stroke-gray-900",
+ },
+ {
+ icon: ,
+ title: "Share",
+ buttonEmphasis: "low",
+ iconClassName: "w-6 fill-gray-900 stroke-gray-900",
+ },
+];
+
+type ArticleButtonProps = {
+ isAbstractOpen?: boolean;
+ openAbstract?: () => void;
+ children?: React.ReactNode;
+ className?: string;
+ emphasis?: "high" | "low";
+ articleID?: string;
+} & Omit, "">;
+
+export function ArticleInteractionButtons({
+ isAbstractOpen = false,
+ children,
+ openAbstract = () => {},
+ className,
+ articleID,
+ emphasis = "high", //to change displaying of component
+ ...props
+}: ArticleButtonProps) {
+ const [t, i18next] = useTranslation("translation", {
+ keyPrefix: "articlePage.interactionButtons",
+ });
+ /* ----------------------------- Abstract Button ---------------------------- */
+ const abstractButton = (
+
+
+ {t("abstract")}
+
+
+ {!isAbstractOpen ? (
+
+ ) : (
+
+ )}
+
+
+ );
+
+ /* ---------------------------- Read file button ---------------------------- */
+ const readFileButton = (
+
+
+
+ {emphasis === "high" && {t("readFile")} }
+
+
+ );
+ /* ----------------------------- Download button ---------------------------- */
+ const downLoadButton = (
+
+
+
+
+ {emphasis === "high" && {t("download")} }
+
+ );
+
+ /* ------------------------------- Cite button ------------------------------ */
+
+ const citeButton = (
+
+
+
+
+ {emphasis === "high" && {t("cite")} }
+
+ );
+
+ return (
+
+ {emphasis != "high" && abstractButton}
+ {readFileButton}
+ {downLoadButton}
+ {citeButton}
+
+
+ );
+}
+
+ArticleInteractionButtons.displayName = "ArticleInteractionButtons";
diff --git a/src/components/Article/ArticleParts/InteractionButtons/ArticleShareButton.tsx b/src/components/Article/ArticleParts/InteractionButtons/ArticleShareButton.tsx
new file mode 100644
index 0000000..4a5c734
--- /dev/null
+++ b/src/components/Article/ArticleParts/InteractionButtons/ArticleShareButton.tsx
@@ -0,0 +1,87 @@
+import { BASE_URL } from "core/httpClient";
+import { useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { Button } from "../../../Button/Button";
+import { SVGCopy, SVGShare, SVGXmark } from "../../../icons";
+import Typography from "../../../typography/Typography";
+import { CopyToClipboard } from "react-copy-to-clipboard";
+import { Popover } from "@headlessui/react";
+
+type Props = {
+ emphasis?: "high" | "low";
+ linktoCopy?: string;
+};
+
+export function ShareButton({ emphasis, linktoCopy }: Props) {
+ const [t, i18next] = useTranslation("translation", {
+ keyPrefix: "articlePage.interactionButtons",
+ });
+ const [copied, setCopied] = useState(false);
+
+ const copyValue =
+ BASE_URL != undefined && linktoCopy != undefined
+ ? BASE_URL + linktoCopy
+ : t("searchResults.nothingFound");
+
+ function onCopy() {
+ setCopied(true);
+ setTimeout(() => {
+ setCopied(false);
+ }, 1500);
+ };
+
+ const handleFocus = (event: any) => event.target.select();
+
+ return (
+
+
+
+
+
+
+ {emphasis === "high" && {t("share")} }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {copied && (
+
+ {t("copied")}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/ArticleSearchResult.tsx b/src/components/ArticleSearchResult.tsx
new file mode 100644
index 0000000..a1571be
--- /dev/null
+++ b/src/components/ArticleSearchResult.tsx
@@ -0,0 +1,65 @@
+import { useState } from "react";
+import { Article } from "components/Article/Article";
+import { Article as ArticleTypes } from "article/domain/articleEntity";
+import classNames from "classnames";
+import { debounce } from "lodash";
+import { useSearchStoreImplementation } from "searchResults/data/searchStoreImplementation";
+import { useSearchViewModel } from "searchResults/controller/searchResultsViewModel";
+import { Loader } from "./Loader/Loader";
+
+type Props = {
+ searchItem: ArticleTypes;
+};
+
+export const ArticleSearchResult = ({ searchItem }: Props) => {
+ const store = useSearchStoreImplementation();
+ const { isLoading } = useSearchViewModel(store);
+
+ const [openAbstract, setOpenAbstract] = useState(false);
+
+ const debouncedTask = debounce((task) => Promise.resolve(task()), 200);
+
+ function open() {
+ debouncedTask(() => setOpenAbstract(!openAbstract));
+ }
+
+ return (
+
+
+
+ {[
+ `${searchItem.topic}`,
+ `${searchItem.topic}`,
+ `${searchItem.topic}`,
+ `${searchItem.topic}`,
+ ]}
+
+
+
+
+
+ {searchItem.title}
+
+
+ {searchItem.authors}
+
+
+ {searchItem.tags}
+
+
+
+ {searchItem.summary}
+
+
+ );
+};
diff --git a/src/components/AspectRatio.tsx b/src/components/AspectRatio.tsx
new file mode 100755
index 0000000..ba3f290
--- /dev/null
+++ b/src/components/AspectRatio.tsx
@@ -0,0 +1,38 @@
+import classNames from "classnames";
+import React from "react";
+
+export type Props = {
+ /**
+ * The style of component
+ */
+ className?: string;
+ /**
+ * The optional child
+ */
+ children?: React.ReactNode;
+};
+const AspectRatio = ({ className, children }: Props) => {
+ return (
+
+ {children}
+
+ );
+};
+
+AspectRatio.Content = function AspectRatioContent({
+ className,
+ children,
+}: Props) {
+ return (
+
+ {children}
+
+ );
+};
+
+export default AspectRatio;
diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx
new file mode 100755
index 0000000..7de6456
--- /dev/null
+++ b/src/components/Avatar.tsx
@@ -0,0 +1,83 @@
+import classNames from "classnames";
+import React from "react";
+
+export type Props = {
+ /**
+ * The style avatar
+ */
+ className?: string;
+ /**
+ * The path of image
+ */
+ src?: string;
+ /**
+ * The alternative text in case the image is not valid
+ */
+ alt?: string;
+ /**
+ * The text inside avatar as a child
+ */
+ children?: React.ReactNode;
+};
+
+// Choosing random color for the avatar background
+function getRandomInt(max: number) {
+ return Math.floor(Math.random() * max);
+}
+let colors: string[] = [
+ "bg-red-400",
+ "bg-orange-400",
+ "bg-green-400",
+ "bg-amber-400",
+ "bg-yellow-400",
+ "bg-lime-400",
+ "bg-emerald-400",
+ "bg-teal-400",
+ "bg-cyan-400",
+ "bg-sky-400",
+ "bg-blue-400",
+ "bg-indigo-400",
+ "bg-violet-400",
+ "bg-purple-400",
+ "bg-fuchsia-400",
+];
+
+const Avatar = ({ className, src, alt, children }: Props) => {
+ const wrapperClasses = src ? "" : colors[getRandomInt(colors.length)];
+ const position = src ? "relative pt-[100%]" : "";
+ return (
+
+ {/* In case the src is valid, it will show the image */}
+ {src && (
+
+ )}
+ {/* The text will be shown in case it is valid */}
+ {children && (
+
+ {children}
+
+ )}
+
+ );
+};
+
+export default Avatar;
diff --git a/src/components/Badge.stories.tsx b/src/components/Badge.stories.tsx
new file mode 100755
index 0000000..f5ecb4a
--- /dev/null
+++ b/src/components/Badge.stories.tsx
@@ -0,0 +1,30 @@
+import React, { Children } from "react";
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import Badge from "./Badge";
+
+
+
+export default{
+ title: 'Badge',
+ component: Badge,
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => ;
+
+export const High = Template.bind({});
+High.args = {
+ emphasis: 'high',
+ children: ['Tom Cook'],
+};
+
+export const Medium = Template.bind({});
+Medium.args = {
+ emphasis: 'medium',
+ children: ['Tanya Fox'],
+};
+
+export const Low = Template.bind({});
+Low.args = {
+ emphasis:'low',
+ children:['Hellen Schmidt'],
+};
\ No newline at end of file
diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx
new file mode 100755
index 0000000..5b4810b
--- /dev/null
+++ b/src/components/Badge.tsx
@@ -0,0 +1,65 @@
+/* -------------------------------------------------------------------------- */
+/* Imports */
+/* -------------------------------------------------------------------------- */
+
+import classNames from "classnames";
+import { StyleType } from "core/_variants";
+import React from "react";
+import { ReactComponent as DeleteIcon } from "../assets/svg/xmark.svg";
+
+/* -------------------------------------------------------------------------- */
+/* Component props */
+/* -------------------------------------------------------------------------- */
+
+type Props = {
+ emphasis?: StyleType;
+ children: React.ReactNode;
+ className?: string;
+ iconed?: boolean;
+ onClick?: (element: any) => void;
+ closeOption?: Boolean;
+} & Omit, "">;
+
+/* -------------------------------------------------------------------------- */
+/* Component implementation */
+/* -------------------------------------------------------------------------- */
+
+function Badge({
+ className,
+ children,
+ onClick,
+ emphasis = "low",
+ closeOption = false,
+ ...props
+}: Props): JSX.Element {
+ return (
+ <>
+
+ {children}
+ {closeOption && (
+
+
+
+
+
+ )}
+
+ >
+ );
+}
+export default Badge;
diff --git a/src/components/BaseLayout.tsx b/src/components/BaseLayout.tsx
new file mode 100755
index 0000000..237777d
--- /dev/null
+++ b/src/components/BaseLayout.tsx
@@ -0,0 +1,24 @@
+import { joinClassnames } from "core/helpers";
+import React from "react";
+import { Footer } from "./parts/Footer";
+import Header from "./parts/Header";
+type Props = {
+ header?: React.ReactElement;
+ children: React.ReactNode;
+ footer?: React.ReactElement;
+ className?: string;
+};
+
+function BaseLayout({ header, footer, children, className }: Props) {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}
+
+export default BaseLayout;
diff --git a/src/components/BottomSheetBar.tsx b/src/components/BottomSheetBar.tsx
new file mode 100755
index 0000000..03e7f61
--- /dev/null
+++ b/src/components/BottomSheetBar.tsx
@@ -0,0 +1,58 @@
+import { useState } from "react";
+import { Dialog } from "@headlessui/react";
+import { Button } from "./Button/Button";
+import Typography from "components/typography/Typography";
+
+export function BottomSheetBar() {
+ // The open/closed state lives outside of the Dialog and is managed by you
+ let [isOpen, setIsOpen] = useState(true);
+
+ function handleDeactivate() {
+ alert("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ }
+
+ return (
+ /*
+ Pass `isOpen` to the `open` prop, and use `onClose` to set
+ the state back to `false` when the user clicks outside of
+ the dialog or presses the escape key.
+ */
+ {}}
+ className="absolute bottom-0 bg-blue-900 text-white w-full"
+ >
+
+
+ By clicking “Accept All Cookies”, you agree to the storing of cookies
+ on your device to enhance site navigation, analyze site usage, and
+ assist in our marketing efforts.
+
+ {/*
+ You can render additional buttons to dismiss your dialog by setting
+ `isOpen` to `false`.
+ */}
+
+
+
+ Customize settings
+
+
+ setIsOpen(false)}
+ >
+
+ Accept all cookies
+
+
+
+
+
+ );
+}
diff --git a/src/components/Burger.tsx b/src/components/Burger.tsx
new file mode 100644
index 0000000..99b9e67
--- /dev/null
+++ b/src/components/Burger.tsx
@@ -0,0 +1,198 @@
+import { Menu, Transition } from "@headlessui/react";
+import React, { Fragment } from "react";
+import classNames from "classnames";
+import { Disclosure } from "@headlessui/react";
+
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import ContextMenuAction from "./drop-down-menu/ContextMenuAction";
+import ContextMenu from "./drop-down-menu/ContextMenu";
+import { Button } from "./Button/Button";
+import Link from "./typography/Link";
+
+/* -------------------------------------------------------------------------- */
+/* Icons */
+/* -------------------------------------------------------------------------- */
+import { ReactComponent as SVGFavoriteOutlined } from "assets/svg/favorite-outlined.svg";
+import { ReactComponent as SVGHamburger } from "assets/svg/hamburger.svg";
+import { ReactComponent as SVGFolder } from "assets/svg/folder.svg";
+import { ReactComponent as SVGFile } from "assets/svg/file.svg";
+import { ReactComponent as SVGEye } from "assets/svg/eye.svg";
+import { ReactComponent as SVGArrowUp } from "assets/svg/arrow-up.svg";
+import { ReactComponent as SVGCaretDown } from "assets/svg/caret-down.svg";
+
+type Props = React.ComponentPropsWithoutRef<"div">;
+
+const Burger = (props: Props) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ create new
+
+
+
+
+ {({ open }) => (
+ <>
+
+ my library
+
+
+
+
+
+
+ My Publications
+
+
+
+
+
+
+ My Favorites
+
+
+
+
+
+
+ My Collections
+
+
+
+
+
+
+ Recent Viewed
+
+
+ >
+ )}
+
+
+ {/* Third list - start */}
+
+ {({ open }) => (
+ <>
+
+ About
+
+
+
+
+
+ About Freeland
+
+
+
+
+
+ Contact Us
+
+
+
+
+
+ Help
+
+
+ >
+ )}
+
+ {/* Third list - end */}
+
+
+
+
+
+ );
+};
+
+export default Burger;
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
new file mode 100755
index 0000000..36b3b8c
--- /dev/null
+++ b/src/components/Button/Button.tsx
@@ -0,0 +1,92 @@
+/* -------------------------------------------------------------------------- */
+/* Imports */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import classNames from "classnames";
+import { StyleType } from "core/_variants";
+import * as btnEmphasis from "./_btnEmphasis";
+import IconButton from "./IconButton";
+
+/* -------------------------------------------------------------------------- */
+/* Extention for component */
+/* -------------------------------------------------------------------------- */
+
+type ButtonExtentions = {
+ Icon: React.FC<{
+ children: Required;
+ className?: string;
+ }>;
+};
+
+/* -------------------------------------------------------------------------- */
+/* Component props */
+/* -------------------------------------------------------------------------- */
+type ButtonProps = {
+ defaultStyle?: boolean;
+ emphasis?: StyleType | undefined; //Choose one of three variants of button. By default it will be "high"
+ disabled?: boolean;
+} & Omit, "">;
+
+/* -------------------------------------------------------------------------- */
+/* Component implementation */
+/* -------------------------------------------------------------------------- */
+export const Button: React.FC & ButtonExtentions = ({
+ defaultStyle = true,
+ emphasis = "high",
+ disabled = false,
+ className,
+ children,
+ ...props
+}) => {
+ const isOnlyIcon =
+ children &&
+ React.isValidElement(children) &&
+ React.Children.only(children).type == IconButton;
+
+ const padding = isOnlyIcon ? "px-2.5 py-2" : "px-4 py-2";
+
+ return (
+ {} : undefined}
+ className={classNames([
+ "flex content-center justify-between",
+ "text-center",
+ padding,
+ "rounded",
+ {
+ "!cursor-default": disabled,
+ "transition-all": !disabled,
+ "transition duration-200": !disabled,
+ },
+ {
+ // Define high emphasis
+ [`${btnEmphasis.EnableHigh}`]:
+ defaultStyle && !disabled && emphasis === "high",
+ [`${btnEmphasis.DisabledHigh}`]:
+ defaultStyle && disabled && emphasis === "high",
+ [`${btnEmphasis.GeneralHigh}`]: defaultStyle && emphasis === "high",
+ // Define medium emphasis
+ [`${btnEmphasis.EnabledMedium}`]:
+ defaultStyle && !disabled && emphasis === "medium",
+ [`${btnEmphasis.DisabledMedium}`]:
+ defaultStyle && disabled && emphasis === "medium",
+ [`${btnEmphasis.GeneralMedium}`]:
+ defaultStyle && emphasis === "medium",
+ // Define low emphasis
+ [`${btnEmphasis.EnabledLow}`]:
+ defaultStyle && !disabled && emphasis === "low",
+ [`${btnEmphasis.DisabledLow}`]:
+ defaultStyle && disabled && emphasis === "low",
+ [`${btnEmphasis.GenerealLow}`]: defaultStyle && emphasis === "low",
+ },
+ className,
+ ])}
+ {...props}
+ >
+ {children}
+
+ );
+};
+
+Button.Icon = IconButton;
diff --git a/src/components/Button/IconButton.tsx b/src/components/Button/IconButton.tsx
new file mode 100755
index 0000000..09c7ae4
--- /dev/null
+++ b/src/components/Button/IconButton.tsx
@@ -0,0 +1,21 @@
+import classNames from "classnames";
+import React, { FunctionComponent as FC } from "react";
+import { SVGSearch } from "../icons";
+
+type IconButtonProps = {
+ children: Required;
+ className?: string;
+} & Omit, "">;
+
+const IconButton: React.FC = ({
+ children,
+ className,
+}): JSX.Element => {
+ return className
+ ? React.cloneElement(children, {
+ className: classNames(className),
+ })
+ : children;
+};
+IconButton.displayName = "ButtonIcon";
+export default IconButton;
diff --git a/src/components/Button/_btnEmphasis.tsx b/src/components/Button/_btnEmphasis.tsx
new file mode 100755
index 0000000..cbb43b8
--- /dev/null
+++ b/src/components/Button/_btnEmphasis.tsx
@@ -0,0 +1,65 @@
+/* -------------------------------------------------------------------------- */
+/* styles */
+/* -------------------------------------------------------------------------- */
+
+export const EnableHigh = `bg-blue-600
+ hover:bg-blue-500
+ active:bg-blue-700
+ focus:shadow-lg shadow-blue-500
+ focus:outline outline-blue-400/10 outline-8
+`;
+
+export const DisabledHigh = `bg-gray-200
+ focus:outline-none`;
+
+export const GeneralHigh = `
+ text-white
+ fill-white
+ stroke-white
+ `;
+
+export const EnabledMedium = `text-blue-600
+ border-gray-500
+ active:border-blue-700
+ active:text-blue-700
+ hover:border-blue-500
+ hover:text-blue-500
+ focus:outline outline-blue-400/10 outline-8
+ focus:border-blue-700/70
+ fill-blue-600
+ hover:fill-blue-500
+ active:fill-blue-700
+ focus:fill-blue-700
+ stroke-blue-600
+ hover:stroke-blue-500
+ active:stroke-blue-700
+ focus:stroke-blue-700
+`;
+
+export const DisabledMedium = `text-gray-200
+ border-gray-200
+ fill-gray-200
+ stroke-gray-200
+ focus:outline-none`;
+
+export const GeneralMedium = `bg-white
+ border`;
+
+export const EnabledLow = ` text-gray-900
+ hover:bg-gray-100
+ active:text-blue-700
+ active:bg-blue-100
+ focus:bg-blue-100
+ fill-gray-900
+ stroke-gray-900
+ active:fill-blue-600
+ active:stroke-blue-600
+ `;
+
+export const DisabledLow = `text-gray-200
+ fill-gray-200
+ stroke-gray-200`;
+
+export const GenerealLow = `focus:outline-none
+ bg-transparent
+`;
diff --git a/src/components/Card.tsx b/src/components/Card.tsx
new file mode 100755
index 0000000..3fbc9d4
--- /dev/null
+++ b/src/components/Card.tsx
@@ -0,0 +1,127 @@
+import classNames from "classnames";
+import React from "react";
+
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import Typography from "./typography/Typography";
+import Heading from "./typography/Heading";
+import Link from "./typography/Link";
+/* -------------------------------------------------------------------------- */
+/* Props */
+/* -------------------------------------------------------------------------- */
+type Props = {
+ /**
+ * Card component accept children
+ */
+ children: React.ReactNode;
+ /**
+ * Styling the card component
+ */
+ className?: string | undefined;
+};
+
+const Card = ({ children, className }: Props) => {
+ return (
+
+ {children}
+
+ );
+};
+
+// Avatar function
+type AvatarProps = {
+ children?: React.ReactNode;
+};
+Card.Avatar = function CardAvatar({ children }: AvatarProps) {
+ return {children}
;
+};
+
+// Title function
+Card.Title = function CardTitle({ children, className }: Props) {
+ return (
+
+ {children}
+
+ );
+};
+
+// SubTitle function
+Card.SubTitle = function CardSubTitle({ children, className }: Props) {
+ return (
+ {children}
+ );
+};
+
+// Body function
+Card.Body = function CardTitle({ children, className }: Props) {
+ return (
+
+ {children}
+
+ );
+};
+
+// Cardheader function
+Card.CardHeader = function CardCardHeader({ children, className }: Props) {
+ return (
+
+ {children}
+
+ );
+};
+
+// Cardcontent function
+Card.CardContent = function CardCardContent({ children, className }: Props) {
+ return (
+
+ {children}
+
+ );
+};
+
+// Cardaction function
+type CardActionProps = {
+ children: React.ReactNode;
+ className?: string | undefined;
+ href?: string;
+};
+Card.CardAction = function CardCardAction({
+ children,
+ className,
+ href = "#",
+}: CardActionProps) {
+ return (
+
+ {children}
+
+ );
+};
+
+// CardMedia function
+type CardMediaProps = {
+ children?: React.ReactNode;
+ className?: string | undefined;
+ src?: string;
+};
+Card.CardMedia = function CardCardMedia({
+ className,
+ src = "#",
+}: CardMediaProps) {
+ return (
+
+ );
+};
+
+export default Card;
diff --git a/src/components/Cards/CategoryCard.tsx b/src/components/Cards/CategoryCard.tsx
new file mode 100755
index 0000000..2d6a42d
--- /dev/null
+++ b/src/components/Cards/CategoryCard.tsx
@@ -0,0 +1,57 @@
+import React from "react";
+import { SVGMedicine } from "../icons";
+import Typography from "components/typography/Typography";
+import { Button } from "components/Button/Button";
+import classNames from "classnames";
+import { useTranslation } from "react-i18next";
+
+type Props = {
+ count?: number;
+ title: string;
+
+ iconChild: Required;
+} & Omit, "">;
+
+function CategoryCard({ count, title, iconChild, className, ...props }: Props) {
+ const [t, i18next] = useTranslation();
+ const iconChildStyle =
+ "h-7 fill-gray-500 stroke-gray-500 group-focus:fill-blue-600 group-active:fill-blue-600 group-focus:stroke-blue-600 group-active:stroke-blue-600";
+
+ return (
+
+
+
+
+ {React.cloneElement(iconChild, {
+ className: classNames(iconChildStyle, className),
+ })}
+
+
+
+
+ {t(title)}
+
+
+
+
+ {count}{" "}
+ {t("mainPage.article_many", { count: count }).toString()}
+
+
+
+
+
+
+ );
+}
+
+export default CategoryCard;
diff --git a/src/components/Checkbox.stories.tsx b/src/components/Checkbox.stories.tsx
new file mode 100755
index 0000000..9c1c42e
--- /dev/null
+++ b/src/components/Checkbox.stories.tsx
@@ -0,0 +1,43 @@
+import React, { useState } from "react";
+import { Meta, Story, ComponentStory, ComponentMeta } from "@storybook/react";
+
+import Checkbox from "./Checkbox";
+
+export default {
+ title: "Checkbox",
+ component: Checkbox,
+ // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
+ argTypes: {
+ isChecked: {
+ type: "boolean",
+ },
+ children: {
+ type: "string",
+ defaultValue: "Use light theme",
+ },
+ disabled: {
+ type: "boolean",
+ defaultValue: "false",
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+export const Checked = Template.bind({});
+Checked.args = {
+ isChecked: true,
+ children: "This is custom checkbox",
+};
+
+export const Unchecked = Template.bind({});
+Unchecked.args = {
+ isChecked: false,
+};
+
+export const Disabled = Template.bind({});
+Disabled.args = {
+ disabled: true,
+};
diff --git a/src/components/Checkbox.tsx b/src/components/Checkbox.tsx
new file mode 100755
index 0000000..8ba3fc9
--- /dev/null
+++ b/src/components/Checkbox.tsx
@@ -0,0 +1,44 @@
+import React from "react";
+import classNames from "classnames";
+import { ReactComponent as Checkmark } from "../assets/svg/arrow-down.svg";
+
+export type Props = {
+ /**
+ * Control the state of checkbox
+ */
+ isChecked?: boolean;
+ /**
+ * An Element next to the checkbox
+ */
+ children?: React.ReactNode;
+} & Omit, "type">;
+
+const Checkbox = ({ children, className, isChecked, ...props }: Props) => {
+ return (
+
+
+ {children}
+
+ );
+};
+export default Checkbox;
diff --git a/src/components/CircleLoader.tsx b/src/components/CircleLoader.tsx
old mode 100644
new mode 100755
index b3a9586..958c731
--- a/src/components/CircleLoader.tsx
+++ b/src/components/CircleLoader.tsx
@@ -1,30 +1,30 @@
-import React from "react";
-
-
-export default function CircleLoader() {
- return (
-
-
-
-
-
-
-
-
-
- );
-}
+import React from "react";
+
+
+export default function CircleLoader() {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Container.stories.tsx b/src/components/Container.stories.tsx
new file mode 100755
index 0000000..ecaab15
--- /dev/null
+++ b/src/components/Container.stories.tsx
@@ -0,0 +1,88 @@
+import React from 'react';
+
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+
+import Container from './Container';
+
+export default {
+ // Title inside navigation bar
+ title: 'Container',
+ // Component to test
+ component: Container,
+ // Clarifying the way how to process specific
+ // properties of your component and which values
+ // it can accept.
+} as ComponentMeta;
+
+/**
+ * This is a way to define a tempalte for your component.
+ *
+ * This template should cover all the states.
+ *
+ * In most cases you should just distruct args attribute
+ * on a returning component.
+ */
+const Template: ComponentStory = (args) => (
+
+);
+//
+/* -------------------------------------------------------------------------- */
+/* States of your component */
+/* -------------------------------------------------------------------------- */
+
+export const Desktop: ComponentStory = Template.bind({});
+Desktop.args = {
+ className: 'bg-blue-500 w-840 block',
+ children: 840px
,
+
+}
+
+Desktop.parameters = {
+ viewport: {
+ defaultViewport: 'desktop',
+
+ viewports: {
+
+ desktop: {
+ name: 'main_container',
+ styles: {
+ width: '840px',
+ height: '100%',
+ paddingLeft: '0.5rem',
+ paddingRight: '0.5rem',
+ },
+ type: 'desktop',
+ },
+
+ screen_sm: {
+ name: 'screen_sm',
+ styles: {
+ width: 'calc(100% - 30px)',
+ height: '100%',
+ },
+ type: 'tablet',
+ },
+
+ screen_md: {
+ name: 'screen_md',
+ styles: {
+ width: 'calc(100% - 30px)',
+ height: '100%',
+ },
+ type: 'tablet',
+ },
+
+ screen_lg_xl_2xl: {
+ name: 'lg-xl-2xl',
+ styles: {
+ width: '840px',
+ height: '100%',
+ },
+ type: 'desktop',
+ },
+ }
+ }
+}
+
+
+
\ No newline at end of file
diff --git a/src/components/Container.tsx b/src/components/Container.tsx
new file mode 100755
index 0000000..9d913b3
--- /dev/null
+++ b/src/components/Container.tsx
@@ -0,0 +1,28 @@
+import classNames from "classnames";
+import React from "react";
+type Props = {
+ /**
+ * Content of component
+ */
+ children: React.ReactNode;
+ /**
+ * Display variants of container
+ */
+ variant?: "straight" | "wide";
+ /**
+ * Component styling
+ */
+ className?: string;
+};
+/**
+ * Main container to handle page content max-width on
+ * different screen sizes
+ */
+export default function Container({ children, variant, className }: Props) {
+ const wideClass = variant == "straight" ? "container" : "container-wide";
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/Disclosure.tsx b/src/components/Disclosure.tsx
new file mode 100644
index 0000000..e17f290
--- /dev/null
+++ b/src/components/Disclosure.tsx
@@ -0,0 +1,55 @@
+/* -------------------------------------------------------------------------- */
+/* Imports */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import { Disclosure as Disclose } from "@headlessui/react";
+import { ReactComponent as SelectIcon } from "../assets/svg/arrow-down.svg";
+import classNames from "classnames";
+/* -------------------------------------------------------------------------- */
+/* Component props */
+/* -------------------------------------------------------------------------- */
+type Props = {
+ children?: React.ReactNode;
+ className?: string;
+ caption?: string;
+};
+/* -------------------------------------------------------------------------- */
+/* styles */
+/* -------------------------------------------------------------------------- */
+const ButtonStyle = `
+flex w-full
+justify-between
+items-center
+py-2 text-left
+text-base
+font-medium
+text-black-400
+`;
+/* -------------------------------------------------------------------------- */
+/* Component implementation */
+/* -------------------------------------------------------------------------- */
+export default function Disclosure({ children, className, caption }: Props) {
+ return (
+
+
+
+ {({ open }) => (
+ <>
+
+ {caption}
+
+
+
+ {children}
+
+ >
+ )}
+
+
+
+ );
+}
diff --git a/src/components/Filters/AppiledFilters.tsx b/src/components/Filters/AppiledFilters.tsx
new file mode 100644
index 0000000..e4562ff
--- /dev/null
+++ b/src/components/Filters/AppiledFilters.tsx
@@ -0,0 +1,69 @@
+/* -------------------------------------------------------------------------- */
+/* Imports */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import Badge from "components/Badge";
+import { IProduct } from "./IProdutct";
+import classNames from "classnames";
+import { useTranslation } from "react-i18next";
+
+/* -------------------------------------------------------------------------- */
+/* Component props */
+/* -------------------------------------------------------------------------- */
+type Props = {
+ hints?: IProduct[];
+ delFilter: (element: IProduct) => void;
+ clearAll: () => void;
+ className?: string;
+};
+/* -------------------------------------------------------------------------- */
+/* Component implementation */
+/* -------------------------------------------------------------------------- */
+export default function AppiledFilters({
+ hints,
+ delFilter,
+ clearAll,
+ className,
+}: Props) {
+
+ const [t, i18next] = useTranslation()
+
+ return (
+ <>
+
+
+
{t("filters.appliedFitlers")}
+
+
+ {t("filters.clearAll")}
+
+
+
+
+ {hints &&
+ hints.length > 0 &&
+ hints.map((item: IProduct) => (
+
+ delFilter(item)}
+ >
+ {item.brand}
+
+
+ ))}
+
+
+ >
+ );
+}
diff --git a/src/components/Filters/Filter.tsx b/src/components/Filters/Filter.tsx
new file mode 100644
index 0000000..16ea49d
--- /dev/null
+++ b/src/components/Filters/Filter.tsx
@@ -0,0 +1,112 @@
+import React from "react";
+import Disclosure from "components/Disclosure";
+import AppiledFilters from "./AppiledFilters";
+import SearchFilterBar from "./SearchFilterBar";
+import axios from "axios";
+import { useDebounce } from "./functions/debounce";
+import { IProduct } from "./IProdutct";
+import { useTranslation } from "react-i18next";
+
+type Props = {
+ className?: string;
+};
+
+export default function Fiter({ className }: Props) {
+ const [checkedId, setCheckedId] = React.useState>([]);
+ const [activeFilter, setActiveFilter] = React.useState>([]);
+
+ /* -------------------------------------------------------------------------- */
+ /* Test request to demonstrate the basic functionality of the filter */
+ /* -------------------------------------------------------------------------- */
+
+ const [query, setQuery] = React.useState("");
+ const [hints, setHints] = React.useState>([]);
+
+ const onChange = (e: React.ChangeEvent) => {
+ setQuery(e.target.value);
+ };
+ const debounced = useDebounce(query);
+
+ const [t, i18next] = useTranslation();
+
+ async function fetchProducts() {
+ const response = await axios.get(
+ `https://dummyjson.com/products/search?q=${debounced}`
+ );
+ setHints(response.data.products);
+ }
+ React.useEffect(() => {
+ fetchProducts();
+ }, [debounced, query]);
+
+ const isChecked = (item: number) => (checkedId.includes(item) ? true : false);
+
+ const handleChange = (e: React.ChangeEvent) => {
+ const selectedId = parseInt(e.target.value);
+
+ /* ------------- Check if "checkedId" contains "selectedIds" ------------- */
+ /* -------------- If true, this checkbox is already checked -------------- */
+
+ if (checkedId.includes(selectedId)) {
+ setCheckedId(checkedId.filter((id) => id !== selectedId));
+ setActiveFilter(activeFilter.filter((item) => item.id !== selectedId));
+ } else {
+ const newId = [...checkedId];
+ newId.push(selectedId);
+ setCheckedId(newId);
+
+ /* --------------- Adding checked filters to AppliedFilters component -------------- */
+
+ hints.forEach((hint) => {
+ if (hint.id === selectedId)
+ activeFilter.push({ id: selectedId, brand: hint.brand });
+ setActiveFilter(activeFilter);
+ });
+ }
+ };
+
+ /* ------------- Delete filter from AppliedFilters component ------------- */
+
+ const DelFilter = (e: IProduct) => {
+ setActiveFilter(activeFilter.filter((item) => item.id !== e.id));
+ setCheckedId(checkedId.filter((id) => id !== e.id));
+ };
+ const clearAll = () => {
+ setActiveFilter([]);
+ setCheckedId([]);
+ setQuery("");
+ };
+ //console.log(activeFilter)
+ return (
+ <>
+
+
+
+
+
+
+
+ {t("filters.content")}
+
+
+ {t("filters.content")}
+
+
+ {t("filters.content")}
+
+
+
+ >
+ );
+}
diff --git a/src/components/Filters/IProdutct.ts b/src/components/Filters/IProdutct.ts
new file mode 100644
index 0000000..9101267
--- /dev/null
+++ b/src/components/Filters/IProdutct.ts
@@ -0,0 +1,23 @@
+ export interface IProduct {
+ id?: number;
+ title?: string;
+ description?: string;
+ price?: number;
+ discountPercentage?: number;
+ rating?: number;
+ stock?: number;
+ brand?: string;
+ category?: string;
+ thumbnail?: string;
+ images?: string[];
+ }
+
+ export interface serverResponse {
+ products: T[];
+ total: number;
+ skip: number;
+ limit: number;
+ }
+
+
+
diff --git a/src/components/Filters/SearchFilterBar.tsx b/src/components/Filters/SearchFilterBar.tsx
new file mode 100644
index 0000000..0314ac1
--- /dev/null
+++ b/src/components/Filters/SearchFilterBar.tsx
@@ -0,0 +1,120 @@
+import { useState, useCallback, Fragment } from "react";
+import { Combobox, Transition } from "@headlessui/react";
+import { IProduct } from "./IProdutct";
+import Checkbox from "components/Checkbox";
+import { ReactComponent as SearchIcon } from "assets/svg/search.svg";
+import classNames from "classnames";
+import { useTranslation } from "react-i18next";
+
+
+type Props = {
+ hints: IProduct[];
+ handleChange: (e: React.ChangeEvent) => void;
+ isChecked: (item: number) => boolean;
+ className?: string;
+ onChange: (e: React.ChangeEvent) => void;
+ placeHolder: string;
+ query: string;
+};
+
+export default function SearchFilterBar({
+ hints,
+ isChecked,
+ handleChange,
+ className,
+ onChange,
+ placeHolder,
+ query,
+}: Props): JSX.Element {
+ const [checkList, setCheckList] = useState(hints);
+ const [showFilters, SetShowFilters] = useState(false);
+ const [t, i18next] = useTranslation()
+
+ const ShowAllFilters = useCallback(() => {
+ SetShowFilters((prev) => !prev);
+ }, [SetShowFilters]);
+
+ return (
+ <>
+
+ {({ open }) => (
+
+
+
+
+
+ {open && (
+
+
+ {hints.length >= 6 && query !== "" && (
+
+ {t("filters.showAll")}({hints.length})
+
+ )}
+
+ {hints.length > 0 ? (
+ hints.map((item) => (
+
+
+ {item.brand}
+
+
+ ))
+ ) : (
+ Ничего не найдено
+ )}
+
+ )}
+
+
+
+
+ )}
+
+ >
+ );
+}
diff --git a/src/components/Filters/functions/debounce.ts b/src/components/Filters/functions/debounce.ts
new file mode 100644
index 0000000..7d7bc78
--- /dev/null
+++ b/src/components/Filters/functions/debounce.ts
@@ -0,0 +1,11 @@
+import React from "react";
+
+export function useDebounce(value: string, delay: number = 300) {
+ const [debounced, setDebouced] = React.useState(value);
+ React.useEffect(() => {
+ const handler = setTimeout(() => setDebouced(value), delay);
+ return () => clearTimeout(handler);
+ }, [value, delay]);
+
+ return debounced;
+}
diff --git a/src/components/Hexagon.tsx b/src/components/Hexagon.tsx
old mode 100644
new mode 100755
index f0cf640..a0e758a
--- a/src/components/Hexagon.tsx
+++ b/src/components/Hexagon.tsx
@@ -1,69 +1,69 @@
-import classNames from "classnames";
-import { StyleColorVariants, StyleColorVariantsMap } from "core/_variants";
-import React from "react";
-
-type Props = {
- className?: string | undefined;
- children: string;
- fontSize?: number;
- variant?: StyleColorVariants | undefined;
-};
-
-const hexagonStyles: StyleColorVariantsMap = {
- gray: "fill-gray-500",
- pink: "fill-pink-500",
- blue: "fill-blue-500",
- purple: "fill-purple-500",
- red: "fill-red-500",
- yellow: "fill-yellow-600",
- emerald: "fill-emerald-500",
- sky: "fill-sky-500",
- "dark-coral": "fill-dark-coral-500"
-};
-
-/**
- * Hexagon sign
- * @param {string} children Characters to exclude from svg figure
- * @param {string|undefined} className Classes used to customize svg element
- * @param {number} fontSize Font size for excluding characters
- * @return {JSX.Element}
- */
-export default function Hexagon({
- className,
- children,
- fontSize = 24,
- variant = "blue",
-}: Props): JSX.Element {
- const classes = hexagonStyles[variant];
- return (
-
- {fontSize && (
-
-
-
-
- {children}
-
-
-
- )}
-
-
- );
-}
+import classNames from "classnames";
+import { StyleColorVariants, StyleColorVariantsMap } from "core/_variants";
+import React from "react";
+
+type Props = {
+ className?: string | undefined;
+ children: string;
+ fontSize?: number;
+ variant?: StyleColorVariants | undefined;
+};
+
+const hexagonStyles: StyleColorVariantsMap = {
+ gray: "fill-gray-500",
+ pink: "fill-pink-500",
+ blue: "fill-blue-500",
+ purple: "fill-purple-500",
+ red: "fill-red-500",
+ yellow: "fill-yellow-600",
+ emerald: "fill-emerald-500",
+ sky: "fill-sky-500",
+ "dark-coral": "fill-dark-coral-500"
+};
+
+/**
+ * Hexagon sign
+ * @param {string} children Characters to exclude from svg figure
+ * @param {string|undefined} className Classes used to customize svg element
+ * @param {number} fontSize Font size for excluding characters
+ * @return {JSX.Element}
+ */
+export default function Hexagon({
+ className,
+ children,
+ fontSize = 24,
+ variant = "blue",
+}: Props): JSX.Element {
+ const classes = hexagonStyles[variant];
+ return (
+
+ {fontSize && (
+
+
+
+
+ {children}
+
+
+
+ )}
+
+
+ );
+}
diff --git a/src/components/HexagonOutlined.tsx b/src/components/HexagonOutlined.tsx
old mode 100644
new mode 100755
index 9aa7f35..d64a7ba
--- a/src/components/HexagonOutlined.tsx
+++ b/src/components/HexagonOutlined.tsx
@@ -1,65 +1,65 @@
-import classNames from "classnames";
-import { StyleColorVariants, StyleColorVariantsMap } from "core/_variants";
-import React from "react";
-
-type Props = {
- className?: string | undefined;
- children: string;
- fontSize?: number;
- variant?: StyleColorVariants | undefined;
-};
-
-const hexagonStyles: StyleColorVariantsMap = {
- gray: "stroke-gray-500",
- pink: "stroke-pink-500",
- blue: "stroke-blue-500",
- purple: "stroke-purple-500",
- red: "stroke-red-500",
- yellow: "stroke-yellow-600",
- emerald: "stroke-emerald-500",
- sky: "stroke-sky-500",
- "dark-coral": "stroke-dark-coral-500",
-};
-
-/**
- * Hexagon sign
- * @param {string} children Characters to exclude from svg figure
- * @param {string|undefined} className Classes used to customize svg element
- * @param {number} fontSize Font size for excluding characters
- * @return {JSX.Element}
- */
-export default function HexagonOutlined({
- className,
- children,
- fontSize = 24,
- variant,
-}: Props): JSX.Element {
- const classes = variant ? hexagonStyles[variant] : "stroke-white";
- return (
-
-
-
- {children}
-
-
- );
-}
+import classNames from "classnames";
+import { StyleColorVariants, StyleColorVariantsMap } from "core/_variants";
+import React from "react";
+
+type Props = {
+ className?: string | undefined;
+ children: string;
+ fontSize?: number;
+ variant?: StyleColorVariants | undefined;
+};
+
+const hexagonStyles: StyleColorVariantsMap = {
+ gray: "stroke-gray-500",
+ pink: "stroke-pink-500",
+ blue: "stroke-blue-500",
+ purple: "stroke-purple-500",
+ red: "stroke-red-500",
+ yellow: "stroke-yellow-600",
+ emerald: "stroke-emerald-500",
+ sky: "stroke-sky-500",
+ "dark-coral": "stroke-dark-coral-500",
+};
+
+/**
+ * Hexagon sign
+ * @param {string} children Characters to exclude from svg figure
+ * @param {string|undefined} className Classes used to customize svg element
+ * @param {number} fontSize Font size for excluding characters
+ * @return {JSX.Element}
+ */
+export default function HexagonOutlined({
+ className,
+ children,
+ fontSize = 24,
+ variant,
+}: Props): JSX.Element {
+ const classes = variant ? hexagonStyles[variant] : "stroke-white";
+ return (
+
+
+
+ {children}
+
+
+ );
+}
diff --git a/src/components/Icons.stories.tsx b/src/components/Icons.stories.tsx
new file mode 100755
index 0000000..e516cfd
--- /dev/null
+++ b/src/components/Icons.stories.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+
+import {
+ SVGBookmark
+} from "./icons";
+
+
+type Props = {
+ item: React.FunctionComponent>
+}& React.SVGProps;
+
+const Icon = ({item, ...props}: Props) => {
+ const Item = item;
+ return
+}
+
+export default {
+ // Title inside navigation bar
+ title: 'Icons',
+ // Component to test
+ component: Icon,
+ // Specify subcomponents to be able to work with
+ // nested structure,
+} as ComponentMeta;
+
+
+const Template: ComponentStory = (args) => (
+
+);
+
+export const Bookmark = Template.bind({});
+Bookmark.args = {
+ item: SVGBookmark,
+ className: "fill-blue-500"
+};
\ No newline at end of file
diff --git a/src/components/Loader/Loader.css b/src/components/Loader/Loader.css
new file mode 100644
index 0000000..2699c78
--- /dev/null
+++ b/src/components/Loader/Loader.css
@@ -0,0 +1,24 @@
+.loader {
+ position: relative;
+ background: #096DD9;
+ transform: rotateX(65deg) rotate(45deg);
+ color: #003A8C;
+ animation: layers1 1s linear infinite alternate;
+ }
+ .loader:after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: rgba(24, 144, 255, 0.7);
+ animation: layerTr 1s linear infinite alternate;
+ }
+
+ @keyframes layers1 {
+ 0% { box-shadow: 0px 0px 0 0px }
+ 90% , 100% { box-shadow: 8px 8px 0 -4px }
+ }
+ @keyframes layerTr {
+ 0% { transform: translate(0, 0) scale(1) }
+ 100% { transform: translate(-10px, -10px) scale(1) }
+ }
+
\ No newline at end of file
diff --git a/src/components/Loader/Loader.tsx b/src/components/Loader/Loader.tsx
new file mode 100644
index 0000000..8e52204
--- /dev/null
+++ b/src/components/Loader/Loader.tsx
@@ -0,0 +1,10 @@
+import classNames from "classnames"
+import "./Loader.css"
+
+type Props = {
+ className: string,
+}
+
+export const Loader = ({className}:Props)=>{
+ return (
)
+}
\ No newline at end of file
diff --git a/src/components/LocalizationButton.tsx b/src/components/LocalizationButton.tsx
new file mode 100644
index 0000000..27413dc
--- /dev/null
+++ b/src/components/LocalizationButton.tsx
@@ -0,0 +1,89 @@
+import React, { Fragment, useState } from "react";
+import { Trans, useTranslation } from "react-i18next";
+
+import { Button } from "./Button/Button";
+import { Menu, Transition } from "@headlessui/react";
+import classNames from "classnames";
+
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faLanguage } from "@fortawesome/free-solid-svg-icons";
+
+type Props = React.ComponentPropsWithoutRef<"div">;
+
+const LocalizationButton = (props: Props) => {
+ const { t, i18n } = useTranslation();
+ const changeLanguage = (lng: string) => {
+ i18n.changeLanguage(lng);
+ setLanguage(lng);
+ };
+ const [language, setLanguage] = useState("en");
+ return (
+
+
+
+
+
+
+
+ {/* {language} */}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default LocalizationButton;
diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx
new file mode 100755
index 0000000..c7c7dd1
--- /dev/null
+++ b/src/components/Logo.tsx
@@ -0,0 +1,60 @@
+import React from "react";
+import classNames from "classnames";
+export type Props = {
+ className?: string;
+ fillColors?: "blue" | "gray";
+};
+
+const Logo = ({ className, fillColors = "blue" }: Props) => {
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+export default Logo;
diff --git a/src/components/LogoScipaper.tsx b/src/components/LogoScipaper.tsx
new file mode 100755
index 0000000..8b0079f
--- /dev/null
+++ b/src/components/LogoScipaper.tsx
@@ -0,0 +1,39 @@
+import React from "react";
+import classNames from "classnames";
+export type Props = {
+ className?: string;
+};
+
+const LogoScipaper = ({ className }: Props) => {
+ return (
+
+ );
+};
+
+export default LogoScipaper;
diff --git a/src/components/Logotype.tsx b/src/components/Logotype.tsx
deleted file mode 100644
index a699268..0000000
--- a/src/components/Logotype.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react';
-import {ReactComponent as SVGLogotype} from 'assets/svg/logotype.svg';
-import { Link } from 'react-router-dom';
-
-type Props = {
- name?: string;
-}
-
-/**
- * Horizontal variant of logotype component
- * @param {string} name Name of service to attach to logotype
- * @return {React.ReactNode}
- */
-export default function Logotype({name}: Props): JSX.Element {
- return (
-
-
-
-
-
-
-
- {name ?? ''} {process.env.REACT_APP_CMS_APP_NAME?.toLowerCase()}
-
-
- );
-}
diff --git a/src/components/MainPage/sections/FeaturedArticlesCards.tsx b/src/components/MainPage/sections/FeaturedArticlesCards.tsx
new file mode 100755
index 0000000..ad48905
--- /dev/null
+++ b/src/components/MainPage/sections/FeaturedArticlesCards.tsx
@@ -0,0 +1,233 @@
+import { useRef } from "react";
+
+/* -------------------------------------------------------------------------- */
+/* Skeleton */
+/* -------------------------------------------------------------------------- */
+import "react-loading-skeleton/dist/skeleton.css";
+
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import Typography from "../../typography/Typography";
+import SkeletonCard from "../../SkeletonCard";
+import AspectRatio from "../../AspectRatio";
+import Card from "../../Card";
+import Link from "../../typography/Link";
+
+/* -------------------------------------------------------------------------- */
+/* Icons */
+/* -------------------------------------------------------------------------- */
+import { ReactComponent as SVGArrowRight } from "assets/svg/arrow-right.svg";
+import { ReactComponent as SVGCaretRight } from "assets/svg/caret-right.svg";
+import { ReactComponent as SVGArrowLeft } from "assets/svg/arrow-left.svg";
+
+/* -------------------------------------------------------------------------- */
+/* Swiper */
+/* -------------------------------------------------------------------------- */
+import { Swiper, SwiperSlide } from "swiper/react";
+import SwiperCore, { Navigation } from "swiper";
+import { Pagination } from "swiper";
+import "swiper/css/pagination";
+import "swiper/css/navigation";
+// import "./styles.css";
+import "swiper/css";
+import { useTranslation } from "react-i18next";
+/* -------------------------------------------------------------------------- */
+/* Article mock data */
+/* -------------------------------------------------------------------------- */
+
+const articles = [
+ {
+ CoverImg:
+ "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ Body: "1 ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.sit amet consectetur adipisicing elit.Consequuntur ma",
+ Link: "http://pinterest.com",
+ },
+ {
+ CoverImg:
+ "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ Body: "2 ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.",
+ Link: "http://pinterest.com",
+ },
+ {
+ CoverImg:
+ "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ Body: "2 ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.",
+ Link: "http://pinterest.com",
+ },
+ {
+ CoverImg:
+ "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ Body: "2 ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.",
+ Link: "http://pinterest.com",
+ },
+ {
+ CoverImg:
+ "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ Body: "2 ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.",
+ Link: "http://pinterest.com",
+ },
+ {
+ CoverImg:
+ "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ Body: "2 ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.",
+ Link: "http://pinterest.com",
+ },
+ {
+ CoverImg:
+ "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ Body: "2 ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.",
+ Link: "http://pinterest.com",
+ },
+];
+/* -------------------------------------------------------------------------- */
+/* How many Skeleton cards should be added to the design */
+/* -------------------------------------------------------------------------- */
+let twoCards: boolean = false;
+let threeCards: boolean = false;
+console.log(`Number of cards ${articles.length}`);
+if (articles.length == 2) {
+ twoCards = true;
+} else if (articles.length == 3) {
+ threeCards = true;
+}
+
+SwiperCore.use([Navigation]);
+
+const FeaturedArticlesCards = () => {
+ const navigationPrevRef = useRef(null);
+ const navigationNextRef = useRef(null);
+ const paginationRef = useRef(null);
+ const [t, i18next] = useTranslation();
+
+ return (
+
+
+
+
4 ? true : false}
+ pagination={{ el: ".pagination", clickable: true }}
+ navigation={{
+ prevEl: ".prev",
+ nextEl: ".next",
+ }}
+ breakpoints={{
+ 768: {
+ slidesPerView: 2,
+ slidesPerGroup: 2,
+ },
+ 1024: {
+ slidesPerView: 2,
+ slidesPerGroup: 2,
+ },
+ 1280: {
+ slidesPerView: 4,
+ slidesPerGroup: 4,
+ },
+ 1580: {
+ slidesPerView: 4,
+ slidesPerGroup: 4,
+ },
+ }}
+ spaceBetween={25}
+ loopFillGroupWithBlank={true}
+ modules={[Pagination, Navigation]}
+ >
+ {articles.map((Articale) => (
+
+
+
+
+
+
+
+
+
+
+ {Articale.Body}
+
+
+
+
+
+ {t("mainPage.more")}
+
+
+
+
+
+ ))}
+
+ {twoCards && [
+
+
+
+
+
+
+
+
+
+
+ ,
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ ]}
+
+ {threeCards && [
+
+
+
+
+
+
+
+
+
+
+ ,
+ ]}
+
+
+
+
+ );
+};
+
+export default FeaturedArticlesCards;
diff --git a/src/components/MainPage/sections/FeaturedArticlesCategories.tsx b/src/components/MainPage/sections/FeaturedArticlesCategories.tsx
new file mode 100755
index 0000000..30e0758
--- /dev/null
+++ b/src/components/MainPage/sections/FeaturedArticlesCategories.tsx
@@ -0,0 +1,63 @@
+import React, { useMemo } from "react";
+import Typography from "components/typography/Typography";
+import CategoryCard from "components/Cards/CategoryCard";
+import {
+ SVGMedicine,
+ SVGAgricultural,
+ SVGHumanitarian,
+ SVGSocials,
+ SVGTechnicsAndTechology,
+ SVGFundamental,
+} from "components/icons";
+import { useTranslation} from "react-i18next";
+
+const categories = [
+ { id: 1, title: "mainPage.featuredArticles.categories.Medical", count: 5617813, icon: },
+ {
+ id: 2,
+ title: "mainPage.featuredArticles.categories.TechnicsAndTechlonogies",
+ count: 5617813,
+ icon: ,
+ },
+ { id: 3, title: "mainPage.featuredArticles.categories.Fundamental", count: 5617813, icon: },
+ { id: 4, title: "mainPage.featuredArticles.categories.Humanitarian", count: 5617813, icon: },
+ { id: 5, title: "mainPage.featuredArticles.categories.Agricultural", count: 5617813, icon: },
+ { id: 6, title: "mainPage.featuredArticles.categories.Social", count: 5617813, icon: },
+];
+
+export function FeaturedArticlesCategories() {
+ const [t, i18next] = useTranslation();
+
+ const categoryCards = useMemo(
+ () =>
+ categories.map((category) => (
+
+ )),
+ categories
+ );
+
+ return (
+
+
+
+ {t("mainPage.featuredArticles.title")}
+
+
+ {t("mainPage.featuredArticles.descriptionPart1")}
+ {t("mainPage.featuredArticles.descriptionPart2")}
+
+
+
+ {categoryCards}
+
+
+ );
+}
diff --git a/src/components/MainPage/sections/FeaturedAuthorsCards.tsx b/src/components/MainPage/sections/FeaturedAuthorsCards.tsx
new file mode 100755
index 0000000..87fff9c
--- /dev/null
+++ b/src/components/MainPage/sections/FeaturedAuthorsCards.tsx
@@ -0,0 +1,196 @@
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import Heading from "../../typography/Heading";
+import SkeletonCard from "../../SkeletonCard";
+import { Button } from "../../Button/Button";
+import Avatar from "../../Avatar";
+import Card from "../../Card";
+
+/* -------------------------------------------------------------------------- */
+/* Swiper */
+/* -------------------------------------------------------------------------- */
+import { Swiper, SwiperSlide } from "swiper/react";
+import SwiperCore, { Navigation } from "swiper";
+import "swiper/css/pagination";
+import "swiper/css/navigation";
+// import "./styles.css";
+import "swiper/css";
+
+import { ReactComponent as SVGCaretRight } from "assets/svg/caret-right.svg";
+import Link from "../../typography/Link";
+import { useTranslation } from "react-i18next";
+/* -------------------------------------------------------------------------- */
+/* Variables */
+/* -------------------------------------------------------------------------- */
+SwiperCore.use([Navigation]);
+
+/* -------------------------------------------------------------------------- */
+/* Authors data mock */
+/* -------------------------------------------------------------------------- */
+const authors = [
+ {
+ Title: "JSON Three",
+ Body: "Lorem ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.am elit.Consequuntur maxime, adipisicing elit.",
+ Link: "http://pinterest.com",
+ },
+ {
+ Title: "JSON Two",
+ Body: "Lorem ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.am elit.Consequuntur maxime, libero est unde sapiente repellendus quam",
+ Link: "http://pinterest.com",
+ img: "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ },
+ {
+ Title: "JSON Two",
+ Body: "Lorem ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.am elit.Consequuntur maxime, libero est unde sapiente repellendus quam",
+ Link: "http://pinterest.com",
+ img: "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ },
+ {
+ Title: "JSON Two",
+ Body: "Lorem ipsum dolor sit amet consectetur adipisicing elit.Consequuntur maxime, adipisicing elit.am elit.Consequuntur maxime, libero est unde sapiente repellendus quam",
+ Link: "http://pinterest.com",
+ img: "https://i.kinja-img.com/gawker-media/image/upload/t_original/ijsi5fzb1nbkbhxa2gc1.png",
+ },
+];
+
+/* -------------------------------------------------------------------------- */
+/* Number of Cards */
+/* -------------------------------------------------------------------------- */
+let twoCards: boolean = false;
+let threeCards: boolean = false;
+console.log(`Number of cards ${authors.length}`);
+if (authors.length == 2) {
+ twoCards = true;
+} else if (authors.length == 3) {
+ threeCards = true;
+}
+
+/**
+ * Featured authors component to display ...
+ */
+export default function FeaturedAuthorsCards(): JSX.Element {
+ const [t, i18next] = useTranslation();
+
+ return (
+
+ {/* The Title of Featured Authors section */}
+
+ {t("mainPage.featuredAuthors")}
+
+
+ {/* Featured Authors section */}
+
+
4 ? true : false}
+ loopFillGroupWithBlank={false}
+ breakpoints={{
+ 768: {
+ slidesPerView: 2,
+ slidesPerGroup: 2,
+ },
+ 1024: {
+ slidesPerView: 2,
+ slidesPerGroup: 2,
+ },
+ 1280: {
+ slidesPerView: 4,
+ slidesPerGroup: 4,
+ },
+ 1580: {
+ slidesPerView: 4,
+ slidesPerGroup: 4,
+ },
+ }}
+ >
+ {authors.map((card) => (
+
+
+
+
+
+ {card.img ? (
+
+ ) : (
+
+ {card.Title[0]}
+
+ )}
+
+ {card.Title}
+
+ {card.Body}
+
+
+
+
+ {t("mainPage.more")}
+
+
+
+
+
+ ))}
+
+ {twoCards && [
+
+
+
+
+
+
+
+
+
+
+
+ ,
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ ]}
+
+ {threeCards && [
+
+
+
+
+
+
+
+
+
+
+
+ ,
+ ]}
+
+
+
+
+ {t("mainPage.showAll")}
+
+
+ );
+}
diff --git a/src/components/MainPage/sections/MainSection.tsx b/src/components/MainPage/sections/MainSection.tsx
new file mode 100755
index 0000000..bd156cc
--- /dev/null
+++ b/src/components/MainPage/sections/MainSection.tsx
@@ -0,0 +1,40 @@
+/* -------------------------------------------------------------------------- */
+/* Imports */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import { t as nextT } from "i18next";
+import { useTranslation } from "react-i18next";
+import { SearchBar } from "../../search/SearchBar";
+import { formatNumber } from "core/helpers";
+
+export default function MainSection() {
+ const { t, i18n } = useTranslation();
+ const amountArticles = 4202020;
+
+ return (
+
+
+
+ {t("mainPage.title")}
+
+
+
{t("mainPage.search")}
+
+ {formatNumber(amountArticles)}
+
+
+ {nextT("mainPage.article_many", {
+ count: amountArticles,
+ }).toString()}
+
+
+
+
+
+ {t("mainPage.advancedSearch")}
+
+
+
+
+ );
+}
diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx
new file mode 100644
index 0000000..f8e05b7
--- /dev/null
+++ b/src/components/Markdown.tsx
@@ -0,0 +1,171 @@
+/* -------------------------------------------------------------------------- */
+/* MarkDown */
+/* -------------------------------------------------------------------------- */
+import ReactMarkdown from "react-markdown";
+import remarkGfm from "remark-gfm";
+
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import Typography from "./typography/Typography";
+import Heading from "./typography/Heading";
+
+/* -------------------------------------------------------------------------- */
+/* Code */
+/* -------------------------------------------------------------------------- */
+import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
+import { dark } from "react-syntax-highlighter/dist/esm/styles/prism";
+import Link from "./typography/Link";
+import style from "react-syntax-highlighter/dist/esm/styles/hljs/a11y-dark";
+
+export type Props = {
+ markdown: string;
+};
+
+const Markdown = ({ markdown }: Props) => {
+ return (
+ (
+
+ ),
+
+ ol: ({ node, ...props }) => (
+
+ ),
+
+ h1: ({ node, ...props }) => (
+
+ ),
+
+ h2: ({ node, ...props }) => (
+
+ ),
+
+ h3: ({ node, ...props }) => (
+
+ ),
+
+ h4: ({ node, ...props }) => (
+
+ ),
+
+ h5: ({ node, ...props }) => (
+
+ ),
+
+ h6: ({ node, ...props }) => (
+
+ ),
+ p: Typography,
+
+ a: ({ node, ...props }) => (
+
+ ),
+
+ code({ node, inline, className, children, ...props }) {
+ const match = /language-(\w+)/.exec(className || "");
+ return !inline && match ? (
+
+ ) : (
+
+ {children}
+
+ );
+ },
+ }}
+ />
+ );
+};
+
+export default Markdown;
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
new file mode 100755
index 0000000..23a21f8
--- /dev/null
+++ b/src/components/Navbar.tsx
@@ -0,0 +1,147 @@
+import { Menu, Transition } from "@headlessui/react";
+import React, { Fragment } from "react";
+import classNames from "classnames";
+
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import ContextMenuAction from "./drop-down-menu/ContextMenuAction";
+import ContextMenu from "./drop-down-menu/ContextMenu";
+import { Button } from "./Button/Button";
+
+/* -------------------------------------------------------------------------- */
+/* Icons */
+/* -------------------------------------------------------------------------- */
+import { ReactComponent as SVGFavoriteOutlined } from "assets/svg/favorite-outlined.svg";
+import { ReactComponent as SVGHamburger } from "assets/svg/hamburger.svg";
+import { ReactComponent as SVGFolder } from "assets/svg/folder.svg";
+import { ReactComponent as SVGFile } from "assets/svg/file.svg";
+import { ReactComponent as SVGEye } from "assets/svg/eye.svg";
+
+type Props = React.ComponentPropsWithoutRef<"div">;
+
+const Navbar = (props: Props) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Navbar;
diff --git a/src/components/Page.tsx b/src/components/Page.tsx
deleted file mode 100644
index 09517d1..0000000
--- a/src/components/Page.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import classNames from "classnames";
-import React, { useEffect, useLayoutEffect } from "react";
-import Scrollbar from "react-scrollbars-custom";
-import { WithRouteProps } from "routes";
-import { withRouteParams } from "routes/withRoute";
-import { useUIViewModel } from "ui/controller/uiViewModel";
-import { useUIStore } from "ui/data/uiSlice";
-import DrawerController from "ui/views/DrawerController";
-import Header from "./parts/Header";
-import SideNav from "./parts/sidenav/SideNav";
-
-type Props = {
- title?: string;
- withOutlet?: boolean;
- children: React.ReactElement;
-} & WithRouteProps;
-
-const Page = ({ title, withOutlet, children, activePath }: Props) => {
- const uiStore = useUIStore();
- const { isDrawerCollapsed, initDrawer } = useUIViewModel(uiStore);
-
- useLayoutEffect(() => {
- initDrawer();
- }, [initDrawer]);
-
- return (
-
-
-
-
-
-
-
-
- {children}
-
-
-
- );
-};
-
-export default withRouteParams(Page);
diff --git a/src/components/Radio.tsx b/src/components/Radio.tsx
new file mode 100755
index 0000000..4b8bf0e
--- /dev/null
+++ b/src/components/Radio.tsx
@@ -0,0 +1,41 @@
+import classNames from "classnames";
+import React from "react";
+import { ReactComponent as Checkmark } from "assets/svg/check.svg";
+
+export type Props = {
+ /**
+ * An Element next to the Radio
+ */
+ children?: React.ReactNode;
+} & Omit, "type">;
+
+const Radio = ({ children, className, ...props }: Props) => {
+ return (
+
+
+
+ {children}
+
+ );
+};
+export default Radio;
diff --git a/src/components/RoutesRenderer.tsx b/src/components/RoutesRenderer.tsx
deleted file mode 100644
index ee12e1c..0000000
--- a/src/components/RoutesRenderer.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { withAuth } from "auth/hoc/withAuth";
-import React from "react";
-import { RouteObject, useRoutes } from "react-router-dom";
-import { RoutePathDefinition } from "routes";
-import { mapDefinitionToRoute } from "routes/mapDefinitionToRoute";
-
-type Props = {
- routes: RoutePathDefinition[];
-};
-
-
-const RoutesRenderer = ({ routes }: Props) => {
-
- const mappedRoutes = React.useMemo( () => {
- return routes.map((props) => mapDefinitionToRoute(props));
- }, [routes]);
-
- const Renderer = useRoutes(mappedRoutes);
- return Renderer;
-}
-
-export default withAuth(RoutesRenderer);
diff --git a/src/components/SearchResultsSection.tsx b/src/components/SearchResultsSection.tsx
new file mode 100644
index 0000000..f769b5c
--- /dev/null
+++ b/src/components/SearchResultsSection.tsx
@@ -0,0 +1,46 @@
+import React, { useState } from "react";
+import Typography from "./typography/Typography";
+import { useSearchStoreImplementation } from "searchResults/data/searchStoreImplementation";
+import { useSearchViewModel } from "../searchResults/controller/searchResultsViewModel";
+import { ArticleSearchResult } from "./ArticleSearchResult";
+import { Loader } from "./Loader/Loader";
+import { useTranslation } from "react-i18next";
+
+export const SearchResultSection = () => {
+ const store = useSearchStoreImplementation();
+ const { searchResults, isLoading } = useSearchViewModel(store);
+ const [t, i18next] = useTranslation()
+
+ function getResults() {
+ if (searchResults === undefined || searchResults?.data.length === 0) {
+ return (
+
+ {t("searchResults.nothingFound")}.
+
+ );
+ } else {
+ const results = searchResults.data.map((searchItem) => (
+
+ ));
+ return results;
+ }
+ }
+
+ return (
+
+
+
+ {t("searchResults.title")}
+
+
+ {t("searchResults.totalResults")}: {searchResults?.meta.total}
+
+
+
+
{getResults()}
+
+ );
+};
diff --git a/src/components/SearchSection.tsx b/src/components/SearchSection.tsx
new file mode 100644
index 0000000..452b911
--- /dev/null
+++ b/src/components/SearchSection.tsx
@@ -0,0 +1,17 @@
+import React from "react";
+import { SearchBar } from "./search/SearchBar";
+
+export const SearchSection = ()=>{
+ return (
+
+
+
+
+
+ Advanced Search
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/Select.stories.tsx b/src/components/Select.stories.tsx
new file mode 100755
index 0000000..78ff682
--- /dev/null
+++ b/src/components/Select.stories.tsx
@@ -0,0 +1,57 @@
+import React from "react";
+import { ComponentMeta, ComponentStory } from "@storybook/react";
+import { useState } from "react";
+
+import Select from "./Select";
+
+export default {
+ // Title inside navigation bar
+ title: "Select",
+ // Component to test
+ component: Select,
+ // Clarifying the way how to process specific
+ // properties of your component and which values
+ // it can accept.
+ argTypes: {},
+} as ComponentMeta;
+
+/**
+ * This is a way to define a tempalte for your component.
+ *
+ * This template should cover all the states.
+ *
+ * In most cases you should just distruct args attribute
+ * on a returning component.
+ */
+
+const Template: ComponentStory = (args) => {
+ const { options = [{ name: String }] } = args;
+ const [selected, setSelected] = useState(options[0]);
+ return (
+
+
+ options={options}
+ value={selected}
+ onChange={setSelected}
+ displayValueResolver={(options) => options.name}
+ />
+
+ );
+};
+
+/* -------------------------------------------------------------------------- */
+/* States of your component */
+/* -------------------------------------------------------------------------- */
+
+export const Default = Template.bind({});
+
+Default.args = {
+ options: [
+ { name: "Wade Cooper" },
+ { name: "Arlene Mccoy" },
+ { name: "Devon Webb" },
+ { name: "Tom Cook" },
+ { name: "Tanya Fox" },
+ { name: "Hellen Schmidt" },
+ ],
+};
diff --git a/src/components/Select.tsx b/src/components/Select.tsx
new file mode 100755
index 0000000..291f29d
--- /dev/null
+++ b/src/components/Select.tsx
@@ -0,0 +1,151 @@
+/* -------------------------------------------------------------------------- */
+/* Imports */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import { Fragment } from "react";
+import { Listbox, Transition } from "@headlessui/react";
+import classNames from "classnames";
+import "../index.css";
+import { ReactComponent as SelectIcon } from "../assets/svg/caret-down.svg";
+import { Scrollbar } from "react-scrollbars-custom";
+
+/* -------------------------------------------------------------------------- */
+/* Component props */
+/* -------------------------------------------------------------------------- */
+
+type Props = {
+ inGroup?: boolean;
+ options?: T[];
+ disabled?: boolean;
+ className?: string;
+ value: T;
+ displayValueResolver?: (element: T) => any;
+ onChange: (element: T) => void;
+ maxScrollSize?: number;
+ elementScrollSize?: number;
+} & Omit, "value" | "onChange">;
+
+/* -------------------------------------------------------------------------- */
+/* styles */
+/* -------------------------------------------------------------------------- */
+
+const SelectButtonStyle = `
+ relative w-full
+ cursor-default
+ rounded
+ border border-gray-50
+ outline-8
+ bg-white
+ py-2 pl-3 pr-10 text-left
+ hover:border-gray-300
+ focus:outline-1
+ focus-visible:border-gray-500
+ sm:text-sm
+ `;
+
+const SelectOptionsStyle = `
+ absolute z-10 mt-1 w-full max-h-56
+ bg-white shadow-lg
+ rounded py-1
+ overflow-hidden
+ focus:outline-none
+ text-base
+ sm:text-sm
+ `;
+
+const SelectIconStyle = `
+ pointer-events-none
+ absolute inset-y-0 right-0
+ flex items-center pr-2
+ `;
+
+const inputInGroup = [
+ ` border-none
+ hover:none
+ active:none
+ focus:none
+ `,
+];
+/* -------------------------------------------------------------------------- */
+/* Component implementation */
+/* -------------------------------------------------------------------------- */
+function Select({
+ inGroup = false, // We should use this flag to choose how we will style our component
+ className,
+ options = [],
+ value,
+ onChange,
+ displayValueResolver,
+ disabled,
+ maxScrollSize = 140,
+ elementScrollSize = 36,
+ ...props
+}: Props): JSX.Element {
+ return (
+
+
+
+
+ {({ open }) => (
+ <>
+ {`${
+ displayValueResolver ? displayValueResolver(value) : value
+ }`}
+
+
+
+ >
+ )}
+
+
+
+ {/* */}
+ {options.map((option, id) => (
+
+ classNames(
+ active ? "text-gray-900 bg-blue-50" : "font-normal ",
+ "cursor-default select-none relative py-2 pl-3 pr-9",
+ selected ? "text-gray-900 bg-blue-100" : "font-normal "
+ )
+ }
+ value={option}
+ >
+ {`${
+ displayValueResolver
+ ? displayValueResolver(option)
+ : option
+ }`}
+
+ ))}
+ {/* */}
+
+
+
+
+
+ //
+ );
+}
+export default Select;
diff --git a/src/components/ServiceLink.tsx b/src/components/ServiceLink.tsx
deleted file mode 100644
index 9d0af80..0000000
--- a/src/components/ServiceLink.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import classNames from 'classnames';
-import ServiceComponent from './containers/ServiceComponent';
-import { Service } from 'services/domain/serviceEntity';
-
-type Props = {
- service: Service;
- disabled?: boolean | undefined;
-};
-
-/**
- * Service link component
- * @param {Service} service Service object
- * @return {JSX.Element}
- */
-export default function ServiceLink({service, disabled = false}: Props) {
- return (
-
-
- {service.name}
- {disabled ? (
- soon
- ) : (
- {service.href}
- )}
-
-
- );
-}
diff --git a/src/components/SkeletonCard.tsx b/src/components/SkeletonCard.tsx
new file mode 100755
index 0000000..7bacec2
--- /dev/null
+++ b/src/components/SkeletonCard.tsx
@@ -0,0 +1,94 @@
+import Skeleton from "react-loading-skeleton";
+import AspectRatio from "./AspectRatio";
+import classNames from "classnames";
+
+/* -------------------------------------------------------------------------- */
+/* Props */
+/* -------------------------------------------------------------------------- */
+type Props = {
+ /**
+ * Card component accept children
+ */
+ children?: React.ReactNode;
+ /**
+ * Styling the card component
+ */
+ className?: string | undefined;
+};
+
+const SkeletonCard = ({ className, children }: Props) => {
+ return (
+
+ {children}
+
+ );
+};
+
+// Media
+SkeletonCard.Media = function SkeletonCardCardMedia() {
+ return (
+
+
+
+
+
+ );
+};
+
+// Content
+SkeletonCard.Content = function SkeletonCardCardContent({
+ children,
+ className,
+}: Props) {
+ return (
+
+ {children}
+
+ );
+};
+
+// Header
+SkeletonCard.Header = function SkeletonCardCardHeader({
+ children,
+ className,
+}: Props) {
+ return (
+
+ {children}
+
+ );
+};
+
+// Avatar
+SkeletonCard.Avatar = function SkeletonCardCardAvatar() {
+ return ;
+};
+
+// Title
+SkeletonCard.Title = function SkeletonCardCardTitle() {
+ return ;
+};
+
+// Body
+SkeletonCard.Body = function SkeletonCardCardBody({
+ children,
+ className,
+}: Props) {
+ return ;
+};
+
+// Action
+SkeletonCard.Action = function SkeletonCardCardAction({ className }: Props) {
+ return (
+
+
+
+ );
+};
+
+export default SkeletonCard;
diff --git a/src/components/StandalonePage.tsx b/src/components/StandalonePage.tsx
deleted file mode 100644
index c34997a..0000000
--- a/src/components/StandalonePage.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from "react";
-import { WithRouteProps } from "routes";
-
-type Props = {
- children: React.ReactElement;
-};
-
-export default function StandalonePage({ children }: Props) {
- return (
-
-
- {children}
-
-
- );
-}
diff --git a/src/components/TextInput.tsx b/src/components/TextInput.tsx
new file mode 100755
index 0000000..a4c9610
--- /dev/null
+++ b/src/components/TextInput.tsx
@@ -0,0 +1,56 @@
+import classNames from "classnames";
+import { initial, isEmpty, omit } from "lodash";
+import React from "react";
+import "../index.css";
+
+type Props = {
+ inGroup?: boolean,
+ placeholder?: string | undefined;
+ className?: string | undefined;
+} & Omit, "">;
+
+const inputNotInGroup = [
+ `border
+ border-2
+ border-solid
+ border-gray-200
+ hover:border-gray-500
+ active:border-gray-500
+ focus:border-gray-500`
+]
+
+const inputInGroup = [
+ `border-none
+ hover:none
+ active:none
+ focus:none
+ `
+]
+
+export const TextInput = ({
+ inGroup = false, // We should use this flag to choose how we will style our text input component
+ placeholder,
+ className,
+ ...props
+}: Props) => {
+
+ return (
+
+ );
+};
diff --git a/src/components/Tooltip.tsx b/src/components/Tooltip.tsx
new file mode 100755
index 0000000..dca6fff
--- /dev/null
+++ b/src/components/Tooltip.tsx
@@ -0,0 +1,40 @@
+import { ReactNode } from "react";
+
+export interface TooltipProps {
+ /**
+ * Tooltip text - can be string or another component
+ */
+ tooltipMessage: ReactNode;
+ /**
+ * The targeted component
+ */
+ children: ReactNode;
+}
+
+const Tooltip = ({ tooltipMessage, children }: TooltipProps) => {
+ return (
+
+ {/* Tooltip will be shown under the component(children) when the children is onHover */}
+ {children}
+ {/* Creating the tooltip and give it a postion */}
+
+ {tooltipMessage}
+
+
+ );
+};
+
+export default Tooltip;
diff --git a/src/components/breadcrumbs.tsx b/src/components/breadcrumbs.tsx
new file mode 100755
index 0000000..2b48b74
--- /dev/null
+++ b/src/components/breadcrumbs.tsx
@@ -0,0 +1,48 @@
+import React, { Children, ReactElement } from "react";
+
+type Props = {
+ divider: "dotted" | "slash";
+ children: React.ReactNode;
+ className?: string;
+} ;
+
+/* ---------- a random number for a unique key (react requirement) ---------- */
+
+function randomInteger( min: number, max: number ) {
+ let rand = min + Math.random() * (max + 1 - min);
+ return Math.floor(rand);
+ }
+
+/* ----------------------- get the divider as designed ---------------------- */
+
+function GetDivider( symbol: string ) {
+ const dividers = {
+ 'dotted' : ,
+ 'slash' : / ,
+ }
+ return ( symbol === 'dotted') ? dividers.dotted : dividers.slash
+}
+
+export default function Breadcrumbs( { divider ,children, className }: Props ) {
+
+ /* -------------- Convert the children into an array and insert ------------- */
+ /* ------------------the elements with the divider into it ------------------ */
+
+ let childrenToArray = React.Children.toArray(children)
+ let LinkAndDivider: any
+ LinkAndDivider = [];
+
+ for( let i = 0; i < childrenToArray.length; i++ ) {
+
+ let dividerComponent = GetDivider( divider )
+ LinkAndDivider = LinkAndDivider.concat( {childrenToArray[i]}
).concat( dividerComponent )
+ }
+ LinkAndDivider.pop();
+
+ return(
+
+ { LinkAndDivider }
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/containers/Avatar.tsx b/src/components/containers/Avatar.tsx
old mode 100644
new mode 100755
index 5c2bd46..e7a12a9
--- a/src/components/containers/Avatar.tsx
+++ b/src/components/containers/Avatar.tsx
@@ -1,35 +1,35 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React from "react";
-/* -------------------------------------------------------------------------- */
-/* SVG */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGUserIcon } from "assets/svg/user.svg";
-import classNames from "classnames";
-/* -------------------------------------------------------------------------- */
-/* Avatar container */
-/* -------------------------------------------------------------------------- */
-
-type Props = {
- children?: React.ReactNode;
- width?: string;
- className?:string |undefined;
-};
-
-export default function Avatar({ children, width, className }: Props) {
- return (
-
-
- {children ? (
- children
- ) : (
-
- )}
-
-
- );
-}
+/* -------------------------------------------------------------------------- */
+/* Libraries */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+/* -------------------------------------------------------------------------- */
+/* SVG */
+/* -------------------------------------------------------------------------- */
+import { ReactComponent as SVGUserIcon } from "assets/svg/user.svg";
+import classNames from "classnames";
+/* -------------------------------------------------------------------------- */
+/* Avatar container */
+/* -------------------------------------------------------------------------- */
+
+type Props = {
+ children?: React.ReactNode;
+ width?: string;
+ className?:string |undefined;
+};
+
+export default function Avatar({ children, width, className }: Props) {
+ return (
+
+
+ {children ? (
+ children
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/src/components/containers/Card.tsx b/src/components/containers/Card.tsx
old mode 100644
new mode 100755
index 080b87f..dccf3ab
--- a/src/components/containers/Card.tsx
+++ b/src/components/containers/Card.tsx
@@ -1,43 +1,43 @@
-import classNames from "classnames";
-import { StyleColorVariants, StyleColorVariantsMap } from "core/_variants";
-import React from "react";
-
-type Props = {
- children: React.ReactNode;
- className?: string;
- variant?: StyleColorVariants;
-};
-
-const backgrounds: StyleColorVariantsMap = {
- blue: "bg-blue-500",
- emerald: "bg-emerald-500",
- gray: "bg-gray-800",
- pink: "bg-pink-500",
- purple: "bg-purple-500",
- red: "bg-red-500",
- sky: "bg-sky-500",
- yellow: "bg-yellow-500",
- "dark-coral": "bg-dark-coral-500",
-};
-
-/**
- * Card component
- * @param {string|undefined} className Card customization classes
- * @param {React.ReactNode} children Children to paste inside Card component
- * @return {JSX.Element}
- */
-export default function Card({ children, className, variant }: Props) {
- return (
-
- {children}
-
- );
-}
+import classNames from "classnames";
+import { StyleColorVariants, StyleColorVariantsMap } from "core/_variants";
+import React from "react";
+
+type Props = {
+ children: React.ReactNode;
+ className?: string;
+ variant?: StyleColorVariants;
+};
+
+const backgrounds: StyleColorVariantsMap = {
+ blue: "bg-blue-500",
+ emerald: "bg-emerald-500",
+ gray: "bg-gray-800",
+ pink: "bg-pink-500",
+ purple: "bg-purple-500",
+ red: "bg-red-500",
+ sky: "bg-sky-500",
+ yellow: "bg-yellow-500",
+ "dark-coral": "bg-dark-coral-500",
+};
+
+/**
+ * Card component
+ * @param {string|undefined} className Card customization classes
+ * @param {React.ReactNode} children Children to paste inside Card component
+ * @return {JSX.Element}
+ */
+export default function Card({ children, className, variant }: Props) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/containers/CircleMarker.tsx b/src/components/containers/CircleMarker.tsx
old mode 100644
new mode 100755
index 0f8e56b..d3ca38b
--- a/src/components/containers/CircleMarker.tsx
+++ b/src/components/containers/CircleMarker.tsx
@@ -1,39 +1,39 @@
-import classNames from 'classnames';
-import {StyleColorVariants, StyleColorVariantsMap} from 'core/_variants';
-import React from 'react';
-
-type Props = {
- variant: StyleColorVariants;
- children: React.ReactNode;
-};
-
-const markerVariant: StyleColorVariantsMap = {
- gray: 'bg-gray-500 shadow-gray-500',
- blue: 'bg-blue-500 shadow-blue-500',
- pink: 'bg-pink-500 shadow-pink-500',
- red: 'bg-red-500 shadow-red-500',
- purple: 'bg-purple-500 shadow-purple-500',
- yellow: 'bg-yellow-500 shadow-yellow-500',
- emerald: 'bg-emerald-500 shadow-emerald-500',
- sky: 'bg-sky-500 shadow-sky-500',
- "dark-coral": "bg-dark-coral-500 shadow-dark-coral-500",
-};
-
-/**
- * Cirlce shape to wrap [children]
- * @param {React.ReactNode} children Children to paste inside circle shape
- * @param {StyleColorVariants} variant Varant of marker
- * @return {JSX.Element}
- */
-export default function CircleMarker({variant, children}: Props) {
- return (
-
- {children}
-
- );
-}
+import classNames from 'classnames';
+import {StyleColorVariants, StyleColorVariantsMap} from 'core/_variants';
+import React from 'react';
+
+type Props = {
+ variant: StyleColorVariants;
+ children: React.ReactNode;
+};
+
+const markerVariant: StyleColorVariantsMap = {
+ gray: 'bg-gray-500 shadow-gray-500',
+ blue: 'bg-blue-500 shadow-blue-500',
+ pink: 'bg-pink-500 shadow-pink-500',
+ red: 'bg-red-500 shadow-red-500',
+ purple: 'bg-purple-500 shadow-purple-500',
+ yellow: 'bg-yellow-500 shadow-yellow-500',
+ emerald: 'bg-emerald-500 shadow-emerald-500',
+ sky: 'bg-sky-500 shadow-sky-500',
+ "dark-coral": "bg-dark-coral-500 shadow-dark-coral-500",
+};
+
+/**
+ * Cirlce shape to wrap [children]
+ * @param {React.ReactNode} children Children to paste inside circle shape
+ * @param {StyleColorVariants} variant Varant of marker
+ * @return {JSX.Element}
+ */
+export default function CircleMarker({variant, children}: Props) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/containers/MarkedItem.tsx b/src/components/containers/MarkedItem.tsx
old mode 100644
new mode 100755
index fa9e7e0..e31ca51
--- a/src/components/containers/MarkedItem.tsx
+++ b/src/components/containers/MarkedItem.tsx
@@ -1,32 +1,32 @@
-import classNames from 'classnames';
-import React from 'react';
-
-type Props = {
- marker: React.ReactNode;
- children: React.ReactNode;
- className?: string | undefined;
-};
-
-/**
- * Marked children
- * @param {React.ReactNode} marker Marker component
- * @return {React.ReactNode}
- */
-export default function MarkedItem({
- marker,
- children,
- className,
-}: Props): JSX.Element {
- return (
-
-
{marker}
-
{children}
-
- );
-}
+import classNames from 'classnames';
+import React from 'react';
+
+type Props = {
+ marker: React.ReactNode;
+ children: React.ReactNode;
+ className?: string | undefined;
+};
+
+/**
+ * Marked children
+ * @param {React.ReactNode} marker Marker component
+ * @return {React.ReactNode}
+ */
+export default function MarkedItem({
+ marker,
+ children,
+ className,
+}: Props): JSX.Element {
+ return (
+
+
{marker}
+
{children}
+
+ );
+}
diff --git a/src/components/containers/ServiceComponent.tsx b/src/components/containers/ServiceComponent.tsx
old mode 100644
new mode 100755
index 6fcb22b..35ecb63
--- a/src/components/containers/ServiceComponent.tsx
+++ b/src/components/containers/ServiceComponent.tsx
@@ -1,24 +1,24 @@
-import Hexagon from 'components/Hexagon'
-import { StyleColorVariants } from 'core/_variants';
-import React from 'react'
-
-type Props = {
- variant?: StyleColorVariants | undefined;
- children: React.ReactNode;
- clipText: string;
-}
-
-export default function ServiceComponent({variant, children, clipText}: Props) {
- return (
-
-
-
- {clipText}
-
-
-
- {children}
-
-
- )
+import Hexagon from 'components/Hexagon'
+import { StyleColorVariants } from 'core/_variants';
+import React from 'react'
+
+type Props = {
+ variant?: StyleColorVariants | undefined;
+ children: React.ReactNode;
+ clipText: string;
+}
+
+export default function ServiceComponent({variant, children, clipText}: Props) {
+ return (
+
+
+
+ {clipText}
+
+
+
+ {children}
+
+
+ )
}
\ No newline at end of file
diff --git a/src/components/containers/area/Area.tsx b/src/components/containers/area/Area.tsx
old mode 100644
new mode 100755
index 7ebebc1..af95b8d
--- a/src/components/containers/area/Area.tsx
+++ b/src/components/containers/area/Area.tsx
@@ -1,13 +1,13 @@
-import React from 'react'
-
-type Props = {
- children: React.ReactNode;
-}
-
-export default function Area({children}: Props) {
- return (
-
- )
+import React from 'react'
+
+type Props = {
+ children: React.ReactNode;
+}
+
+export default function Area({children}: Props) {
+ return (
+
+ )
}
\ No newline at end of file
diff --git a/src/components/containers/area/AreaCaption.tsx b/src/components/containers/area/AreaCaption.tsx
old mode 100644
new mode 100755
index 92b7091..2b0bd58
--- a/src/components/containers/area/AreaCaption.tsx
+++ b/src/components/containers/area/AreaCaption.tsx
@@ -1,14 +1,14 @@
-import classNames from 'classnames';
-import React from 'react'
-
-type Props = {
- className?: string | undefined;
- children?: string | undefined;
- secondary?: boolean;
-}
-
-export default function AreaCaption({children, className, secondary}: Props) {
- return (
- {children}
- )
+import classNames from 'classnames';
+import React from 'react'
+
+type Props = {
+ className?: string | undefined;
+ children?: string | undefined;
+ secondary?: boolean;
+}
+
+export default function AreaCaption({children, className, secondary}: Props) {
+ return (
+ {children}
+ )
}
\ No newline at end of file
diff --git a/src/components/containers/area/AreaDescription.tsx b/src/components/containers/area/AreaDescription.tsx
old mode 100644
new mode 100755
index 3499752..1948d9c
--- a/src/components/containers/area/AreaDescription.tsx
+++ b/src/components/containers/area/AreaDescription.tsx
@@ -1,13 +1,13 @@
-import React from 'react'
-
-type Props = {
- children?: React.ReactNode;
-}
-
-export default function AreaDescription({children}: Props) {
- return (
-
- {children}
-
- )
+import React from 'react'
+
+type Props = {
+ children?: React.ReactNode;
+}
+
+export default function AreaDescription({children}: Props) {
+ return (
+
+ {children}
+
+ )
}
\ No newline at end of file
diff --git a/src/components/containers/area/AreaValue.tsx b/src/components/containers/area/AreaValue.tsx
old mode 100644
new mode 100755
index 801e3f6..0800718
--- a/src/components/containers/area/AreaValue.tsx
+++ b/src/components/containers/area/AreaValue.tsx
@@ -1,11 +1,11 @@
-import React from 'react'
-
-type Props = {
- children: React.ReactNode;
-}
-
-export default function AreaValue({children}: Props) {
- return (
- {children}
- );
+import React from 'react'
+
+type Props = {
+ children: React.ReactNode;
+}
+
+export default function AreaValue({children}: Props) {
+ return (
+ {children}
+ );
}
\ No newline at end of file
diff --git a/src/components/containers/area/IconedAreaCaption.tsx b/src/components/containers/area/IconedAreaCaption.tsx
old mode 100644
new mode 100755
index 09af9ef..5828bc3
--- a/src/components/containers/area/IconedAreaCaption.tsx
+++ b/src/components/containers/area/IconedAreaCaption.tsx
@@ -1,23 +1,23 @@
-import React from 'react'
-import AreaCaption from './AreaCaption';
-
-type Props = {
- caption: string;
- icon?: React.ReactNode;
-}
-
-export default function IconedAreaCaption({icon, caption}: Props) {
- if(icon) {
- return (
-
-
- {icon}
-
-
{caption}
-
- )
- }
- return (
- {caption}
- )
+import React from 'react'
+import AreaCaption from './AreaCaption';
+
+type Props = {
+ caption: string;
+ icon?: React.ReactNode;
+}
+
+export default function IconedAreaCaption({icon, caption}: Props) {
+ if(icon) {
+ return (
+
+
+ {icon}
+
+
{caption}
+
+ )
+ }
+ return (
+ {caption}
+ )
}
\ No newline at end of file
diff --git a/src/components/containers/contextmenu/ContextMenu.tsx b/src/components/containers/contextmenu/ContextMenu.tsx
old mode 100644
new mode 100755
index 6b54e80..1998f17
--- a/src/components/containers/contextmenu/ContextMenu.tsx
+++ b/src/components/containers/contextmenu/ContextMenu.tsx
@@ -1,57 +1,57 @@
-import React, { Fragment } from "react";
-import { Menu, Transition } from "@headlessui/react";
-import { PropsPartion } from "./ContextMenuItem";
-import classNames from "classnames";
-
-type ChildType = React.ReactElement;
-type ChildrenType = ChildType[] | ChildType;
-
-type MenuProps = {
- className?: string | undefined;
- button: React.ReactNode;
- children: ChildrenType;
-};
-
-/**
- * Context menu component
- * @param {React.ReactNode} children
- * @return {JSX.Element}
- */
-export default function ContextMenu({
- button,
- children,
- className,
-}: MenuProps) {
- return (
-
- {({ open }) => (
- <>
-
-
{button}
-
-
-
- {children}
-
-
- >
- )}
-
- );
-}
+import React, { Fragment } from "react";
+import { Menu, Transition } from "@headlessui/react";
+import { PropsPartion } from "./ContextMenuItem";
+import classNames from "classnames";
+
+type ChildType = React.ReactElement;
+type ChildrenType = ChildType[] | ChildType;
+
+type MenuProps = {
+ className?: string | undefined;
+ button: React.ReactNode;
+ children: ChildrenType;
+};
+
+/**
+ * Context menu component
+ * @param {React.ReactNode} children
+ * @return {JSX.Element}
+ */
+export default function ContextMenu({
+ button,
+ children,
+ className,
+}: MenuProps) {
+ return (
+
+ {({ open }) => (
+ <>
+
+
{button}
+
+
+
+ {children}
+
+
+ >
+ )}
+
+ );
+}
diff --git a/src/components/containers/contextmenu/ContextMenuAction.tsx b/src/components/containers/contextmenu/ContextMenuAction.tsx
old mode 100644
new mode 100755
index 68afe5d..d0f2e34
--- a/src/components/containers/contextmenu/ContextMenuAction.tsx
+++ b/src/components/containers/contextmenu/ContextMenuAction.tsx
@@ -1,33 +1,33 @@
-import classNames from "classnames";
-import React from "react";
-
-type Props = {
- action: Function;
- caption: string;
- disabled?: boolean;
- icon?: React.ReactNode;
- className?: string | undefined;
-};
-
-export default function ContextMenuAction({
- action,
- caption,
- disabled,
- icon,
- className,
-}: Props) {
- return (
- action(e)}
- disabled={disabled}
- className={classNames([
- "flex space-x-3 items-center text-left",
- { "opacity-50": disabled, "cursor-pointer": !disabled },
- className,
- ])}
- >
- {icon && {icon}
}
- {caption}
-
- );
-}
+import classNames from "classnames";
+import React from "react";
+
+type Props = {
+ action: Function;
+ caption: string;
+ disabled?: boolean;
+ icon?: React.ReactNode;
+ className?: string | undefined;
+};
+
+export default function ContextMenuAction({
+ action,
+ caption,
+ disabled,
+ icon,
+ className,
+}: Props) {
+ return (
+ action(e)}
+ disabled={disabled}
+ className={classNames([
+ "flex space-x-3 items-center text-left",
+ { "opacity-50": disabled, "cursor-pointer": !disabled },
+ className,
+ ])}
+ >
+ {icon && {icon}
}
+ {caption}
+
+ );
+}
diff --git a/src/components/containers/contextmenu/ContextMenuItem.tsx b/src/components/containers/contextmenu/ContextMenuItem.tsx
old mode 100644
new mode 100755
index eb444b6..f06e8b2
--- a/src/components/containers/contextmenu/ContextMenuItem.tsx
+++ b/src/components/containers/contextmenu/ContextMenuItem.tsx
@@ -1,27 +1,27 @@
-import {Menu} from '@headlessui/react';
-import React from 'react';
-
-export type PropsPartion = {
- disabled?: boolean | undefined;
- active?: boolean | undefined;
-};
-
-type Props = {
- children: React.ReactElement<
- any & PropsPartion
- >;
-} & PropsPartion;
-
-/**
- * Context menu item component
- * @return {JSX.Element}
- */
-export default function ContextMenuItem({children, ...props}: Props) {
- return (
-
- {(params) => {
- return React.cloneElement(children, params);
- }}
-
- );
-}
+import {Menu} from '@headlessui/react';
+import React from 'react';
+
+export type PropsPartion = {
+ disabled?: boolean | undefined;
+ active?: boolean | undefined;
+};
+
+type Props = {
+ children: React.ReactElement<
+ any & PropsPartion
+ >;
+} & PropsPartion;
+
+/**
+ * Context menu item component
+ * @return {JSX.Element}
+ */
+export default function ContextMenuItem({children, ...props}: Props) {
+ return (
+
+ {(params) => {
+ return React.cloneElement(children, params);
+ }}
+
+ );
+}
diff --git a/src/components/containers/modal/BottomBarAcceptCookies.tsx b/src/components/containers/modal/BottomBarAcceptCookies.tsx
new file mode 100755
index 0000000..afef6a6
--- /dev/null
+++ b/src/components/containers/modal/BottomBarAcceptCookies.tsx
@@ -0,0 +1,17 @@
+import { AcceptCookies } from "components/AcceptCookies";
+import { BottomSheetModal } from "components/containers/modal/BottomSheetModal";
+import { useState } from "react";
+
+export function BottomBarAcceptCookies() {
+ let [isShowing, setIsShowing] = useState(true);
+
+ function closeModal() {
+ setIsShowing(false);
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/src/components/containers/modal/BottomSheetModal.tsx b/src/components/containers/modal/BottomSheetModal.tsx
new file mode 100755
index 0000000..51e7195
--- /dev/null
+++ b/src/components/containers/modal/BottomSheetModal.tsx
@@ -0,0 +1,39 @@
+import React, { Fragment, useState } from "react";
+import { Dialog, Transition } from "@headlessui/react";
+import { AcceptCookies } from "components/AcceptCookies";
+
+type ButtonSheetBarProps = {
+ isShowing: boolean;
+ onClose: () => void;
+ children?: React.ReactNode;
+} & Omit, "">;
+
+export function BottomSheetModal({
+ isShowing,
+ onClose,
+ children,
+}: ButtonSheetBarProps) {
+ return (
+
+ {}}
+ >
+ {children}
+
+
+ );
+}
diff --git a/src/components/containers/modal/Modal.tsx b/src/components/containers/modal/Modal.tsx
old mode 100644
new mode 100755
index 1eccc0b..55fb7d1
--- a/src/components/containers/modal/Modal.tsx
+++ b/src/components/containers/modal/Modal.tsx
@@ -1,62 +1,60 @@
-import { Dialog, Transition } from "@headlessui/react";
-import React, { Fragment } from "react";
-
-import _ModalTitle from "./_ModalTitle";
-import _ModalFooter from "./_ModalFooter";
-import classNames from "classnames";
-import _ModalCloseButton from "./_ModalCloseButton";
-
-type Props = {
- children: React.ReactNode;
- isOpen: boolean;
- onClose: () => void;
- className?: string;
-};
-
-function Modal ({
- className,
- isOpen,
- onClose,
- children,
-}: Props) {
- return (
-
-
-
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
- );
-}
-
-Modal.Header = _ModalTitle;
-Modal.Footer = _ModalFooter;
-Modal.CloseButton = _ModalCloseButton;
-
-export default Modal;
+import { Dialog, Transition } from "@headlessui/react";
+import React, { Fragment } from "react";
+
+import _ModalTitle from "./_ModalTitle";
+import _ModalFooter from "./_ModalFooter";
+import classNames from "classnames";
+
+type Props = {
+ children: React.ReactNode;
+ isOpen: boolean;
+ onClose: () => void;
+ className?: string;
+};
+
+function Modal ({
+ className,
+ isOpen,
+ onClose,
+ children,
+}: Props) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+}
+
+Modal.Header = _ModalTitle;
+Modal.Footer = _ModalFooter;
+
+export default Modal;
diff --git a/src/components/containers/modal/_ModalCloseButton.tsx b/src/components/containers/modal/_ModalCloseButton.tsx
deleted file mode 100644
index 3e1e399..0000000
--- a/src/components/containers/modal/_ModalCloseButton.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from "react";
-import Button from "components/controls/Button";
-import { ReactComponent as SVGTimesIcon } from "assets/svg/times.svg";
-
-type Props = {
- onClose: VoidFunction,
-};
-
-export default function _ModalCloseButton({onClose}: Props) {
- return (
-
-
-
-
-
- );
-}
diff --git a/src/components/containers/modal/_ModalFooter.tsx b/src/components/containers/modal/_ModalFooter.tsx
old mode 100644
new mode 100755
index 7da09df..c93dac0
--- a/src/components/containers/modal/_ModalFooter.tsx
+++ b/src/components/containers/modal/_ModalFooter.tsx
@@ -1,15 +1,15 @@
-import classNames from 'classnames';
-import React from 'react'
-
-type Props = {
- children: React.ReactNode;
- className?: string | undefined;
-}
-
-export default function _ModalFooter({children, className}: Props) {
- return (
-
- {children}
-
- );
+import classNames from 'classnames';
+import React from 'react'
+
+type Props = {
+ children: React.ReactNode;
+ className?: string | undefined;
+}
+
+export default function _ModalFooter({children, className}: Props) {
+ return (
+
+ {children}
+
+ );
}
\ No newline at end of file
diff --git a/src/components/containers/modal/_ModalTitle.tsx b/src/components/containers/modal/_ModalTitle.tsx
old mode 100644
new mode 100755
index 694d5e1..782bb45
--- a/src/components/containers/modal/_ModalTitle.tsx
+++ b/src/components/containers/modal/_ModalTitle.tsx
@@ -1,21 +1,21 @@
-import { Dialog } from "@headlessui/react";
-import React from "react";
-
-type Props = {
- children: React.ReactNode;
-};
-
-const _ModalTitle: React.FC = ({ children }: Props) => {
- if (typeof children === "string") {
- return (
-
- {children}
-
- );
- }
- return ;
-}
-
-_ModalTitle.displayName = "ModalTitle";
-
+import { Dialog } from "@headlessui/react";
+import React from "react";
+
+type Props = {
+ children: React.ReactNode;
+};
+
+const _ModalTitle: React.FC = ({ children }: Props) => {
+ if (typeof children === "string") {
+ return (
+
+ {children}
+
+ );
+ }
+ return ;
+}
+
+_ModalTitle.displayName = "ModalTitle";
+
export default _ModalTitle;
\ No newline at end of file
diff --git a/src/components/controls/Button.tsx b/src/components/controls/Button.tsx.deprecated
old mode 100644
new mode 100755
similarity index 96%
rename from src/components/controls/Button.tsx
rename to src/components/controls/Button.tsx.deprecated
index 2107bac..a599fa8
--- a/src/components/controls/Button.tsx
+++ b/src/components/controls/Button.tsx.deprecated
@@ -1,239 +1,239 @@
-import classNames from 'classnames';
-import {
- StyleColorVariants,
- StyleColorVariantsMap,
- StyleType,
-} from 'core/_variants';
-import React from 'react';
-
-type Props = {
- href?: string;
- variant?: StyleColorVariants;
- type?: StyleType;
- iconed?: boolean | "only";
- glowing?: boolean;
- disabled?: boolean;
- onClick?: (event: React.MouseEvent) => void;
- children?: React.ReactNode;
- className?: string | undefined;
- rel?: string | undefined;
- htmlType?: 'button' | 'submit' | 'reset' | undefined;
-};
-
-const buttonBgVariants: StyleColorVariantsMap = {
- gray: 'bg-gray-500',
- blue: 'bg-blue-500',
- emerald: 'bg-emerald-600',
- pink: 'bg-pink-500',
- purple: 'bg-purple-500',
- red: 'bg-red-500',
- sky: 'bg-sky-500',
- yellow: 'bg-yellow-500',
- "dark-coral": "bg-dark-coral-500",
-};
-
-const buttonBgDisabledVariants: StyleColorVariantsMap = {
- gray: 'bg-gray-200',
- blue: 'bg-blue-100/10',
- emerald: 'bg-emerald-200/50',
- pink: 'bg-pink-200/50',
- purple: 'bg-purple-200/50',
- red: 'bg-red-200/50',
- sky: 'bg-sky-200/50',
- yellow: 'bg-yellow-200/50',
- "dark-coral": "bg-dark-coral-200/50",
-};
-
-const buttonBgDisabledHoverVariants: StyleColorVariantsMap = {
- gray: 'hover:bg-gray-500/50',
- blue: 'hover:bg-blue-100/10',
- emerald: 'hover:bg-emerald-500/50',
- pink: 'hover:bg-pink-500/50',
- purple: 'hover:bg-purple-500/50',
- red: 'hover:bg-red-500/50',
- sky: 'hover:bg-sky-500/50',
- yellow: 'hover:bg-yellow-500/50',
- "dark-coral": "hover:bg-dark-coral-500/50",
-};
-
-const buttonHoverBgVariants: StyleColorVariantsMap = {
- gray: 'hover:bg-gray-600',
- blue: 'hover:bg-blue-400',
- emerald: 'hover:bg-emerald-600',
- pink: 'hover:bg-pink-600',
- purple: 'hover:bg-purple-600',
- red: 'hover:bg-red-600',
- sky: 'hover:bg-sky-600',
- yellow: 'hover:bg-yellow-600',
- "dark-coral": "hover:bg-dark-coral-600",
-};
-
-const buttonBorderVariants: StyleColorVariantsMap = {
- gray: 'border-gray-500',
- blue: 'border-blue-500',
- emerald: 'border-emerald-700',
- pink: 'border-pink-300',
- purple: 'border-purple-300',
- red: 'border-red-300',
- sky: 'border-sky-300',
- yellow: 'border-yellow-300',
- "dark-coral": "border-dark-coral-500",
-};
-
-const buttonBorderHoverVariants: StyleColorVariantsMap = {
- gray: 'hover:border-gray-500',
- blue: 'hover:border-blue-400',
- emerald: 'hover:border-emerald-900',
- pink: 'hover:border-pink-400',
- purple: 'hover:border-purple-400',
- red: 'hover:border-red-500',
- sky: 'hover:border-sky-400',
- yellow: 'hover:border-yellow-400',
- "dark-coral": "hover:border-dark-coral-400",
-};
-
-const buttonTextVariants: StyleColorVariantsMap = {
- gray: 'text-white',
- blue: 'text-white',
- emerald: 'text-emerald-300',
- pink: 'text-white',
- purple: 'text-white',
- red: 'text-white',
- sky: 'text-white',
- yellow: 'text-white',
- "dark-coral": "text-white",
-};
-
-const buttonTextHoverVariants: StyleColorVariantsMap = {
- blue: 'hover:text-white',
- emerald: 'hover:text-emerald-900',
- gray: 'hover:text-white',
- pink: 'hover:text-white',
- purple: 'hover:text-white',
- red: 'hover:text-white',
- sky: 'hover:text-white',
- yellow: 'hover:text-white',
- "dark-coral": 'hover:text-white',
-}
-
-const buttonTextMutedVariants: StyleColorVariantsMap = {
- gray: 'text-gray-500',
- blue: 'text-gray-50',
- emerald: 'text-gray-50',
- pink: 'text-gray-50',
- purple: 'text-gray-50',
- red: 'text-gray-50',
- sky: 'text-gray-50',
- yellow: 'text-gray-50',
- "dark-coral": "text-gray-500",
-};
-
-const buttonGlowVariants: StyleColorVariantsMap = {
- gray: 'shadow-gray-300/50',
- blue: 'shadow-blue-500/50',
- emerald: 'shadow-blue-700/50',
- pink: 'shadow-blue-300/50',
- purple: 'shadow-blue-300/50',
- red: 'shadow-blue-300/50',
- sky: 'shadow-blue-300/50',
- yellow: 'shadow-blue-300/50',
- "dark-coral": "shadow-dark-coral-300/50",
-};
-
-const buttonHoverGlowVariants: StyleColorVariantsMap = {
- gray: 'hover:shadow-gray-300/50',
- blue: 'hover:shadow-blue-400/50',
- emerald: 'hover:shadow-blue-300/50',
- pink: 'hover:shadow-blue-300/50',
- purple: 'hover:shadow-blue-300/50',
- red: 'hover:shadow-blue-300/50',
- sky: 'hover:shadow-blue-300/50',
- yellow: 'hover:shadow-blue-300/50',
- "dark-coral": "hover:shadow-dark-coral-300/50",
-};
-
-const isURL = (str: string) =>
- /^(?:\w+:)?\/\/([^\s.]+\.\S{2}|localhost[:?\d]*)\S*$/.test(str);
-
-const isInternalURL = (str: string) => /^\/(\S*\/)*\S*\/?$/.test(str);
-
-/**
- * Common button component
- * @param {string|undefined} href New location link
- * @param {boolean} iconed Flag to process component with icon. Default: `false`
- * @param {StyleGlobalVariants|undefined} variant Button variant.
- * Default: `base`
- * @param {StyleType} type Button type.
- * Default: `fill`
- * @param {boolean|undefined} glowing Enables glowing shadow around
- * button element. Default `false`
- * @param {boolean|undefined} disabled Shows button element as
- * disabled item
- * @return {JSX.Element}
- */
-const Button = ({
- href,
- iconed,
- variant = 'gray',
- type = 'fill',
- glowing = false,
- disabled,
- onClick: onPress,
- children,
- className,
- rel,
- htmlType,
-}: Props) =>{
- const isExternal = isURL(href ?? '');
- const isInternal = isInternalURL(href ?? '');
-
- const As = isExternal || isInternal ? 'a' : 'button';
-
- return (
-
- {children}
-
- );
-};
-
-export default Button;
+import classNames from 'classnames';
+import {
+ StyleColorVariants,
+ StyleColorVariantsMap,
+ StyleType,
+} from 'core/_variants';
+import React from 'react';
+
+type Props = {
+ href?: string;
+ variant?: StyleColorVariants;
+ type?: StyleType;
+ iconed?: boolean | "only";
+ glowing?: boolean;
+ disabled?: boolean;
+ onClick?: (event: React.MouseEvent) => void;
+ children?: React.ReactNode;
+ className?: string | undefined;
+ rel?: string | undefined;
+ htmlType?: 'button' | 'submit' | 'reset' | undefined;
+};
+
+const buttonBgVariants: StyleColorVariantsMap = {
+ gray: 'bg-gray-500',
+ blue: 'bg-blue-500',
+ emerald: 'bg-emerald-600',
+ pink: 'bg-pink-500',
+ purple: 'bg-purple-500',
+ red: 'bg-red-500',
+ sky: 'bg-sky-500',
+ yellow: 'bg-yellow-500',
+ "dark-coral": "bg-dark-coral-500",
+};
+
+const buttonBgDisabledVariants: StyleColorVariantsMap = {
+ gray: 'bg-gray-200',
+ blue: 'bg-blue-100/10',
+ emerald: 'bg-emerald-200/50',
+ pink: 'bg-pink-200/50',
+ purple: 'bg-purple-200/50',
+ red: 'bg-red-200/50',
+ sky: 'bg-sky-200/50',
+ yellow: 'bg-yellow-200/50',
+ "dark-coral": "bg-dark-coral-200/50",
+};
+
+const buttonBgDisabledHoverVariants: StyleColorVariantsMap = {
+ gray: 'hover:bg-gray-500/50',
+ blue: 'hover:bg-blue-100/10',
+ emerald: 'hover:bg-emerald-500/50',
+ pink: 'hover:bg-pink-500/50',
+ purple: 'hover:bg-purple-500/50',
+ red: 'hover:bg-red-500/50',
+ sky: 'hover:bg-sky-500/50',
+ yellow: 'hover:bg-yellow-500/50',
+ "dark-coral": "hover:bg-dark-coral-500/50",
+};
+
+const buttonHoverBgVariants: StyleColorVariantsMap = {
+ gray: 'hover:bg-gray-600',
+ blue: 'hover:bg-blue-400',
+ emerald: 'hover:bg-emerald-600',
+ pink: 'hover:bg-pink-600',
+ purple: 'hover:bg-purple-600',
+ red: 'hover:bg-red-600',
+ sky: 'hover:bg-sky-600',
+ yellow: 'hover:bg-yellow-600',
+ "dark-coral": "hover:bg-dark-coral-600",
+};
+
+const buttonBorderVariants: StyleColorVariantsMap = {
+ gray: 'border-gray-500',
+ blue: 'border-blue-500',
+ emerald: 'border-emerald-700',
+ pink: 'border-pink-300',
+ purple: 'border-purple-300',
+ red: 'border-red-300',
+ sky: 'border-sky-300',
+ yellow: 'border-yellow-300',
+ "dark-coral": "border-dark-coral-500",
+};
+
+const buttonBorderHoverVariants: StyleColorVariantsMap = {
+ gray: 'hover:border-gray-500',
+ blue: 'hover:border-blue-400',
+ emerald: 'hover:border-emerald-900',
+ pink: 'hover:border-pink-400',
+ purple: 'hover:border-purple-400',
+ red: 'hover:border-red-500',
+ sky: 'hover:border-sky-400',
+ yellow: 'hover:border-yellow-400',
+ "dark-coral": "hover:border-dark-coral-400",
+};
+
+const buttonTextVariants: StyleColorVariantsMap = {
+ gray: 'text-white',
+ blue: 'text-white',
+ emerald: 'text-emerald-300',
+ pink: 'text-white',
+ purple: 'text-white',
+ red: 'text-white',
+ sky: 'text-white',
+ yellow: 'text-white',
+ "dark-coral": "text-white",
+};
+
+const buttonTextHoverVariants: StyleColorVariantsMap = {
+ blue: 'hover:text-white',
+ emerald: 'hover:text-emerald-900',
+ gray: 'hover:text-white',
+ pink: 'hover:text-white',
+ purple: 'hover:text-white',
+ red: 'hover:text-white',
+ sky: 'hover:text-white',
+ yellow: 'hover:text-white',
+ "dark-coral": 'hover:text-white',
+}
+
+const buttonTextMutedVariants: StyleColorVariantsMap = {
+ gray: 'text-gray-500',
+ blue: 'text-gray-50',
+ emerald: 'text-gray-50',
+ pink: 'text-gray-50',
+ purple: 'text-gray-50',
+ red: 'text-gray-50',
+ sky: 'text-gray-50',
+ yellow: 'text-gray-50',
+ "dark-coral": "text-gray-500",
+};
+
+const buttonGlowVariants: StyleColorVariantsMap = {
+ gray: 'shadow-gray-300/50',
+ blue: 'shadow-blue-500/50',
+ emerald: 'shadow-blue-700/50',
+ pink: 'shadow-blue-300/50',
+ purple: 'shadow-blue-300/50',
+ red: 'shadow-blue-300/50',
+ sky: 'shadow-blue-300/50',
+ yellow: 'shadow-blue-300/50',
+ "dark-coral": "shadow-dark-coral-300/50",
+};
+
+const buttonHoverGlowVariants: StyleColorVariantsMap = {
+ gray: 'hover:shadow-gray-300/50',
+ blue: 'hover:shadow-blue-400/50',
+ emerald: 'hover:shadow-blue-300/50',
+ pink: 'hover:shadow-blue-300/50',
+ purple: 'hover:shadow-blue-300/50',
+ red: 'hover:shadow-blue-300/50',
+ sky: 'hover:shadow-blue-300/50',
+ yellow: 'hover:shadow-blue-300/50',
+ "dark-coral": "hover:shadow-dark-coral-300/50",
+};
+
+const isURL = (str: string) =>
+ /^(?:\w+:)?\/\/([^\s.]+\.\S{2}|localhost[:?\d]*)\S*$/.test(str);
+
+const isInternalURL = (str: string) => /^\/(\S*\/)*\S*\/?$/.test(str);
+
+/**
+ * Common button component
+ * @param {string|undefined} href New location link
+ * @param {boolean} iconed Flag to process component with icon. Default: `false`
+ * @param {StyleGlobalVariants|undefined} variant Button variant.
+ * Default: `base`
+ * @param {StyleType} type Button type.
+ * Default: `fill`
+ * @param {boolean|undefined} glowing Enables glowing shadow around
+ * button element. Default `false`
+ * @param {boolean|undefined} disabled Shows button element as
+ * disabled item
+ * @return {JSX.Element}
+ */
+const Button = ({
+ href,
+ iconed,
+ variant = 'gray',
+ type = 'fill',
+ glowing = false,
+ disabled,
+ onClick: onPress,
+ children,
+ className,
+ rel,
+ htmlType,
+}: Props) =>{
+ const isExternal = isURL(href ?? '');
+ const isInternal = isInternalURL(href ?? '');
+
+ const As = isExternal || isInternal ? 'a' : 'button';
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default Button;
diff --git a/src/components/controls/Combox.tsx b/src/components/controls/Combox.tsx
old mode 100644
new mode 100755
index f9e6dda..34a5e57
--- a/src/components/controls/Combox.tsx
+++ b/src/components/controls/Combox.tsx
@@ -1,88 +1,83 @@
-import { Combobox } from "@headlessui/react";
-import { useState } from "react";
-import { ReactComponent as SVGDropdownIcon } from "assets/svg/dropdown.svg";
-import classNames from "classnames";
-
-type OptionPropsArg = {
- selected: boolean;
- active: boolean;
- disabled: boolean;
-};
-type Props = {
- options: T[];
- selected: T;
- setSelected: (item: T) => void;
- filterRule: (query: string, item: T) => boolean;
- displayValue: (item: T) => string;
- keyMaper: (item: T) => string;
- valueMaper?: (item: T) => any;
- itemBuilder: (props: OptionPropsArg, item: T) => React.ReactNode;
-};
-
-export default function Combox({
- options,
- selected,
- setSelected,
- filterRule,
- displayValue,
- keyMaper,
- valueMaper,
- itemBuilder,
-}: Props) {
- const [query, setQuery] = useState("");
-
- const filtered =
- query === "" ? options : options.filter((item) => filterRule(query, item));
-
- return (
-
-
-
- setQuery(event.target.value)}
- />
-
-
-
-
-
- {filtered.length === 0 && query !== "" ? (
-
- Nothing found.
-
- ) : (
- filtered.map((option) => (
- classNames(
- "p-2 py-4 space-x-5",
- "rounded-md hover:bg-gray-300/5 transition-colors cursor-pointer",
- { "bg-gray-300/5": active }
- )
- }
- value={valueMaper ? valueMaper(option) : option}
- >
- {(stats) => itemBuilder(stats, option)}
-
- ))
- )}
-
-
-
- );
-}
+import { Combobox } from "@headlessui/react";
+import { useState } from "react";
+import classNames from "classnames";
+
+type OptionPropsArg = {
+ selected: boolean;
+ active: boolean;
+ disabled: boolean;
+};
+type Props = {
+ options: T[];
+ selected: T;
+ setSelected: (item: T) => void;
+ filterRule: (query: string, item: T) => boolean;
+ displayValue: (item: T) => string;
+ keyMaper: (item: T) => string;
+ valueMaper?: (item: T) => any;
+ itemBuilder: (props: OptionPropsArg, item: T) => React.ReactNode;
+};
+
+export default function Combox({
+ options,
+ selected,
+ setSelected,
+ filterRule,
+ displayValue,
+ keyMaper,
+ valueMaper,
+ itemBuilder,
+}: Props) {
+ const [query, setQuery] = useState("");
+
+ const filtered =
+ query === "" ? options : options.filter((item) => filterRule(query, item));
+
+ return (
+
+
+
+ setQuery(event.target.value)}
+ />
+
+
+
+
+ {filtered.length === 0 && query !== "" ? (
+
+ Nothing found.
+
+ ) : (
+ filtered.map((option) => (
+ classNames(
+ "p-2 py-4 space-x-5",
+ "rounded-md hover:bg-gray-300/5 transition-colors cursor-pointer",
+ { "bg-gray-300/5": active }
+ )
+ }
+ value={valueMaper ? valueMaper(option) : option}
+ >
+
+
+ ))
+ )}
+
+
+
+ );
+}
diff --git a/src/components/controls/ControlLabel.tsx b/src/components/controls/ControlLabel.tsx
old mode 100644
new mode 100755
index 7e5ff37..7eecfc4
--- a/src/components/controls/ControlLabel.tsx
+++ b/src/components/controls/ControlLabel.tsx
@@ -1,21 +1,21 @@
-import classNames from "classnames";
-import React from "react";
-
-type Props = {
- children?: React.ReactNode;
- label: string;
- className?: string | undefined;
-};
-
-export default function ControlLabel({
- label,
- children,
- className,
-}: Props): JSX.Element {
- return (
-
- );
-}
+import classNames from "classnames";
+import React from "react";
+
+type Props = {
+ children?: React.ReactNode;
+ label: string;
+ className?: string | undefined;
+};
+
+export default function ControlLabel({
+ label,
+ children,
+ className,
+}: Props): JSX.Element {
+ return (
+
+ );
+}
diff --git a/src/components/controls/IconControl.tsx b/src/components/controls/IconControl.tsx
old mode 100644
new mode 100755
index 595e67c..781ce6e
--- a/src/components/controls/IconControl.tsx
+++ b/src/components/controls/IconControl.tsx
@@ -1,9 +1,9 @@
-import React from "react";
-
-type Props = {
- children: React.ReactNode;
-};
-
-export default function IconControl({ children }: Props) {
- return {children}
;
-}
+import React from "react";
+
+type Props = {
+ children: React.ReactNode;
+};
+
+export default function IconControl({ children }: Props) {
+ return {children}
;
+}
diff --git a/src/components/controls/InputField.tsx b/src/components/controls/InputField.tsx
old mode 100644
new mode 100755
index f73c5b6..bc2e855
--- a/src/components/controls/InputField.tsx
+++ b/src/components/controls/InputField.tsx
@@ -1,44 +1,44 @@
-import { Transition } from "@headlessui/react";
-import classNames from "classnames";
-import React from "react";
-
-type Props = React.DetailedHTMLProps<
- React.InputHTMLAttributes,
- HTMLInputElement
-> & {
- error?: string;
- touched?: boolean;
- label?: string;
-};
-
-export default function InputField({ label, touched, error, ...props }: Props) {
- return (
-
- {label &&
{label} }
-
-
-
- {error}
-
-
-
- );
-}
+import { Transition } from "@headlessui/react";
+import classNames from "classnames";
+import React from "react";
+
+type Props = React.DetailedHTMLProps<
+ React.InputHTMLAttributes,
+ HTMLInputElement
+> & {
+ error?: string;
+ touched?: boolean;
+ label?: string;
+};
+
+export default function InputField({ label, touched, error, ...props }: Props) {
+ return (
+
+ {label &&
{label} }
+
+
+
+ {error}
+
+
+
+ );
+}
diff --git a/src/components/controls/Switch.tsx b/src/components/controls/Switch.tsx
old mode 100644
new mode 100755
index 36f4139..2a090a2
--- a/src/components/controls/Switch.tsx
+++ b/src/components/controls/Switch.tsx
@@ -1,40 +1,40 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React from "react";
-import { Switch as RawSwitch } from "@headlessui/react";
-import classNames from "classnames";
-/* -------------------------------------------------------------------------- */
-/* Properties type */
-/* -------------------------------------------------------------------------- */
-type Props = {
- alterText?: string;
- enabled: boolean;
- onChange: () => void;
-};
-
-export default function Switch({ alterText, enabled, onChange }: Props) {
- return (
-
- {alterText}
-
-
- );
-}
+/* -------------------------------------------------------------------------- */
+/* Libraries */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import { Switch as RawSwitch } from "@headlessui/react";
+import classNames from "classnames";
+/* -------------------------------------------------------------------------- */
+/* Properties type */
+/* -------------------------------------------------------------------------- */
+type Props = {
+ alterText?: string;
+ enabled: boolean;
+ onChange: () => void;
+};
+
+export default function Switch({ alterText, enabled, onChange }: Props) {
+ return (
+
+ {alterText}
+
+
+ );
+}
diff --git a/src/components/controls/TextAction.tsx b/src/components/controls/TextAction.tsx
old mode 100644
new mode 100755
index a9155fa..8770df6
--- a/src/components/controls/TextAction.tsx
+++ b/src/components/controls/TextAction.tsx
@@ -1,48 +1,48 @@
-import classNames from "classnames";
-import { StyleColorVariants, StyleColorVariantsMap } from "core/_variants";
-import React from "react";
-
-type Props = {
- children: React.ReactNode;
- variant?: StyleColorVariants;
- href?: string;
- className?: string;
- onClick?: (
- event: React.MouseEvent
- ) => void;
-};
-
-const textColorsMap: StyleColorVariantsMap = {
- blue: "text-blue-500",
- emerald: "text-emerald-500",
- gray: "text-gray-500",
- pink: "text-pink-500",
- red: "text-red-500",
- purple: "text-purple-500",
- yellow: "text-yellow-500",
- sky: "text-sky-500",
- "dark-coral": "text-dark-coral-500",
-};
-
-export default function TextAction({
- children,
- onClick,
- href,
- variant,
- className,
-}: Props) {
- const Component = href ? "a" : "button";
- return (
-
- {children}
-
- );
-}
+import classNames from "classnames";
+import { StyleColorVariants, StyleColorVariantsMap } from "core/_variants";
+import React from "react";
+
+type Props = {
+ children: React.ReactNode;
+ variant?: StyleColorVariants;
+ href?: string;
+ className?: string;
+ onClick?: (
+ event: React.MouseEvent
+ ) => void;
+};
+
+const textColorsMap: StyleColorVariantsMap = {
+ blue: "text-blue-500",
+ emerald: "text-emerald-500",
+ gray: "text-gray-500",
+ pink: "text-pink-500",
+ red: "text-red-500",
+ purple: "text-purple-500",
+ yellow: "text-yellow-500",
+ sky: "text-sky-500",
+ "dark-coral": "text-dark-coral-500",
+};
+
+export default function TextAction({
+ children,
+ onClick,
+ href,
+ variant,
+ className,
+}: Props) {
+ const Component = href ? "a" : "button";
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/drop-down-menu/ContextMenu.tsx b/src/components/drop-down-menu/ContextMenu.tsx
new file mode 100755
index 0000000..f960634
--- /dev/null
+++ b/src/components/drop-down-menu/ContextMenu.tsx
@@ -0,0 +1,118 @@
+/* -------------------------------------------------------------------------- */
+/* Imports */
+/* -------------------------------------------------------------------------- */
+import React, { Fragment } from "react";
+import { Menu, Transition } from "@headlessui/react";
+import { PropsPartion } from "./ContextMenuItem";
+import classNames from "classnames";
+import { SVGCaretDown } from "components/icons";
+type ChildType = React.ReactElement;
+type ChildrenType = ChildType[] | ChildType;
+
+/* -------------------------------------------------------------------------- */
+/* Component props */
+/* -------------------------------------------------------------------------- */
+
+type MenuProps = {
+ emphasis?: "high" | "low";
+ disabled?: boolean;
+ className?: string | undefined;
+ button: React.ReactNode;
+ children: ChildrenType;
+};
+/* -------------------------------------------------------------------------- */
+/* Styles */
+/* -------------------------------------------------------------------------- */
+
+const MenuButtonStyle = `
+items-center
+inline-flex
+justify-center w-full
+cursor-default
+rounded
+border border-gray-100
+outline-8
+py-2
+pl-4
+pr-1
+text-base`;
+
+const MenuItemStyle = `
+absolute
+left-0
+mt-2 w-60
+origin-top-left
+rounded
+bg-white
+shadow-lg
+focus:outline-none
+py-2 px-4 sm:text-sm`;
+
+/* -------------------------------------------------------------------------- */
+/* Component implementation */
+/* -------------------------------------------------------------------------- */
+/**
+ * Use width ContextMenuAction.tsx , for example:
+ *
+ * alert('click')}
+ * >
+ * ...
+ *
+ */
+export default function ContextMenu({
+ button,
+ children,
+ className,
+ emphasis = "low",
+}: MenuProps) {
+ return (
+
+ {({ open }) => (
+ <>
+
+ {button}
+
+
+
+
+
+ {children}
+
+
+ >
+ )}
+
+ );
+}
diff --git a/src/components/drop-down-menu/ContextMenuAction.tsx b/src/components/drop-down-menu/ContextMenuAction.tsx
new file mode 100755
index 0000000..356a0f3
--- /dev/null
+++ b/src/components/drop-down-menu/ContextMenuAction.tsx
@@ -0,0 +1,32 @@
+import classNames from "classnames";
+import React from "react";
+
+type Props = {
+ action: Function;
+ caption: string;
+ disabled?: boolean;
+ icon?: React.ReactNode;
+ className?: string | undefined;
+};
+
+export default function ContextMenuAction({
+ action,
+ caption,
+ disabled,
+ icon,
+ className,
+}: Props) {
+ return (
+ action(e)}
+ className={classNames([
+ "group flex px-2 rounded items-center text-base hover:bg-gray-100",
+ className,
+ ])}
+ >
+ {icon && {icon}
}
+ {caption}
+
+ );
+}
diff --git a/src/components/drop-down-menu/ContextMenuItem.tsx b/src/components/drop-down-menu/ContextMenuItem.tsx
new file mode 100755
index 0000000..f06e8b2
--- /dev/null
+++ b/src/components/drop-down-menu/ContextMenuItem.tsx
@@ -0,0 +1,27 @@
+import {Menu} from '@headlessui/react';
+import React from 'react';
+
+export type PropsPartion = {
+ disabled?: boolean | undefined;
+ active?: boolean | undefined;
+};
+
+type Props = {
+ children: React.ReactElement<
+ any & PropsPartion
+ >;
+} & PropsPartion;
+
+/**
+ * Context menu item component
+ * @return {JSX.Element}
+ */
+export default function ContextMenuItem({children, ...props}: Props) {
+ return (
+
+ {(params) => {
+ return React.cloneElement(children, params);
+ }}
+
+ );
+}
diff --git a/src/components/drop-down-menu/MenuIcons.tsx b/src/components/drop-down-menu/MenuIcons.tsx
new file mode 100755
index 0000000..d4b7f06
--- /dev/null
+++ b/src/components/drop-down-menu/MenuIcons.tsx
@@ -0,0 +1,101 @@
+import React from "react";
+
+
+/* -------------------------------------------------------------------------- */
+/* Icons for header menu as our disign */
+/* -------------------------------------------------------------------------- */
+
+
+export const Publications = (
+
+
+
+
+
+);
+
+export const MyFavorite = (
+
+
+
+);
+export const MyCollection = (
+
+
+
+
+
+);
+
+export const RecentViewed = (
+
+
+
+
+);
diff --git a/src/components/fetchAnArticle/AnArticle.tsx b/src/components/fetchAnArticle/AnArticle.tsx
new file mode 100644
index 0000000..73a8f91
--- /dev/null
+++ b/src/components/fetchAnArticle/AnArticle.tsx
@@ -0,0 +1,86 @@
+import { useArticleViewModel } from "article/controller/articleViewModel";
+import { useArticleStore } from "article/data/articleStoreImplementation";
+import { useEffect } from "react";
+import * as ArticlePart from "../../components/Article/Article";
+import { useParams } from "react-router";
+import AskeletonArticle from "./AskeletonArticle";
+import Container from "components/Container";
+import NotFound from "./NotFound";
+import { SVGSearch } from "components/icons";
+import BaseLayout from "components/BaseLayout";
+import Typography from "components/typography/Typography";
+import { useTranslation } from "react-i18next";
+
+const AnArticle = () => {
+ const store = useArticleStore();
+ const { article, hasError, shouldShowLoading } = useArticleViewModel(store);
+ const { i18n, t } = useTranslation();
+
+ const { id } = useParams();
+ const newId = `${id}`;
+
+ useEffect(() => {
+ store.getArticle(newId);
+ }, [id]);
+
+ if (hasError) {
+ return ;
+ }
+ return (
+
+
+ {shouldShowLoading ? (
+
+ ) : (
+ <>
+
+ {article?.topic}
+
+
+
+ {article?.title}
+
+
+ {article?.authors !== undefined ? article?.authors : "Unknown"}
+
+
+
+
+
+ {article?.tags && (
+
+
+ {t("articlePage.keywords")}
+
+
+
+ {article?.tags}
+
+
+ )}
+
+
+ {t("articlePage.abstract")}
+
+
+ {article?.summary !== undefined ? (
+ article?.summary
+ ) : (
+
+
+
+ )}
+
+
+ >
+ )}
+
+
+ );
+};
+
+// \n
+export default AnArticle;
diff --git a/src/components/fetchAnArticle/AnArticleBody.tsx b/src/components/fetchAnArticle/AnArticleBody.tsx
new file mode 100644
index 0000000..8186ca3
--- /dev/null
+++ b/src/components/fetchAnArticle/AnArticleBody.tsx
@@ -0,0 +1,64 @@
+import { useArticleViewModel } from "article/controller/articleViewModel";
+import { useArticleStore } from "article/data/articleStoreImplementation";
+import "react-loading-skeleton/dist/skeleton.css";
+import Skeleton from "react-loading-skeleton";
+import { useParams } from "react-router";
+import { useEffect } from "react";
+
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import * as ArticlePart from "../../components/Article/Article";
+import BaseLayout from "components/BaseLayout";
+import Container from "components/Container";
+import NotFound from "./NotFound";
+import Markdown from "components/Markdown";
+
+const AnArticleBody = () => {
+ const store = useArticleStore();
+ const { article, hasError, shouldShowLoading } = useArticleViewModel(store);
+ const { id } = useParams();
+ const newId = `${id}`;
+ useEffect(() => {
+ store.getArticle(newId);
+ }, [id]);
+ if (hasError) ;
+ return (
+
+
+ {shouldShowLoading ? (
+ <>
+
+
+
+
+
+ >
+ ) : (
+ <>
+
+ {article?.topic}
+
+
+
+ {article?.title}
+
+
+
+
+
+
+ >
+ )}
+
+
+ );
+};
+
+export default AnArticleBody;
diff --git a/src/components/fetchAnArticle/AskeletonArticle.tsx b/src/components/fetchAnArticle/AskeletonArticle.tsx
new file mode 100644
index 0000000..d8bdcfc
--- /dev/null
+++ b/src/components/fetchAnArticle/AskeletonArticle.tsx
@@ -0,0 +1,59 @@
+import Skeleton from "react-loading-skeleton";
+import "react-loading-skeleton/dist/skeleton.css";
+const AskeletonArticle = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default AskeletonArticle;
diff --git a/src/components/fetchAnArticle/NotFound.tsx b/src/components/fetchAnArticle/NotFound.tsx
new file mode 100644
index 0000000..55f28a8
--- /dev/null
+++ b/src/components/fetchAnArticle/NotFound.tsx
@@ -0,0 +1,40 @@
+import React from "react";
+import Container from "components/Container";
+import { Button } from "components/Button/Button";
+import Link from "components/typography/Link";
+
+import Lottie from "react-lottie";
+import animationData from "../../assets/lotties/notFoundAnimation.json";
+
+const NotFound = () => {
+ const defaultOptions = {
+ loop: true,
+ autoplay: true,
+ animationData: animationData,
+ rendererSettings: {
+ preserveAspectRatio: "xMidYMid slice",
+ },
+ };
+
+ return (
+
+
+ 404
+ Page does not exist
+
+ Maybe you got a broken link, or maybe you made a misprint in the address
+ bar
+
+
+ Go to home
+
+
+ );
+};
+
+export default NotFound;
diff --git a/src/components/icons.tsx b/src/components/icons.tsx
new file mode 100755
index 0000000..1975632
--- /dev/null
+++ b/src/components/icons.tsx
@@ -0,0 +1,76 @@
+export { ReactComponent as SVGAgricultural } from "assets/svg/agricultural.svg";
+export { ReactComponent as SVGArrowBigRight } from "assets/svg/arrow-big-right.svg";
+export { ReactComponent as SVGArrowDown } from "assets/svg/arrow-down.svg";
+export { ReactComponent as SVGArrowLeft } from "assets/svg/arrow-left.svg";
+export { ReactComponent as SVGArrowRight } from "assets/svg/arrow-right.svg";
+export { ReactComponent as SVGArrowUp } from "assets/svg/arrow-up.svg";
+export { ReactComponent as SVGBell } from "assets/svg/bell.svg";
+export { ReactComponent as SVGBellNotification } from "assets/svg/bell-notification.svg";
+export { ReactComponent as SVGBookmarkFilled } from "assets/svg/bookmark-filled.svg";
+export { ReactComponent as SVGBookmarkOutlined } from "assets/svg/bookmark-outlined.svg";
+export { ReactComponent as SVGCaretDown } from "assets/svg/caret-down.svg";
+export { ReactComponent as SVGCaretLeft } from "assets/svg/caret-left.svg";
+export { ReactComponent as SVGCaretRight } from "assets/svg/caret-right.svg";
+export { ReactComponent as SVGCaretUp } from "assets/svg/caret-up.svg";
+export { ReactComponent as SVGChart } from "assets/svg/chart.svg";
+export { ReactComponent as SVGChevronesLeft } from "assets/svg/chevrones-left.svg";
+export { ReactComponent as SVGChevronesRight } from "assets/svg/chevrones-right.svg";
+export { ReactComponent as SVGCircle } from "assets/svg/circle.svg";
+export { ReactComponent as SVGCite } from "assets/svg/cite.svg";
+export { ReactComponent as SVGCopy } from "assets/svg/copy.svg";
+export { ReactComponent as SVGDelete } from "assets/svg/delete.svg";
+export { ReactComponent as SVGDownload } from "assets/svg/download.svg";
+export { ReactComponent as SVGDuplicate } from "assets/svg/duplicate.svg";
+export { ReactComponent as SVGEdit1 } from "assets/svg/edit1.svg";
+export { ReactComponent as SVGEdit2 } from "assets/svg/edit2.svg";
+export { ReactComponent as SVGError } from "assets/svg/error.svg";
+export { ReactComponent as SVGEye } from "assets/svg/eye.svg";
+export { ReactComponent as SVGFacebook } from "assets/svg/facebook.svg";
+export { ReactComponent as SVGFavoriteFilled } from "assets/svg/favorite-filled.svg";
+export { ReactComponent as SVGFavoriteOutlined } from "assets/svg/favorite-outlined.svg";
+export { ReactComponent as SVGFile } from "assets/svg/file.svg";
+export { ReactComponent as SVGFiletext } from "assets/svg/filetext.svg";
+export { ReactComponent as SVGFilter } from "assets/svg/filter.svg";
+export { ReactComponent as SVGFlag } from "assets/svg/flag.svg";
+export { ReactComponent as SVGFolder } from "assets/svg/folder.svg";
+export { ReactComponent as SVGFormula } from "assets/svg/formula.svg";
+export { ReactComponent as SVGFundamental } from "assets/svg/fundamental.svg";
+export { ReactComponent as SVGGrid } from "assets/svg/grid.svg";
+export { ReactComponent as SVGHamburger } from "assets/svg/hamburger.svg";
+export { ReactComponent as SVGHelp } from "assets/svg/help.svg";
+export { ReactComponent as SVGHorizontal } from "assets/svg/horizontal.svg";
+export { ReactComponent as SVGHumanitarian } from "assets/svg/humanitarian.svg";
+export { ReactComponent as SVGImage } from "assets/svg/image.svg";
+export { ReactComponent as SVGInfo } from "assets/svg/info.svg";
+export { ReactComponent as SVGInstagram } from "assets/svg/instagram.svg";
+export { ReactComponent as SVGKey } from "assets/svg/key.svg";
+export { ReactComponent as SVGLeftMenu } from "assets/svg/left-menu.svg";
+export { ReactComponent as SVGLink } from "assets/svg/link.svg";
+export { ReactComponent as SVGList } from "assets/svg/list.svg";
+export { ReactComponent as SVGLogo } from "assets/svg/logo.svg";
+export { ReactComponent as SVGMedicine } from "assets/svg/medicine.svg";
+export { ReactComponent as SVGMinus } from "assets/svg/minus.svg";
+export { ReactComponent as SVGMore } from "assets/svg/more.svg";
+export { ReactComponent as SVGPages } from "assets/svg/pages.svg";
+export { ReactComponent as SVGPalete } from "assets/svg/palete.svg";
+export { ReactComponent as SVGPlot } from "assets/svg/plot.svg";
+export { ReactComponent as SVGPlus } from "assets/svg/plus.svg";
+export { ReactComponent as SVGPrinter } from "assets/svg/printer.svg";
+export { ReactComponent as SVGRightMenu } from "assets/svg/right-menu.svg";
+export { ReactComponent as SVGRotate } from "assets/svg/rotate.svg";
+export { ReactComponent as SVGSearch } from "assets/svg/search.svg";
+export { ReactComponent as SVGSelection } from "assets/svg/selection.svg";
+export { ReactComponent as SVGSend } from "assets/svg/send.svg";
+export { ReactComponent as SVGSettings } from "assets/svg/settings.svg";
+export { ReactComponent as SVGShape } from "assets/svg/shape.svg";
+export { ReactComponent as SVGShare } from "assets/svg/share.svg";
+export { ReactComponent as SVGSmile } from "assets/svg/smile.svg";
+export { ReactComponent as SVGSocials } from "assets/svg/socials.svg";
+export { ReactComponent as SVGTable } from "assets/svg/table.svg";
+export { ReactComponent as SVGTechnicsAndTechology } from "assets/svg/technics-and-techology.svg";
+export { ReactComponent as SVGText } from "assets/svg/text.svg";
+export { ReactComponent as SVGTextTransition } from "assets/svg/text-transition.svg";
+export { ReactComponent as SVGUser } from "assets/svg/user.svg";
+export { ReactComponent as SVGVertical } from "assets/svg/vertical.svg";
+export { ReactComponent as SVGVideo } from "assets/svg/video.svg";
+export { ReactComponent as SVGXmark } from "assets/svg/xmark.svg";
diff --git a/src/components/indicators/AvailabilityIndicator.tsx b/src/components/indicators/AvailabilityIndicator.tsx
old mode 100644
new mode 100755
index 4fa2cd2..04f51c9
--- a/src/components/indicators/AvailabilityIndicator.tsx
+++ b/src/components/indicators/AvailabilityIndicator.tsx
@@ -1,21 +1,21 @@
-import classNames from 'classnames';
-import React from 'react'
-
-type Props = {
- children: React.ReactNode,
- active: boolean,
-}
-
-export default function AvailabilityIndicator({ active, children }: Props) {
- const statusClass = active ? "bg-green-400" : "bg-gray-500";
- return (
-
- )
+import classNames from 'classnames';
+import React from 'react'
+
+type Props = {
+ children: React.ReactNode,
+ active: boolean,
+}
+
+export default function AvailabilityIndicator({ active, children }: Props) {
+ const statusClass = active ? "bg-green-400" : "bg-gray-500";
+ return (
+
+ )
}
\ No newline at end of file
diff --git a/src/components/layouts/FullWideColumn.tsx b/src/components/layouts/FullWideColumn.tsx
old mode 100644
new mode 100755
index e82804a..314a727
--- a/src/components/layouts/FullWideColumn.tsx
+++ b/src/components/layouts/FullWideColumn.tsx
@@ -1,13 +1,13 @@
-import React from 'react'
-
-type Props = {
- children: React.ReactNode;
-}
-
-export default function FullWideColumn({children}: Props) {
- return (
-
- {children}
-
- )
+import React from 'react'
+
+type Props = {
+ children: React.ReactNode;
+}
+
+export default function FullWideColumn({children}: Props) {
+ return (
+
+ {children}
+
+ )
}
\ No newline at end of file
diff --git a/src/components/layouts/ThinSingleColumn.tsx b/src/components/layouts/ThinSingleColumn.tsx
old mode 100644
new mode 100755
index 6f4a1fd..4ba09aa
--- a/src/components/layouts/ThinSingleColumn.tsx
+++ b/src/components/layouts/ThinSingleColumn.tsx
@@ -1,13 +1,13 @@
-import React from 'react'
-
-type Props = {
- children: React.ReactNode;
-}
-
-export default function ThinSingleColumn({children}: Props) {
- return (
-
- {children}
-
- )
+import React from 'react'
+
+type Props = {
+ children: React.ReactNode;
+}
+
+export default function ThinSingleColumn({children}: Props) {
+ return (
+
+ {children}
+
+ )
}
\ No newline at end of file
diff --git a/src/components/layouts/ThreeColumn/ColumnLayout.css b/src/components/layouts/ThreeColumn/ColumnLayout.css
new file mode 100755
index 0000000..b23243a
--- /dev/null
+++ b/src/components/layouts/ThreeColumn/ColumnLayout.css
@@ -0,0 +1,41 @@
+.left-bar {
+ grid-area: lb;
+ max-width: 300px;
+ overflow: hidden;
+}
+
+.main-bar {
+ grid-area: main;
+}
+
+.right-bar {
+ grid-area: rb;
+ max-width: 300px;
+}
+
+.column-layout-grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-areas: "main" "rb";
+}
+
+@media (min-width: 768px) {
+ .right-bar {
+ max-width: 100%;
+ }
+ .column-layout-grid {
+ display: grid;
+ grid-template-columns: minmax(300px, 1fr);
+ grid-template-areas:
+ "lb main"
+ "lb rb";
+ }
+}
+
+@media (min-width: 1280px) {
+ .column-layout-grid {
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+ grid-template-areas: "lb main rb";
+ }
+}
diff --git a/src/components/layouts/ThreeColumn/ColumnLayout.tsx b/src/components/layouts/ThreeColumn/ColumnLayout.tsx
new file mode 100755
index 0000000..663abd0
--- /dev/null
+++ b/src/components/layouts/ThreeColumn/ColumnLayout.tsx
@@ -0,0 +1,113 @@
+import React, {
+ useState,
+ useRef,
+ useEffect,
+} from "react";
+import "./ColumnLayout.css";
+import MainColumn from "./Maincolumn";
+import LeftColumn, {LeftColumnProps} from "./LeftColumn";
+import RightColumn from "./RightColumn";
+/* -------------------------------------------------------------------------- */
+/* Custom hook to track container width */
+/* -------------------------------------------------------------------------- */
+const useScreenWidth = () => {
+ const ref: any = useRef();
+ const [width, setWidth] = useState(null);
+
+ const observer = useRef(
+ new ResizeObserver((entries) => {
+ const { width } = entries[0].contentRect;
+ setWidth(width);
+ })
+ );
+
+ useEffect(() => {
+ observer.current.observe(ref.current);
+ }, [ref, observer]);
+
+ return [ref, width];
+};
+
+/* -------------------------------------------------------------------------- */
+/* Component extentions */
+/* -------------------------------------------------------------------------- */
+
+type ColumnExtentions = {
+ Left: React.FC<{
+ children: React.ReactNode;
+ openLeftBar?: boolean;
+ widthElement?: number;
+ className?: string;
+ }>;
+ Main: React.FC<{
+ children: React.ReactNode;
+ className?: string;
+ }>;
+ Right: React.FC<{
+ children: React.ReactNode;
+ className?: string;
+ }>;
+};
+
+/* -------------------------------------------------------------------------- */
+/* Component properties */
+/* -------------------------------------------------------------------------- */
+
+type ColumnLayoutProps = {
+ children: React.ReactNode; //Column layout gets as children not more than three children
+} & Omit, "">;
+
+/* -------------------------------------------------------------------------- */
+/* Component function */
+/* -------------------------------------------------------------------------- */
+export const ColumnLayout: React.FC & ColumnExtentions = ({
+ children,
+}) => {
+ const mdScreen = 768;
+
+ //Hooks
+ const [ref, widthElement] = useScreenWidth(); // to track width of screen
+ const [openLeftBar, setOpenLeftBar] = useState(false); //to open or close left bar
+ function leftBar() {
+ return setOpenLeftBar(!openLeftBar);
+ }
+
+ // Change openLeftBar when width of screen > 768
+ useEffect(() => {
+ if (widthElement > mdScreen) {
+ setOpenLeftBar(false);
+ }
+ });
+
+ // TODO
+ const amountChildren = React.Children.count(children);
+ if (amountChildren > 3) {
+ alert("Layout gets only 3 or lesser children");
+ }
+
+ const columns = React.Children.map(children, (child) => {
+ if (
+ child &&
+ React.isValidElement(child) &&
+ React.Children.only(child).type === LeftColumn
+ ) {
+ return React.cloneElement(child as React.ReactElement,
+ { openLeftBar: openLeftBar, widthElement: widthElement },
+ );
+ } else {
+ return child;
+ }
+ });
+
+ return (
+
+
+ {amountChildren <= 3 ? columns : undefined}
+
+
+ );
+};
+
+ColumnLayout.Left = LeftColumn;
+ColumnLayout.Main = MainColumn;
+ColumnLayout.Right = RightColumn;
diff --git a/src/components/layouts/ThreeColumn/LeftColumn.tsx b/src/components/layouts/ThreeColumn/LeftColumn.tsx
new file mode 100755
index 0000000..3e51ca3
--- /dev/null
+++ b/src/components/layouts/ThreeColumn/LeftColumn.tsx
@@ -0,0 +1,49 @@
+import React, { Fragment, FunctionComponent as FC } from "react";
+import { Transition } from "@headlessui/react";
+import classNames from "classnames";
+
+export type LeftColumnProps = {
+ children: React.ReactNode;
+ openLeftBar?: boolean;
+ widthElement?: number;
+ className?: string;
+} & Omit, "">;
+
+const LeftColumn: React.FC = ({
+ className,
+ children,
+ widthElement = 0,
+ openLeftBar = false,
+}): JSX.Element => {
+ const mdScreen = 768;
+ return (
+
+
+ {children}
+
+
+ );
+};
+
+LeftColumn.displayName = "LeftColumn";
+export default LeftColumn;
diff --git a/src/components/layouts/ThreeColumn/Maincolumn.tsx b/src/components/layouts/ThreeColumn/Maincolumn.tsx
new file mode 100755
index 0000000..01ada11
--- /dev/null
+++ b/src/components/layouts/ThreeColumn/Maincolumn.tsx
@@ -0,0 +1,18 @@
+import classNames from "classnames";
+import React from "react";
+
+type MainColumnProps = {
+ children: React.ReactNode;
+ className?: string;
+};
+
+function MainColumn({ children, className }: MainColumnProps) {
+ return (
+
+ {children}
+
+ );
+}
+
+export default MainColumn;
+MainColumn.displayName = "MainColumn";
diff --git a/src/components/layouts/ThreeColumn/RightColumn.tsx b/src/components/layouts/ThreeColumn/RightColumn.tsx
new file mode 100755
index 0000000..3026119
--- /dev/null
+++ b/src/components/layouts/ThreeColumn/RightColumn.tsx
@@ -0,0 +1,18 @@
+import classNames from "classnames";
+import React from "react";
+
+type RightColumnProps = {
+ children: React.ReactNode;
+ className?: string;
+};
+
+function RightColumn({ children, className }: RightColumnProps) {
+ return (
+
+ {children}
+
+ );
+}
+
+export default RightColumn;
+RightColumn.displayName = "RightColumn";
diff --git a/src/components/parts/Footer.tsx b/src/components/parts/Footer.tsx
new file mode 100755
index 0000000..fdcb3af
--- /dev/null
+++ b/src/components/parts/Footer.tsx
@@ -0,0 +1,127 @@
+import React, { useMemo } from "react";
+/* -------------------------------------------------------------------------- */
+/* Import Components */
+/* -------------------------------------------------------------------------- */
+import Typography from "components/typography/Typography";
+import { SVGFacebook, SVGInstagram, SVGCircle } from "components/icons";
+import { RouterLink } from "components/typography/RouterLink";
+import Link from "components/typography/Link";
+import { useTranslation } from "react-i18next";
+
+/* -------------------------------------------------------------------------- */
+/* Define consts */
+/* -------------------------------------------------------------------------- */
+const mainLinks = [
+ { label: "footer.accountSettings", url: "/account/settings", disabled: false },
+ { label: "footer.about", url: "/about", disabled: false },
+ { label: "footer.help", url: "/help", disabled: false },
+ { label: "footer.contactUs", url: "/contact-us", disabled: false },
+];
+
+const secondaryLinks = [
+ { index: 1, label: "footer.termsOfUse", url: "/terms-of-use", disabled: false },
+ {
+ index: 2,
+ label: "footer.privacyPolicy",
+ url: "/privacy-policy",
+ disabled: false,
+ },
+ {
+ index: 3,
+ label: "footer.coockiesPolicy",
+ url: "/cookies-policy",
+ disabled: false,
+ },
+];
+
+/* -------------------------------------------------------------------------- */
+/* Difine parts of footer */
+/* -------------------------------------------------------------------------- */
+
+/* -------------------- Icons with social networks icons -------------------- */
+const circleDivider = (
+
+);
+
+/* -------------------------------------------------------------------------- */
+/* Define component footer */
+/* -------------------------------------------------------------------------- */
+
+export function Footer() {
+
+ const { t, i18n } = useTranslation();
+ /* -------------------------- Part with main links -------------------------- */
+ const mainLinksPart = useMemo(
+ () =>
+ mainLinks.map((link) => (
+
+
+ {t(link.label).toUpperCase()}
+
+
+ )),
+ mainLinks
+ );
+ /* ------------------------ Part with secondary links ----------------------- */
+ const secondaryLinksPart = useMemo(
+ () =>
+ secondaryLinks.map((link) => (
+
+ {link.index != 1 && circleDivider}
+
+ {t(link.label) }
+
+
+ )),
+ secondaryLinks
+ );
+
+ /* -------------------------------------------------------------------------- */
+ /* Implement footer component */
+ /* -------------------------------------------------------------------------- */
+ return (
+
+
+
+
+
+ Scipaper
+
+
+
+
+ {mainLinksPart}
+
+
+
+
+
+
+ {" "}
+
+
+
+
+
+
+
+ @ Copyright 2022 Freeland - {t("footer.allRightsReserved")}
+
+
{circleDivider}
+
{secondaryLinksPart}
+
+
+ {t("footer.supportedBy")}
+
+ Comfortel
+
+
+
+
+ );
+}
diff --git a/src/components/parts/GlobalControls.tsx b/src/components/parts/GlobalControls.tsx
old mode 100644
new mode 100755
index f81ac05..46b8ed5
--- a/src/components/parts/GlobalControls.tsx
+++ b/src/components/parts/GlobalControls.tsx
@@ -1,56 +1,40 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React from "react";
-import { useNavigate } from "react-router-dom";
-import { Transition } from "@headlessui/react";
-/* -------------------------------------------------------------------------- */
-/* Hooks */
-/* -------------------------------------------------------------------------- */
-import { useTranslation } from "react-i18next";
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import Button from "components/controls/Button";
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGBackIcon } from "assets/svg/backstab.svg";
-/* -------------------------------------------------------------------------- */
-/* Component */
-/* -------------------------------------------------------------------------- */
-
-export default function GlobalControls() {
- /* ------------------------ // Navigation hook usage ------------------------ */
- const navigate = useNavigate();
- const {t} = useTranslation();
- /* -------------------------------- Component ------------------------------- */
- return (
- <>
-
- navigate(-1)}
- className="font-bold"
- iconed
- >
-
-
-
- >
- );
-}
+/* -------------------------------------------------------------------------- */
+/* Libraries */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import { useNavigate } from "react-router-dom";
+import { Transition } from "@headlessui/react";
+/* -------------------------------------------------------------------------- */
+/* Hooks */
+/* -------------------------------------------------------------------------- */
+import { useTranslation } from "react-i18next";
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* Icons */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* Component */
+/* -------------------------------------------------------------------------- */
+
+export default function GlobalControls() {
+ /* ------------------------ // Navigation hook usage ------------------------ */
+ const navigate = useNavigate();
+ const {t} = useTranslation();
+ /* -------------------------------- Component ------------------------------- */
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/components/parts/Header.tsx b/src/components/parts/Header.tsx
old mode 100644
new mode 100755
index 2682ab6..e38c1d5
--- a/src/components/parts/Header.tsx
+++ b/src/components/parts/Header.tsx
@@ -1,130 +1,183 @@
-/* -------------------------------------------------------------------------- */
-/* Libs */
-/* -------------------------------------------------------------------------- */
-import React, { useEffect } from "react";
-import { useNavigate } from "react-router-dom";
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import ContextMenu from "components/containers/contextmenu/ContextMenu";
-import Logotype from "components/Logotype";
-import Button from "components/controls/Button";
-import Avatar from "components/containers/Avatar";
-import ContextMenuAction from "components/containers/contextmenu/ContextMenuAction";
-/* -------------------------------------------------------------------------- */
-/* SVG */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGSearchIcon } from "assets/svg/search.svg";
-import { ReactComponent as SVGBellIcon } from "assets/svg/bell.svg";
-import { ReactComponent as SVGCogIcon } from "assets/svg/cog.svg";
-import { ReactComponent as SVGUserAddIcon } from "assets/svg/user-add.svg";
-import { ReactComponent as SVGExitIcon } from "assets/svg/exit.svg";
-/* -------------------------------------------------------------------------- */
-/* Hooks */
-/* -------------------------------------------------------------------------- */
-import { useUserStore } from "user/data/userSlice";
-import { useUserViewModel } from "user/controller/userViewModel";
-import { useUIStore } from "ui/data/uiSlice";
-import { useUIViewModel } from "ui/controller/uiViewModel";
-import { useTranslation } from "react-i18next";
-import ServicesNavigationContext from "services/views/ServicesNavigationContext";
-import { useAuthStore } from "auth/data/authSlice";
-import { useAuthViewModel } from "auth/controller/useAuthViewModel";
-
-/* -------------------------------------------------------------------------- */
-/* Main application header */
-/* -------------------------------------------------------------------------- */
-type Props = {
- title?: string;
-}
-/**
- * Main application header
- * @return {JSX.Element}
- */
-export default function Header({title}: Props) {
-
- const {t} = useTranslation();
- const userStore = useUserStore();
- const uiStore = useUIStore();
- const authStore = useAuthStore();
- const {signOut} = useAuthViewModel(authStore);
- const { user, isLoading, getUser } = useUserViewModel(userStore);
- const { showSearchbar } = useUIViewModel(uiStore);
-
- useEffect(() => {
- getUser();
- }, [getUser]);
-
- const navigate = useNavigate();
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
}
- className="absolute w-full min-h-14 sm:w-80 -bottom-3
- sm:top-auto sm:bottom-auto right-0
- mt-5 z-10 sm:transform -translate-1/2
- origin-top-center flex flex-col items-center justify-center !pt-8"
- >
-
-
-
- {isLoading
- ? " "
- : t("hellousr", {
- username: [user?.lastname, user?.firstname].join(" "),
- })}
-
-
- {isLoading ? " " : user?.email}
-
-
{
- navigate('/personal-information');
- }}
- disabled={isLoading}
- className="group w-full py-2 -mx-4 px-4 hover:bg-gray-200/20 rounded-md hover:text-blue-400 transition-colors"
- caption="Account settings"
- icon={
-
- }
- />
- {
- navigate("");
- }}
- disabled={isLoading}
- className="group w-full py-2 -mx-4 px-4 hover:bg-gray-200/20 rounded-md hover:text-blue-400 transition-colors"
- caption={t("account.connect")}
- icon={
-
- }
- />
-
- }
- />
-
-
-
- );
-}
+import classNames from "classnames";
+import { useState, useTransition } from "react";
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import ContextMenuAction from "../drop-down-menu/ContextMenuAction";
+import ContextMenu from "../drop-down-menu/ContextMenu";
+import { Button } from "../Button/Button";
+import Avatar from "../Avatar";
+import Navbar from "../Navbar";
+import Logo from "../Logo";
+import { RouterLink } from "components/typography/RouterLink";
+
+/* -------------------------------------------------------------------------- */
+/* Icons */
+/* -------------------------------------------------------------------------- */
+import {
+ SVGBellNotification,
+ SVGBell,
+ SVGFavoriteOutlined,
+ SVGFolder,
+ SVGFile,
+ SVGEye,
+} from "components/icons";
+import i18n from "localization/i18n";
+import { useTranslation } from "react-i18next";
+import Link from "components/typography/Link";
+import LocalizationButton from "components/LocalizationButton";
+import LogoScipaper from "components/LogoScipaper";
+import Burger from "components/Burger";
+
+const Header = () => {
+ const [authenticated, setAuthenticated] = useState(false);
+ const onClick = () => setAuthenticated(true);
+ const [notification, setNotification] = useState(false);
+ const { t, i18n } = useTranslation();
+
+ /* -------------------------------------------------------------------------- */
+ /* Implement Header Component */
+ /* -------------------------------------------------------------------------- */
+ return (
+
+ {/* Logo and Menu */}
+
+ {/* Logo - start - className="w-7 sm:w-10 " /> */}
+
+
+
+
+ {/* Logo - end - */}
+
+ {/* Menu( Create new - My library - About ) Start */}
+
+ {/* Link Create now - start - */}
+
+ {t("navbar.createNew")}
+
+ {/* Link Create now - end - */}
+
+ {/* Dropdown Menu My library - start - */}
+
+ console.log("My publications")}
+ icon={ }
+ >
+
+ console.log("My Favorites")}
+ icon={ }
+ >
+
+ console.log("My Collections")}
+ icon={ }
+ >
+
+ console.log("Recent Viewed")}
+ icon={ }
+ >
+
+ {/* Dropdown Menu My library - End - */}
+
+ {/* Dropdown Menu About - start - */}
+
+ console.log("About Freeland")}
+ >
+
+ console.log("Contact Us")}
+ >
+
+ console.log("Help")}
+ >
+
+ {/* Dropdown Menu About - End - */}
+
+ {/* Menu( Create new - My library - About ) End */}
+
+
+ {/* Sign in - Sign up - Notification - Avatar - Burger */}
+
+ {!authenticated
+ ? [
+ <>
+
+
+ {t("navbar.auth.signIn")}
+
+ >,
+
+ {t("navbar.auth.signUp")}
+ ,
+ ]
+ : [
+
+
+ {!notification ? (
+
+ ) : (
+
+ )}
+
+ ,
+
+
+
+ K
+
+ ,
+ ]}
+ {/* Burger component will be shown for the small screens */}
+
+
+
+ );
+};
+
+export default Header;
diff --git a/src/components/parts/Loader.tsx b/src/components/parts/Loader.tsx
old mode 100644
new mode 100755
index 1e356c1..794d38a
--- a/src/components/parts/Loader.tsx
+++ b/src/components/parts/Loader.tsx
@@ -1,12 +1,12 @@
-import React from "react";
-import CircleLoader from "components/CircleLoader";
-
-export default function AppLoader() {
- return (
-
- );
-}
+import React from "react";
+import CircleLoader from "components/CircleLoader";
+
+export default function AppLoader() {
+ return (
+
+ );
+}
diff --git a/src/components/parts/sidenav/SideNav.tsx b/src/components/parts/sidenav/SideNav.tsx
old mode 100644
new mode 100755
index b8f1db4..212c6c6
--- a/src/components/parts/sidenav/SideNav.tsx
+++ b/src/components/parts/sidenav/SideNav.tsx
@@ -1,58 +1,58 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React from "react";
-import { useTranslation } from "react-i18next";
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGUserIcon } from "assets/svg/user.svg";
-import { ReactComponent as SVGShieldIcon } from "assets/svg/shield.svg";
-import { ReactComponent as SVGServicesIcon } from "assets/svg/services.svg";
-/* -------------------------------------------------------------------------- */
-/* Types */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import SideNavItem from "./SideNavItem";
-
-/* -------------------------------------------------------------------------- */
-/* SideNav top-level component */
-/* -------------------------------------------------------------------------- */
-type Props = {
- collapsed: boolean;
-};
-
-export default function SideNav({ collapsed }: Props) {
- const { t } = useTranslation();
-
- return (
-
- }
- caption={t("sidemenu.dashboard")}
- to="/"
- />
- }
- caption={t("sidemenu.account")}
- to="/personal-information"
- />
- }
- to="/security"
- />
- }
- to="/services"
- />
-
- );
-}
+/* -------------------------------------------------------------------------- */
+/* Libraries */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import { useTranslation } from "react-i18next";
+/* -------------------------------------------------------------------------- */
+/* Icons */
+/* -------------------------------------------------------------------------- */
+import { ReactComponent as SVGUserIcon } from "assets/svg/user.svg";
+import { ReactComponent as SVGShieldIcon } from "assets/svg/shield.svg";
+import { ReactComponent as SVGServicesIcon } from "assets/svg/services.svg";
+/* -------------------------------------------------------------------------- */
+/* Types */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import SideNavItem from "./SideNavItem";
+
+/* -------------------------------------------------------------------------- */
+/* SideNav top-level component */
+/* -------------------------------------------------------------------------- */
+type Props = {
+ collapsed: boolean;
+};
+
+export default function SideNav({ collapsed }: Props) {
+ const { t } = useTranslation();
+
+ return (
+
+ }
+ caption={t("sidemenu.dashboard")}
+ to="/"
+ />
+ }
+ caption={t("sidemenu.account")}
+ to="/personal-information"
+ />
+ }
+ to="/security"
+ />
+ }
+ to="/services"
+ />
+
+ );
+}
diff --git a/src/components/parts/sidenav/SideNavItem.tsx b/src/components/parts/sidenav/SideNavItem.tsx
old mode 100644
new mode 100755
index 84d7dcd..fc2b629
--- a/src/components/parts/sidenav/SideNavItem.tsx
+++ b/src/components/parts/sidenav/SideNavItem.tsx
@@ -1,75 +1,75 @@
-import { Transition } from "@headlessui/react";
-import classNames from "classnames";
-import React from "react";
-import {
- NavLinkProps,
- useHref,
- useLinkClickHandler,
- useMatch,
- useResolvedPath,
-} from "react-router-dom";
-
-type Props = {
- caption: React.ReactNode;
- icon?: React.ReactNode;
- collapsed?: boolean;
-} & NavLinkProps;
-
-export default function SideNavItem({
- caption,
- to,
- icon,
- replace,
- state,
- target,
- onClick,
- reloadDocument,
- collapsed,
-}: Props) {
- let internalOnClick = useLinkClickHandler(to, {
- replace,
- state,
- target,
- });
-
- const href = useHref(to);
- const resolved = useResolvedPath(to);
- const match = useMatch({ path: resolved.pathname, end: true });
-
- function handleClick(event: React.MouseEvent) {
- if (onClick) onClick(event);
-
- if (!event.defaultPrevented && !reloadDocument) {
- internalOnClick(event);
- }
- }
-
- return (
-
- {icon && {icon}
}
-
- {caption}
-
-
- );
-}
+import { Transition } from "@headlessui/react";
+import classNames from "classnames";
+import React from "react";
+import {
+ NavLinkProps,
+ useHref,
+ useLinkClickHandler,
+ useMatch,
+ useResolvedPath,
+} from "react-router-dom";
+
+type Props = {
+ caption: React.ReactNode;
+ icon?: React.ReactNode;
+ collapsed?: boolean;
+} & NavLinkProps;
+
+export default function SideNavItem({
+ caption,
+ to,
+ icon,
+ replace,
+ state,
+ target,
+ onClick,
+ reloadDocument,
+ collapsed,
+}: Props) {
+ let internalOnClick = useLinkClickHandler(to, {
+ replace,
+ state,
+ target,
+ });
+
+ const href = useHref(to);
+ const resolved = useResolvedPath(to);
+ const match = useMatch({ path: resolved.pathname, end: true });
+
+ function handleClick(event: React.MouseEvent) {
+ if (onClick) onClick(event);
+
+ if (!event.defaultPrevented && !reloadDocument) {
+ internalOnClick(event);
+ }
+ }
+
+ return (
+
+ {icon && {icon}
}
+
+ {caption}
+
+
+ );
+}
diff --git a/src/components/search/Inputgroup.tsx b/src/components/search/Inputgroup.tsx
new file mode 100755
index 0000000..3897f06
--- /dev/null
+++ b/src/components/search/Inputgroup.tsx
@@ -0,0 +1,53 @@
+/**
+ * A container for components
+ * Any component can be a child for this container
+ */
+import React, { useEffect, useState, cloneElement } from "react";
+import classNames from "classnames";
+
+const focusable = ["input", "select", "textarea", "button"];
+
+export type Props = {
+ /**
+ * Container's children
+ */
+ children?: React.ReactNode | React.ReactNode[];
+} & React.HTMLProps;
+
+const Inputgroup = ({ children, className }: Props) => {
+ const [focused, setFocused] = useState(false);
+ useEffect(() => {}, [focused]);
+ const arrChildren = React.Children.toArray(children);
+
+ return (
+
+ );
+};
+
+export default Inputgroup;
diff --git a/src/components/search/SearchBar.tsx b/src/components/search/SearchBar.tsx
new file mode 100644
index 0000000..813b100
--- /dev/null
+++ b/src/components/search/SearchBar.tsx
@@ -0,0 +1,51 @@
+import React, { useState, useEffect } from "react";
+import Inputgroup from "./Inputgroup";
+import SearchInput from "./SearchInput";
+import { Button } from "components/Button/Button";
+import { SVGSearch } from "components/icons";
+import Link from "components/typography/Link";
+import { useTranslation } from "react-i18next";
+
+
+type Props = {
+ className?: string;
+};
+
+/* ------------------------------- Categories ------------------------------- */
+
+
+export function SearchBar({ className }: Props) {
+ const [onSelected, setOnSelected] = useState(""); // Selected item from response list
+ const { t, i18n } = useTranslation();
+
+
+ const searchResolver = (item: any) => {
+ setOnSelected(item.caption);
+ console.log(onSelected);
+ };
+ return (
+ //TOdO Need to add dropdown with categories
+
+
+
+
+ {}}>
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/search/SearchInput.tsx b/src/components/search/SearchInput.tsx
new file mode 100755
index 0000000..1e6e0b5
--- /dev/null
+++ b/src/components/search/SearchInput.tsx
@@ -0,0 +1,163 @@
+/* -------------------------------------------------------------------------- */
+/* Imports */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import classNames from "classnames";
+import { Combobox, Transition } from "@headlessui/react";
+import { Fragment, useEffect, useState } from "react";
+import { useSearchStoreImplementation } from "searchResults/data/searchStoreImplementation";
+import { useSearchViewModel } from "searchResults/controller/searchResultsViewModel";
+import { Loader } from "components/Loader/Loader";
+/* -------------------------------------------------------------------------- */
+/* Component props */
+/* -------------------------------------------------------------------------- */
+type Hint = {
+ id: string;
+ caption: string;
+};
+
+type Props = {
+ onSelected: (value: Hint) => void;
+ disabled?: boolean;
+ inGroup?: boolean;
+ className?: string;
+ maxScrollSize?: number;
+ elementScrollSize?: number;
+ placeHolder?: string;
+};
+/* -------------------------------------------------------------------------- */
+/* styles */
+/* -------------------------------------------------------------------------- */
+const inputStyle = `
+ w-full
+ cursor-default
+ rounded
+ overflow-hidden
+ border
+ border-solid
+ shadow-none
+ border-gray-100
+ focus:outline-none
+ focus:border-gray-200
+ hover:border-gray-200
+ py-2 pl-3
+ text-sm
+ text-gray-900
+`;
+
+const inputList = `
+ absolute z-10 mt-1 w-full max-h-56
+ bg-white shadow-lg
+ rounded
+ overflow-hidden
+ focus:outline-none
+ text-base
+ sm:text-sm
+ overflow-y-scroll
+ shadow-md
+ scrollbar-thin
+ scrollbar-thumb-gray-500
+ scrollbar-track-inherit
+ scrollbar-thumb-rounded`;
+
+const inputInGroup = [
+ `border-none
+ hover:none
+ active:none
+ focus:none
+ `,
+];
+
+/* -------------------------------------------------------------------------- */
+/* Component implementation */
+/* -------------------------------------------------------------------------- */
+
+export default function SearchInput({
+ onSelected,
+ disabled,
+ inGroup = false,
+ className,
+ maxScrollSize = 140,
+ elementScrollSize = 36,
+ placeHolder = "Search...",
+ ...props
+}: Props) {
+ const store = useSearchStoreImplementation();
+ const {
+ changeInputValue,
+ currentSearchState,
+ inputValue,
+ searchResults,
+ isLoading,
+ isFailed,
+ } = useSearchViewModel(store);
+ return (
+
+
+
+
+ changeInputValue(event.target.value)}
+ placeholder={placeHolder}
+ displayValue={() => inputValue}
+ />
+
+
+ {currentSearchState !== "" && !isFailed && !isLoading && (
+
+
+ {searchResults === undefined ||
+ (searchResults.data.length === 0 &&
+ currentSearchState !== "") ? (
+ Nothing found
+ ) : null}
+ {searchResults !== undefined &&
+ searchResults.data.map((result) => (
+
+ classNames(
+ active ? "text-gray-900 bg-blue-50" : "font-normal ",
+ "cursor-default select-none relative py-2 pl-3 pr-9",
+ selected
+ ? "text-gray-900 bg-blue-100"
+ : "font-normal "
+ )
+ }
+ value={result.title}
+ onClick={() => {
+ if (result.title != undefined) {
+ changeInputValue(result.title);
+ }
+ }}
+ >
+ {result.title}
+
+ ))}
+
+
+ )}
+ {isLoading && (
+
+ )}
+
+
+
+ );
+}
diff --git a/src/components/typography/Heading.tsx b/src/components/typography/Heading.tsx
old mode 100644
new mode 100755
index a476ea4..ba7bab6
--- a/src/components/typography/Heading.tsx
+++ b/src/components/typography/Heading.tsx
@@ -1,9 +1,20 @@
-import React from "react";
-
-type Props = {
- children: React.ReactNode;
-};
-
-export default function Heading({ children }: Props) {
- return {children} ;
-}
+import React from "react";
+import classNames from "classnames";
+
+type Props = {
+ className?: string | undefined;
+ children: React.ReactNode;
+};
+
+export default function Heading({ children, className }: Props) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/typography/Link.tsx b/src/components/typography/Link.tsx
new file mode 100755
index 0000000..f26d55e
--- /dev/null
+++ b/src/components/typography/Link.tsx
@@ -0,0 +1,61 @@
+import React from "react";
+import { NavLink, NavLinkProps, To } from "react-router-dom";
+import classNames from "classnames";
+
+type Props = {
+ to?: To;
+ children: React.ReactNode;
+ disabled?: boolean;
+ className?: string;
+} & Omit;
+
+function getURL(to: To): URL {
+ if (typeof to !== "string") {
+ return getURL(to.pathname ?? "");
+ }
+ try {
+ return new URL(to as string);
+ } catch {
+ let outurl = `${window.location.origin}${
+ to.startsWith("/") ? to : "/" + to
+ }`;
+ return new URL(outurl);
+ }
+}
+
+export default function Link({
+ to,
+ children,
+ disabled,
+ className,
+ style,
+ ...props
+}: Props) {
+ const link =
+ to && getURL(to).hostname === window.location.hostname ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ );
+ return link;
+}
diff --git a/src/components/typography/RouterLink.tsx b/src/components/typography/RouterLink.tsx
new file mode 100755
index 0000000..94c86d2
--- /dev/null
+++ b/src/components/typography/RouterLink.tsx
@@ -0,0 +1,23 @@
+import classNames from "classnames";
+import { NavLink, NavLinkProps } from "react-router-dom";
+
+type Props = {
+ disabled?: boolean;
+ children?: React.ReactNode;
+} & NavLinkProps;
+
+export function RouterLink({
+ children,
+ disabled = false,
+ className,
+ to,
+}: Props) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/typography/Typography.stories.tsx b/src/components/typography/Typography.stories.tsx
new file mode 100755
index 0000000..aa45ed0
--- /dev/null
+++ b/src/components/typography/Typography.stories.tsx
@@ -0,0 +1,17 @@
+
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import Typography from './Typography';
+
+export default {
+ title: 'Typography',
+ component: Typography,
+ } as ComponentMeta;
+
+ const Template: ComponentStory = (args) => (
+
+ );
+
+export const TypographyExample = Template.bind({});
+TypographyExample.args = {
+ children: ''
+};
diff --git a/src/components/typography/Typography.tsx b/src/components/typography/Typography.tsx
new file mode 100755
index 0000000..d815927
--- /dev/null
+++ b/src/components/typography/Typography.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import classNames from 'classnames';
+import {
+ FontWeightVariants,
+ TypographyHtmlTagVariants,
+ TypographyFontWeightVariantsMap
+} from 'core/_variants'
+
+
+interface Props extends React.ComponentPropsWithoutRef<"div"> {
+ htmlTag?: TypographyHtmlTagVariants | undefined;
+ fontWeightVariant?: FontWeightVariants;
+};
+
+const typographyFontWeightVariants: TypographyFontWeightVariantsMap = {
+ thin: 'font-thin',
+ extralight: 'font-extralight',
+ light: 'font-light',
+ normal: 'font-normal',
+ medium: 'font-medium',
+ semibold: 'font-semibold',
+ bold: 'font-bold',
+ extrabold: 'font-extrabold',
+ black: 'font-black'
+
+}
+
+const Typography = ({
+ children,
+ htmlTag,
+ fontWeightVariant='normal',
+ className
+ }: Props) => {
+ const As = htmlTag || 'div' ;
+ return (
+
+ {children}
+
+ );
+}
+
+export default Typography
diff --git a/src/core/_variants.ts b/src/core/_variants.ts
old mode 100644
new mode 100755
index 6b41568..621dff1
--- a/src/core/_variants.ts
+++ b/src/core/_variants.ts
@@ -1,13 +1,21 @@
-export type StyleType = "fill" | "outline" | "text";
-
-export type StyleColorVariants = "blue" | "pink" | "red" | "purple" | "yellow" | "sky" | "emerald" | "gray" | "dark-coral";
-
-export type StyleGlobalVariants = "primary" | "base" | "secondary";
-
-export type StyleColorVariantsMap = {
- [Property in StyleColorVariants]: T;
-};
-
-export type StyleGlobalVarinatsMap = {
- [Property in StyleGlobalVariants]: T;
+export type StyleType = "high" | "medium" | "low";
+
+export type StyleColorVariants = "blue" | "pink" | "red" | "purple" | "yellow" | "sky" | "emerald" | "gray" | "dark-coral";
+
+export type StyleGlobalVariants = "primary" | "base" | "secondary";
+
+export type StyleColorVariantsMap = {
+ [Property in StyleColorVariants]: T;
+};
+
+export type StyleGlobalVarinatsMap = {
+ [Property in StyleGlobalVariants]: T;
+}
+
+export type FontWeightVariants = 'thin' | 'extralight' | 'light' | 'normal' | 'medium' | 'semibold' | 'bold' | 'extrabold' | 'black'
+
+export type TypographyHtmlTagVariants = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p";
+
+export type TypographyFontWeightVariantsMap = {
+ [Property in FontWeightVariants]: T;
}
\ No newline at end of file
diff --git a/src/core/dto_model.ts b/src/core/dto_model.ts
old mode 100644
new mode 100755
index 831e76f..789b05a
--- a/src/core/dto_model.ts
+++ b/src/core/dto_model.ts
@@ -1,18 +1,31 @@
-export interface DTOModel {
- status: number;
- type: string;
- message: string;
- description: string;
- data: T;
-}
-
-export interface FailureDTO {
- statusCode: number;
- message: string;
-}
-
-export interface GraphQLFailureDTO {
- errors: {code: string, message: string, name: string}[]
-}
-
+import _ from "lodash";
+export interface DTOModel {
+ status: number;
+ type: string;
+ message: string;
+ description: string;
+ data: T;
+}
+
+export interface FailureDTO {
+ statusCode: number;
+ message: string;
+}
+
+export interface GraphQLFailureDTO {
+ errors: {code: string, message: string, name: string}[]
+}
+
+export function isDTOModel(obj: any): obj is DTOModel {
+ return obj !== null && typeof obj === "object" && !!obj.isDTOModel;
+}
+
+export const isDTO = (obj: any): obj is DTOModel => obj.data !== null &&
+_.isObject(obj.data) &&
+_.has(obj.data, "status") &&
+_.has(obj.data, "type") &&
+_.has(obj.data, "message") &&
+_.has(obj.data, "description") &&
+_.has(obj.data, "data")
+
export const GRAPHQL_UNAUTHORIZED_KEY = "UNAUTHENTICATED";
\ No newline at end of file
diff --git a/src/core/failure.ts b/src/core/failure.ts
old mode 100644
new mode 100755
index 4343033..52090a4
--- a/src/core/failure.ts
+++ b/src/core/failure.ts
@@ -1,92 +1,113 @@
-import { AxiosError } from "axios";
-import _ from "lodash";
-import { DTOModel } from "./dto_model";
-
-
-export interface FailureI {
- status?: number;
- message: string;
- key?: string;
- meta?: FailureMeta;
-}
-
-interface FailureMeta {
- isRequestFailure?: boolean;
- isNetworkFailure?: boolean;
- isPlainFailure?: boolean;
- data?: any;
- [prop: string]: any | undefined;
-}
-
-class Failure implements FailureI {
- message: string;
- key?: string | undefined;
- status?: number | undefined;
- meta?: FailureMeta;
- static isf = true;
-
- /**
- *
- * @param {Failure} o
- * @returns {boolean}
- */
- static isFailure(o: any): o is Failure {
- if(!_.isObjectLike(o)) return false;
- if(_.has(o, 'message')) return true;
- return false;
- };
-
-
-
- constructor({ status, message, key, meta }: FailureI) {
- this.status = status;
- this.message = message;
- this.key = key;
- this.meta = meta;
- console.error([key ?? '', message].filter((i) => i.length !== 0).join(':'));
- }
-
- static fromReason(reason: AxiosError>, key?: string): Failure {
- if (reason.response) {
- return new Failure({
- status: reason.response!.status,
- message:
- reason.response!.data.message ?? "Unknown response error model",
- key: key,
- meta: {
- isRequestFailure: true,
- data: reason.response.data,
- },
- });
- }
- if (reason.request) {
- return new Failure({
- message: `Something went wrong while ${reason.config.url} had been called`,
- key: `unreachedResponse(${key})`,
- meta: {
- isNetworkFailure: true,
- },
- });
- }
- return new Failure({
- message: reason.message,
- key: key,
- meta: { isPlainFailure: true },
- });
- }
-
- toString(): string {
- return `${this.message} (${this.key ?? "key unknown"}) ${
- this.status ?? "status unknown"
- }`;
- }
-
- mapKeyWith(transpiler: (key: string) => string) {
- if (!this.key) {
- return this.message;
- }
- return transpiler(this.key);
- }
-}
-
-export default Failure;
+import { AxiosError } from "axios";
+import _ from "lodash";
+import { DTOModel, isDTO } from "./dto_model";
+
+export interface FailureI {
+ status?: number;
+ message: string;
+ key?: string;
+ meta?: FailureMeta;
+}
+
+interface FailureMeta {
+ isRequestFailure?: boolean;
+ isNetworkFailure?: boolean;
+ isPlainFailure?: boolean;
+ data?: any;
+ [prop: string]: any | undefined;
+}
+
+class Failure implements FailureI {
+ message: string;
+ key?: string | undefined;
+ status?: number | undefined;
+ meta?: FailureMeta;
+ static isf = true;
+
+ /**
+ *
+ * @param {Failure} o
+ * @returns {boolean}
+ */
+ static isFailure(o: any): o is Failure {
+ if (!_.isObjectLike(o)) return false;
+ if (_.has(o, "message")) return true;
+ return false;
+ }
+
+ constructor({ status, message, key, meta }: FailureI) {
+ this.status = status;
+ this.message = message;
+ this.key = key;
+ this.meta = meta;
+ console.error([key ?? "", message].filter((i) => i.length !== 0).join(":"));
+ }
+
+ static fromReason(reason: AxiosError, key?: string): Failure {
+
+ try {
+ if(!Failure.verifyContainsDTO(reason)) {
+ throw reason;
+ }
+ if (reason.response) {
+ return new Failure({
+ status: reason.response!.status,
+ message:
+ reason.response!.data.message ?? "Unknown response error model",
+ key: key,
+ meta: {
+ isRequestFailure: true,
+ data: reason.response.data,
+ },
+ });
+ }
+ if (reason.request) {
+ return new Failure({
+ message: `Something went wrong while ${reason.config.url} had been called`,
+ key: `unreachedResponse(${key})`,
+ meta: {
+ isNetworkFailure: true,
+ },
+ });
+ }
+ throw reason;
+ } catch (error) {
+ return new Failure({
+ message: reason.message,
+ key: key,
+ meta: { isPlainFailure: true },
+ });
+ }
+ }
+
+ /**
+ * Verifies that passed object matches [DTOModel] structure
+ *
+ * Otherwise it will rethrow passed object
+ */
+ static verifyContainsDTO(
+ reason: AxiosError
+ ): reason is AxiosError, any> {
+ return [reason.response, reason.request].reduce((acc, obj) => {
+ if (acc || isDTO(obj)) {
+ return true;
+ }
+ return false;
+ }, false);
+ }
+
+ toString(): string {
+ return `${this.message} (${this.key ?? "key unknown"}) ${
+ this.status ?? "status unknown"
+ }`;
+ }
+
+ mapKeyWith(transpiler: (key: string) => string) {
+ if (!this.key) {
+ return this.message;
+ }
+ return transpiler(this.key);
+ }
+}
+
+export default Failure;
diff --git a/src/core/graphql_schemas.ts b/src/core/graphql_schemas.ts
old mode 100644
new mode 100755
index df5d026..4b85416
--- a/src/core/graphql_schemas.ts
+++ b/src/core/graphql_schemas.ts
@@ -1,4 +1,4 @@
-export interface GraphQLRequest {
- query: string,
-
+export interface GraphQLRequest {
+ query: string,
+
}
\ No newline at end of file
diff --git a/src/core/handlers/execOnAnyFailureHandler.ts b/src/core/handlers/execOnAnyFailureHandler.ts
old mode 100644
new mode 100755
index 6674891..e5c360f
--- a/src/core/handlers/execOnAnyFailureHandler.ts
+++ b/src/core/handlers/execOnAnyFailureHandler.ts
@@ -1,26 +1,26 @@
-import Failure from "core/failure";
-import {
- BaseFailureHandler,
- ResponseFailureHandler,
-} from "core/handlers/responseFailureHandler";
-
-class ExecOnAnyHandler
- extends BaseFailureHandler
- implements ResponseFailureHandler
-{
- private callback: (reason: Failure) => void;
-
- constructor(
- callback: (reason: Failure) => void,
- nextHandler?: ResponseFailureHandler
- ) {
- super(nextHandler);
- this.callback = callback;
- }
-
- handle(reason: Failure): void {
- return this.callback(reason);
- }
-}
-
-export default ExecOnAnyHandler;
+import Failure from "core/failure";
+import {
+ BaseFailureHandler,
+ ResponseFailureHandler,
+} from "core/handlers/responseFailureHandler";
+
+class ExecOnAnyHandler
+ extends BaseFailureHandler
+ implements ResponseFailureHandler
+{
+ private callback: (reason: Failure) => void;
+
+ constructor(
+ callback: (reason: Failure) => void,
+ nextHandler?: ResponseFailureHandler
+ ) {
+ super(nextHandler);
+ this.callback = callback;
+ }
+
+ handle(reason: Failure): void {
+ return this.callback(reason);
+ }
+}
+
+export default ExecOnAnyHandler;
diff --git a/src/core/handlers/forbidenFailureHandler.ts b/src/core/handlers/forbidenFailureHandler.ts
old mode 100644
new mode 100755
index 5d28ac5..4a2a0f7
--- a/src/core/handlers/forbidenFailureHandler.ts
+++ b/src/core/handlers/forbidenFailureHandler.ts
@@ -1,27 +1,27 @@
-import Failure from "core/failure";
-import {
- BaseFailureHandler,
- ResponseFailureHandler,
-} from "./responseFailureHandler";
-
-class ForbidenFailureHandler
- extends BaseFailureHandler
- implements ResponseFailureHandler
-{
- private callback: Function;
- constructor(callback: Function, nextHandler?: ResponseFailureHandler) {
- super(nextHandler);
- this.callback = callback;
- }
-
- handle(reason: Failure): void {
- if (reason.meta && reason.meta.isRequestFailure && reason.status === 403) {
- this.callback(reason);
- return;
- }
-
- return super.handle(reason);
- }
-}
-
-export default ForbidenFailureHandler;
+import Failure from "core/failure";
+import {
+ BaseFailureHandler,
+ ResponseFailureHandler,
+} from "./responseFailureHandler";
+
+class ForbidenFailureHandler
+ extends BaseFailureHandler
+ implements ResponseFailureHandler
+{
+ private callback: Function;
+ constructor(callback: Function, nextHandler?: ResponseFailureHandler) {
+ super(nextHandler);
+ this.callback = callback;
+ }
+
+ handle(reason: Failure): void {
+ if (reason.meta && reason.meta.isRequestFailure && reason.status === 403) {
+ this.callback(reason);
+ return;
+ }
+
+ return super.handle(reason);
+ }
+}
+
+export default ForbidenFailureHandler;
diff --git a/src/core/handlers/notFoundFailureHandler.ts b/src/core/handlers/notFoundFailureHandler.ts
old mode 100644
new mode 100755
index 913177c..1d24671
--- a/src/core/handlers/notFoundFailureHandler.ts
+++ b/src/core/handlers/notFoundFailureHandler.ts
@@ -1,27 +1,27 @@
-import Failure from "core/failure";
-import {
- BaseFailureHandler,
- ResponseFailureHandler,
-} from "./responseFailureHandler";
-
-class NotFoundFailureHandler
- extends BaseFailureHandler
- implements ResponseFailureHandler
-{
- private callback: Function;
- constructor(callback: Function, nextHandler?: ResponseFailureHandler) {
- super(nextHandler);
- this.callback = callback;
- }
-
- handle(reason: Failure): void {
- if (reason.meta && reason.meta.isRequestFailure && reason.status === 404) {
- this.callback(reason);
- return;
- }
-
- return super.handle(reason);
- }
-}
-
-export default NotFoundFailureHandler;
+import Failure from "core/failure";
+import {
+ BaseFailureHandler,
+ ResponseFailureHandler,
+} from "./responseFailureHandler";
+
+class NotFoundFailureHandler
+ extends BaseFailureHandler
+ implements ResponseFailureHandler
+{
+ private callback: Function;
+ constructor(callback: Function, nextHandler?: ResponseFailureHandler) {
+ super(nextHandler);
+ this.callback = callback;
+ }
+
+ handle(reason: Failure): void {
+ if (reason.meta && reason.meta.isRequestFailure && reason.status === 404) {
+ this.callback(reason);
+ return;
+ }
+
+ return super.handle(reason);
+ }
+}
+
+export default NotFoundFailureHandler;
diff --git a/src/core/handlers/responseFailureHandler.ts b/src/core/handlers/responseFailureHandler.ts
old mode 100644
new mode 100755
index 6a4a600..6bf5b4b
--- a/src/core/handlers/responseFailureHandler.ts
+++ b/src/core/handlers/responseFailureHandler.ts
@@ -1,30 +1,30 @@
-import Failure from "../failure";
-
-interface ResponseFailureHandler {
- nextHandler?: ResponseFailureHandler;
- handle(reason: Failure): void;
- setNext(next: ResponseFailureHandler): void;
-}
-
-type FailureCallbackFunction = (reason?: Failure) => void;
-
-class BaseFailureHandler implements ResponseFailureHandler {
- nextHandler?: ResponseFailureHandler | undefined;
-
- constructor(nextHandler?: ResponseFailureHandler) {
- this.nextHandler = nextHandler;
- }
-
- handle(reason: Failure) {
- if (this.nextHandler) {
- return this.nextHandler.handle(reason);
- }
- }
-
- setNext(next: ResponseFailureHandler): void {
- this.nextHandler = next;
- }
-}
-
-export { BaseFailureHandler };
-export type { ResponseFailureHandler, FailureCallbackFunction };
+import Failure from "../failure";
+
+interface ResponseFailureHandler {
+ nextHandler?: ResponseFailureHandler;
+ handle(reason: Failure): void;
+ setNext(next: ResponseFailureHandler): void;
+}
+
+type FailureCallbackFunction = (reason?: Failure) => void;
+
+class BaseFailureHandler implements ResponseFailureHandler {
+ nextHandler?: ResponseFailureHandler | undefined;
+
+ constructor(nextHandler?: ResponseFailureHandler) {
+ this.nextHandler = nextHandler;
+ }
+
+ handle(reason: Failure) {
+ if (this.nextHandler) {
+ return this.nextHandler.handle(reason);
+ }
+ }
+
+ setNext(next: ResponseFailureHandler): void {
+ this.nextHandler = next;
+ }
+}
+
+export { BaseFailureHandler };
+export type { ResponseFailureHandler, FailureCallbackFunction };
diff --git a/src/core/handlers/unauthorizedHandler.ts b/src/core/handlers/unauthorizedHandler.ts
old mode 100644
new mode 100755
index 849a82d..f109ace
--- a/src/core/handlers/unauthorizedHandler.ts
+++ b/src/core/handlers/unauthorizedHandler.ts
@@ -1,37 +1,37 @@
-import { GraphQLFailureDTO, GRAPHQL_UNAUTHORIZED_KEY } from "core/dto_model";
-import Failure from "core/failure";
-import {
- BaseFailureHandler,
- FailureCallbackFunction,
- ResponseFailureHandler,
-} from "core/handlers/responseFailureHandler";
-
-class UnauthorizedHandler
- extends BaseFailureHandler
- implements ResponseFailureHandler
-{
- private cb: FailureCallbackFunction;
-
- constructor(callback: FailureCallbackFunction, nextHandler?: ResponseFailureHandler) {
- super(nextHandler);
- this.cb = callback;
- }
-
- handle(reason: Failure): void {
- if (!reason.meta || !reason.meta!.isRequestFailure) {
- return super.handle(reason);
- }
- if (
- reason.status === 401 ||
- (reason.meta.data &&
- Array.isArray(reason.meta.data.erros) &&
- (reason.meta.data as GraphQLFailureDTO).errors.findIndex((error) => {
- return error.code === GRAPHQL_UNAUTHORIZED_KEY;
- }) > -1)
- ) {
- this.cb();
- }
- }
-}
-
-export default UnauthorizedHandler;
+import { GraphQLFailureDTO, GRAPHQL_UNAUTHORIZED_KEY } from "core/dto_model";
+import Failure from "core/failure";
+import {
+ BaseFailureHandler,
+ FailureCallbackFunction,
+ ResponseFailureHandler,
+} from "core/handlers/responseFailureHandler";
+
+class UnauthorizedHandler
+ extends BaseFailureHandler
+ implements ResponseFailureHandler
+{
+ private cb: FailureCallbackFunction;
+
+ constructor(callback: FailureCallbackFunction, nextHandler?: ResponseFailureHandler) {
+ super(nextHandler);
+ this.cb = callback;
+ }
+
+ handle(reason: Failure): void {
+ if (!reason.meta || !reason.meta!.isRequestFailure) {
+ return super.handle(reason);
+ }
+ if (
+ reason.status === 401 ||
+ (reason.meta.data &&
+ Array.isArray(reason.meta.data.erros) &&
+ (reason.meta.data as GraphQLFailureDTO).errors.findIndex((error) => {
+ return error.code === GRAPHQL_UNAUTHORIZED_KEY;
+ }) > -1)
+ ) {
+ this.cb();
+ }
+ }
+}
+
+export default UnauthorizedHandler;
diff --git a/src/core/helpers.ts b/src/core/helpers.ts
old mode 100644
new mode 100755
index 61f41e3..fe40fa8
--- a/src/core/helpers.ts
+++ b/src/core/helpers.ts
@@ -1,13 +1,24 @@
-export const handleScrollTo = (e: React.MouseEvent) => {
- e.preventDefault();
- const link: HTMLAnchorElement = e.currentTarget;
- if (link.hash && link.hash != "") {
- document
- .getElementById(link.hash.substring(1, undefined))
- ?.scrollIntoView({ behavior: "smooth", block: "start" });
- }
-};
-
-export function capitalization (str: string) {
- return str.substring(0,1).toUpperCase() + str.substring(1);
+export const handleScrollTo = (e: React.MouseEvent) => {
+ e.preventDefault();
+ const link: HTMLAnchorElement = e.currentTarget;
+ if (link.hash && link.hash != "") {
+ document
+ .getElementById(link.hash.substring(1, undefined))
+ ?.scrollIntoView({ behavior: "smooth", block: "start" });
+ }
+};
+
+export function capitalization(str: string): string {
+ return str.substring(0, 1).toUpperCase() + str.substring(1);
+}
+
+export function formatNumber(num: number): string {
+ return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 ');
+}
+
+export function joinClassnames(first?: string, second?: string): string {
+ return [
+ first ?? '',
+ second ?? '',
+ ].join('');
}
\ No newline at end of file
diff --git a/src/core/httpClient.ts b/src/core/httpClient.ts
new file mode 100755
index 0000000..83f7924
--- /dev/null
+++ b/src/core/httpClient.ts
@@ -0,0 +1,12 @@
+import axios from "axios";
+export const BASE_URL = process.env.REACT_APP_INTEGRATOR_URL;
+export const GRAPHQL_URL = process.env.REACT_APP_GRAPHQL_URL ?? "";
+
+const instance = axios.create({
+ baseURL: BASE_URL + (process.env.REACT_APP_INTEGRATOR_API_VERSION ?? ""),
+});
+export { instance };
+const integratorApiClient = axios.create({
+ baseURL: BASE_URL + (process.env.REACT_APP_INTEGRATOR_API_VERSION ?? ""),
+});
+export { integratorApiClient };
diff --git a/src/gql/gqlQuery.ts b/src/gql/gqlQuery.ts
old mode 100644
new mode 100755
index a0bfa98..9d9e47f
--- a/src/gql/gqlQuery.ts
+++ b/src/gql/gqlQuery.ts
@@ -1,4 +1,4 @@
-export interface GraphQLQueryInterface {
- query: string;
- variables?: T
+export interface GraphQLQueryInterface {
+ query: string;
+ variables?: T
}
\ No newline at end of file
diff --git a/src/gql/updateUserMutation.ts b/src/gql/updateUserMutation.ts
old mode 100644
new mode 100755
index 531c545..76d5f89
--- a/src/gql/updateUserMutation.ts
+++ b/src/gql/updateUserMutation.ts
@@ -1,31 +1,31 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import gql from "graphql-tag";
-import { GraphQLQueryInterface } from "./gqlQuery";
-/* -------------------------------------------------------------------------- */
-/* GraphQL */
-/* -------------------------------------------------------------------------- */
-const updateGraphQLQuery = gql`
- mutation UpdateProfile($data: UserProfileInputObject!) {
- updateProfile(data: $data) {
- __typename
- }
- }
-`;
-
-export interface QueryInterface
- extends GraphQLQueryInterface<{
- data: {
- account_id?: String;
- username?: String;
- email?: String;
- locale?: String;
- image_url?: String;
- first_name?: String;
- last_name?: String;
- bio?: String;
- }
- }> {}
-
-export default updateGraphQLQuery;
+/* -------------------------------------------------------------------------- */
+/* Libraries */
+/* -------------------------------------------------------------------------- */
+import gql from "graphql-tag";
+import { GraphQLQueryInterface } from "./gqlQuery";
+/* -------------------------------------------------------------------------- */
+/* GraphQL */
+/* -------------------------------------------------------------------------- */
+const updateGraphQLQuery = gql`
+ mutation UpdateProfile($data: UserProfileInputObject!) {
+ updateProfile(data: $data) {
+ __typename
+ }
+ }
+`;
+
+export interface QueryInterface
+ extends GraphQLQueryInterface<{
+ data: {
+ account_id?: String;
+ username?: String;
+ email?: String;
+ locale?: String;
+ image_url?: String;
+ first_name?: String;
+ last_name?: String;
+ bio?: String;
+ }
+ }> {}
+
+export default updateGraphQLQuery;
diff --git a/src/index.css b/src/index.css
old mode 100644
new mode 100755
index a56bc58..f765ccc
--- a/src/index.css
+++ b/src/index.css
@@ -1,296 +1,207 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
- @font-face {
- font-family: "Poppins";
- src: url("assets/fonts/Poppins-Bold.eot");
- src: url("assets/fonts/Poppins-Bold.eot?#iefix") format("embedded-opentype"),
- url("assets/fonts/Poppins-Bold.woff2") format("woff2"),
- url("assets/fonts/Poppins-Bold.ttf") format("truetype");
- font-weight: 700;
- font-style: normal;
- font-display: swap;
- }
-
- @font-face {
- font-family: "Poppins";
- src: url("assets/fonts/Poppins-Medium.eot");
- src: url("assets/fonts/Poppins-Medium.eot?#iefix") format("embedded-opentype"),
- url("assets/fonts/Poppins-Medium.woff2") format("woff2"),
- url("assets/fonts/Poppins-Medium.ttf") format("truetype");
- font-weight: 500;
- font-style: normal;
- font-display: swap;
- }
-
- @font-face {
- font-family: "Poppins";
- src: url("assets/fonts/Poppins-Regular.eot");
- src: url("assets/fonts/Poppins-Regular.eot?#iefix") format("embedded-opentype"),
- url("assets/fonts/Poppins-Regular.woff2") format("woff2"),
- url("assets/fonts/Poppins-Regular.ttf") format("truetype");
- font-weight: normal;
- font-style: normal;
- font-display: swap;
- }
-
- @font-face {
- font-family: "Poppins";
- src: url("assets/fonts/Poppins-Thin.eot");
- src: url("assets/fonts/Poppins-Thin.eot?#iefix") format("embedded-opentype"),
- url("assets/fonts/Poppins-Thin.woff2") format("woff2"),
- url("assets/fonts/Poppins-Thin.ttf") format("truetype");
- font-weight: 300;
- font-style: normal;
- font-display: swap;
- }
-
- html {
- font-family: "Poppins", -apple-system, "Segoe UI", system-ui, "Roboto",
- "Helvetica Neue", "Arial";
- color: var(--color-text);
- }
-
- body {
- background-color: var(--color-aside);
- }
-
- *,
- ::before,
- ::after,
- .theme-dark *,
- .theme-dark::before,
- .theme-dark::after {
- /* ------------------------------- Body color ------------------------------- */
- --color-body: rgba(15, 20, 34, 1);
- --color-aside: #0a0e1a;
- --color-footer: 10 10 10;
- --color-text: rgb(255 255 255);
- --color-serv: 25 29 43;
- --color-serv-50: 227 230 237;
- --color-serv-100: 200 205 223;
- --color-serv-200: 142 152 189;
- --color-serv-300: 90 104 155;
- --color-serv-400: 58 67 100;
- --color-serv-500: 25 29 43;
- --color-serv-600: 21 24 35;
- --color-serv-700: 15 17 26;
- --color-serv-800: 9 11 16;
- --color-serv-900: 6 7 10;
- /* ---------------------------------- Blue ---------------------------------- */
- --color-blue-50: 229 241 255;
- --color-blue-100: 204 228 255;
- --color-blue-200: 153 201 255;
- --color-blue-300: 102 173 255;
- --color-blue-400: 51 146 255;
- --color-blue-500: 0 117 255;
- --color-blue-600: 0 95 204;
- --color-blue-700: 0 71 153;
- --color-blue-800: 0 48 102;
- --color-blue-900: 0 24 51;
- /* ---------------------------------- Pink ---------------------------------- */
- --color-pink-50: 253 232 252;
- --color-pink-100: 251 214 249;
- --color-pink-200: 246 167 243;
- --color-pink-300: 241 126 237;
- --color-pink-400: 237 85 232;
- --color-pink-500: 232 40 227;
- --color-pink-600: 198 21 192;
- --color-pink-700: 147 16 143;
- --color-pink-800: 97 10 94;
- --color-pink-900: 51 5 49;
- /* ----------------------------------- Red ---------------------------------- */
- --color-red-50: 254 236 231;
- --color-red-100: 252 221 212;
- --color-red-200: 250 184 163;
- --color-red-300: 247 150 120;
- --color-red-400: 245 116 77;
- --color-red-500: 242 78 30;
- --color-red-600: 207 58 12;
- --color-red-700: 154 43 9;
- --color-red-800: 101 28 6;
- --color-red-900: 53 15 3;
- /* --------------------------------- Purple --------------------------------- */
- --color-purple-50: 246 240 255;
- --color-purple-100: 238 224 255;
- --color-purple-200: 217 189 255;
- --color-purple-300: 200 158 255;
- --color-purple-400: 180 122 255;
- --color-purple-500: 162 89 255;
- --color-purple-600: 122 20 255;
- --color-purple-700: 91 0 209;
- --color-purple-800: 60 0 138;
- --color-purple-900: 31 0 71;
- /* --------------------------------- Yellow --------------------------------- */
- --color-yellow-50: 254 250 241;
- --color-yellow-100: 252 243 222;
- --color-yellow-200: 249 229 185;
- --color-yellow-300: 245 214 143;
- --color-yellow-400: 241 199 101;
- --color-yellow-500: 236 178 46;
- --color-yellow-600: 220 160 20;
- --color-yellow-700: 192 139 17;
- --color-yellow-800: 159 116 14;
- --color-yellow-900: 117 85 11;
- /* ---------------------------------- Gray ---------------------------------- */
- --color-gray-50: 250 250 250;
- --color-gray-100: 242 242 242;
- --color-gray-200: 232 232 232;
- --color-gray-300: 217 217 217;
- --color-gray-400: 201 201 201;
- --color-gray-500: 186 186 186;
- --color-gray-600: 168 168 168;
- --color-gray-700: 148 148 148;
- --color-gray-800: 125 125 125;
- --color-gray-900: 89 89 89;
- /* ----------------------------------- Sky ---------------------------------- */
- --color-sky-50: 240 251 255;
- --color-sky-100: 225 246 255;
- --color-sky-200: 184 235 255;
- --color-sky-300: 143 223 254;
- --color-sky-400: 93 208 254;
- --color-sky-500: 26 188 254;
- --color-sky-600: 1 171 239;
- --color-sky-700: 1 149 208;
- --color-sky-800: 1 124 173;
- --color-sky-900: 1 91 127;
- /* --------------------------------- Emerald -------------------------------- */
- --color-emerald-50: 240 255 254;
- --color-emerald-100: 225 255 253;
- --color-emerald-200: 189 254 251;
- --color-emerald-300: 149 254 248;
- --color-emerald-400: 89 253 245;
- --color-emerald-500: 3 248 237;
- --color-emerald-600: 3 227 216;
- --color-emerald-700: 2 197 187;
- --color-emerald-800: 2 166 158;
- --color-emerald-900: 1 121 115;
- /* ------------------------------- Dark coral ------------------------------- */
- --color-dark-coral-50: 231 237 243;
- --color-dark-coral-100: 207 218 231;
- --color-dark-coral-200: 159 182 208;
- --color-dark-coral-300: 112 145 184;
- --color-dark-coral-400: 74 110 150;
- --color-dark-coral-500: 51 75 103;
- --color-dark-coral-600: 41 60 82;
- --color-dark-coral-700: 30 45 61;
- --color-dark-coral-800: 20 30 41;
- --color-dark-coral-900: 10 15 20;
- }
-
- .pl,
- .pl__worm {
- animation-duration: 3s;
- animation-iteration-count: infinite;
- }
-
- .pl {
- animation-name: bump;
- animation-timing-function: linear;
- width: 8em;
- height: 8em;
- }
-
- .pl__worm {
- animation-name: worm;
- animation-timing-function: cubic-bezier(0.42, 0.17, 0.75, 0.83);
- }
-
- /* Dark theme */
- @media (prefers-color-scheme: dark) {
- :root {
- --bg: hsl(var(--hue), 10%, 10%);
- --fg: hsl(var(--hue), 10%, 90%);
- }
- }
-
- /* Animations */
- @keyframes bump {
-
- from,
- 42%,
- 46%,
- 51%,
- 55%,
- 59%,
- 63%,
- 67%,
- 71%,
- 74%,
- 78%,
- 81%,
- 85%,
- 88%,
- 92%,
- to {
- transform: translate(0, 0);
- }
-
- 44% {
- transform: translate(1.33%, 6.75%);
- }
-
- 53% {
- transform: translate(-16.67%, -0.54%);
- }
-
- 61% {
- transform: translate(3.66%, -2.46%);
- }
-
- 69% {
- transform: translate(-0.59%, 15.27%);
- }
-
- 76% {
- transform: translate(-1.92%, -4.68%);
- }
-
- 83% {
- transform: translate(9.38%, 0.96%);
- }
-
- 90% {
- transform: translate(-4.55%, 1.98%);
- }
- }
-
- @keyframes worm {
- from {
- stroke-dashoffset: 10;
- }
-
- 25% {
- stroke-dashoffset: 295;
- }
-
- to {
- stroke-dashoffset: 1165;
- }
- }
-}
-
-@layer components {
- /* Separate
- ======================================================================== */
-
- .separate {
- @apply flex items-center text-center;
- }
-
- .separate::after,
- .separate::before {
- content: '';
- @apply border-b border-gray-300 flex-1;
- }
-
- .separate:not(:empty)::after {
- @apply ml-2;
- }
-
- .separate:not(:empty)::before {
- @apply mr-2;
- }
-}
\ No newline at end of file
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ @font-face {
+ font-family: "Poppins";
+ src: url("assets/fonts/Poppins-Bold.eot");
+ src: url("assets/fonts/Poppins-Bold.eot?#iefix") format("embedded-opentype"),
+ url("assets/fonts/Poppins-Bold.woff2") format("woff2"),
+ url("assets/fonts/Poppins-Bold.ttf") format("truetype");
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Poppins";
+ src: url("assets/fonts/Poppins-Medium.eot");
+ src: url("assets/fonts/Poppins-Medium.eot?#iefix")
+ format("embedded-opentype"),
+ url("assets/fonts/Poppins-Medium.woff2") format("woff2"),
+ url("assets/fonts/Poppins-Medium.ttf") format("truetype");
+ font-weight: 500;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Poppins";
+ src: url("assets/fonts/Poppins-Regular.eot");
+ src: url("assets/fonts/Poppins-Regular.eot?#iefix")
+ format("embedded-opentype"),
+ url("assets/fonts/Poppins-Regular.woff2") format("woff2"),
+ url("assets/fonts/Poppins-Regular.ttf") format("truetype");
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Poppins";
+ src: url("assets/fonts/Poppins-Thin.eot");
+ src: url("assets/fonts/Poppins-Thin.eot?#iefix") format("embedded-opentype"),
+ url("assets/fonts/Poppins-Thin.woff2") format("woff2"),
+ url("assets/fonts/Poppins-Thin.ttf") format("truetype");
+ font-weight: 300;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-Black.ttf") format("truetype");
+ font-weight: 900;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-ExtraBold.ttf") format("truetype");
+ font-weight: 800;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-Bold.ttf") format("truetype");
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-SemiBold.ttf") format("truetype");
+ font-weight: 600;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-Medium.ttf") format("truetype");
+ font-weight: 500;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-Regular.ttf") format("truetype");
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-Light.ttf") format("truetype");
+ font-weight: 300;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-ExtraLight.ttf") format("truetype");
+ font-weight: 200;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ @font-face {
+ font-family: "Inter";
+ src: url("assets/fonts/Inter-Thin.ttf") format("truetype");
+ font-weight: 100;
+ font-style: normal;
+ font-display: swap;
+ }
+
+ html {
+ font-family: "Inter", "Poppins", -apple-system, "Segoe UI", system-ui,
+ "Roboto", "Helvetica Neue", "Arial";
+ color: var(--color-text);
+ }
+
+ *,
+ ::before,
+ ::after,
+ .theme-dark *,
+ .theme-dark::before,
+ .theme-dark::after {
+ /* ------------------------------- Body color ------------------------------- */
+ --color-aside: #0a0e1a;
+ --color-footer: 10 10 10;
+ --color-text: rgb(38, 38, 38);
+ /* -------------------------------------------------------------------------- */
+ --color-serv: 25 29 43;
+ --color-serv-50: 227 230 237;
+ --color-serv-100: 200 205 223;
+ --color-serv-200: 142 152 189;
+ --color-serv-300: 90 104 155;
+ --color-serv-400: 58 67 100;
+ --color-serv-500: 25 29 43;
+ --color-serv-600: 21 24 35;
+ --color-serv-700: 15 17 26;
+ --color-serv-800: 9 11 16;
+ --color-serv-900: 6 7 10;
+ /* ---------------------------------- Blue ---------------------------------- */
+ --color-blue-50: 230 247 255;
+ --color-blue-100: 186 231 255;
+ --color-blue-200: 145 213 255;
+ --color-blue-300: 89 192 255;
+ --color-blue-400: 64 169 255;
+ --color-blue-500: 24 144 255;
+ --color-blue-600: 9 109 217;
+ --color-blue-700: 0 80 179;
+ --color-blue-800: 0 58 140;
+ --color-blue-900: 0 39 102;
+ /* ---------------------------------- Gray ---------------------------------- */
+ --color-gray-50: 250 250 250;
+ --color-gray-75: 240 240 240;
+ --color-gray-100: 235 235 235;
+ --color-gray-200: 214 214 214;
+ --color-gray-300: 191 191 191;
+ --color-gray-400: 166 166 166;
+ --color-gray-500: 140 140 140;
+ --color-gray-600: 115 115 115;
+ --color-gray-700: 89 89 89;
+ --color-gray-800: 64 64 64;
+ --color-gray-900: 38 38 38;
+ }
+
+ /* Dark theme */
+ @media (prefers-color-scheme: dark) {
+ :root {
+ --bg: hsl(var(--hue), 10%, 10%);
+ --fg: hsl(var(--hue), 10%, 90%);
+ }
+ }
+}
+
+@layer components {
+ /* Separate
+ ======================================================================== */
+
+ .separate {
+ @apply flex items-center text-center;
+ }
+
+ .separate::after,
+ .separate::before {
+ content: "";
+ @apply border-b border-gray-300 flex-1;
+ }
+
+ .separate:not(:empty)::after {
+ @apply ml-2;
+ }
+
+ .separate:not(:empty)::before {
+ @apply mr-2;
+ }
+}
diff --git a/src/index.tsx b/src/index.tsx
old mode 100644
new mode 100755
index 67c9221..9f61062
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,19 +1,56 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import './index.css';
-import App from './App';
-import reportWebVitals from './reportWebVitals';
-
-import "./localization/i18n";
-
-ReactDOM.render(
-
-
- ,
- document.getElementById('root')
-);
-
-// If you want to start measuring performance in your app, pass a function
-// to log results (for example: reportWebVitals(console.log))
-// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals();
+import React from "react";
+import ReactDOM from "react-dom/client";
+import { BrowserRouter, Routes, Route } from "react-router-dom";
+import "./index.css";
+import App from "./App";
+import reportWebVitals from "./reportWebVitals";
+
+import "./localization/i18n";
+import About from "pages/Information/About";
+import Help from "pages/Information/Help";
+import ContactUs from "pages/Information/ContactUs";
+import TermsOfUse from "pages/Information/TermsOfUse";
+import PrivacyPolicy from "pages/Information/PrivacyPolicy";
+import CookiesPolicy from "pages/Information/CookiesPolicy";
+import AccountSettings from "pages/Information/AccountSettings";
+import { store } from "store/store";
+import { Provider } from "react-redux";
+import { SearchResultsPage } from "pages/SearchResultsPage";
+import AnArticle from "components/fetchAnArticle/AnArticle";
+import NotFound from "components/fetchAnArticle/NotFound";
+import AnArticleBody from "components/fetchAnArticle/AnArticleBody";
+
+const rootElement = document.getElementById("root");
+if (!rootElement) throw new Error("Failed to find the root element");
+const root = ReactDOM.createRoot(rootElement);
+root.render(
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+ } />
+ } />
+
+
+ } />
+
+ } />
+ }>
+
+
+
+
+);
+
+// If you want to start measuring performance in your app, pass a function
+// to log results (for example: reportWebVitals(console.log))
+// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+reportWebVitals();
diff --git a/src/localization/controller/langugeViewModel.ts b/src/localization/controller/langugeViewModel.ts
old mode 100644
new mode 100755
index 7674ba3..748effa
--- a/src/localization/controller/langugeViewModel.ts
+++ b/src/localization/controller/langugeViewModel.ts
@@ -1,27 +1,27 @@
-import { Lang, Langs, languages, popularLangKeys } from "localization/i18n";
-import React from "react";
-import { useTranslation } from "react-i18next";
-
-const useLangugeViewModel = () => {
- const { i18n } = useTranslation();
- const switchLang = React.useCallback(
- (lang: string) => i18n.changeLanguage(lang),
- [i18n]
- );
-
- const popularLanguages = Object.keys(languages)
- .filter((k) => popularLangKeys.includes(k))
- .reduce((acc, k) => {
- acc.set(k, languages[k as Langs]);
- return acc;
- }, new Map());
-
- return {
- selected: i18n.resolvedLanguage as Langs,
- languages: languages,
- popularLanguages,
- switchLang,
- };
-};
-
-export { useLangugeViewModel };
+import { Lang, Langs, languages, popularLangKeys } from "localization/i18n";
+import React from "react";
+import { useTranslation } from "react-i18next";
+
+const useLangugeViewModel = () => {
+ const { i18n } = useTranslation();
+ const switchLang = React.useCallback(
+ (lang: string) => i18n.changeLanguage(lang),
+ [i18n]
+ );
+
+ const popularLanguages = Object.keys(languages)
+ .filter((k) => popularLangKeys.includes(k))
+ .reduce((acc, k) => {
+ acc.set(k, languages[k as Langs]);
+ return acc;
+ }, new Map());
+
+ return {
+ selected: i18n.resolvedLanguage as Langs,
+ languages: languages,
+ popularLanguages,
+ switchLang,
+ };
+};
+
+export { useLangugeViewModel };
diff --git a/src/localization/i18n.ts b/src/localization/i18n.ts
old mode 100644
new mode 100755
index 603bf62..dd4c454
--- a/src/localization/i18n.ts
+++ b/src/localization/i18n.ts
@@ -1,35 +1,35 @@
-import i18n from "i18next";
-import { initReactI18next } from "react-i18next";
-import LanguageDetector from "i18next-browser-languagedetector";
-import Backend from "i18next-http-backend";
-
-export type Langs = "ru" | "en";
-
-export interface Lang {
- nativeName: string;
-}
-
-export const languages: Record = {
- ru: {
- nativeName: "Русский",
- },
- en: {
- nativeName: "English",
- },
-};
-export const popularLangKeys = ["ru", "en"];
-const fallbackLng: Langs = "en";
-
-i18n
- .use(Backend)
- .use(LanguageDetector)
- .use(initReactI18next)
- .init({
- debug: process.env.NODE_ENV === "development" ? true : false,
- fallbackLng,
- interpolation: {
- escapeValue: false,
- },
- });
-
-export default i18n;
+import i18n from "i18next";
+import { initReactI18next } from "react-i18next";
+import LanguageDetector from "i18next-browser-languagedetector";
+import Backend from "i18next-http-backend";
+
+export type Langs = "ru" | "en";
+
+export interface Lang {
+ nativeName: string;
+}
+
+export const languages: Record = {
+ ru: {
+ nativeName: "Русский",
+ },
+ en: {
+ nativeName: "English",
+ },
+};
+export const popularLangKeys = ["ru", "en"];
+const fallbackLng: Langs = "ru";
+
+i18n
+ .use(Backend)
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ debug: process.env.NODE_ENV === "development" ? true : false,
+ fallbackLng,
+ interpolation: {
+ escapeValue: false,
+ },
+ });
+
+export default i18n;
diff --git a/src/localization/views/LanguageSelector.tsx b/src/localization/views/LanguageSelector.tsx
old mode 100644
new mode 100755
index 15791a1..afa45fb
--- a/src/localization/views/LanguageSelector.tsx
+++ b/src/localization/views/LanguageSelector.tsx
@@ -1,90 +1,89 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React from "react";
-import { Langs } from "localization/i18n";
-import { useTranslation } from "react-i18next";
-/* -------------------------------------------------------------------------- */
-/* Hooks */
-/* -------------------------------------------------------------------------- */
-import { useLangugeViewModel } from "localization/controller/langugeViewModel";
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import Combox from "components/controls/Combox";
-import Modal from "components/containers/modal/Modal";
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import classNames from "classnames";
-/* -------------------------------------------------------------------------- */
-/* Properties */
-/* -------------------------------------------------------------------------- */
-type Props = {
- isShown: boolean;
- onClose: VoidFunction;
-};
-/* -------------------------------------------------------------------------- */
-/* Language switcher */
-/* -------------------------------------------------------------------------- */
-function CheckIcon(props: React.SVGProps) {
- return (
-
-
-
-
- );
-}
-
-export default function LanguageSelector({ isShown, onClose }: Props) {
- const { t } = useTranslation();
- const { selected, languages, switchLang } = useLangugeViewModel();
-
- return (
-
-
- {t("selectLanguage")}
- languages[key as Langs].nativeName}
- keyMaper={(key) => key}
- setSelected={switchLang}
- filterRule={(query, item) => {
- if (query === "") {
- return true;
- }
- return languages[item as Langs].nativeName
- .toLowerCase()
- .replace(/\s+/g, "")
- .includes(query.toLowerCase().replace(/\s+/g, ""));
- }}
- selected={selected}
- itemBuilder={({ selected, active }, item) => {
- return (
-
-
- {languages[item as Langs].nativeName}
-
-
-
- );
- }}
- />
-
- );
-}
+/* -------------------------------------------------------------------------- */
+/* Libraries */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+import { Langs } from "localization/i18n";
+import { useTranslation } from "react-i18next";
+/* -------------------------------------------------------------------------- */
+/* Hooks */
+/* -------------------------------------------------------------------------- */
+import { useLangugeViewModel } from "localization/controller/langugeViewModel";
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+import Combox from "components/controls/Combox";
+import Modal from "components/containers/modal/Modal";
+/* -------------------------------------------------------------------------- */
+/* Icons */
+/* -------------------------------------------------------------------------- */
+import classNames from "classnames";
+/* -------------------------------------------------------------------------- */
+/* Properties */
+/* -------------------------------------------------------------------------- */
+type Props = {
+ isShown: boolean;
+ onClose: VoidFunction;
+};
+/* -------------------------------------------------------------------------- */
+/* Language switcher */
+/* -------------------------------------------------------------------------- */
+function CheckIcon(props: React.SVGProps) {
+ return (
+
+
+
+
+ );
+}
+
+export default function LanguageSelector({ isShown, onClose }: Props) {
+ const { t } = useTranslation();
+ const { selected, languages, switchLang } = useLangugeViewModel();
+
+ return (
+
+ {t("selectLanguage")}
+ languages[key as Langs].nativeName}
+ keyMaper={(key) => key}
+ setSelected={switchLang}
+ filterRule={(query, item) => {
+ if (query === "") {
+ return true;
+ }
+ return languages[item as Langs].nativeName
+ .toLowerCase()
+ .replace(/\s+/g, "")
+ .includes(query.toLowerCase().replace(/\s+/g, ""));
+ }}
+ selected={selected}
+ itemBuilder={({ selected, active }, item) => {
+ return (
+
+
+ {languages[item as Langs].nativeName}
+
+
+
+ );
+ }}
+ />
+
+ );
+}
diff --git a/src/logo.svg b/src/logo.svg
old mode 100644
new mode 100755
diff --git a/src/pages/Account.tsx b/src/pages/Account.tsx
deleted file mode 100644
index 572cbec..0000000
--- a/src/pages/Account.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React, { useEffect, useState } from "react";
-import { useTranslation } from "react-i18next";
-/* -------------------------------------------------------------------------- */
-/* Hooks */
-/* -------------------------------------------------------------------------- */
-import { useUserStore } from "user/data/userSlice";
-import { useUserViewModel } from "user/controller/userViewModel";
-import { useLangugeViewModel } from "localization/controller/langugeViewModel";
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import Area from "components/containers/area/Area";
-import IconedAreaCaption from "components/containers/area/IconedAreaCaption";
-import Avatar from "components/containers/Avatar";
-import AreaCaption from "components/containers/area/AreaCaption";
-import TextAction from "components/controls/TextAction";
-import AreaValue from "components/containers/area/AreaValue";
-import AreaDescription from "components/containers/area/AreaDescription";
-import PersonalInfomrationEditor from "user/views/PersonalInfomrationEditor";
-import LanguageSelector from "localization/views/LanguageSelector";
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGEnvelopeIcon } from "assets/svg/envelope.svg";
-import { ReactComponent as SVGGlobeIcon } from "assets/svg/globe.svg";
-import { ReactComponent as SVGUsersIcon } from "assets/svg/users.svg";
-/* -------------------------------------------------------------------------- */
-/* Account information management page */
-/* -------------------------------------------------------------------------- */
-/**
- * Account information management page
- * @return {JSX.Element}
- */
-const AccountSettings = (): JSX.Element => {
- const store = useUserStore();
-
- const [personalInfoModalShown, setPersonalInfoModalShown] = useState(false);
- const [languageSelecting, setLanguageSelecting] = useState(false);
-
- const { user, getUser } = useUserViewModel(store);
- const { languages, selected } = useLangugeViewModel();
-
- useEffect(() => {
- getUser();
- }, [getUser]);
-
- const { t } = useTranslation();
-
- return (
-
-
-
-
-
- {user?.firstname}
-
- {user?.lastname}
-
-
- {user?.username}
-
-
- setPersonalInfoModalShown(true)}>
- {t("edit")}
-
-
-
-
-
}
- caption={t("account.mail")}
- />
-
{user?.email}
-
-
-
}
- caption={t("language")}
- />
-
{languages[selected].nativeName.toLowerCase()}
-
- setLanguageSelecting(true)}>
- {t("edit")}
-
-
-
-
-
}
- caption={t("account.connectedAccounts", { count: 0 })}
- />
-
nothing)
-
- {t("edit")}
-
-
-
-
setLanguageSelecting(false)}
- />
- setPersonalInfoModalShown(false)}
- />
-
- );
-};
-
-export default AccountSettings;
diff --git a/src/pages/AuthFailure.tsx b/src/pages/AuthFailure.tsx
deleted file mode 100644
index 0717cec..0000000
--- a/src/pages/AuthFailure.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import Button from "components/controls/Button";
-import StandalonePage from "components/StandalonePage";
-import React from "react";
-
-/* -------------------------------------------------------------------------- */
-/* Unauthorized page */
-/* -------------------------------------------------------------------------- */
-export default function AuthFailure() {
- return (
-
-
-
You have to be authorized
-
to access techpal dashboard
-
- Войти
-
-
-
- );
-}
diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx
deleted file mode 100644
index e2757ab..0000000
--- a/src/pages/Dashboard.tsx
+++ /dev/null
@@ -1,185 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React, { useEffect } from "react";
-/* -------------------------------------------------------------------------- */
-/* Misc */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import Card from "components/containers/Card";
-import TextAction from "components/controls/TextAction";
-import HexagonOutlined from "components/HexagonOutlined";
-import AvailabilityIndicator from "components/indicators/AvailabilityIndicator";
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGArrowRightShortIcon } from "assets/svg/arrow-right-short.svg";
-/* -------------------------------------------------------------------------- */
-/* Dashboard page */
-/* -------------------------------------------------------------------------- */
-
-import { useServicesStore } from "services/data/servicesSlice";
-import { useServicesViewModel } from "services/controller/servicesViewModel";
-import CircleLoader from "components/CircleLoader";
-import _ from "lodash";
-import { useUIStore } from "ui/data/uiSlice";
-import Button from "components/controls/Button";
-import { useNavigate } from "react-router-dom";
-import { useSubscriptionsStore } from "subscriptions/data/subscriptionsSlice";
-import { useSubscriptionsViewModel } from "subscriptions/controller/subscriptionsViewModel";
-
-export default function Dashboard() {
- const navigate = useNavigate();
- /* --------------------------------- Stores --------------------------------- */
- const servicesStore = useServicesStore();
- const uiStore = useUIStore();
- const subscriptionsStore = useSubscriptionsStore();
- /* ------------------------------- View models ------------------------------ */
- const { services, isLoading: servicesLoading } = useServicesViewModel({
- ...servicesStore,
- notify: uiStore.notify,
- });
- const {
- forkTo,
- isLoading: subscriptionsLoading,
- subscriptions,
- hasSubscription,
- } = useSubscriptionsViewModel(subscriptionsStore, uiStore);
- /* ---------------------------------- Hooks --------------------------------- */
- const Wrapper: React.FC = React.useMemo(
- () => (props) =>
- (
-
- {props.children}
-
-
-
-
- new
-
-
-
- navigate("/services")}
- >
- Connect service
-
-
-
-
-
- navigate("/services")}
- className="font-semibold inline-flex items-center space-x-2 hover:space-x-3"
- >
- connect
-
-
-
-
-
- ),
- []
- );
- /* --------------------------------- Markup --------------------------------- */
- if (subscriptionsLoading) {
- return (
-
- );
- }
- if (!services || servicesLoading) {
- return (
-
- {_.map(subscriptions, (index) => (
-
-
-
-
-
-
-
- Loading..
-
-
- unknown
-
-
-
-
-
-
- open
-
-
-
-
- ))}
-
- );
- }
- return (
-
- {services
- ?.filter((service) => {
- return hasSubscription(service.id);
- })
- .map((service) => (
-
-
-
-
- {service.shortName}
-
-
-
-
- {service.name}
-
-
- active
-
-
-
-
- {service.description}
-
-
- {
- e.preventDefault();
- forkTo(service.id, service.href);
- }}
- className="font-semibold inline-flex items-center space-x-2 hover:space-x-3"
- >
- open
-
-
-
-
- ))}
-
- );
-}
diff --git a/src/pages/Gitpal.tsx b/src/pages/Gitpal.tsx
deleted file mode 100644
index 1625c8f..0000000
--- a/src/pages/Gitpal.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import FullWideColumn from "components/layouts/FullWideColumn";
-import React from "react";
-
-export default function Gitpal() {
- return (
-
-
-
-
-
- );
-}
diff --git a/src/pages/Information/About.tsx b/src/pages/Information/About.tsx
new file mode 100755
index 0000000..2f732ce
--- /dev/null
+++ b/src/pages/Information/About.tsx
@@ -0,0 +1,15 @@
+import BaseLayout from "components/BaseLayout";
+import { Footer } from "components/parts/Footer";
+import Header from "components/parts/Header";
+import Typography from "components/typography/Typography";
+import React from "react";
+
+type Props = {};
+
+export default function About({}: Props) {
+ return (
+
+ About page
+
+ );
+}
diff --git a/src/pages/Information/AccountSettings.tsx b/src/pages/Information/AccountSettings.tsx
new file mode 100755
index 0000000..096b2dc
--- /dev/null
+++ b/src/pages/Information/AccountSettings.tsx
@@ -0,0 +1,15 @@
+import BaseLayout from "components/BaseLayout";
+import { Footer } from "components/parts/Footer";
+import Header from "components/parts/Header";
+import Typography from "components/typography/Typography";
+import React from "react";
+
+type Props = {};
+
+export default function AccountSettings({}: Props) {
+ return (
+
+ Accont Setting page
+
+ );
+}
diff --git a/src/pages/Information/ContactUs.tsx b/src/pages/Information/ContactUs.tsx
new file mode 100755
index 0000000..cb3c75e
--- /dev/null
+++ b/src/pages/Information/ContactUs.tsx
@@ -0,0 +1,15 @@
+import BaseLayout from "components/BaseLayout";
+import { Footer } from "components/parts/Footer";
+import Header from "components/parts/Header";
+import Typography from "components/typography/Typography";
+import React from "react";
+
+type Props = {};
+
+export default function ContactUs({}: Props) {
+ return (
+
+ Contact us page
+
+ );
+}
diff --git a/src/pages/Information/CookiesPolicy.tsx b/src/pages/Information/CookiesPolicy.tsx
new file mode 100755
index 0000000..f594591
--- /dev/null
+++ b/src/pages/Information/CookiesPolicy.tsx
@@ -0,0 +1,15 @@
+import BaseLayout from "components/BaseLayout";
+import { Footer } from "components/parts/Footer";
+import Header from "components/parts/Header";
+import Typography from "components/typography/Typography";
+import React from "react";
+
+type Props = {};
+
+export default function CookiesPolicy({}: Props) {
+ return (
+
+ Privacy Cookies page
+
+ );
+}
diff --git a/src/pages/Information/Help.tsx b/src/pages/Information/Help.tsx
new file mode 100755
index 0000000..5974116
--- /dev/null
+++ b/src/pages/Information/Help.tsx
@@ -0,0 +1,15 @@
+import BaseLayout from "components/BaseLayout";
+import { Footer } from "components/parts/Footer";
+import Header from "components/parts/Header";
+import Typography from "components/typography/Typography";
+import React from "react";
+
+type Props = {};
+
+export default function Help({}: Props) {
+ return (
+
+ Help page
+
+ );
+}
diff --git a/src/pages/Information/PrivacyPolicy.tsx b/src/pages/Information/PrivacyPolicy.tsx
new file mode 100755
index 0000000..55627af
--- /dev/null
+++ b/src/pages/Information/PrivacyPolicy.tsx
@@ -0,0 +1,15 @@
+import BaseLayout from "components/BaseLayout";
+import { Footer } from "components/parts/Footer";
+import Header from "components/parts/Header";
+import Typography from "components/typography/Typography";
+import React from "react";
+
+type Props = {};
+
+export default function PrivacyPolicy({}: Props) {
+ return (
+
+ Privacy Policy page
+
+ );
+}
diff --git a/src/pages/Information/TermsOfUse.tsx b/src/pages/Information/TermsOfUse.tsx
new file mode 100755
index 0000000..dad0adf
--- /dev/null
+++ b/src/pages/Information/TermsOfUse.tsx
@@ -0,0 +1,15 @@
+import BaseLayout from "components/BaseLayout";
+import { Footer } from "components/parts/Footer";
+import Header from "components/parts/Header";
+import Typography from "components/typography/Typography";
+import React from "react";
+
+type Props = {};
+
+export default function TermsOfUse({}: Props) {
+ return (
+
+ Terms of use page
+
+ );
+}
diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx
new file mode 100755
index 0000000..ec6bb1b
--- /dev/null
+++ b/src/pages/MainPage.tsx
@@ -0,0 +1,26 @@
+import React from "react";
+import BaseLayout from "components/BaseLayout";
+import FeaturedArticlesCards from "components/MainPage/sections/FeaturedArticlesCards";
+import FeaturedAuthorsCards from "components/MainPage/sections/FeaturedAuthorsCards";
+import { FeaturedArticlesCategories } from "components/MainPage/sections/FeaturedArticlesCategories";
+import { BottomBarAcceptCookies } from "components/containers/modal/BottomBarAcceptCookies";
+import MainSection from "components/MainPage/sections/MainSection";
+type Props = {
+ className?: string;
+};
+
+
+
+export default function MainPage({ className }: Props) {
+ return (
+
+
+ {/* */}
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/NoMatch.tsx b/src/pages/NoMatch.tsx
old mode 100644
new mode 100755
index b107f07..614e47b
--- a/src/pages/NoMatch.tsx
+++ b/src/pages/NoMatch.tsx
@@ -1,57 +1,39 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React from "react";
-/* -------------------------------------------------------------------------- */
-/* Hooks */
-/* -------------------------------------------------------------------------- */
-import { useTranslation } from "react-i18next";
-import { useNavigate } from "react-router-dom";
-/* -------------------------------------------------------------------------- */
-/* Components */
-import Button from "components/controls/Button";
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* Misc */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGBackIcon } from "assets/svg/backstab.svg";
-/* -------------------------------------------------------------------------- */
-/* Account information management page */
-/* -------------------------------------------------------------------------- */
-/**
- * Account information management page
- * @return {JSX.Element}
- */
-const NoMatch = (): JSX.Element => {
- const { t } = useTranslation();
- const navigate = useNavigate();
- return (
-
-
404
-
Page Not Found
-
{t('serv.noSuchPath')}
-
- navigate("/", {
- replace: true,
- })
- }
- >
-
-
-
-
-
{t("serv.goHome")}
-
-
-
- );
-};
-
-export default NoMatch;
+/* -------------------------------------------------------------------------- */
+/* Libraries */
+/* -------------------------------------------------------------------------- */
+import React from "react";
+/* -------------------------------------------------------------------------- */
+/* Hooks */
+/* -------------------------------------------------------------------------- */
+import { useTranslation } from "react-i18next";
+import { useNavigate } from "react-router-dom";
+/* -------------------------------------------------------------------------- */
+/* Components */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* Misc */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* Icons */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* Account information management page */
+/* -------------------------------------------------------------------------- */
+/**
+ * Account information management page
+ * @return {JSX.Element}
+ */
+const NoMatch = (): JSX.Element => {
+ const { t } = useTranslation();
+ const navigate = useNavigate();
+ return (
+
+
404
+
Page Not Found
+
{t('serv.noSuchPath')}
+
+
+ );
+};
+
+export default NoMatch;
diff --git a/src/pages/SearchResultsPage.tsx b/src/pages/SearchResultsPage.tsx
new file mode 100644
index 0000000..00f7833
--- /dev/null
+++ b/src/pages/SearchResultsPage.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+import BaseLayout from "components/BaseLayout";
+import { SearchSection } from "components/SearchSection";
+import { ColumnLayout } from "components/layouts/ThreeColumn/ColumnLayout";
+import { SearchResultSection } from "components/SearchResultsSection";
+import Fiter from "components/Filters/Filter";
+
+export const SearchResultsPage = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/pages/Security.tsx b/src/pages/Security.tsx
deleted file mode 100644
index b48f9ab..0000000
--- a/src/pages/Security.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React from "react";
-import { useTranslation } from "react-i18next";
-/* -------------------------------------------------------------------------- */
-/* Hooks */
-/* -------------------------------------------------------------------------- */
-import { useUserStore } from "user/data/userSlice";
-import { useUserViewModel } from "user/controller/userViewModel";
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import Area from "components/containers/area/Area";
-import IconedAreaCaption from "components/containers/area/IconedAreaCaption";
-import ThinSingleColumn from "components/layouts/ThinSingleColumn";
-import AreaDescription from "components/containers/area/AreaDescription";
-import ControlLabel from "components/controls/ControlLabel";
-import Switch from "components/controls/Switch";
-import TextAction from 'components/controls/TextAction'
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGLockIcon } from "assets/svg/lock.svg";
-import { ReactComponent as SVGFingerprintIcon } from "assets/svg/fingerprint.svg";
-
-const Security = () => {
- const store = useUserStore();
-
- const {t} = useTranslation();
-
- const {
- getUser,
- twoFactorEnabled,
- toggleTwoFactorAuthenitcation,
- } = useUserViewModel(store);
-
- React.useEffect(() => {
- getUser();
- }, [getUser]);
-
- return (
-
-
- }
- />
-
- {t('edit')}
-
-
- Last edit month ago
-
-
-
-
- }
- />
-
- toggleTwoFactorAuthenitcation()}
- alterText="two factor authentication changing"
- />
-
-
- {t('security.password.description')}
-
-
-
- }
- />
-
-
- {t('viewHistory')}
-
-
- {t('logOutEverywhere')}
-
-
-
- );
-}
-
-export default Security;
\ No newline at end of file
diff --git a/src/pages/Services.tsx b/src/pages/Services.tsx
deleted file mode 100644
index 760d45e..0000000
--- a/src/pages/Services.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React, { useEffect, useState } from "react";
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import ServiceComponent from "components/containers/ServiceComponent";
-import Switch from "components/controls/Switch";
-import TextAction from "components/controls/TextAction";
-import ThinSingleColumn from "components/layouts/ThinSingleColumn";
-import { useServicesStore } from "services/data/servicesSlice";
-import { useServicesViewModel } from "services/controller/servicesViewModel";
-import _ from "lodash";
-import { useUIStore } from "ui/data/uiSlice";
-import { useSubscriptionsStore } from "subscriptions/data/subscriptionsSlice";
-import { useSubscriptionsViewModel } from "subscriptions/controller/subscriptionsViewModel";
-import { Service } from "services/domain/serviceEntity";
-import PasswordConfirmation from "user/views/PasswordConfirmation";
-import CircleLoader from "components/CircleLoader";
-import Failure from "core/failure";
-/* -------------------------------------------------------------------------- */
-/* Component */
-/* -------------------------------------------------------------------------- */
-const Services = () => {
- const [password, setPassword] = useState();
- const [confirmShown, toggleConfirm] = useState(false);
- const [selectedService, setSelectedService] = useState<
- Service["id"] | null
- >();
- /* ---------------------------------- Store --------------------------------- */
- const servicesStore = useServicesStore();
- const uiStore = useUIStore();
- const subscriptionsStore = useSubscriptionsStore();
- const { services, isLoading, loadServices } = useServicesViewModel({
- ...servicesStore,
- notify: uiStore.notify,
- });
- const { hasSubscription, subscribe, processingSubscription } =
- useSubscriptionsViewModel(subscriptionsStore, uiStore);
- /* ---------------------------------- Hooks --------------------------------- */
- useEffect(() => {
- loadServices();
- }, [loadServices]);
-
- useEffect(() => {
- if(!selectedService || password) return;
- toggleConfirm(true);
- }, [selectedService, password]);
-
- useEffect(() => {
- if (!selectedService) {
- return;
- }
- if (hasSubscription(selectedService)) {
- return;
- }
- if (!password) {
- return;
- }
- if (password !== null) {
- subscribe(selectedService, password!)?.catch((failure) => {
- if(Failure.isFailure(failure) && failure.status === 403) {
- setPassword(null);
- }
- });
- setSelectedService(null);
- }
- }, [selectedService, hasSubscription, subscribe, password]);
-
- const handleSubscription = (serviceId: Service["id"]) => {
- setSelectedService(serviceId);
- };
- /* --------------------------------- Markup --------------------------------- */
- return (
-
-
- {!services?.length || isLoading
- ? _.times(3, (number) => (
-
-
-
-
Techpal
-
about service
-
-
- null} />
-
-
-
- ))
- : services!.map((item, index) => {
- return (
-
-
-
-
{item.name}
-
about service
-
-
- {processingSubscription(item.id) ? (
-
-
-
- ) : (
-
handleSubscription(item.id)}
- />
- )}
-
-
-
- );
- })}
-
- {confirmShown && selectedService && (
- setPassword(p)}
- isShown={true}
- onClose={() => toggleConfirm(false)}
- />
- )}
-
- );
-};
-
-export default Services;
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
old mode 100644
new mode 100755
index 6431bc5..ece12df
--- a/src/react-app-env.d.ts
+++ b/src/react-app-env.d.ts
@@ -1 +1 @@
-///
+///
diff --git a/src/reportWebVitals.ts b/src/reportWebVitals.ts
old mode 100644
new mode 100755
index 49a2a16..f5ae799
--- a/src/reportWebVitals.ts
+++ b/src/reportWebVitals.ts
@@ -1,15 +1,15 @@
-import { ReportHandler } from 'web-vitals';
-
-const reportWebVitals = (onPerfEntry?: ReportHandler) => {
- if (onPerfEntry && onPerfEntry instanceof Function) {
- import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
- getCLS(onPerfEntry);
- getFID(onPerfEntry);
- getFCP(onPerfEntry);
- getLCP(onPerfEntry);
- getTTFB(onPerfEntry);
- });
- }
-};
-
-export default reportWebVitals;
+import { ReportHandler } from 'web-vitals';
+
+const reportWebVitals = (onPerfEntry?: ReportHandler) => {
+ if (onPerfEntry && onPerfEntry instanceof Function) {
+ import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
+ getCLS(onPerfEntry);
+ getFID(onPerfEntry);
+ getFCP(onPerfEntry);
+ getLCP(onPerfEntry);
+ getTTFB(onPerfEntry);
+ });
+ }
+};
+
+export default reportWebVitals;
diff --git a/src/routes/definition.tsx b/src/routes/definition.tsx
deleted file mode 100644
index 769fb3e..0000000
--- a/src/routes/definition.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* Pages */
-/* -------------------------------------------------------------------------- */
-import Account from "pages/Account";
-import Dashboard from "pages/Dashboard";
-import NoMatch from "pages/NoMatch";
-import Security from "pages/Security";
-import Services from "pages/Services";
-import AuthFailure from "pages/AuthFailure";
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import Page from "components/Page";
-import StandalonePage from "components/StandalonePage";
-/* -------------------------------------------------------------------------- */
-/* Types */
-/* -------------------------------------------------------------------------- */
-import { RoutePathDefinition } from "./types";
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGUserIcon } from "assets/svg/user.svg";
-import { ReactComponent as SVGShieldIcon } from "assets/svg/shield.svg";
-import { ReactComponent as SVGServicesIcon } from "assets/svg/services.svg";
-/* -------------------------------------------------------------------------- */
-/* Routes definition */
-/* -------------------------------------------------------------------------- */
-
-const routes: RoutePathDefinition[] = [
- {
- path: "/",
- name: "dashboard",
- Component: (props) => (
-
-
-
- ),
- },
- {
- path: "/personal-information",
- title: ({ translation }) => translation("sidemenu.account"),
- icon: SVGUserIcon,
- name: "account",
- Component: (props) => (
-
-
-
- ),
- },
- {
- path: "/security",
- name: "account",
- title: ({ translation }) => translation("sidemenu.security"),
- icon: SVGShieldIcon,
- Component: (props) => (
-
-
-
- ),
- },
- {
- path: "/services/*",
- name: "account",
- title: ({ translation }) => translation("sidemenu.services"),
- icon: SVGServicesIcon,
- Component: (props) => (
-
-
-
- ),
- },
- { path: "/auth", name: 'auth', element: },
- {
- path: "*",
- name: "404",
- title: "404",
- element: (
-
-
-
- ),
- },
-];
-
-const useRoutesDefinition = function (): RoutePathDefinition[] {
- return routes;
-};
-
-export { useRoutesDefinition };
-export default routes;
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
deleted file mode 100644
index e1bc217..0000000
--- a/src/routes/index.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import routes from "./definition";
-import {RoutePathDefinition, WithRouteProps} from "./types";
-
-export type {RoutePathDefinition, WithRouteProps};
-export default routes;
diff --git a/src/routes/mapDefinitionToRoute.tsx b/src/routes/mapDefinitionToRoute.tsx
deleted file mode 100644
index 45ec230..0000000
--- a/src/routes/mapDefinitionToRoute.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { RouteObject } from "react-router-dom";
-import { RoutePathDefinition } from "./types";
-
-export function mapDefinitionToRoute(props: RoutePathDefinition): RouteObject {
- const { Component, element } = props;
- if(typeof element === "undefined" && typeof Component === "undefined") {
- throw Error("Either `element` or `Component` ")
- }
- return props.element
- ? props
- : Component! && {
- ...props,
- children:
- props.children &&
- props.children.map((props, index) => mapDefinitionToRoute(props)),
- element: ,
- };
-}
diff --git a/src/routes/types.ts b/src/routes/types.ts
deleted file mode 100644
index 55f52e0..0000000
--- a/src/routes/types.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React from "react";
-import { PathMatch, RouteObject } from "react-router-dom";
-/* -------------------------------------------------------------------------- */
-/* Custom routes types */
-/* -------------------------------------------------------------------------- */
-
-export type ActiveRoutePathTitleCallbackParams = {
- definition: RoutePathDefinition;
- match: PathMatch;
- locationPathname: string;
- translation: (key: string) => string,
-};
-
-export type ActiveRoutePathTitleCallback = (params: ActiveRoutePathTitleCallbackParams) => string;
-
-export interface WithRouteProps {
- name: string;
- pageName?: string;
- path: string;
- activePath: ActiveRoutePath[];
-}
-
-
-export interface RoutePathDefinition<
- K = Pick,
- T extends K = {} & K
-> extends RouteObject {
- name: string;
- title?: string | ActiveRoutePathTitleCallback;
- nav?: boolean;
- children?: RoutePathDefinition[];
- path: string;
- icon?: React.FC>,
- Component?: React.FC | React.ClassicComponentClass;
-}
-
-export type ActiveRoutePath = {
- title?: string;
- match: PathMatch
- definition: RoutePathDefinition;
- siblings: ActiveRoutePath[];
-};
\ No newline at end of file
diff --git a/src/routes/withRoute.tsx b/src/routes/withRoute.tsx
deleted file mode 100644
index 8276bca..0000000
--- a/src/routes/withRoute.tsx
+++ /dev/null
@@ -1,246 +0,0 @@
-import React from "react";
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import { useTranslation } from "react-i18next";
-import { matchPath, PathMatch, useLocation } from "react-router-dom";
-import { useRoutesDefinition } from "./definition";
-/* -------------------------------------------------------------------------- */
-/* Types */
-/* -------------------------------------------------------------------------- */
-import {
- ActiveRoutePath,
- ActiveRoutePathTitleCallbackParams,
- RoutePathDefinition,
- WithRouteProps,
-} from "./types";
-/* -------------------------------------------------------------------------- */
-/* Misc */
-/* -------------------------------------------------------------------------- */
-
-/* -------------------------------------------------------------------------- */
-/* Helpers */
-/* -------------------------------------------------------------------------- */
-function joinPaths(paths: string[]): string {
- return paths.join("/").replace(/\/\/+/g, "/");
-}
-
-function concatPaths(parent: string, current: string) {
- const jointPaths = joinPaths([parent, current]);
- return jointPaths;
-}
-
-function addActiveRoutePathIfPossible(
- activeRoutePaths: ActiveRoutePath[],
- activePath: ActiveRoutePath
-) {
- if (canBeAddedToActiveRoutes(activeRoutePaths, activePath.match)) {
- activeRoutePaths.push(activePath);
- }
-}
-
-function isResolvedAsActive(
- toPathname: string,
- locationPathname: string,
- definition: RoutePathDefinition,
-) {
- return (
- isPathActiveForLocation(toPathname, locationPathname) &&
- isNotCatchAll(definition.path || "")
- );
-}
-
-function canBeAddedToActiveRoutes(
- activeRoutePaths: ActiveRoutePath[],
- match: PathMatch
-) {
- return (
- isNotSameAsPreviousMatch(activeRoutePaths, match) &&
- isMoreSpecificThanPreviousMatch(activeRoutePaths, match.pathname)
- );
-}
-
-function getPreviousMatch(
- previousMatches: ActiveRoutePath[]
-): ActiveRoutePath | undefined {
- return previousMatches[previousMatches.length - 1];
-}
-
-function isNotSameAsPreviousMatch(
- previousMatches: ActiveRoutePath[],
- match: PathMatch
-): boolean {
- const previousMatchedPathname =
- getPreviousMatch(previousMatches)?.match.pattern ?? "";
- return previousMatchedPathname !== match.pattern;
-}
-
-function isMoreSpecificThanPreviousMatch(
- previousMatches: ActiveRoutePath[],
- toPathname: string
-): boolean {
- const previousMatchedPathname =
- getPreviousMatch(previousMatches)?.match.pathname ?? "";
- return toPathname.length > previousMatchedPathname.length;
-}
-
-function isNotCatchAll(path: string) {
- return path !== "*";
-}
-
-function isPathActiveForLocation(
- pathName: string,
- locationPathname: string,
-) {
- return (
- locationPathname === pathName ||
- (locationPathname.startsWith(pathName) &&
- locationPathname.charAt(pathName.length) === "/")
- );
-}
-
-function matchPatternInPath(
- pathPattern: string,
- locationPathname: string,
- requireExactMatch: boolean = false
-): PathMatch | null {
- return matchPath(
- { path: pathPattern, end: requireExactMatch },
- locationPathname
- );
-}
-
-export function mapActivePathBranch(
- siblings: RoutePathDefinition[],
- locationPathname: string,
- translation: ActiveRoutePathTitleCallbackParams["translation"],
- parentPath: string = ""
-) {
- if (siblings.length === 0) {
- return [];
- }
- const activeBranch: ActiveRoutePath[] = [];
-
- siblings.forEach((definition) => {
- const pathPatternWithParent = concatPaths(parentPath, definition.path);
-
- if (pathPatternWithParent === "/") return;
-
- const match = matchPatternInPath(pathPatternWithParent, locationPathname);
- if (!match) return;
- const activeRoutePath: ActiveRoutePath = mapRouteDefinitionToActivePath(
- definition,
- [],
- match,
- locationPathname,
- parentPath,
- translation
- );
- addActiveRoutePathIfPossible(activeBranch, activeRoutePath);
- });
-
- return activeBranch;
-}
-
-export function mapRouteDefinitionToActivePath(
- definition: RoutePathDefinition,
- siblings: RoutePathDefinition[],
- match: PathMatch,
- locationPathname: string,
- parentPath: string = "",
- transition: ActiveRoutePathTitleCallbackParams["translation"]
-): ActiveRoutePath {
- return {
- definition: definition,
- title:
- typeof definition.title === "function"
- ? definition.title({
- definition,
- match,
- locationPathname: locationPathname,
- translation: transition,
- })
- : definition.title,
- match: match,
- siblings: mapActivePathBranch(
- siblings,
- locationPathname,
- transition,
- parentPath
- ),
- };
-}
-
-export function mapDefinitionToActivePath(
- definitions: RoutePathDefinition[],
- locationPathname: string,
- translation: ActiveRoutePathTitleCallbackParams["translation"],
- parentPath: string = ""
-): ActiveRoutePath[] {
- const activeRoutePaths: ActiveRoutePath[] = [];
-
- definitions.forEach((definition, index) => {
- const pathPatternWithParent = concatPaths(parentPath, definition.path);
- const match = matchPatternInPath(pathPatternWithParent, locationPathname);
- if (!match) {
- return;
- }
-
- if (isResolvedAsActive(match.pathname, locationPathname, definition)) {
- const activeRoutePath: ActiveRoutePath = mapRouteDefinitionToActivePath(
- definition,
- definitions,
- match,
- locationPathname,
- parentPath,
- translation
- );
- addActiveRoutePathIfPossible(activeRoutePaths, activeRoutePath);
-
- if (definition.children) {
- const nestedMatches = mapDefinitionToActivePath(
- definition.children,
- locationPathname,
- translation,
- pathPatternWithParent
- );
- nestedMatches.forEach((activePath) => {
- addActiveRoutePathIfPossible(activeRoutePaths, activePath);
- });
- }
- }
- });
- return activeRoutePaths;
-}
-
-/* -------------------------------------------------------------------------- */
-/* HOC with route params */
-/* -------------------------------------------------------------------------- */
-const withRouteParams = (
- Component: React.ComponentType,
- componentName = Component.displayName ?? Component.name
-): {
- (props: Omit>): JSX.Element;
- displayName: string;
-} => {
- function WithRouteParams(
- props: Omit>
- ) {
- const { t } = useTranslation();
- const withRouteProps: WithRouteProps = {
- activePath: mapDefinitionToActivePath(
- useRoutesDefinition(),
- useLocation().pathname,
- t
- ),
- path: props.path,
- name: props.name,
- };
- return ;
- }
- WithRouteParams.displayName = `withRouteParams(${componentName})`;
-
- return WithRouteParams;
-};
-
-export { withRouteParams };
diff --git a/src/searchResults/controller/searchResultsViewModel.ts b/src/searchResults/controller/searchResultsViewModel.ts
new file mode 100755
index 0000000..be262f4
--- /dev/null
+++ b/src/searchResults/controller/searchResultsViewModel.ts
@@ -0,0 +1,51 @@
+import {useState, useEffect, useCallback} from "react";
+import { SearchResultsStore } from "searchResults/domain/searchStore";
+import { changeSearchRequestUseCase } from "searchResults/useCases/changeSearchRequestUseCase";
+import { searchUseCase } from "searchResults/useCases/searchUseCase";
+
+function useSearchViewModel(store: SearchResultsStore) {
+ const search = useCallback(
+ function () {
+ return searchUseCase({
+ searchResults: store.searchResults,
+ searchRequest: store.searchRequest,
+ search: store.search,
+ });
+ },
+ [store.searchRequest, store.search, store.searchResults]
+ );
+
+ const changeSearchRequest = useCallback(
+ function (searchRequest: string) {
+ return changeSearchRequestUseCase({
+ searchRequest: searchRequest,
+ changeSearchRequest: store.changeSearchRequest,
+ });
+ },
+ [store.searchRequest, store.changeSearchRequest]
+ );
+ const [inputValue, setInputValue] = useState(store.searchRequest);
+
+ const changeInputValue = (value: string) => {
+ setInputValue(value);
+ changeSearchRequest(value);
+ };
+
+ useEffect(() => {
+ if (store.searchRequest !== "") {
+ search();
+ }
+ }, [store.searchRequest]);
+
+ return {
+ searchResults: store.searchResults,
+ currentSearchState: store.searchRequest,
+ failure: store.failure,
+ isLoading: store.isLoading,
+ isFailed: store.isFailed,
+ search,
+ changeInputValue,
+ inputValue,
+ };
+}
+export { useSearchViewModel };
diff --git a/src/searchResults/data/actions/searchActionTypes.ts b/src/searchResults/data/actions/searchActionTypes.ts
new file mode 100755
index 0000000..97ee905
--- /dev/null
+++ b/src/searchResults/data/actions/searchActionTypes.ts
@@ -0,0 +1,4 @@
+export const CHANGE_SEARCH_REQUEST = "@searchresults/changesSearchRequest";
+export const GET_SEARCH_RESULTS = "@searchresults/fetch.all";
+export const GET_SEARCH_RESULTS_SUCCESS = "@searchresults/fetch.all.success";
+export const GET_SEARCH_RESULTS_FAILURE = "@searchresults/fetch.all.failure";
diff --git a/src/searchResults/data/actions/searchActions.ts b/src/searchResults/data/actions/searchActions.ts
new file mode 100755
index 0000000..8bf15ce
--- /dev/null
+++ b/src/searchResults/data/actions/searchActions.ts
@@ -0,0 +1,32 @@
+import * as actionTypes from "./searchActionTypes";
+import { search } from "../searchService";
+import { dispatchStatus } from "store";
+import Failure from "core/failure";
+
+const SearchAction = (query: string) => (dispatch: any) => {
+ dispatch({ type: actionTypes.GET_SEARCH_RESULTS });
+
+ return search(query)
+ .then((searchResult) => {
+ dispatchStatus(
+ actionTypes.GET_SEARCH_RESULTS,
+ ".success",
+ searchResult
+ )(dispatch);
+ return searchResult;
+ })
+ .catch((failure: Failure) => {
+ dispatchStatus(
+ actionTypes.GET_SEARCH_RESULTS,
+ ".failure",
+ failure
+ )(dispatch);
+ return undefined;
+ });
+};
+const changeSearchRequestAction =
+ (searchRequest: string) => (dispatch: any) => {
+ dispatch({ type: actionTypes.CHANGE_SEARCH_REQUEST, searchRequest });
+ };
+
+export { SearchAction, changeSearchRequestAction };
diff --git a/src/searchResults/data/dto/queryDTO.ts b/src/searchResults/data/dto/queryDTO.ts
new file mode 100755
index 0000000..a6292d0
--- /dev/null
+++ b/src/searchResults/data/dto/queryDTO.ts
@@ -0,0 +1,6 @@
+export type QueryDTO = {
+ query: string;
+ offset: string;
+ limit: number;
+ endpoints: string;
+};
diff --git a/src/searchResults/data/dto/searchResultsDTO.ts b/src/searchResults/data/dto/searchResultsDTO.ts
new file mode 100755
index 0000000..b84b276
--- /dev/null
+++ b/src/searchResults/data/dto/searchResultsDTO.ts
@@ -0,0 +1,20 @@
+import { Article } from "article/domain/articleEntity";
+
+export type SearchResultsDTO = {
+ data: ArticleDTO[];
+ meta: SearchResultsMeta;
+};
+
+export type ArticleDTO = {
+ id: string;
+ title: string;
+ authors: string[];
+ topic: string[];
+ summary: string;
+ tags: string[];
+ content: string;
+};
+
+export type SearchResultsMeta = {
+ total: string;
+}
diff --git a/src/searchResults/data/searchReducer.ts b/src/searchResults/data/searchReducer.ts
new file mode 100755
index 0000000..11d0c9b
--- /dev/null
+++ b/src/searchResults/data/searchReducer.ts
@@ -0,0 +1,54 @@
+import { SearchResultsStore } from "searchResults/domain/searchStore";
+import * as actionTypes from "./actions/searchActionTypes";
+import { AnyAction } from "@reduxjs/toolkit";
+
+type SearchStoreState = Omit<
+ SearchResultsStore,
+ "search" | "changeSearchRequest"
+>;
+
+const ININTIAL_STATE: SearchStoreState = {
+ searchRequest: "",
+ searchResults: undefined,
+ failure: undefined,
+ isFailed: false,
+ isLoading: false,
+};
+
+const searchResultReducer = (
+ state: SearchStoreState = ININTIAL_STATE,
+ action: AnyAction
+): SearchStoreState => {
+ switch (action.type) {
+ case actionTypes.CHANGE_SEARCH_REQUEST:
+ return {
+ ...state,
+ searchRequest: action.searchRequest,
+ };
+ case actionTypes.GET_SEARCH_RESULTS:
+ return {
+ ...state,
+ isLoading: true,
+ isFailed: false,
+ };
+ case actionTypes.GET_SEARCH_RESULTS_SUCCESS:
+ return {
+ ...state,
+ searchResults: action.payload,
+ isLoading: false,
+ isFailed: false,
+ };
+ case actionTypes.GET_SEARCH_RESULTS_FAILURE:
+ return {
+ ...state,
+ isFailed: true,
+ failure: action.payload,
+ isLoading: false,
+ };
+ default:
+ return state;
+ }
+};
+
+export { searchResultReducer };
+export type { SearchStoreState };
diff --git a/src/searchResults/data/searchService.ts b/src/searchResults/data/searchService.ts
new file mode 100755
index 0000000..ba4a75d
--- /dev/null
+++ b/src/searchResults/data/searchService.ts
@@ -0,0 +1,37 @@
+import axios from "axios";
+import Failure from "core/failure";
+import { SearchResults } from "searchResults/domain/searchResultsEntity";
+import { SearchResultsDTO } from "./dto/searchResultsDTO";
+import { create } from "../domain/searchResultsModel";
+import { integratorApiClient } from "core/httpClient";
+
+const searchEndpoint = "/papers/search";
+
+async function search(request: string): Promise {
+ try {
+ const response = await integratorApiClient.get(
+ searchEndpoint + `?query=` + request
+ // "https://run.mocky.io/v3/ea705665-2479-4039-8b81-412e011fc145"
+ );
+ const dto = response.data;
+ return create({
+ data: dto.data.map((e) => {
+ return {
+ authors: e.authors,
+ content: e.content,
+ id: e.id,
+ summary: e.summary,
+ tags: e.tags,
+ title: e.title,
+ topic: e.topic,
+ }
+ }), meta: dto.meta
+ });
+ } catch (reason) {
+ if (axios.isAxiosError(reason)) {
+ throw Failure.fromReason(reason, "failures.services.load");
+ }
+ throw reason;
+ }
+}
+export { search };
diff --git a/src/searchResults/data/searchStoreImplementation.ts b/src/searchResults/data/searchStoreImplementation.ts
new file mode 100755
index 0000000..d43a468
--- /dev/null
+++ b/src/searchResults/data/searchStoreImplementation.ts
@@ -0,0 +1,35 @@
+import React from "react";
+import { SearchResultsStore } from "searchResults/domain/searchStore";
+import { RootState, useAppDispatch, useAppSelector } from "store";
+import { changeSearchRequestAction, SearchAction } from "./actions/searchActions";
+import { SearchStoreState } from "./searchReducer";
+
+const searchSelector = (state: RootState): SearchStoreState =>
+ state.searchResults;
+
+const useSearchStoreImplementation = (): SearchResultsStore => {
+ const { searchResults, failure, isFailed, isLoading, searchRequest} =
+ useAppSelector(searchSelector);
+
+ const dispatch = useAppDispatch();
+
+ const search = React.useCallback(
+ (searchRequest: string) => SearchAction(searchRequest)(dispatch),
+ [dispatch]
+ );
+ const changeSearchRequest = React.useCallback(
+ (searchRequest: string) => changeSearchRequestAction(searchRequest)(dispatch), [dispatch]
+ )
+
+ return {
+ isLoading,
+ isFailed,
+ searchResults,
+ failure,
+ searchRequest,
+ changeSearchRequest: changeSearchRequest,
+ search: search,
+ };
+};
+
+export { useSearchStoreImplementation };
diff --git a/src/searchResults/domain/searchResultsEntity.ts b/src/searchResults/domain/searchResultsEntity.ts
new file mode 100755
index 0000000..d55667a
--- /dev/null
+++ b/src/searchResults/domain/searchResultsEntity.ts
@@ -0,0 +1,10 @@
+import { Article } from "article/domain/articleEntity";
+
+export interface SearchResults {
+ data: Article[];
+ meta: SearchResultsMeta;
+}
+
+export interface SearchResultsMeta {
+ total: string;
+}
\ No newline at end of file
diff --git a/src/searchResults/domain/searchResultsModel.ts b/src/searchResults/domain/searchResultsModel.ts
new file mode 100755
index 0000000..bded5cd
--- /dev/null
+++ b/src/searchResults/domain/searchResultsModel.ts
@@ -0,0 +1,13 @@
+import { SearchResults } from "./searchResultsEntity";
+
+export type CreateSearchResultParams = {
+ data: SearchResults["data"];
+ meta: SearchResults["meta"]
+};
+
+const create = (params: CreateSearchResultParams) => ({
+ data: params.data,
+ meta: params.meta
+});
+
+export { create };
diff --git a/src/searchResults/domain/searchStore.ts b/src/searchResults/domain/searchStore.ts
new file mode 100755
index 0000000..e9588ae
--- /dev/null
+++ b/src/searchResults/domain/searchStore.ts
@@ -0,0 +1,14 @@
+import { SearchResults } from "./searchResultsEntity";
+
+interface SearchResultsStore {
+ searchResults: SearchResults | undefined;
+ isLoading: boolean;
+ isFailed: boolean;
+ failure: undefined;
+ searchRequest: string;
+
+ /* --------------------------------- Actions -------------------------------- */
+ search(request: string): Promise;
+ changeSearchRequest(searchRequest: string): void;
+}
+export type { SearchResultsStore };
diff --git a/src/searchResults/useCases/changeSearchRequestUseCase.ts b/src/searchResults/useCases/changeSearchRequestUseCase.ts
new file mode 100755
index 0000000..30d6121
--- /dev/null
+++ b/src/searchResults/useCases/changeSearchRequestUseCase.ts
@@ -0,0 +1,15 @@
+import { debounce } from "lodash";
+import { SearchResultsStore } from "searchResults/domain/searchStore";
+
+type ChangeRequestStore = Pick<
+ SearchResultsStore,
+ "changeSearchRequest" | "searchRequest"
+>;
+
+const debouncedTask = debounce((task) => Promise.resolve(task()), 200);
+
+const changeSearchRequestUseCase = (store: ChangeRequestStore) => {
+ return debouncedTask(() => store.changeSearchRequest(store.searchRequest));
+};
+
+export { changeSearchRequestUseCase };
diff --git a/src/searchResults/useCases/searchUseCase.ts b/src/searchResults/useCases/searchUseCase.ts
new file mode 100755
index 0000000..0953102
--- /dev/null
+++ b/src/searchResults/useCases/searchUseCase.ts
@@ -0,0 +1,17 @@
+import { debounce } from "lodash";
+import type { SearchResultsStore } from "searchResults/domain/searchStore";
+import { SearchResults } from "searchResults/domain/searchResultsEntity";
+
+type GetSearchResultsStore = Pick<
+ SearchResultsStore,
+ "searchResults" | "search" | "searchRequest"
+>;
+const debouncedTask = debounce((task) => Promise.resolve(task()), 200);
+
+const searchUseCase = (
+ store: GetSearchResultsStore
+): Promise | undefined => {
+ return debouncedTask(() => store.search(store.searchRequest));
+};
+
+export { searchUseCase };
diff --git a/src/services/controller/servicesViewModel.ts b/src/services/controller/servicesViewModel.ts
deleted file mode 100644
index dbdcff0..0000000
--- a/src/services/controller/servicesViewModel.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from "react";
-import { ServicesStore } from "services/domain/servicesStore";
-import { loadServicesUseCase } from "services/useCases/loadServicesUseCase";
-import { UIStore } from "ui/domain/uiStore";
-
-function useServicesViewModel(store: ServicesStore & Pick) {
- const loadServices = React.useCallback(
- function () {
- loadServicesUseCase({
- loadServices: store.loadServices,
- });
- },
- [store.loadServices]
- );
-
- React.useEffect(() => {
- loadServices();
- }, [loadServices]);
-
- return {
- services: store.services,
- failure: store.failure,
- isLoading: store.isLoading,
- isFailed: store.isFailed,
- loadServices,
- };
-}
-
-export { useServicesViewModel };
diff --git a/src/services/data/dto/serviceDTO.ts b/src/services/data/dto/serviceDTO.ts
deleted file mode 100644
index c9aa35f..0000000
--- a/src/services/data/dto/serviceDTO.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { StyleVariantDTO } from "./variantDTO";
-
-export type ServiceDTO = {
- _id: string,
- title: string;
- short_name: string;
- category: string;
- address: string;
- color?: StyleVariantDTO;
-};
\ No newline at end of file
diff --git a/src/services/data/dto/variantDTO.ts b/src/services/data/dto/variantDTO.ts
deleted file mode 100644
index 19ffda0..0000000
--- a/src/services/data/dto/variantDTO.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { StyleColorVariants } from "core/_variants";
-
-export type StyleVariantDTO = StyleColorVariants;
\ No newline at end of file
diff --git a/src/services/data/servicesActionTypes.ts b/src/services/data/servicesActionTypes.ts
deleted file mode 100644
index 805c5e7..0000000
--- a/src/services/data/servicesActionTypes.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-/* ------------------------------ Services list ----------------------------- */
-export const GET_SERVICES = "@services/fetch.all";
-export const GET_SERVICES_SUCCESS = "@services/fetch.all.success";
-export const GET_SERVICES_FAILURE = "@services/fetch.all.failure";
-
-/* --------------------------- Service credentials -------------------------- */
-export const SERVICE_CREDENTIALS = "@services/service.credentials.get"
-export const SERVICE_CREDENTIALS_FETCHED = "@services/service.credentials.success"
-export const SERVICE_CREDENTIALS_FAILURE = "@services/service.credentials.failure"
\ No newline at end of file
diff --git a/src/services/data/servicesActions.ts b/src/services/data/servicesActions.ts
deleted file mode 100644
index a0b72c3..0000000
--- a/src/services/data/servicesActions.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import Failure from "core/failure";
-import { dispatchStatus } from "store";
-import * as actionTypes from "./servicesActionTypes";
-import { loadServices } from "./servicesService";
-
-const getServicesAction = () => (dispatch: any) => {
- dispatch({ type: actionTypes.GET_SERVICES });
-
- return loadServices()
- .then((services) =>
- dispatchStatus(actionTypes.GET_SERVICES, ".success", services)(dispatch)
- )
- .catch((failure: Failure) => {
- dispatchStatus(actionTypes.GET_SERVICES, ".failure", failure)(dispatch);
- });
-};
-
-
-export {
- getServicesAction,
-};
diff --git a/src/services/data/servicesReducer.ts b/src/services/data/servicesReducer.ts
deleted file mode 100644
index b5a00c6..0000000
--- a/src/services/data/servicesReducer.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { AnyAction } from "@reduxjs/toolkit";
-import { ServicesStore } from "services/domain/servicesStore";
-import * as actionTypes from "./servicesActionTypes";
-
-type ServicesStoreState = Omit<
- ServicesStore,
- "loadServices" | "setServices" | "subscribe" | "forkTo"
->;
-
-const INITIAL_STATE: ServicesStoreState = {
- services: undefined,
- failure: undefined,
- isLoading: false,
- isFailed: false,
-};
-
-const servicesReducer = (
- state: ServicesStoreState = INITIAL_STATE,
- action: AnyAction
-): ServicesStoreState => {
- switch (action.type) {
- /* ---------------------------- Services fetching --------------------------- */
- case actionTypes.GET_SERVICES:
- return {
- ...state,
- isLoading: typeof state.services === typeof undefined,
- isFailed: false,
- };
- case actionTypes.GET_SERVICES_SUCCESS:
- return {
- ...state,
- isLoading: false,
- isFailed: false,
- services: action.payload,
- };
- case actionTypes.GET_SERVICES_FAILURE:
- return {
- ...state,
- failure: action.payload,
- isLoading: false,
- isFailed: true,
- };
- /* ------------------------------- Credentials ------------------------------ */
- case actionTypes.SERVICE_CREDENTIALS:
- return {
- ...state,
- };
- /* --------------------------------- Default -------------------------------- */
- default:
- return state;
- }
-};
-
-export { servicesReducer };
-export type { ServicesStoreState };
diff --git a/src/services/data/servicesService.ts b/src/services/data/servicesService.ts
deleted file mode 100644
index 75f8e85..0000000
--- a/src/services/data/servicesService.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { integratorApiClient } from "auth/data/authService";
-import axios from "axios";
-import { DTOModel } from "core/dto_model";
-import Failure from "core/failure";
-import { Service } from "services/domain/serviceEntity";
-import { create } from "services/domain/serviceModel";
-import { ServiceDTO } from "./dto/serviceDTO";
-
-async function loadServices(): Promise {
- try {
- const response = await integratorApiClient
- .get>("/service");
- const content = response.data.data;
- return content.map((dto) => {
- return create({
- id: dto._id,
- name: dto.title,
- shortName: dto.short_name,
- href: dto.address,
- category: dto.category,
- variant: dto.color,
- subscribed: false,
- })
- });
- } catch (reason) {
- if(axios.isAxiosError(reason)) {
- throw Failure.fromReason(reason, "failures.services.load");
- }
- throw reason;
- }
-}
-
-
-export { loadServices };
diff --git a/src/services/data/servicesSlice.ts b/src/services/data/servicesSlice.ts
deleted file mode 100644
index 9e35ec0..0000000
--- a/src/services/data/servicesSlice.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import React from "react";
-import { ServicesStore } from "services/domain/servicesStore";
-import { RootState, useAppDispatch, useAppSelector } from "store";
-import { getServicesAction } from "./servicesActions";
-import { ServicesStoreState } from "./servicesReducer";
-
-const servicesSelector = (state: RootState): ServicesStoreState =>
- state.services;
-
-const useServicesStore = (): ServicesStore => {
- const { services, isLoading, isFailed, failure } =
- useAppSelector(servicesSelector);
-
- const dispatch = useAppDispatch();
-
- const loadServices = React.useCallback(
- () => getServicesAction()(dispatch),
- [dispatch]
- );
-
- return {
- /* ---------------------------------- State --------------------------------- */
- isLoading,
- isFailed,
- services,
- failure,
- /* --------------------------------- Actions -------------------------------- */
- loadServices,
- };
-};
-
-export { useServicesStore };
diff --git a/src/services/domain/serviceEntity.ts b/src/services/domain/serviceEntity.ts
deleted file mode 100644
index 392a0b3..0000000
--- a/src/services/domain/serviceEntity.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { StyleColorVariants } from "core/_variants";
-
-export interface Service {
- id: string | number;
- name: string;
- shortName: string;
- description: string;
- category: string;
- href: string;
- subscribed: boolean;
- variant?: StyleColorVariants;
-}
diff --git a/src/services/domain/serviceModel.ts b/src/services/domain/serviceModel.ts
deleted file mode 100644
index ec970ab..0000000
--- a/src/services/domain/serviceModel.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Service } from "./serviceEntity";
-
-const create = (
- props: Omit
-): Service => ({
- ...props,
- shortName: props.shortName,
- description: "",
-});
-
-export { create };
diff --git a/src/services/domain/servicesStore.ts b/src/services/domain/servicesStore.ts
deleted file mode 100644
index 33707c6..0000000
--- a/src/services/domain/servicesStore.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Service } from "./serviceEntity";
-
-interface ServicesStore {
- /* ---------------------------------- State --------------------------------- */
- services: Service[] | undefined;
- failure: undefined;
- isLoading: boolean;
- isFailed: boolean;
- /* --------------------------------- Actions -------------------------------- */
- loadServices(): Promise;
-}
-
-export type { ServicesStore };
diff --git a/src/services/useCases/loadServicesUseCase.ts b/src/services/useCases/loadServicesUseCase.ts
deleted file mode 100644
index f0851b8..0000000
--- a/src/services/useCases/loadServicesUseCase.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { debounce } from "lodash";
-import { ServicesStore } from "services/domain/servicesStore";
-
-type LoadServicesStore = Pick;
-
-const deboucedTask = debounce((task) => Promise.resolve(task()), 500);
-
-const loadServicesUseCase = (store: LoadServicesStore) => {
- return deboucedTask(() => store.loadServices());
-};
-
-export { loadServicesUseCase };
diff --git a/src/services/views/ServicesNavigationContext.tsx b/src/services/views/ServicesNavigationContext.tsx
deleted file mode 100644
index a8e2016..0000000
--- a/src/services/views/ServicesNavigationContext.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Libraries */
-/* -------------------------------------------------------------------------- */
-import React, { useEffect } from "react";
-/* -------------------------------------------------------------------------- */
-/* Components */
-/* -------------------------------------------------------------------------- */
-import ContextMenu from "components/containers/contextmenu/ContextMenu";
-import ServiceLink from "components/ServiceLink";
-import ContextMenuItem from "components/containers/contextmenu/ContextMenuItem";
-/* -------------------------------------------------------------------------- */
-/* Icons */
-/* -------------------------------------------------------------------------- */
-import { ReactComponent as SVGServicesIcon } from "assets/svg/services.svg";
-import { useServicesStore } from "services/data/servicesSlice";
-import { useServicesViewModel } from "services/controller/servicesViewModel";
-import CircleLoader from "components/CircleLoader";
-import { useUIStore } from "ui/data/uiSlice";
-
-/* -------------------------------------------------------------------------- */
-/* Services context menu component */
-/* -------------------------------------------------------------------------- */
-
-export default function ServicesNavigationContext() {
- const servicesStore = useServicesStore();
- const uiStore = useUIStore();
-
- const { isLoading, services } = useServicesViewModel({
- ...servicesStore,
- notify: uiStore.notify,
- });
-
- return (
-
- }
- className="absolute w-full min-h-14 sm:w-96 -bottom-3
- sm:top-auto sm:bottom-auto
- mt-5 z-10 sm:transform -translate-1/2
- origin-top-center"
- >
- {!services || isLoading ? (
-
- ) : (
- <>
- {services
- .filter((service) => service.subscribed)
- .map((service) => (
-
-
-
- ))}
- >
- )}
-
- );
-}
diff --git a/src/setupTests.ts b/src/setupTests.ts
old mode 100644
new mode 100755
index 8f2609b..141e479
--- a/src/setupTests.ts
+++ b/src/setupTests.ts
@@ -1,5 +1,5 @@
-// jest-dom adds custom jest matchers for asserting on DOM nodes.
-// allows you to do things like:
-// expect(element).toHaveTextContent(/react/i)
-// learn more: https://github.com/testing-library/jest-dom
-import '@testing-library/jest-dom';
+// jest-dom adds custom jest matchers for asserting on DOM nodes.
+// allows you to do things like:
+// expect(element).toHaveTextContent(/react/i)
+// learn more: https://github.com/testing-library/jest-dom
+import '@testing-library/jest-dom';
diff --git a/src/store/hooks.ts b/src/store/hooks.ts
old mode 100644
new mode 100755
index 4e17df4..c8c666d
--- a/src/store/hooks.ts
+++ b/src/store/hooks.ts
@@ -1,10 +1,10 @@
-import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
-import type { RootState, AppDispatch } from "./store";
-
-// Use throughout your app instead of plain `useDispatch` and `useSelector`
-export const useAppDispatch = () => useDispatch();
-export const useAppSelector: TypedUseSelectorHook = useSelector;
-export const dispatchStatus =
- (action: string, status: ".success" | ".failure", payload: any) =>
- (dispatch: any) =>
- dispatch({ type: action + status, payload: payload });
+import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
+import type { RootState, AppDispatch } from "./store";
+
+// Use throughout your app instead of plain `useDispatch` and `useSelector`
+export const useAppDispatch = () => useDispatch();
+export const useAppSelector: TypedUseSelectorHook = useSelector;
+export const dispatchStatus =
+ (action: string, status: ".success" | ".failure", payload: any) =>
+ (dispatch: any) =>
+ dispatch({ type: action + status, payload: payload });
diff --git a/src/store/index.ts b/src/store/index.ts
old mode 100644
new mode 100755
index 96e699d..2dab57d
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -1,2 +1,2 @@
-export type {RootState, AppDispatch} from "./store";
+export type {RootState, AppDispatch} from "./store";
export {useAppDispatch, useAppSelector, dispatchStatus} from "./hooks";
\ No newline at end of file
diff --git a/src/store/store.ts b/src/store/store.ts
old mode 100644
new mode 100755
index 8d5fa22..bb1665f
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -1,26 +1,18 @@
-import { configureStore } from "@reduxjs/toolkit";
-import { authReducer } from "auth/data/authReducer";
-import { servicesReducer } from "services/data/servicesReducer";
-import { uiReducer } from "ui/data/uiReducer";
-import { userReducer } from "user/data/userReducer";
-import thunk from "redux-thunk";
-import { subscriptionsReducer } from "subscriptions/data/subscriptionsReducer";
-
-const store = configureStore({
- reducer: {
- user: userReducer,
- ui: uiReducer,
- services: servicesReducer,
- auth: authReducer,
- subscriptions: subscriptionsReducer,
- },
- middleware: [
- thunk,
- ]
-});
-
-export {store};
-
-export type RootState = ReturnType;
-
-export type AppDispatch = typeof store.dispatch;
+import { configureStore } from "@reduxjs/toolkit";
+import { articleReducer } from "article/data/articleReducer";
+import thunk from "redux-thunk";
+import { searchResultReducer } from "searchResults/data/searchReducer";
+
+const store = configureStore({
+ reducer: {
+ searchResults: searchResultReducer,
+ article: articleReducer,
+ },
+ middleware: [thunk],
+});
+
+export { store };
+
+export type RootState = ReturnType;
+
+export type AppDispatch = typeof store.dispatch;
diff --git a/src/stories/AllIcons.stories.tsx b/src/stories/AllIcons.stories.tsx
new file mode 100755
index 0000000..fb69a0a
--- /dev/null
+++ b/src/stories/AllIcons.stories.tsx
@@ -0,0 +1,270 @@
+import React, { Component } from "react";
+import { ComponentMeta, ComponentStory, Meta} from "@storybook/react";
+import { ReactComponent as SVGArrowBigRight } from "../assets/svg/arrow-big-right.svg" ;
+import { ReactComponent as SVGArrowRight} from "../assets/svg/arrow-right.svg";
+// import {ReactComponent as SVGArrowLeft} from "../assets/svg/arrow-left.svg";
+import {ReactComponent as SVGArrowDown} from "../assets/svg/arrow-down.svg";
+import {ReactComponent as SVGBookmark} from "../assets/svg/bookmark.svg";
+import {ReactComponent as SVGCaretDown} from "../assets/svg/caret-down.svg"
+import {ReactComponent as SVGCaretLeft} from "../assets/svg/caret-left.svg"
+import {ReactComponent as SVGCaretRight} from "../assets/svg/caret-right.svg"
+import {ReactComponent as SVGCaretUp} from "../assets/svg/caret-up.svg";
+import {ReactComponent as SVGChevronesLeft} from "../assets/svg/chevrones-left.svg";
+import {ReactComponent as SVGChevronesRight} from "../assets/svg/chevrones-right.svg";
+import {ReactComponent as SVGCite} from "../assets/svg/cite.svg";
+import {ReactComponent as SVGCopy} from "../assets/svg/copy.svg";
+import {ReactComponent as SVGDelete} from "../assets/svg/delete.svg";
+import {ReactComponent as SVGDownload} from "../assets/svg/download.svg";
+import {ReactComponent as SVGEdit1} from "../assets/svg/edit1.svg";
+import {ReactComponent as SVGEdit2} from "../assets/svg/edit2.svg";
+import {ReactComponent as SVGError} from "../assets/svg/error.svg";
+import {ReactComponent as SVGEye} from "../assets/svg/eye.svg";
+import {ReactComponent as SVGFavorite} from "../assets/svg/favorite.svg";
+import {ReactComponent as SVGFiletext} from "../assets/svg/filetext.svg";
+import {ReactComponent as SVGFolder} from "../assets/svg/folder.svg";
+import {ReactComponent as SVGKey} from "../assets/svg/key.svg";
+import {ReactComponent as SVGList} from "../assets/svg/list.svg";
+import {ReactComponent as SVGMinus} from "../assets/svg/minus.svg";
+import {ReactComponent as SVGMore} from "../assets/svg/more.svg";
+import {ReactComponent as SVGPlus} from "../assets/svg/plus.svg";
+import {ReactComponent as SVGPrinter} from "../assets/svg/printer.svg";
+import {ReactComponent as SVGSearch} from "../assets/svg/search.svg";
+import {ReactComponent as SVGShare} from "../assets/svg/share.svg";
+import {ReactComponent as SVGUser} from "../assets/svg/user.svg";
+import {ReactComponent as SVGXMark} from "../assets/svg/xmark.svg";
+
+
+export default {
+ title: "Icons",
+}
+
+
+const ArrowBigRightStory: ComponentStory = (args) => ;
+const ArrowRightStory: ComponentStory = (args) => ;
+//const SVGArrowLeftStory : ComponentStory = (args) =>
+const SVGArrowDownStory : ComponentStory = (args) =>
+const SVGBookmarkStory : ComponentStory = (args) =>
+const SVGCaretDownStory : ComponentStory = (args) =>
+const SVGCaretLeftStory : ComponentStory = (args) =>
+const SVGCaretRightStory : ComponentStory = (args) =>
+const SVGCaretUpStory : ComponentStory = (args) =>
+const SVGChevronesLeftStory : ComponentStory = (args) =>
+const SVGChevronesRightStory : ComponentStory = (args) =>
+const SVGCiteStory : ComponentStory = (args) =>
+const SVGCopyStory : ComponentStory = (args) =>
+const SVGDeleteStory : ComponentStory = (args) =>
+const SVGDownloadStory : ComponentStory = (args) =>
+const SVGEdit1Story : ComponentStory = (args) =>
+const SVGEdit2Story : ComponentStory = (args) =>
+const SVGErrorStory : ComponentStory = (args) =>
+const SVGEyeStory : ComponentStory = (args) =>
+const SVGFavoriteStory : ComponentStory = (args) =>
+const SVGFiletextStory : ComponentStory = (args) =>
+const SVGFolderStory : ComponentStory = (args) =>
+const SVGKeyStory : ComponentStory = (args) =>
+const SVGListStory : ComponentStory = (args) =>
+const SVGMinusStory : ComponentStory = (args) =>
+const SVGMoreStory : ComponentStory = (args) =>
+const SVGPlusStory : ComponentStory = (args) =>
+const SVGPrinterStory : ComponentStory = (args) =>
+const SVGSearchStory : ComponentStory = (args) =>
+const SVGShareStory : ComponentStory = (args) =>
+const SVGUserStory : ComponentStory = (args) =>
+const SVGXMarkStory : ComponentStory = (args) =>
+
+export const ArrowBigRight= ArrowBigRightStory.bind({});
+export const ArrowRight = ArrowRightStory.bind({});
+//export const ArrowLeft = SVGArrowLeftStory.bind({});
+export const ArrowDown = SVGArrowDownStory.bind({});
+export const Bookmark = SVGBookmarkStory.bind({});
+export const CaretDown = SVGCaretDownStory.bind({});
+export const CaretLeft = SVGCaretLeftStory.bind({});
+export const CaretRight = SVGCaretRightStory.bind({});
+export const CaretUp = SVGCaretUpStory.bind({});
+export const ChevronesLeft = SVGChevronesLeftStory.bind({});
+export const ChevronesRight = SVGChevronesRightStory.bind({});
+export const Cite = SVGCiteStory.bind({});
+export const Copy = SVGCopyStory.bind({});
+export const Delete = SVGDeleteStory.bind({});
+export const Download = SVGDownloadStory.bind({});
+export const Edit1 = SVGEdit1Story.bind({});
+export const Edit2 = SVGEdit2Story.bind({});
+export const Error = SVGErrorStory.bind({});
+export const Eye = SVGEyeStory.bind({});
+export const Favorite = SVGFavoriteStory.bind({});
+export const Filetext = SVGFiletextStory.bind({});
+export const Folder = SVGFolderStory.bind({});
+export const Key = SVGKeyStory.bind({});
+export const List = SVGListStory.bind({});
+export const Minus = SVGMinusStory.bind({});
+export const More = SVGMoreStory.bind({});
+export const Plus = SVGPlusStory.bind({});
+export const Printer = SVGPrinterStory.bind({});
+export const Search = SVGSearchStory.bind({});
+export const Share = SVGShareStory.bind({});
+export const User = SVGUserStory.bind({});
+export const XMark = SVGXMarkStory.bind({});
+
+
+ArrowBigRight.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+
+ArrowRight.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+
+// ArrowLeft.args = {
+// fill: 'blue',
+// stroke: 'blue',
+// width: 25,
+// };
+ArrowDown.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Bookmark.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+CaretDown.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+CaretLeft.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+CaretRight.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+CaretUp.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+ChevronesLeft.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+ChevronesRight.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Cite.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Copy.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Delete.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Download.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Edit1.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Edit2.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Error.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Eye.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Favorite.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Filetext.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Folder.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Key.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+List.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Minus.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+More.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Plus.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Printer.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Search.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+Share.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+User.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+};
+XMark.args = {
+ fill: 'blue',
+ stroke: 'blue',
+ width: 25,
+}
diff --git a/src/stories/Button.stories.tsx b/src/stories/Button.stories.tsx
new file mode 100755
index 0000000..766432a
--- /dev/null
+++ b/src/stories/Button.stories.tsx
@@ -0,0 +1,41 @@
+import React, { Children } from "react";
+import { ComponentStory, ComponentMeta } from "@storybook/react";
+import { Button } from "../components/Button/Button";
+import { SVGBookmark } from "../components/icons";
+
+export default {
+ title: "Button",
+ component: Button,
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => ;
+
+export const High = Template.bind({});
+High.args = {
+ emphasis: "high",
+ children: (
+
+
+
+ ),
+};
+
+export const Medium = Template.bind({});
+Medium.args = {
+ emphasis: "medium",
+ children: (
+
+
+
+ ),
+};
+
+export const Low = Template.bind({});
+Low.args = {
+ emphasis: "low",
+ children: (
+
+
+
+ ),
+};
diff --git a/src/stories/Button.tsx b/src/stories/Button.tsx
new file mode 100755
index 0000000..54fa8b9
--- /dev/null
+++ b/src/stories/Button.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import './button.css';
+
+interface ButtonProps {
+ /**
+ * Is this the principal call to action on the page?
+ */
+ primary?: boolean;
+ /**
+ * What background color to use
+ */
+ backgroundColor?: string;
+ /**
+ * How large should the button be?
+ */
+ size?: 'small' | 'medium' | 'large';
+ /**
+ * Button contents
+ */
+ label: string;
+ /**
+ * Optional click handler
+ */
+ onClick?: () => void;
+}
+
+/**
+ * Primary UI component for user interaction
+ */
+export const Button = ({
+ primary = false,
+ size = 'medium',
+ backgroundColor,
+ label,
+ ...props
+}: ButtonProps) => {
+ const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
+ return (
+
+ {label}
+
+ );
+};
diff --git a/src/stories/Header.stories.tsx b/src/stories/Header.stories.tsx
new file mode 100755
index 0000000..8c28e55
--- /dev/null
+++ b/src/stories/Header.stories.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+
+import { Header } from './Header';
+
+export default {
+ title: 'Example/Header',
+ component: Header,
+ parameters: {
+ // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout
+ layout: 'fullscreen',
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => ;
+
+export const LoggedIn = Template.bind({});
+LoggedIn.args = {
+ user: {
+ name: 'Jane Doe',
+ },
+};
+
+export const LoggedOut = Template.bind({});
+LoggedOut.args = {};
diff --git a/src/stories/Header.tsx b/src/stories/Header.tsx
new file mode 100755
index 0000000..c972abc
--- /dev/null
+++ b/src/stories/Header.tsx
@@ -0,0 +1,56 @@
+import React from 'react';
+
+import { Button } from './Button';
+import './header.css';
+
+type User = {
+ name: string;
+};
+
+interface HeaderProps {
+ user?: User;
+ onLogin: () => void;
+ onLogout: () => void;
+ onCreateAccount: () => void;
+}
+
+export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => (
+
+);
diff --git a/src/stories/Introduction.stories.mdx b/src/stories/Introduction.stories.mdx
new file mode 100755
index 0000000..12f1990
--- /dev/null
+++ b/src/stories/Introduction.stories.mdx
@@ -0,0 +1,211 @@
+import { Meta } from '@storybook/addon-docs';
+import Code from './assets/code-brackets.svg';
+import Colors from './assets/colors.svg';
+import Comments from './assets/comments.svg';
+import Direction from './assets/direction.svg';
+import Flow from './assets/flow.svg';
+import Plugin from './assets/plugin.svg';
+import Repo from './assets/repo.svg';
+import StackAlt from './assets/stackalt.svg';
+
+
+
+
+
+# Welcome to Storybook
+
+Storybook helps you build UI components in isolation from your app's business logic, data, and context.
+That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA.
+
+Browse example stories now by navigating to them in the sidebar.
+View their code in the `stories` directory to learn how they work.
+We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages.
+
+Configure
+
+
+
+Learn
+
+
+
+
+ Tip Edit the Markdown in{' '}
+ stories/Introduction.stories.mdx
+
diff --git a/src/stories/Page.stories.tsx b/src/stories/Page.stories.tsx
new file mode 100755
index 0000000..f75af66
--- /dev/null
+++ b/src/stories/Page.stories.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { within, userEvent } from '@storybook/testing-library';
+import { Page } from './Page';
+
+export default {
+ title: 'Example/Page',
+ component: Page,
+ parameters: {
+ // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout
+ layout: 'fullscreen',
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => ;
+
+export const LoggedOut = Template.bind({});
+
+export const LoggedIn = Template.bind({});
+
+// More on interaction testing: https://storybook.js.org/docs/react/writing-tests/interaction-testing
+LoggedIn.play = async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ const loginButton = await canvas.getByRole('button', { name: /Log in/i });
+ await userEvent.click(loginButton);
+};
diff --git a/src/stories/Page.tsx b/src/stories/Page.tsx
new file mode 100755
index 0000000..6c82529
--- /dev/null
+++ b/src/stories/Page.tsx
@@ -0,0 +1,73 @@
+import React from 'react';
+
+import { Header } from './Header';
+import './page.css';
+
+type User = {
+ name: string;
+};
+
+export const Page: React.VFC = () => {
+ const [user, setUser] = React.useState();
+
+ return (
+
+ setUser({ name: 'Jane Doe' })}
+ onLogout={() => setUser(undefined)}
+ onCreateAccount={() => setUser({ name: 'Jane Doe' })}
+ />
+
+
+ Pages in Storybook
+
+ We recommend building UIs with a{' '}
+
+ component-driven
+ {' '}
+ process starting with atomic components and ending with pages.
+
+
+ Render pages with mock data. This makes it easy to build and review page states without
+ needing to navigate to them in your app. Here are some handy patterns for managing page
+ data in Storybook:
+
+
+
+ Use a higher-level connected component. Storybook helps you compose such data from the
+ "args" of child component stories
+
+
+ Assemble data in the page component from your services. You can mock these services out
+ using Storybook.
+
+
+
+ Get a guided tutorial on component-driven development at{' '}
+
+ Storybook tutorials
+
+ . Read more in the{' '}
+
+ docs
+
+ .
+
+
+
Tip Adjust the width of the canvas with the{' '}
+
+
+
+
+
+ Viewports addon in the toolbar
+
+
+
+ );
+};
diff --git a/src/stories/Radio.stories.tsx b/src/stories/Radio.stories.tsx
new file mode 100755
index 0000000..25ef76e
--- /dev/null
+++ b/src/stories/Radio.stories.tsx
@@ -0,0 +1,46 @@
+import Radio from "../components/Radio";
+import { Meta, Story, ComponentStory, ComponentMeta } from "@storybook/react";
+import React, { useState } from "react";
+import { ReactComponent as Checkmark } from "assets/svg/check.svg";
+import { boolean } from "yup/lib/locale";
+
+export default {
+ title: "Radio",
+ component: Radio,
+ // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
+ argTypes: {
+ checked: {
+ type: "boolean",
+ },
+ children: {
+ type: "string",
+ defaultValue: "Use light theme",
+ },
+ className: {
+ type: "string",
+ defaultValue: "mt-4 ml-4",
+ },
+ disabled: {
+ type: "boolean",
+ defaultValue: "false",
+ },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => ;
+
+export const Checked = Template.bind({});
+Checked.args = {
+ checked: true,
+ children: "This is a custom Radio",
+};
+
+export const Unchecked = Template.bind({});
+Unchecked.args = {
+ checked: false,
+};
+
+export const Disabled = Template.bind({});
+Disabled.args = {
+ disabled: true,
+};
diff --git a/src/stories/TextInput.stories.tsx b/src/stories/TextInput.stories.tsx
new file mode 100755
index 0000000..84f8c02
--- /dev/null
+++ b/src/stories/TextInput.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import {TextInput} from "../components/TextInput";
+
+export default {
+ title: 'Text Input',
+ component: TextInput,
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) =>
+
+export const TextInputStory = Template.bind({});
+
+TextInputStory.args ={
+ inGroup: true,
+ placeholder: "Search",
+ className: ["px-4", "py-2.5"]
+};
diff --git a/src/stories/assets/code-brackets.svg b/src/stories/assets/code-brackets.svg
new file mode 100755
index 0000000..73de947
--- /dev/null
+++ b/src/stories/assets/code-brackets.svg
@@ -0,0 +1 @@
+illustration/code-brackets
\ No newline at end of file
diff --git a/src/stories/assets/colors.svg b/src/stories/assets/colors.svg
new file mode 100755
index 0000000..17d58d5
--- /dev/null
+++ b/src/stories/assets/colors.svg
@@ -0,0 +1 @@
+illustration/colors
\ No newline at end of file
diff --git a/src/stories/assets/comments.svg b/src/stories/assets/comments.svg
new file mode 100755
index 0000000..6493a13
--- /dev/null
+++ b/src/stories/assets/comments.svg
@@ -0,0 +1 @@
+illustration/comments
\ No newline at end of file
diff --git a/src/stories/assets/direction.svg b/src/stories/assets/direction.svg
new file mode 100755
index 0000000..65676ac
--- /dev/null
+++ b/src/stories/assets/direction.svg
@@ -0,0 +1 @@
+illustration/direction
\ No newline at end of file
diff --git a/src/stories/assets/flow.svg b/src/stories/assets/flow.svg
new file mode 100755
index 0000000..8ac27db
--- /dev/null
+++ b/src/stories/assets/flow.svg
@@ -0,0 +1 @@
+illustration/flow
\ No newline at end of file
diff --git a/src/stories/assets/plugin.svg b/src/stories/assets/plugin.svg
new file mode 100755
index 0000000..29e5c69
--- /dev/null
+++ b/src/stories/assets/plugin.svg
@@ -0,0 +1 @@
+illustration/plugin
\ No newline at end of file
diff --git a/src/stories/assets/repo.svg b/src/stories/assets/repo.svg
new file mode 100755
index 0000000..f386ee9
--- /dev/null
+++ b/src/stories/assets/repo.svg
@@ -0,0 +1 @@
+illustration/repo
\ No newline at end of file
diff --git a/src/stories/assets/stackalt.svg b/src/stories/assets/stackalt.svg
new file mode 100755
index 0000000..9b7ad27
--- /dev/null
+++ b/src/stories/assets/stackalt.svg
@@ -0,0 +1 @@
+illustration/stackalt
\ No newline at end of file
diff --git a/src/stories/button.css b/src/stories/button.css
new file mode 100755
index 0000000..7e932ef
--- /dev/null
+++ b/src/stories/button.css
@@ -0,0 +1,30 @@
+.storybook-button {
+ font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+.storybook-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+.storybook-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+.storybook-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+.storybook-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+.storybook-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/src/stories/header.css b/src/stories/header.css
new file mode 100755
index 0000000..f5907d1
--- /dev/null
+++ b/src/stories/header.css
@@ -0,0 +1,32 @@
+.wrapper {
+ font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ padding: 15px 20px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+svg {
+ display: inline-block;
+ vertical-align: top;
+}
+
+h1 {
+ font-weight: 900;
+ font-size: 20px;
+ line-height: 1;
+ margin: 6px 0 6px 10px;
+ display: inline-block;
+ vertical-align: top;
+}
+
+button + button {
+ margin-left: 10px;
+}
+
+.welcome {
+ color: #333;
+ font-size: 14px;
+ margin-right: 10px;
+}
diff --git a/src/stories/page.css b/src/stories/page.css
new file mode 100755
index 0000000..5cd904d
--- /dev/null
+++ b/src/stories/page.css
@@ -0,0 +1,69 @@
+section {
+ font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 24px;
+ padding: 48px 20px;
+ margin: 0 auto;
+ max-width: 600px;
+ color: #333;
+}
+
+section h2 {
+ font-weight: 900;
+ font-size: 32px;
+ line-height: 1;
+ margin: 0 0 4px;
+ display: inline-block;
+ vertical-align: top;
+}
+
+section p {
+ margin: 1em 0;
+}
+
+section a {
+ text-decoration: none;
+ color: #1ea7fd;
+}
+
+section ul {
+ padding-left: 30px;
+ margin: 1em 0;
+}
+
+section li {
+ margin-bottom: 8px;
+}
+
+section .tip {
+ display: inline-block;
+ border-radius: 1em;
+ font-size: 11px;
+ line-height: 12px;
+ font-weight: 700;
+ background: #e7fdd8;
+ color: #66bf3c;
+ padding: 4px 12px;
+ margin-right: 10px;
+ vertical-align: top;
+}
+
+section .tip-wrapper {
+ font-size: 13px;
+ line-height: 20px;
+ margin-top: 40px;
+ margin-bottom: 40px;
+}
+
+section .tip-wrapper svg {
+ display: inline-block;
+ height: 12px;
+ width: 12px;
+ margin-right: 4px;
+ vertical-align: top;
+ margin-top: 3px;
+}
+
+section .tip-wrapper svg path {
+ fill: #1ea7fd;
+}
diff --git a/src/subscriptions/controller/subscriptionsViewModel.ts b/src/subscriptions/controller/subscriptionsViewModel.ts
deleted file mode 100644
index 86f5db3..0000000
--- a/src/subscriptions/controller/subscriptionsViewModel.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-import Failure from "core/failure";
-import React from "react";
-import { useTranslation } from "react-i18next";
-import { Service } from "services/domain/serviceEntity";
-import { SubscriptionsStore } from "subscriptions/domain/subscriptionsStore";
-import { forkToServiceUseCase } from "subscriptions/useCases/forkToServiceUseCase";
-import { getSubscriptionsUseCase } from "subscriptions/useCases/getSubscriptions";
-import { serviceSubscribeUseCase } from "subscriptions/useCases/serviceSubscribeUseCase";
-import { UIStore } from "ui/domain/uiStore";
-
-function useSubscriptionsViewModel(
- store: SubscriptionsStore,
- notifications: UIStore
-) {
- const { isLoading, isFailed, subscribing, subscriptions } = store;
-
- const { t } = useTranslation();
-
- const loadSubscriptions = React.useCallback(() => {
- return getSubscriptionsUseCase({
- get: store.get,
- });
- }, [store.get]);
-
- React.useEffect(() => {
- loadSubscriptions();
- }, [loadSubscriptions]);
-
- const hasSubscription = React.useCallback(
- (id: Service["id"]) => store.subscriptions?.includes(id) ?? false,
- [store.subscriptions]
- );
-
- const processingSubscription = React.useCallback(
- (id: Service["id"]) => store.subscribing?.includes(id) ?? false,
- [store.subscribing]
- );
-
- const subscribe = React.useCallback(
- (id: Service["id"], password: string) => {
- return serviceSubscribeUseCase(
- {
- subscribe: store.subscribe,
- subscriptions: store.subscriptions,
- },
- id,
- password
- )
- ?.then(() => {
- notifications.notify({
- body: t("subscriptions.subscribed"),
- status: "success",
- title: "Success",
- });
- })
- .catch((failure: any) => {
- if (!Failure.isFailure(failure)) {
- return Promise.reject(failure);
- }
-
- notifications.notify({
- body: failure.key ? t(failure.key) : failure.message,
- status: "error",
- title: t("failures.subscriptions.failure"),
- });
-
- return Promise.reject(failure);
- });
- },
- [store.subscribe, store.subscriptions, t, notifications]
- );
-
- const forkTo = React.useCallback(
- (id: Service["id"], href: string) => {
- return forkToServiceUseCase(
- {
- forkTo: store.forkTo,
- },
- id
- ).then(() => {
- window.open(href);
- }).catch((failure) => {
- if (!Failure.isFailure(failure)) {
- return Promise.reject(failure);
- }
- notifications.notify({
- body: failure.key ? t(failure.key) : failure.message,
- status: "error",
- title: t("failures.subscriptions.failure"),
- });
- });
- },
- [store.forkTo, notifications, t]
- );
-
- return {
- /* --------------------------------- States --------------------------------- */
- subscriptions,
- subscribing,
- isLoading,
- isFailed,
- /* ---------------------------------- Utils --------------------------------- */
- hasSubscription,
- processingSubscription,
- /* --------------------------------- Actions -------------------------------- */
- loadSubscriptions,
- subscribe,
- forkTo,
- };
-}
-
-export { useSubscriptionsViewModel };
diff --git a/src/subscriptions/data/dto/sessionDTO.ts b/src/subscriptions/data/dto/sessionDTO.ts
deleted file mode 100644
index ef2664c..0000000
--- a/src/subscriptions/data/dto/sessionDTO.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export interface SessionDTO {
-
-}
\ No newline at end of file
diff --git a/src/subscriptions/data/dto/subscriptionDTO.ts b/src/subscriptions/data/dto/subscriptionDTO.ts
deleted file mode 100644
index 55d37a1..0000000
--- a/src/subscriptions/data/dto/subscriptionDTO.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export interface SubscriptionDTO {
- service_id: string;
-}
\ No newline at end of file
diff --git a/src/subscriptions/data/dto/subscriptionRequest.ts b/src/subscriptions/data/dto/subscriptionRequest.ts
deleted file mode 100644
index e0d7bda..0000000
--- a/src/subscriptions/data/dto/subscriptionRequest.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { Service } from "services/domain/serviceEntity";
-
-export interface SubscriptionRequest {
- service_id: Service['id'],
- password: string;
-}
\ No newline at end of file
diff --git a/src/subscriptions/data/subscriptionsActionTypes.ts b/src/subscriptions/data/subscriptionsActionTypes.ts
deleted file mode 100644
index d681abf..0000000
--- a/src/subscriptions/data/subscriptionsActionTypes.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/* ------------------------------ Subscriptions ----------------------------- */
-export const SUBSCRIPTIONS_LOAD = "@services/subscriptions.get"
-export const SUBSCRIPTIONS_LOAD_SUCCESS = "@services/subscriptions.get.success"
-export const SUBSCRIPTIONS_LOAD_FAILURE = "@services/subscriptions.get.failure"
-/* ------------------------------ Subscription ------------------------------ */
-export const SUBSCRIBE_SERVICE = "@services/service.subscribe"
-export const SUBSCRIBE_SERVICE_SUCCESS = "@services/service.subscribe.success"
-export const SUBSCRIBE_SERVICE_FAILED = "@services/service.subscribe.failure"
-/* --------------------------- SSO service action --------------------------- */
-export const SESSION_GET = "@services/service.fork"
-export const SESSION_GET_SUCCESS = "@services/service.fork.success"
-export const SESSION_GET_FAILURE = "@services/service.fork.failure"
\ No newline at end of file
diff --git a/src/subscriptions/data/subscriptionsActions.ts b/src/subscriptions/data/subscriptionsActions.ts
deleted file mode 100644
index 48beca3..0000000
--- a/src/subscriptions/data/subscriptionsActions.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import Failure from "core/failure";
-import { Service } from "services/domain/serviceEntity";
-import { notifyAction } from "ui/data/uiActions";
-import { SubscriptionRequest } from "./dto/subscriptionRequest";
-import * as actionTypes from "./subscriptionsActionTypes";
-import { forkTo, loadSubscriptions, subscribe } from "./subscriptionsService";
-
-const getSubscriptionsAction = () => (dispatch: any) => {
- dispatch({ type: actionTypes.SUBSCRIPTIONS_LOAD });
-
- return loadSubscriptions()
- .then((subscriptions) => {
- dispatch({
- type: actionTypes.SUBSCRIPTIONS_LOAD_SUCCESS,
- payload: subscriptions,
- });
- })
- .catch((failure: Failure) => {
- if (failure.meta?.isNetworkFailure) {
- notifyAction({
- title: "Network failure occured",
- body: failure.message,
- delay: 500,
- });
- }
- dispatch({
- type: actionTypes.SUBSCRIPTIONS_LOAD_FAILURE,
- payload: failure,
- });
- });
-};
-
-const subscribeAction = (payload: SubscriptionRequest) => async (dispatch: any): Promise => {
- dispatch({ type: actionTypes.SUBSCRIBE_SERVICE, payload: payload.service_id });
- try {
- await subscribe(payload);
- dispatch({ type: actionTypes.SUBSCRIBE_SERVICE_SUCCESS, payload: payload.service_id });
- } catch (failure) {
- dispatch({
- type: actionTypes.SUBSCRIBE_SERVICE_FAILED,
- payload: payload.service_id,
- });
- return Promise.reject(failure);
- }
-};
-
-const getSessionAction = (id: Service["id"]) => (dispatch: any) => {
- dispatch({ type: actionTypes.SESSION_GET, payload: id });
- return forkTo(id);
-};
-
-export { getSubscriptionsAction, subscribeAction, getSessionAction };
diff --git a/src/subscriptions/data/subscriptionsReducer.ts b/src/subscriptions/data/subscriptionsReducer.ts
deleted file mode 100644
index 91be4b3..0000000
--- a/src/subscriptions/data/subscriptionsReducer.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { AnyAction } from "@reduxjs/toolkit";
-import { SubscriptionsStore } from "subscriptions/domain/subscriptionsStore";
-import * as actionTypes from "./subscriptionsActionTypes";
-
-type SubscriptionsStoreState = Omit<
- SubscriptionsStore,
- "get" | "forkTo" | "subscribe"
->;
-
-const INITIAL_STATE: SubscriptionsStoreState = {
- isLoading: true,
- isFailed: false,
- subscribing: [],
- subscriptions: undefined,
-};
-
-const subscriptionsReducer = (
- state: SubscriptionsStoreState = INITIAL_STATE,
- action: AnyAction
-): SubscriptionsStoreState => {
- switch (action.type) {
- /* ------------------------- Subscriptions fetching ------------------------- */
- case actionTypes.SUBSCRIPTIONS_LOAD:
- return {
- ...state,
- isLoading: typeof state.subscriptions === typeof undefined,
- };
- case actionTypes.SUBSCRIPTIONS_LOAD_SUCCESS:
- return {
- ...state,
- isLoading: false,
- subscriptions: action.payload,
- };
- case actionTypes.SUBSCRIPTIONS_LOAD_FAILURE:
- return {
- ...state,
- isFailed: true,
- isLoading: false,
- };
- /* --------------------------- Services management -------------------------- */
- case actionTypes.SUBSCRIBE_SERVICE:
- return {
- ...state,
- subscribing: [...(state.subscribing ?? []), action.payload],
- };
- case actionTypes.SUBSCRIBE_SERVICE_SUCCESS:
- return {
- ...state,
- subscriptions: [...(state.subscriptions ?? []), action.payload],
- subscribing: state.subscribing?.filter(
- (item) => item !== action.payload
- ),
- };
- case actionTypes.SUBSCRIBE_SERVICE_FAILED:
- return {
- ...state,
- subscribing: state.subscribing?.filter(
- (item) => item !== action.payload
- ),
- };
- /* --------------------------------- Default -------------------------------- */
- default:
- return state;
- }
-};
-
-export { subscriptionsReducer };
-export type { SubscriptionsStoreState };
diff --git a/src/subscriptions/data/subscriptionsService.ts b/src/subscriptions/data/subscriptionsService.ts
deleted file mode 100644
index b40b97f..0000000
--- a/src/subscriptions/data/subscriptionsService.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { integratorApiClient } from "auth/data/authService";
-import axios from "axios";
-import { DTOModel } from "core/dto_model";
-import Failure from "core/failure";
-import { Service } from "services/domain/serviceEntity";
-import { SessionDTO } from "./dto/sessionDTO";
-import { SubscriptionDTO } from "./dto/subscriptionDTO";
-import { ServiceSession } from "../domain/sessionEntity";
-import { SubscriptionRequest } from "./dto/subscriptionRequest";
-import ForbidenFailureHandler from "core/handlers/forbidenFailureHandler";
-import ExecOnAnyHandler from "core/handlers/execOnAnyFailureHandler";
-
-async function loadSubscriptions(): Promise {
- try {
- const response = await integratorApiClient.get>(
- "/session"
- );
- const content = response.data.data;
- return content.map((item) => item.service_id);
- } catch (reason) {
- if (axios.isAxiosError(reason)) {
- throw Failure.fromReason(reason, "failures.services.subscriptions");
- }
- throw reason;
- }
-}
-
-async function subscribe(payload: SubscriptionRequest): Promise {
- try {
- return await integratorApiClient.post("/session/subscribe", payload);
- } catch (reason) {
- if (axios.isAxiosError(reason)) {
- try {
- const handler = new ForbidenFailureHandler(
- () => {
- throw new Failure({
- status: 403,
- message: "Invalid confirmation credentials",
- key: "failures.subscriptions.confirmation",
- });
- },
- new ExecOnAnyHandler((reason) => {
- throw reason;
- })
- );
- handler.handle(Failure.fromReason(reason));
- } catch (error) {
- return Promise.reject(error);
- }
- }
- throw reason;
- }
-}
-
-async function forkTo(id: Service["id"]): Promise {
- try {
- return await integratorApiClient.post>(
- "/session/login",
- {
- service_id: id,
- },
- {
- withCredentials: true,
- }
- );
- } catch (reason) {
- if (axios.isAxiosError(reason)) {
- throw Failure.fromReason(reason, "failures.services.fork");
- }
- throw reason;
- }
-}
-
-export { loadSubscriptions, subscribe, forkTo };
diff --git a/src/subscriptions/data/subscriptionsSlice.ts b/src/subscriptions/data/subscriptionsSlice.ts
deleted file mode 100644
index 3bd23fc..0000000
--- a/src/subscriptions/data/subscriptionsSlice.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from "react";
-import { RootState, useAppDispatch, useAppSelector } from "store";
-import { SubscriptionsStore } from "subscriptions/domain/subscriptionsStore";
-import { SubscriptionRequest } from "./dto/subscriptionRequest";
-import {
- getSessionAction,
- getSubscriptionsAction,
- subscribeAction,
-} from "./subscriptionsActions";
-import { SubscriptionsStoreState } from "./subscriptionsReducer";
-
-const subscriptionsSelector = (state: RootState): SubscriptionsStoreState =>
- state.subscriptions;
-
-const useSubscriptionsStore = (): SubscriptionsStore => {
- const { isFailed, isLoading, subscribing, subscriptions } = useAppSelector(
- subscriptionsSelector
- );
-
- const dispatch = useAppDispatch();
-
- const get = React.useCallback(
- () => getSubscriptionsAction()(dispatch),
- [dispatch]
- );
-
- const subscribe = React.useCallback(
- (payload: SubscriptionRequest) => subscribeAction(payload)(dispatch),
- [dispatch]
- );
-
- const forkTo = React.useCallback(
- (id: string) => getSessionAction(id)(dispatch),
- [dispatch]
- );
-
- return {
- isFailed,
- isLoading,
- subscribing,
- subscriptions,
- forkTo,
- subscribe,
- get,
- };
-};
-
-export { useSubscriptionsStore };
diff --git a/src/subscriptions/domain/sessionEntity.ts b/src/subscriptions/domain/sessionEntity.ts
deleted file mode 100644
index fd48d78..0000000
--- a/src/subscriptions/domain/sessionEntity.ts
+++ /dev/null
@@ -1 +0,0 @@
-export interface ServiceSession {}
\ No newline at end of file
diff --git a/src/subscriptions/domain/subscriptionsStore.ts b/src/subscriptions/domain/subscriptionsStore.ts
deleted file mode 100644
index 7cf45de..0000000
--- a/src/subscriptions/domain/subscriptionsStore.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Service } from "services/domain/serviceEntity";
-import { SubscriptionRequest } from "subscriptions/data/dto/subscriptionRequest";
-import { ServiceSession } from "subscriptions/domain/sessionEntity";
-
-interface SubscriptionsStore {
- /* ---------------------------------- State --------------------------------- */
- subscriptions: Service["id"][] | undefined;
- subscribing: Service['id'][] | undefined;
- isLoading: boolean;
- isFailed: boolean;
- /* --------------------------------- Actions -------------------------------- */
- get(): Promise;
- subscribe(payload: SubscriptionRequest): Promise;
- forkTo(id: Service['id']): Promise;
-}
-
-export type { SubscriptionsStore };
diff --git a/src/subscriptions/useCases/forkToServiceUseCase.ts b/src/subscriptions/useCases/forkToServiceUseCase.ts
deleted file mode 100644
index 65969be..0000000
--- a/src/subscriptions/useCases/forkToServiceUseCase.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import Failure from "core/failure";
-import { Service } from "services/domain/serviceEntity";
-import { ServiceSession } from "subscriptions/domain/sessionEntity";
-import { SubscriptionsStore } from "subscriptions/domain/subscriptionsStore";
-
-type ForkToServiceStore = Pick;
-
-const forkToServiceUseCase = (
- store: ForkToServiceStore,
- id: Service["id"]
-): Promise => {
- return store.forkTo(id).catch((reason) => {
- if (
- reason instanceof Failure &&
- reason.status === 403 &&
- reason.meta?.data.data.redirect_url
- ) {
- window.open(reason.meta?.data.data.redirect_url);
- }
- throw reason;
- });
-};
-
-export { forkToServiceUseCase };
diff --git a/src/subscriptions/useCases/getSubscriptions.ts b/src/subscriptions/useCases/getSubscriptions.ts
deleted file mode 100644
index c603c33..0000000
--- a/src/subscriptions/useCases/getSubscriptions.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { debounce } from "lodash";
-import { SubscriptionsStore } from "subscriptions/domain/subscriptionsStore";
-
-const deboucedTask = debounce((task) => Promise.resolve(task()), 500);
-
-type LoadSubscriptionsStore = Pick;
-
-const getSubscriptionsUseCase = (store: LoadSubscriptionsStore) => {
- return deboucedTask(() => store.get());
-};
-
-export { getSubscriptionsUseCase };
diff --git a/src/subscriptions/useCases/serviceSubscribeUseCase.ts b/src/subscriptions/useCases/serviceSubscribeUseCase.ts
deleted file mode 100644
index 8460dbc..0000000
--- a/src/subscriptions/useCases/serviceSubscribeUseCase.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import Failure from "core/failure";
-import { Service } from "services/domain/serviceEntity";
-import { SubscriptionsStore } from "subscriptions/domain/subscriptionsStore";
-
-type SubscribeServiceStore = Pick<
- SubscriptionsStore,
- "subscriptions" | "subscribe"
->;
-
-const serviceSubscribeUseCase = async (
- store: SubscribeServiceStore,
- id: Service["id"],
- password: string
-): Promise => {
- if (store.subscriptions?.includes(id)) {
- return Promise.reject(
- new Failure({
- key: "failures.subscription.exists",
- message: "Service subscription already eixsts",
- })
- );
- }
- return store.subscribe({
- password: password,
- service_id: id,
- });
-};
-
-export { serviceSubscribeUseCase };
diff --git a/src/ui/controller/uiNotificationsViewModel.ts b/src/ui/controller/uiNotificationsViewModel.ts
deleted file mode 100644
index bc9316c..0000000
--- a/src/ui/controller/uiNotificationsViewModel.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from "react";
-import NotificationI from "ui/domain/uiNotificationEntity";
-import { UIStore } from "ui/domain/uiStore";
-import { clearNotificationsUseCase } from "ui/useCases/clearNotificationsUseCase";
-import {
- notifyDismissibleUseCase,
- notifyUseCase,
-} from "ui/useCases/notifyUseCase";
-import { removeNotificationUseCase } from "ui/useCases/removeNotificationUseCase";
-
-function useUINotifications(store: UIStore) {
- /* -------------------------------- Searchbar ------------------------------- */
- const push = React.useCallback(
- function (notification: Omit) {
- return notifyUseCase(
- {
- notify: store.notify,
- },
- notification
- );
- },
- [store.notify]
- );
- const pushDismissible = React.useCallback(
- function (notification: Omit) {
- return notifyDismissibleUseCase(
- {
- notify: store.notify,
- },
- notification
- );
- },
- [store.notify]
- );
-
- const remove = React.useCallback(
- (uid: NotificationI["uid"]) => {
- return removeNotificationUseCase(
- {
- removeNotification: store.removeNotification,
- },
- uid
- );
- },
- [store.removeNotification]
- );
-
- const removeAll = React.useCallback(() => {
- return clearNotificationsUseCase({
- clearNotifications: store.clearNotifications,
- });
- }, [store.clearNotifications]);
-
- return {
- /* ------------------------------ Notifications ----------------------------- */
- notifications: store.notifications,
- push,
- pushDismissible,
- remove,
- removeAll,
- };
-}
-
-export { useUINotifications };
diff --git a/src/ui/controller/uiViewModel.ts b/src/ui/controller/uiViewModel.ts
deleted file mode 100644
index 4bcc502..0000000
--- a/src/ui/controller/uiViewModel.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from "react";
-import { UIStore } from "ui/domain/uiStore";
-import { changeSearchQueryUseCase } from "ui/useCases/changeSearchQueryUseCase";
-import { drawerCollapseUseCase } from "ui/useCases/drawerCollapseUseCase";
-import { hideSearchbarUseCase } from "ui/useCases/hideSearchbarUseCase";
-import { initDrawerUseCase } from "ui/useCases/initDrawerUseCase";
-import { showSearchbarUseCase } from "ui/useCases/showSearchbarUseCase";
-
-function useUIViewModel(store: UIStore) {
- /* -------------------------------- Searchbar ------------------------------- */
- const showSearchbar = React.useCallback(
- function () {
- showSearchbarUseCase({
- isSearchbarShown: store.isSearchbarShown,
- searchbarShown: store.searchbarShown,
- });
- },
- [store.isSearchbarShown, store.searchbarShown]
- );
-
- const hideSearchbar = React.useCallback(
- function () {
- hideSearchbarUseCase({
- isSearchbarShown: store.isSearchbarShown,
- searchbarShown: store.searchbarShown,
- });
- },
- [store.isSearchbarShown, store.searchbarShown]
- );
- /* --------------------------------- Search --------------------------------- */
- const changeQuery = React.useCallback(
- (query: string) =>
- changeSearchQueryUseCase(
- {
- query: store.query,
- changeQuery: store.changeQuery,
- executeQuery: store.executeQuery,
- },
- query
- ),
- [store.query, store.changeQuery, store.executeQuery]
- );
- /* --------------------------------- Drawer --------------------------------- */
- const initDrawer = React.useCallback(
- function () {
- initDrawerUseCase({
- initDrawer: store.initDrawer,
- });
- },
- [store.initDrawer]
- );
- const toggleDrawer = React.useCallback(
- function () {
- drawerCollapseUseCase({
- drawerIs: store.drawerIs,
- isDrawerCollapsed: store.isDrawerCollapsed,
- });
- },
- [store.drawerIs, store.isDrawerCollapsed]
- );
-
- return {
- /* -------------------------------- Searchbar ------------------------------- */
- isSearchbarShown: store.isSearchbarShown,
- showSearchbar,
- hideSearchbar,
- /* ------------------------------- Search flow ------------------------------ */
- query: store.query,
- changeQuery,
- /* -------------------------------- Side menu ------------------------------- */
- isDrawerCollapsed: store.isDrawerCollapsed,
- toggleDrawer,
- initDrawer,
- };
-}
-
-export { useUIViewModel };
diff --git a/src/ui/data/uiActionTypes.ts b/src/ui/data/uiActionTypes.ts
deleted file mode 100644
index 4df4b59..0000000
--- a/src/ui/data/uiActionTypes.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -------------------------------------------------------------------------- */
-/* Searchbar */
-/* -------------------------------------------------------------------------- */
-export const SET_SEARCHABAR_SHOWN = "@ui/searchbar.state.set";
-/* -------------------------------------------------------------------------- */
-/* Search flow */
-/* -------------------------------------------------------------------------- */
-export const RESET_SEARCH_QUERY = "@ui/searchbar.query.reset";
-export const SET_SEARCH_QUERY = "@ui/searchbar.query.set";
-export const EXECUTE_SEARCH_QUERY = "@ui/searchbar.query.exec";
-export const EXECUTE_SEARCH_SUCCESS = "@ui/searchbar.query.success";
-/* -------------------------------------------------------------------------- */
-/* Drawer */
-/* -------------------------------------------------------------------------- */
-export const SET_DRAWER_SHOWN = "@ui/drawer.state.set";
-/* -------------------------------------------------------------------------- */
-/* Notifications */
-/* -------------------------------------------------------------------------- */
-export const NOTIFICATION_ADD = "@ui/notifications.add";
-export const NOTIFICATION_REMOVE = '@ui/notifications.remove';
-export const NOTIFICATION_REMOVE_ALL = "@ui/notifications.clear";
\ No newline at end of file
diff --git a/src/ui/data/uiActions.ts b/src/ui/data/uiActions.ts
deleted file mode 100644
index d95fe30..0000000
--- a/src/ui/data/uiActions.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { AppDispatch } from "store";
-import NotificationI from "ui/domain/uiNotificationEntity";
-import { search } from "ui/domain/uiService";
-import * as actionTypes from "./uiActionTypes";
-import useUIStorage from "./uiService";
-/* -------------------------------------------------------------------------- */
-/* Searchbar */
-/* -------------------------------------------------------------------------- */
-const setSearchbarShown = (shown: boolean) => (dispatch: any) =>
- dispatch({ type: actionTypes.SET_SEARCHABAR_SHOWN, payload: shown });
-/* -------------------------------------------------------------------------- */
-/* Search flow */
-/* -------------------------------------------------------------------------- */
-const setSearchQuery = (payload: string) => (dispatch: any) =>
- dispatch({ type: actionTypes.SET_SEARCH_QUERY, payload });
-
-const executeSearchQuery = (payload: string) => (dispatch: any) => {
- dispatch({ type: actionTypes.EXECUTE_SEARCH_QUERY });
-
- return search(payload).then((result) => {
- dispatch({ type: actionTypes.EXECUTE_SEARCH_SUCCESS });
-
- return result;
- });
-};
-/* -------------------------------------------------------------------------- */
-/* Drawer */
-/* -------------------------------------------------------------------------- */
-const setDrawer = (shown: boolean) => (dispatch: any) => {
- useUIStorage().setItem("drawer", shown);
- dispatch({ type: actionTypes.SET_DRAWER_SHOWN, payload: shown });
-};
-const getDrawer = () => (dispatch: any) => {
- const shown = useUIStorage().getItem("drawer");
- dispatch({ type: actionTypes.SET_DRAWER_SHOWN, payload: shown });
-};
-/* -------------------------------------------------------------------------- */
-/* Notifications */
-/* -------------------------------------------------------------------------- */
-const notifyAction =
- (notification: Omit) => (dispatch: AppDispatch) => {
- dispatch({
- type: actionTypes.NOTIFICATION_ADD,
- payload: {
- ...notification,
- uid: Date.now() + Math.floor(Math.random() * 100),
- },
- });
- };
-const removeNotificationAction =
- (id: NotificationI["uid"]) => (dispatch: AppDispatch) => {
- dispatch({ type: actionTypes.NOTIFICATION_REMOVE, payload: id });
- };
-
-const removeAllNotificationsAction = () => (dispatch: AppDispatch) => {
- dispatch({type: actionTypes.NOTIFICATION_REMOVE_ALL});
-}
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* Exports */
-/* -------------------------------------------------------------------------- */
-export {
- setSearchbarShown,
- setSearchQuery,
- executeSearchQuery,
- setDrawer,
- getDrawer,
- notifyAction,
- removeNotificationAction,
- removeAllNotificationsAction,
-};
diff --git a/src/ui/data/uiReducer.ts b/src/ui/data/uiReducer.ts
deleted file mode 100644
index 80441b6..0000000
--- a/src/ui/data/uiReducer.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { AnyAction } from "@reduxjs/toolkit";
-import { UIStore } from "ui/domain/uiStore";
-import * as actionTypes from "./uiActionTypes";
-
-type UIStoreState = Omit<
- UIStore,
- | "searchbarShown"
- | "changeQuery"
- | "executeQuery"
- | "drawerIs"
- | "initDrawer"
- | "notify"
- | "removeNotification"
- | "clearNotifications"
->;
-
-const INITIAL_STATE: UIStoreState = {
- isSearchbarShown: false,
- query: "",
- searchResult: undefined,
- isDrawerCollapsed: false,
- notifications: [],
-};
-
-const uiReducer = (
- state: UIStoreState = INITIAL_STATE,
- action: AnyAction
-): UIStoreState => {
- switch (action.type) {
- /* -------------------------------- Searchbar ------------------------------- */
- case actionTypes.SET_SEARCHABAR_SHOWN:
- return { ...state, isSearchbarShown: action.payload };
- /* ------------------------------- Searchflow ------------------------------- */
- case actionTypes.SET_SEARCH_QUERY:
- return { ...state, query: action.payload };
- case actionTypes.RESET_SEARCH_QUERY:
- return { ...state, query: "" };
- case actionTypes.EXECUTE_SEARCH_SUCCESS:
- return { ...state, searchResult: action.payload };
- /* --------------------------------- Drawer --------------------------------- */
- case actionTypes.SET_DRAWER_SHOWN:
- return { ...state, isDrawerCollapsed: action.payload };
- /* ------------------------------ Notifications ----------------------------- */
- case actionTypes.NOTIFICATION_ADD:
- return {
- ...state,
- notifications: state.notifications.concat([action.payload]),
- };
- case actionTypes.NOTIFICATION_REMOVE:
- return {
- ...state,
- notifications: state.notifications.filter(
- (item) => item.uid !== action.payload
- ),
- };
- case actionTypes.NOTIFICATION_REMOVE_ALL:
- return { ...state, notifications: [] };
- /* --------------------------------- Default -------------------------------- */
- default:
- return state;
- }
-};
-
-export { uiReducer };
-export type { UIStoreState };
diff --git a/src/ui/data/uiService.ts b/src/ui/data/uiService.ts
deleted file mode 100644
index 2e3a8d7..0000000
--- a/src/ui/data/uiService.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { LSKeys, LStorage, LStorageHandlers } from "utils/localStorageDefs";
-
-export type UILStorageKeys = keyof Pick;
-
-export default function useUIStorage(): LStorage {
- const handlers: LStorageHandlers = {
- setter: {
- drawer: (key, value) => {
- localStorage.setItem(key, JSON.stringify(value));
- }
- },
- getter: {
- drawer: (key) => {
- return JSON.parse(localStorage.getItem(key) ?? "false");
- }
- }
- }
- return {
- setItem: (key, value) => {
- return handlers["setter"][key](key, value);
- },
- getItem: (key) => {
- return handlers["getter"][key](key);
- }
- }
-}
\ No newline at end of file
diff --git a/src/ui/data/uiSlice.ts b/src/ui/data/uiSlice.ts
deleted file mode 100644
index bd38a1d..0000000
--- a/src/ui/data/uiSlice.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import React from "react";
-import { RootState, useAppDispatch, useAppSelector } from "store";
-import NotificationI from "ui/domain/uiNotificationEntity";
-import { UIStore } from "ui/domain/uiStore";
-import {
- setSearchQuery,
- setSearchbarShown,
- executeSearchQuery,
- setDrawer,
- getDrawer,
- notifyAction,
- removeNotificationAction,
- removeAllNotificationsAction,
-} from "./uiActions";
-import { UIStoreState } from "./uiReducer";
-
-const uiSelector = (state: RootState): UIStoreState => state.ui;
-
-const useUIStore = (): UIStore => {
- const {
- isSearchbarShown,
- query,
- searchResult,
- isDrawerCollapsed,
- notifications,
- } = useAppSelector(uiSelector);
-
- const dispatch = useAppDispatch();
- /* -------------------------------- Searchbar ------------------------------- */
- const updateSearchbar = React.useCallback(
- (isShown: boolean) => setSearchbarShown(isShown)(dispatch),
- [dispatch]
- );
-
- const changeQuery = React.useCallback(
- (query: string) => setSearchQuery(query)(dispatch),
- [dispatch]
- );
-
- const executeQuery = React.useCallback(
- (query: string) => executeSearchQuery(query)(dispatch),
- [dispatch]
- );
- /* --------------------------------- Drawer --------------------------------- */
- const initDrawer = React.useCallback(() => getDrawer()(dispatch), [dispatch]);
- const drawerIs = React.useCallback(
- (payload: boolean) => setDrawer(payload)(dispatch),
- [dispatch]
- );
- /* ------------------------------ Notifications ----------------------------- */
- const notify = React.useCallback(
- (notification: NotificationI) => notifyAction(notification)(dispatch),
- [dispatch]
- );
- const removeNotification = React.useCallback(
- (uid: NotificationI["uid"]) => removeNotificationAction(uid)(dispatch),
- [dispatch]
- );
- const clearNotifications = React.useCallback(
- () => removeAllNotificationsAction()(dispatch),
- [dispatch]
- );
-
- return {
- /* ----------------------------- UI search props ---------------------------- */
- isSearchbarShown,
- searchbarShown: updateSearchbar,
- /* --------------------------- Global search logic -------------------------- */
- query,
- searchResult: searchResult,
- changeQuery,
- executeQuery,
- /* --------------------------------- Drawer --------------------------------- */
- isDrawerCollapsed,
- initDrawer,
- drawerIs,
- /* ------------------------------ Notifications ----------------------------- */
- notifications,
- notify,
- removeNotification,
- clearNotifications,
- };
-};
-
-export { useUIStore };
diff --git a/src/ui/domain/types.ts b/src/ui/domain/types.ts
deleted file mode 100644
index 3826901..0000000
--- a/src/ui/domain/types.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export type NotificationType = "warning" | "error" | "success";
-
-export type NotificationTypeMap = {
- [Prop in NotificationType] : T;
-}
\ No newline at end of file
diff --git a/src/ui/domain/uiNotificationEntity.ts b/src/ui/domain/uiNotificationEntity.ts
deleted file mode 100644
index 0e4d077..0000000
--- a/src/ui/domain/uiNotificationEntity.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { NotificationType } from "./types";
-
-export default interface NotificationI {
- uid: number;
- title?: string;
- body: string;
- status?: NotificationType;
- delay?: number;
-}
\ No newline at end of file
diff --git a/src/ui/domain/uiSearchEntity.ts b/src/ui/domain/uiSearchEntity.ts
deleted file mode 100644
index 6656e37..0000000
--- a/src/ui/domain/uiSearchEntity.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default interface SearchResult {
- result: any[];
-}
\ No newline at end of file
diff --git a/src/ui/domain/uiService.ts b/src/ui/domain/uiService.ts
deleted file mode 100644
index a817a22..0000000
--- a/src/ui/domain/uiService.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import axios from "axios";
-import SearchResult from "./uiSearchEntity";
-
-function search(query: string): Promise {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve({ result: [] });
- }, 2000);
- });
-}
-
-export { search };
diff --git a/src/ui/domain/uiStore.ts b/src/ui/domain/uiStore.ts
deleted file mode 100644
index 7a279ed..0000000
--- a/src/ui/domain/uiStore.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import NotificationI from "./uiNotificationEntity";
-import SearchResult from "./uiSearchEntity";
-
-interface UIStore {
- /* -------------------------------------------------------------------------- */
- /* Searchbar */
- /* -------------------------------------------------------------------------- */
- /* ---------------------------------- State --------------------------------- */
- isSearchbarShown: boolean;
- /* --------------------------------- Actions -------------------------------- */
- searchbarShown(shown: boolean): void;
- /* -------------------------------------------------------------------------- */
- /* Search flow */
- /* -------------------------------------------------------------------------- */
- /* ---------------------------------- State --------------------------------- */
- query: string;
- searchResult: SearchResult[] | undefined;
- /* --------------------------------- Actions -------------------------------- */
- changeQuery(query: string): void;
- executeQuery(query: string): Promise