Compare commits

..

No commits in common. "fix/revert-dev" and "master" have entirely different histories.

49 changed files with 4302 additions and 4178 deletions

View File

@ -1,5 +1,5 @@
.env*
.env.production
!.env.production
node_modules
build
.vscode

6
.env
View File

@ -1,6 +0,0 @@
REACT_APP_CMS_BASE_URL=http://api.scipaper.ru
REACT_APP_CMS_APP_NAME=scipaper
REACT_APP_OPENID_PROVIDER_URL=http://auth.techpal.ru/auth/realms/master/protocol/openid-connect/auth?client_id=techpal&response_type=code
REACT_APP_INTEGRATOR_URL=http://api.scipaper.ru
REACT_APP_INTEGRATOR_API_VERSION=/v1
REACT_APP_GRAPHQL_URL=/graphql

View File

@ -1,6 +1,6 @@
REACT_APP_CMS_BASE_URL=http://api.scipaper.ru
REACT_APP_CMS_BASE_URL=http://scipaper.ru
REACT_APP_CMS_APP_NAME=scipaper
REACT_APP_OPENID_PROVIDER_URL=http://auth.techpal.ru/auth/realms/master/protocol/openid-connect/auth?client_id=techpal&response_type=code
REACT_APP_INTEGRATOR_URL=http://api.scipaper.ru
REACT_APP_INTEGRATOR_URL=http://scipaper.ru
REACT_APP_INTEGRATOR_API_VERSION=/v1
REACT_APP_GRAPHQL_URL=/graphql

View File

@ -1,6 +1,6 @@
REACT_APP_CMS_BASE_URL=http://api.scipaper.ru
REACT_APP_CMS_BASE_URL=http://scipaper.ru
REACT_APP_CMS_APP_NAME=scipaper
REACT_APP_OPENID_PROVIDER_URL=http://auth.techpal.ru/auth/realms/master/protocol/openid-connect/auth?client_id=techpal&response_type=code
REACT_APP_INTEGRATOR_URL=http://api.scipaper.ru
REACT_APP_INTEGRATOR_URL=http://scipaper.ru
REACT_APP_INTEGRATOR_API_VERSION=/v1
REACT_APP_GRAPHQL_URL=/graphql

View File

@ -1,43 +1,41 @@
# Install dependencies of project
FROM node:fermium-alpine AS dependencies
# Install dependencies only when needed
FROM node:16-alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /home/app/
COPY package.json ./
RUN npm i
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Build application to bunch of static files
FROM node:fermium-alpine AS builder
WORKDIR /home/app/
COPY --from=dependencies ./home/app/node_modules ./node_modules
# Rebuild the source code only when needed
FROM node:16-alpine AS builder
ENV NODE_ENV production
WORKDIR /app
# Copy dependencies from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# NGINX image
FROM nginx:1.21.6 as production
# Bundle static assets with nginx
FROM node:16-alpine as production
# Copy built assets from builder
COPY --from=builder /home/app/build /usr/share/nginx/html
# Add nginx.config
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copy setup nginx entrypoint file
COPY ./scripts/entrypoint.sh .
WORKDIR /app
COPY --from=builder /app/build .
# Expose ports
EXPOSE 80
EXPOSE 3000
# Execute script
RUN chmod +x ./entrypoint.sh
COPY .env.production .
ENV NODE_ENV production
# ENV USER_NAME=node_user USER_UID=2000 GROUP_NAME=node_group GROUP_UID=2000
ENV USER_NAME=node_user USER_UID=2000 GROUP_NAME=node_group GROUP_UID=2000
# RUN deluser --remove-home node \
# && addgroup --g ${GROUP_UID} -S ${GROUP_NAME} \
# && adduser -D -S -s /sbin/nologin -u ${USER_UID} -G ${GROUP_NAME} ${USER_NAME}
# USER "${USER_NAME}"
RUN npm i -g serve \
&& deluser --remove-home node \
&& addgroup --g ${GROUP_UID} -S ${GROUP_NAME} \
&& adduser -D -S -s /sbin/nologin -u ${USER_UID} -G ${GROUP_NAME} ${USER_NAME}\
&& chown -R ${USER_NAME}:${GROUP_NAME} "/app/"
ENTRYPOINT ["./entrypoint.sh"]
# Start serving
CMD ["nginx", "-g", "daemon off;"]
USER "${USER_NAME}"
CMD serve -s .

View File

@ -1,9 +1,6 @@
#! /bin/bash
# no verbose
set +x
# save env to file
printenv > .env.production
# config
envFilename='.env.production'
resolvingPath='/usr/share/nginx/html'

8071
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
"@fortawesome/free-brands-svg-icons": "^6.2.0",
"@fortawesome/free-solid-svg-icons": "^6.2.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.3",
"@headlessui/react": "^1.6.6",
"@reduxjs/toolkit": "^1.8.3",
"@types/node": "^16.11.47",
"@types/react": "^18.0.15",
@ -24,9 +24,9 @@
"i18next-http-backend": "^1.4.1",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"react": "^18.1.0",
"react": "^18.2.0",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.1.0",
"react-dom": "^18.2.0",
"react-hotkeys": "^2.0.0",
"react-i18next": "^11.18.3",
"react-loading-skeleton": "^3.1.0",

33
src/article/controller/articleViewModel.ts Executable file → Normal file
View File

@ -1,31 +1,22 @@
import type { ArticleStore } from "../domain/articleStore";
import { getArticleUseCase } from "../useCases/getArticleUseCase";
import { useCallback, useEffect } from "react";
import { GetArticleUseCase } from "article/useCases/getArticleUseCase";
import { FetchArticleUseCase } from "article/useCases/fetchArticleUseCase";
import { useParams } from "react-router-dom";
function useArticleViewModel(
store: ArticleStore,
fetchArticleUseCase: FetchArticleUseCase,
getArticleUseCase: GetArticleUseCase,
) {
const { id } = useParams();
// const getArticle = useCallback(
// () => {
// getArticleUseCase.call(id ?? '').catch((_) => fetchArticleUseCase.call(id ?? ''));
// console.log(id);
// },
// [id]
// );
function useArticleViewModel(store: ArticleStore) {
const _getArticle = useCallback(
(id: string) => getArticleUseCase(store.getArticle, store.setArticle, id),
[store.getArticle, store.setArticle]
);
useEffect(() => {
getArticleUseCase.call(id ?? '').catch((_) => fetchArticleUseCase.call(id ?? ''));
}, []);
if (store.article != undefined) {
_getArticle(store.article.id);
}
}, [store.article?.id]);
return {
article: store.currentArticle,
shouldShowLoading: store.isLoading,
article: store.article,
shouldShowLoading: typeof store.article === "undefined" || store.isLoading,
hasError: store.hasError,
};
}

4
src/article/data/articleAPIService.ts Executable file → Normal file
View File

@ -7,7 +7,7 @@ import { integratorApiClient } from "core/httpClient";
const articleEndpoint = "/papers/"
async function fetchArticle(id: string): Promise<Article> {
async function getArticle(id: string): Promise<Article> {
try {
const response = await integratorApiClient.get<FetchArticleByIdDTO>(
// `https://run.mocky.io/v3/62cd4581-d864-4d46-b1d6-02b45b5d1994/${id}`
@ -33,4 +33,4 @@ async function fetchArticle(id: string): Promise<Article> {
}
}
export { fetchArticle };
export { getArticle };

0
src/article/data/articleActionTypes.ts Executable file → Normal file
View File

30
src/article/data/articleActions.ts Executable file → Normal file
View File

@ -1,25 +1,23 @@
import type { Article } from "../domain/articleEntity";
import { getArticle as getArticleAPI } from "./articleAPIService";
import * as actionTypes from "./articleActionTypes";
import { dispatchStatus } from "../../store/index";
const setArticleAction = (article: Article, articlesList: Array<Article>) => (dispatch: any) => {
if (!articlesList.includes(article)) {
const updatedList = articlesList.concat([article]);
dispatch({ type: actionTypes.SET_ARTICLE, article, updatedList });
}
}
const setArticleAction = (article: Article) => (dispatch: any) =>
dispatch({ type: actionTypes.SET_ARTICLE, article });
const getArticleAction = (id: string, articles: Array<Article>) => (dispatch: any) => {
const filteredArray = articles.filter((e) => e.id == id);
if (filteredArray.length > 0) {
const article = filteredArray[0];
dispatchStatus(actionTypes.GET_ARTICLE, ".success", article)(dispatch);
return article;
}
const getArticleAction = (id: string) => (dispatch: any) => {
dispatch({ type: actionTypes.GET_ARTICLE });
const reason = 'Article not in the store';
dispatchStatus(actionTypes.GET_ARTICLE, ".failure", reason)(dispatch);
return null;
return getArticleAPI(id)
.then((article) => {
dispatchStatus(actionTypes.GET_ARTICLE, ".success", article)(dispatch);
return article;
})
.catch((reason) => {
dispatchStatus(actionTypes.GET_ARTICLE, ".failure", reason)(dispatch);
return reason;
});
};
export { setArticleAction, getArticleAction };

47
src/article/data/articleCommonStateStore.ts Executable file → Normal file
View File

@ -2,58 +2,37 @@ import React, { useCallback, useState } from "react";
import { useDispatch } from "react-redux";
import { ArticleStore } from "../domain/articleStore";
import type { Article } from "../domain/articleEntity";
import { getArticle as getArticleAPI } from "./articleAPIService";
const useArticleCommonStore = (): ArticleStore => {
const [isLoading, setLoading] = useState<boolean>(false);
const [hasError, setError] = useState<boolean>(false);
const [currentArticle, setCurrentArticle] = useState<Article | null>(null);
const [articles, setArticlesState] = useState<Array<Article>>([]);
const [article, setArticleState] = useState<Article | undefined>();
const dispatch = useDispatch();
const getArticle = useCallback(
(id: string) => {
async (id: string) => {
setLoading(true);
if (typeof currentArticle === undefined) {
const fromStore = findArticleFromStore(id);
if (typeof fromStore === undefined) {
setError(true);
return null;
}
try {
const article = await getArticleAPI(id);
setArticleState(article);
setLoading(false);
setCurrentArticle(fromStore);
return fromStore;
return article;
} catch (error) {
setError(true);
return null;
}
setLoading(false);
return currentArticle;
},
[dispatch]
);
const findArticleFromStore = (id: string): Article | null => {
const filteredArray = articles.filter((e) => e.id == id);
if (filteredArray.length > 0) {
return filteredArray[0];
}
return null;
}
const setNewArticle = (newArticle: Article) => {
setCurrentArticle(newArticle);
if (!articles.includes(newArticle)) {
setArticlesState(articles.concat([newArticle]));
} else if (articles.length == 0) {
setArticlesState([newArticle]);
}
}
return {
articles: articles,
currentArticle: currentArticle,
article: article,
isLoading,
hasError,
setArticle: setNewArticle,
getArticle: getArticle,
setArticle: setArticleState,
getArticle,
};
};

7
src/article/data/articleReducer.ts Executable file → Normal file
View File

@ -5,8 +5,7 @@ import * as actionTypes from "./articleActionTypes";
type ArticleStoreState = Omit<ArticleStore, "getArticle" | "setArticle">;
const INITIAL_STATE: ArticleStoreState = {
articles: [],
currentArticle: null,
article: undefined,
isLoading: false,
hasError: false,
};
@ -17,11 +16,11 @@ const articleReducer = (
): ArticleStoreState => {
switch (action.type) {
case actionTypes.SET_ARTICLE:
return { ...state, articles: action.updatedList, currentArticle: action.article, hasError: typeof action.article === undefined };
return { ...state, article: action.article };
case actionTypes.GET_ARTICLE:
return { ...state, isLoading: true };
case actionTypes.GET_ARTICLE_SUCCESS:
return { ...state, isLoading: false, currentArticle: action.payload };
return { ...state, isLoading: false, article: action.payload };
case actionTypes.GET_ARTICLE_FAILURE:
return { ...state, hasError: true, isLoading: false };
default:

9
src/article/data/articleStoreImplementation.ts Executable file → Normal file
View File

@ -9,23 +9,22 @@ import { RootState, useAppSelector } from "store";
const articleSelector = (state: RootState): ArticleStoreState => state.article;
const useArticleStore = (): ArticleStore => {
const { isLoading, hasError, currentArticle, articles } = useAppSelector(articleSelector);
const { isLoading, article, hasError } = useAppSelector(articleSelector);
const dispatch = useDispatch();
const setArticle = useCallback(
(article: Article) => setArticleAction(article, articles)(dispatch),
(article: Article) => setArticleAction(article)(dispatch),
[dispatch]
);
const getArticle = useCallback(
(id: string) => getArticleAction(id, articles)(dispatch),
(id: string) => getArticleAction(id)(dispatch),
[dispatch]
);
return {
articles: articles,
currentArticle: currentArticle,
article: article,
isLoading,
hasError,
setArticle,

0
src/article/data/dto/fetch_article_by_id_dto.ts Executable file → Normal file
View File

0
src/article/domain/articleEntity.ts Executable file → Normal file
View File

0
src/article/domain/articleModel.ts Executable file → Normal file
View File

7
src/article/domain/articleStore.ts Executable file → Normal file
View File

@ -1,14 +1,13 @@
import { Article } from './articleEntity';
interface ArticleStore {
// State
articles: Array<Article>;
currentArticle: Article | null;
article: Article | undefined;
isLoading: boolean;
hasError: boolean;
// Actions
setArticle(article: Article): void;
getArticle(identifier: string): Article | null;
setArticle(article?: Article): void;
getArticle(identifier: string): Promise<Article | null>;
}
export type { ArticleStore };

View File

@ -1,29 +0,0 @@
import { Article } from "article/domain/articleEntity";
import { ArticleStore } from "article/domain/articleStore";
class FetchArticleUseCase {
/* ------------------------------ Dependencies ------------------------------ */
_fetchArticleCallback: (id: string) => Promise<Article | null>;
_store: ArticleStore;
/* -------------------------------------------------------------------------- */
constructor(
fetchArticle: (id: string) => Promise<Article | null>,
store: ArticleStore,
) {
this._fetchArticleCallback = fetchArticle;
this._store = store;
}
/* ----------------------------- Implementation ----------------------------- */
async call(id: string): Promise<Article | null> {
return this._fetchArticleCallback(id).then((article) => {
if (article != null) {
this._store.setArticle(article);
}
return article;
})
}
/* -------------------------------------------------------------------------- */
}
export { FetchArticleUseCase };

31
src/article/useCases/getArticleUseCase.ts Executable file → Normal file
View File

@ -1,25 +1,16 @@
import { Article } from "article/domain/articleEntity";
import type { ArticleStore } from "../domain/articleStore";
class GetArticleUseCase {
/* ------------------------------ Dependencies ------------------------------ */
_store: ArticleStore;
/* -------------------------------------------------------------------------- */
constructor(
store: ArticleStore,
) {
this._store = store;
const getArticleUseCase = async (
getArticle: ArticleStore["getArticle"],
setArticle: ArticleStore["setArticle"],
id: Article["id"]
): Promise<Article | null> => {
const article = await getArticle(id);
if (article) {
await setArticle(article);
}
/* ----------------------------- Implementation ----------------------------- */
async call(id: string): Promise<Article> {
const storedArticle = this._store.getArticle(id);
if (storedArticle != null) {
this._store.setArticle(storedArticle);
return storedArticle;
}
throw new Error('Article not found');
}
/* -------------------------------------------------------------------------- */
}
return article;
};
export { GetArticleUseCase };
export { getArticleUseCase };

0
src/article/useCases/params/create_article_params.ts Executable file → Normal file
View File

0
src/assets/lotties/notFoundAnimation.json Executable file → Normal file
View File

12
src/components/ArticleSearchResult.tsx Executable file → Normal file
View File

@ -27,15 +27,17 @@ export const ArticleSearchResult = ({ searchItem }: Props) => {
<Article className=" pt-6 pb-3 ">
<div className="flex flex-row justify-between">
<Article.Breadcumbs emphasis="low" className="flex flex-wrap flex-row">
{[`${searchItem.topic}`]}
{[
`${searchItem.topic}`,
`${searchItem.topic}`,
`${searchItem.topic}`,
`${searchItem.topic}`,
]}
</Article.Breadcumbs>
<Article.SubscriptionsButtons />
</div>
<Article.Title
linkTo={`/article/info/${searchItem.id}`}
className="text-2xl"
>
<Article.Title linkTo={`/article/info/${searchItem.id}`} className="text-2xl">
{searchItem.title}
</Article.Title>
<Article.Authors emphasis="low" className="flex flex-wrap flex-row">

26
src/components/Burger.tsx Executable file → Normal file
View File

@ -21,12 +21,10 @@ 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";
import { useTranslation } from "react-i18next";
type Props = React.ComponentPropsWithoutRef<"div">;
const Burger = (props: Props) => {
const { t, i18n } = useTranslation();
return (
<div {...props}>
<Menu as="div" className="relative inline-block text-left z-30">
@ -48,15 +46,13 @@ const Burger = (props: Props) => {
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
className="origin-top-right absolute right-0 mt-5 w-48 rounded-md
className="origin-top-right absolute right-0 mt-5 w-44 rounded-md
shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"
>
<div className="py-1">
<Disclosure>
<Disclosure.Button className="uppercase text-base px-2 py-1">
<Link className="text-[#096DD9]">
{t("navbar.createNew")}
</Link>
<Link className="text-[#096DD9]">create new</Link>
</Disclosure.Button>
</Disclosure>
<hr />
@ -70,7 +66,7 @@ const Burger = (props: Props) => {
text-base
"
>
<span>{t("navbar.library.navTitle")}</span>
my library
<SVGArrowUp
className={`${
open ? "rotate-180 transform" : "rotate-360"
@ -87,7 +83,7 @@ const Burger = (props: Props) => {
"
>
<SVGFile className="stroke-black w-4 h-4" />
<span>{t("navbar.library.publications")}</span>
My Publications
</Disclosure.Panel>
</Link>
@ -100,7 +96,7 @@ const Burger = (props: Props) => {
"
>
<SVGFavoriteOutlined className="stroke-black w-4 h-4" />
<span>{t("navbar.library.favorites")}</span>
My Favorites
</Disclosure.Panel>
</Link>
@ -113,7 +109,7 @@ const Burger = (props: Props) => {
"
>
<SVGFolder className="stroke-black fill-black w-4 h-4" />
<span>{t("navbar.library.collections")}</span>
My Collections
</Disclosure.Panel>
</Link>
@ -126,7 +122,7 @@ const Burger = (props: Props) => {
"
>
<SVGEye className="stroke-black w-4 h-4" />
<span>{t("navbar.library.recentViewed")}</span>
Recent Viewed
</Disclosure.Panel>
</Link>
</>
@ -144,7 +140,7 @@ const Burger = (props: Props) => {
text-base
"
>
<span>{t("navbar.about.navTitle")}</span>
About
<SVGArrowUp
className={`${
open ? "rotate-180 transform" : "rotate-360"
@ -160,7 +156,7 @@ const Burger = (props: Props) => {
text-base
"
>
<span>{t("navbar.about.aboutProject")}</span>
About Freeland
</Disclosure.Panel>
</Link>
@ -172,7 +168,7 @@ const Burger = (props: Props) => {
text-base
"
>
<span>{t("navbar.about.contacts")}</span>
Contact Us
</Disclosure.Panel>
</Link>
@ -184,7 +180,7 @@ const Burger = (props: Props) => {
text-base
"
>
<span>{t("navbar.about.help")}</span>
Help
</Disclosure.Panel>
</Link>
</>

0
src/components/Disclosure.tsx Executable file → Normal file
View File

0
src/components/Filters/AppiledFilters.tsx Executable file → Normal file
View File

0
src/components/Filters/Filter.tsx Executable file → Normal file
View File

0
src/components/Filters/IProdutct.ts Executable file → Normal file
View File

0
src/components/Filters/SearchFilterBar.tsx Executable file → Normal file
View File

0
src/components/Filters/functions/debounce.ts Executable file → Normal file
View File

0
src/components/Loader/Loader.css Executable file → Normal file
View File

0
src/components/Loader/Loader.tsx Executable file → Normal file
View File

0
src/components/LocalizationButton.tsx Executable file → Normal file
View File

5
src/components/Markdown.tsx Executable file → Normal file
View File

@ -14,8 +14,9 @@ import Heading from "./typography/Heading";
/* Code */
/* -------------------------------------------------------------------------- */
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
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;
@ -152,7 +153,7 @@ const Markdown = ({ markdown }: Props) => {
return !inline && match ? (
<SyntaxHighlighter
children={String(children).replace(/\n$/, "")}
style={oneLight}
style={dark}
language={match[1]}
PreTag="div"
/>

0
src/components/SearchResultsSection.tsx Executable file → Normal file
View File

0
src/components/SearchSection.tsx Executable file → Normal file
View File

View File

@ -3,7 +3,7 @@
/* -------------------------------------------------------------------------- */
import React from "react";
import { Fragment } from "react";
import { Listbox, Transition } from "@headlessui/react";
import { Listbox, Transition } from "@headlessui/react";
import classNames from "classnames";
import "../index.css";
import { ReactComponent as SelectIcon } from "../assets/svg/caret-down.svg";
@ -83,7 +83,7 @@ function Select<T>({
}: Props<T>): JSX.Element {
return (
<div className={classNames("top-16 w-60", className)}>
<Listbox value={value as any } {...props} onChange={onChange}>
<Listbox value={value} {...props} onChange={onChange}>
<div className="relative">
<Listbox.Button
className={classNames([

17
src/components/fetchAnArticle/AnArticle.tsx Executable file → Normal file
View File

@ -10,20 +10,19 @@ import { SVGSearch } from "components/icons";
import BaseLayout from "components/BaseLayout";
import Typography from "components/typography/Typography";
import { useTranslation } from "react-i18next";
import { FetchArticleUseCase } from "article/useCases/fetchArticleUseCase";
import { GetArticleUseCase } from "article/useCases/getArticleUseCase";
import { fetchArticle } from "article/data/articleAPIService";
const AnArticle = () => {
const store = useArticleStore();
const { article, hasError, shouldShowLoading } = useArticleViewModel(
store,
new FetchArticleUseCase(fetchArticle, store),
new GetArticleUseCase(store),
);
const { article, hasError, shouldShowLoading } = useArticleViewModel(store);
const { i18n, t } = useTranslation();
const { id } = useParams();
const newId = `${id}`;
useEffect(() => {
store.getArticle(newId);
}, [id]);
if (hasError) {
return <NotFound />;
}

14
src/components/fetchAnArticle/AnArticleBody.tsx Executable file → Normal file
View File

@ -13,17 +13,15 @@ import BaseLayout from "components/BaseLayout";
import Container from "components/Container";
import NotFound from "./NotFound";
import Markdown from "components/Markdown";
import { FetchArticleUseCase } from "article/useCases/fetchArticleUseCase";
import { GetArticleUseCase } from "article/useCases/getArticleUseCase";
import { fetchArticle } from "article/data/articleAPIService";
const AnArticleBody = () => {
const store = useArticleStore();
const { article, hasError, shouldShowLoading } = useArticleViewModel(
store,
new FetchArticleUseCase(fetchArticle, store),
new GetArticleUseCase(store),
);
const { article, hasError, shouldShowLoading } = useArticleViewModel(store);
const { id } = useParams();
const newId = `${id}`;
useEffect(() => {
store.getArticle(newId);
}, [id]);
if (hasError) <NotFound />;
return (
<BaseLayout>

0
src/components/fetchAnArticle/AskeletonArticle.tsx Executable file → Normal file
View File

0
src/components/fetchAnArticle/NotFound.tsx Executable file → Normal file
View File

View File

@ -80,7 +80,7 @@ const Header = () => {
<ContextMenu
emphasis="high"
button={t("navbar.library.navTitle")}
className="border-none uppercase z-40"
className="border-none uppercase"
>
<ContextMenuAction
caption={t("navbar.library.publications")}
@ -112,7 +112,7 @@ const Header = () => {
<ContextMenu
emphasis="high"
button={t("navbar.about.navTitle")}
className="border-none uppercase z-40"
className="border-none uppercase"
>
<ContextMenuAction
caption={t("navbar.about.aboutProject")}

0
src/components/search/SearchBar.tsx Executable file → Normal file
View File

View File

@ -46,9 +46,8 @@ export default function Link({
style={
typeof style === "function"
? style({
isActive: true,
isPending: false
})
isActive: true,
})
: style
}
aria-disabled={disabled}

View File

@ -15,7 +15,7 @@ export function RouterLink({
return (
<NavLink
to={to}
className={classNames({ "pointer-events-none": disabled }, className as string)}
className={classNames({ "pointer-events-none": disabled }, className)}
>
{children}
</NavLink>

View File

@ -25,26 +25,28 @@ if (!rootElement) throw new Error("Failed to find the root element");
const root = ReactDOM.createRoot(rootElement);
root.render(
<Provider store={store}>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/about" element={<About />} />
<Route path="/help" element={<Help />} />
<Route path="/contact-us" element={<ContactUs />} />
<Route path="/terms-of-use" element={<TermsOfUse />} />
<Route path="/privacy-policy" element={<PrivacyPolicy />} />
<Route path="/cookies-policy" element={<CookiesPolicy />} />
<Route path="/article">
<Route path="info/:id" element={<AnArticle />} />
<Route path="content/:id" element={<AnArticleBody />} />
</Route>
<Route path="/account">
<Route path="settings" element={<AccountSettings />} />
</Route>
<Route path="/search-results" element={<SearchResultsPage />} />
<Route path="/*" element={<NotFound />}></Route>
</Routes>
</BrowserRouter>
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/about" element={<About />} />
<Route path="/help" element={<Help />} />
<Route path="/contact-us" element={<ContactUs />} />
<Route path="/terms-of-use" element={<TermsOfUse />} />
<Route path="/privacy-policy" element={<PrivacyPolicy />} />
<Route path="/cookies-policy" element={<CookiesPolicy />} />
<Route path="/article">
<Route path="info/:id" element={<AnArticle />} />
<Route path="content/:id" element={<AnArticleBody />} />
</Route>
<Route path="/account">
<Route path="settings" element={<AccountSettings />} />
</Route>
<Route path="/search-results" element={<SearchResultsPage />} />
<Route path="/*" element={<NotFound />}></Route>
</Routes>
</BrowserRouter>
</React.StrictMode>
</Provider>
);

0
src/pages/SearchResultsPage.tsx Executable file → Normal file
View File