Compare commits

...

15 Commits

Author SHA1 Message Date
d324718646 Two requests fixed 2022-11-14 12:27:05 +03:00
e20831fbfc Fixed 404 error on "article/info/$id" pages 2022-11-12 23:55:00 +03:00
fecce76fe1 Improved loading content of and article with saving them in redux store
When article was loaded once than it will be cached in redux store.
On the next loading of article content it will be get from store
2022-11-12 23:41:31 +03:00
335218fdd9 Merge pull request 'fix/fetch-article-loop' (#178) from fix/fetch-article-loop into develop
Reviewed-on: http://85.143.176.51:3000/free-land/front-end/pulls/178
2022-11-12 14:11:20 +00:00
35170d4558 Merge pull request 'topic resolved' (#177) from fix/topic into develop
Reviewed-on: http://85.143.176.51:3000/free-land/front-end/pulls/177
2022-11-12 14:10:10 +00:00
5d28da2f0a Merge pull request 'The overlaping in the navbar resolved' (#176) from fix/navbar-overlap into develop
Reviewed-on: http://85.143.176.51:3000/free-land/front-end/pulls/176
2022-11-12 14:09:51 +00:00
f677bffc16 Merge pull request 'fix/burger-translation' (#175) from fix/burger-translation into develop
Reviewed-on: http://85.143.176.51:3000/free-land/front-end/pulls/175
2022-11-12 14:09:29 +00:00
69d09bb8a4 Remove unused imports 2022-11-11 18:58:32 +03:00
d3d1d9d1af Fetch article useCase added 2022-11-11 18:57:16 +03:00
salar.sali
355a9af985 topic resolved 2022-11-11 08:53:01 -05:00
salar.sali
a4a8116d32 The overlaping in the navbar resolved 2022-11-11 05:16:02 -05:00
salar.sali
a4ef589ca9 The translation in burger component resolved 2022-11-11 05:03:34 -05:00
8d3115d878 Updated style for MD code view 2022-11-09 17:35:25 +03:00
eb17ae6377 Merge pull request 'dockerfile-envs fixe, three bugs in select.tsx,typography/Link.tsx, and typography/RouterLink.tsx fixed' (#174) from bugfix/dockerfile-envs into develop
Reviewed-on: http://85.143.176.51:3000/free-land/front-end/pulls/174
2022-10-31 17:54:56 +00:00
moeidheidari
181a225eff dockerfile-envs fixe, three bugs in select.tsx,typography/Link.tsx, and typography/RouterLink.tsx fixed 2022-10-31 20:50:33 +03:00
49 changed files with 4175 additions and 4299 deletions

View File

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

6
.env Normal file
View File

@ -0,0 +1,6 @@
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://scipaper.ru REACT_APP_CMS_BASE_URL=http://api.scipaper.ru
REACT_APP_CMS_APP_NAME=scipaper 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_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://scipaper.ru REACT_APP_INTEGRATOR_URL=http://api.scipaper.ru
REACT_APP_INTEGRATOR_API_VERSION=/v1 REACT_APP_INTEGRATOR_API_VERSION=/v1
REACT_APP_GRAPHQL_URL=/graphql REACT_APP_GRAPHQL_URL=/graphql

View File

@ -1,6 +1,6 @@
REACT_APP_CMS_BASE_URL=http://scipaper.ru REACT_APP_CMS_BASE_URL=http://api.scipaper.ru
REACT_APP_CMS_APP_NAME=scipaper 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_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://scipaper.ru REACT_APP_INTEGRATOR_URL=http://api.scipaper.ru
REACT_APP_INTEGRATOR_API_VERSION=/v1 REACT_APP_INTEGRATOR_API_VERSION=/v1
REACT_APP_GRAPHQL_URL=/graphql REACT_APP_GRAPHQL_URL=/graphql

View File

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

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

View File

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@ -0,0 +1,29 @@
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 Normal file → Executable file
View File

@ -1,16 +1,25 @@
import { Article } from "article/domain/articleEntity"; import { Article } from "article/domain/articleEntity";
import type { ArticleStore } from "../domain/articleStore"; import type { ArticleStore } from "../domain/articleStore";
const getArticleUseCase = async ( class GetArticleUseCase {
getArticle: ArticleStore["getArticle"], /* ------------------------------ Dependencies ------------------------------ */
setArticle: ArticleStore["setArticle"], _store: ArticleStore;
id: Article["id"] /* -------------------------------------------------------------------------- */
): Promise<Article | null> => { constructor(
const article = await getArticle(id); store: ArticleStore,
if (article) { ) {
await setArticle(article); this._store = store;
} }
return 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');
}
/* -------------------------------------------------------------------------- */
}
export { getArticleUseCase }; export { GetArticleUseCase };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

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

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

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

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

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

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

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

View File

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

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

View File

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

View File

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

View File

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

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