diff --git a/src/components/Editor/Editors/TextPostEditor.tsx b/src/components/Editor/Editors/TextPostEditor.tsx index a7a6b6c67..6e691f6be 100644 --- a/src/components/Editor/Editors/TextPostEditor.tsx +++ b/src/components/Editor/Editors/TextPostEditor.tsx @@ -16,6 +16,7 @@ interface ITextPostEditorProps { onPreviewManuallyChanged?: () => void; onPreviewChange: (value: string) => void; previewPost: IPost; + disableAutoChanges?: () => void; } const PostEditor: React.FC = ({ @@ -27,6 +28,7 @@ const PostEditor: React.FC = ({ onPreviewManuallyChanged, onPreviewChange, previewPost, + disableAutoChanges, }) => { const [textContent, setTextContent] = useState(''); @@ -50,6 +52,7 @@ const PostEditor: React.FC = ({ alignItems="stretch" > void; onManuallyChanged?: () => void; + disableAutoChanges?: () => void; } const PreviewInput: React.FC = ({ @@ -23,6 +24,7 @@ const PreviewInput: React.FC = ({ initialWasManuallyChanged, onPreviewChange, onManuallyChanged, + disableAutoChanges, }) => { const { t } = useTranslation(); @@ -53,6 +55,9 @@ const PreviewInput: React.FC = ({ setIsTextFieldManuallyChanged(true); if (onManuallyChanged) onManuallyChanged(); } + if (disableAutoChanges) { + disableAutoChanges(); + } setTextFieldValue(value); }; diff --git a/src/locales/uk/parts/editor.ts b/src/locales/uk/parts/editor.ts index 63ce2b3e0..db711ab3b 100644 --- a/src/locales/uk/parts/editor.ts +++ b/src/locales/uk/parts/editor.ts @@ -49,5 +49,5 @@ export const editor = { requiredField: 'Заповніть, будь-ласка, обов`язкові поля, помічені зірочкою', notEnoughLength: 'Ви ввели недостатньо символів', notUASymbols: 'Введіть текст українською мовою', - noVideo: 'Додайте, будь-ласка, відео', + noVideo: 'Додайте, будь ласка, відео', }; diff --git a/src/models/expertMaterials/asyncActions.ts b/src/models/expertMaterials/asyncActions.ts index 1709abfe2..33e32835e 100644 --- a/src/models/expertMaterials/asyncActions.ts +++ b/src/models/expertMaterials/asyncActions.ts @@ -18,8 +18,8 @@ export const fetchExpertMaterials = createAsyncThunk( size: LOAD_POSTS_LIMIT, page: page, expert: expertId, - type: filters?.type, - direction: filters?.directions, + type: filters?.type.includes(0) ? [] : filters.type, + direction: filters?.directions.includes(0) ? [] : filters.directions, }, }); diff --git a/src/models/helpers/selectors.ts b/src/models/helpers/selectors.ts index 280bede52..51c2cca16 100644 --- a/src/models/helpers/selectors.ts +++ b/src/models/helpers/selectors.ts @@ -8,3 +8,7 @@ export const selectPostsByIds = (ids: number[]): IPost[] => { export const selectExpertsByIds = (ids: number[]): IExpert[] => { return ids.map((id) => store.getState().experts.data.experts[id]); }; + +export const selectExpertPostsByIds = (ids: number[]): IPost[] => { + return ids.map((id) => store.getState().expertMaterials.data.posts[id]); +}; diff --git a/src/old/lib/components/Users/LoginModal.css b/src/old/lib/components/Users/LoginModal.css new file mode 100644 index 000000000..9db7abd7a --- /dev/null +++ b/src/old/lib/components/Users/LoginModal.css @@ -0,0 +1,35 @@ +.congratulation-container .swal2-popup { + width: 480px; +} + +.congratulation-title-text { + font-family: 'Raleway', sans-serif; + font-weight: 500; + font-size: 64px; + line-height: 66px; + margin-top: -25px; /* -15px and -35px, without 90px */ +} + +.congratulation-sub-text { + font-family: 'Raleway', sans-serif; + font-weight: 500; + font-size: 24px; + line-height: 38px; + margin-top: -22px; +} + +.congratulation-button { + padding: 14px 60px; + border-radius: 50px; + background-color: #4fdfff; + color: whitesmoke; + font-weight: 700; + font-size: 18px; + font-family: 'Raleway', sans-serif; + margin: 10px 0 60px 0; /* '10px 0 40px or 60px 0' bottom, without '18px 0 71px 0' */ +} + +.congratulation-button:hover { + background-color: #106ba3; + cursor: pointer; +} diff --git a/src/old/lib/components/Users/LoginModal.styles.ts b/src/old/lib/components/Users/LoginModal.styles.ts index 74d4cb2e6..f61f35b97 100644 --- a/src/old/lib/components/Users/LoginModal.styles.ts +++ b/src/old/lib/components/Users/LoginModal.styles.ts @@ -140,38 +140,4 @@ export const useStyles = makeStyles((theme: Theme) => ({ backgroundColor: '#ffaa00', }, }, - congratulationContainer: { - '& .swal2-popup': { - width: 480, - }, - }, - congratulationTitleText: { - fontFamily: 'Raleway', - fontWeight: 500, - fontSize: '64px', - lineHeight: '66px', - marginTop: '-25px', // -15px and -35px, without 90px - }, - congratulationSubText: { - fontFamily: 'Raleway', - fontWeight: 500, - fontSize: '24px', - lineHeight: '38px', - padding: '0 -20px', - marginTop: '-22px', - }, - congratulationButton: { - padding: '14px 60px', - borderRadius: 50, - backgroundColor: '#4FDFFF', - color: theme.palette.common.white, - fontWeight: 700, - fontSize: '18px', - fontFamily: 'Raleway', - margin: '10px 0 60px 0', // '10px 0 40px or 60px 0' bottom, without '18px 0 71px 0' - '&:hover': { - backgroundColor: '#106ba3', - cursor: 'pointer', - }, - }, })); diff --git a/src/old/lib/components/Users/LoginModal.tsx b/src/old/lib/components/Users/LoginModal.tsx index b61664b0a..ab80fc01d 100644 --- a/src/old/lib/components/Users/LoginModal.tsx +++ b/src/old/lib/components/Users/LoginModal.tsx @@ -25,6 +25,7 @@ import { langTokens } from '../../../../locales/localizationInit'; import { BasicButton, BasicInput } from '../../../../components/Form'; import { useActions } from '../../../../shared/hooks'; import { getAuthoritiesAsyncAction } from '../../../../models/authorities'; +import './LoginModal.css'; export const LoginModal: React.FC = () => { const { t } = useTranslation(); @@ -54,10 +55,10 @@ export const LoginModal: React.FC = () => { const swalWithCustomButton = Swal.mixin({ customClass: { - container: classes.congratulationContainer, - title: classes.congratulationTitleText, - htmlContainer: classes.congratulationSubText, - confirmButton: classes.congratulationButton, + container: 'congratulation-container', + title: 'congratulation-title-text', + htmlContainer: 'congratulation-sub-text', + confirmButton: 'congratulation-button', }, buttonsStyling: false, }); diff --git a/src/old/lib/constants/editors.ts b/src/old/lib/constants/editors.ts index eec606691..568f79caa 100644 --- a/src/old/lib/constants/editors.ts +++ b/src/old/lib/constants/editors.ts @@ -2,3 +2,8 @@ export const CONTENT_DEBOUNCE_TIMEOUT = 1000; export const PREVIEW_DEBOUNCE_TIMEOUT = 500; export const MAX_PREVIEW_LENGTH = 150; + +export const MIN_TITLE_LENGTH = 10; +export const MIN_CONTENT_LENGTH = 15; +export const CHECK_REG_EXP = /^[а-яєїіґ\d\s\W]+$/i; +export const CLEAR_HTML_REG_EXP = /<\/?[^>]+(>|$)|&[a-z0-9]+;/gi; diff --git a/src/old/modules/experts/components/ExpertMaterialsContainer.tsx b/src/old/modules/experts/components/ExpertMaterialsContainer.tsx index 7ec80045b..15b921598 100644 --- a/src/old/modules/experts/components/ExpertMaterialsContainer.tsx +++ b/src/old/modules/experts/components/ExpertMaterialsContainer.tsx @@ -57,6 +57,7 @@ import { selectExpertsData, } from '../../../../models/expertMaterials'; import { updatePostTypes, updateDir } from '../../utilities/utilityFunctions'; +import { selectExpertPostsByIds } from '../../../../models/helpers/selectors'; export interface IExpertMaterialsContainerProps { expertId: number; @@ -68,7 +69,6 @@ const ExpertMaterialsContainer: React.FC = ({ expert, }) => { const { - posts, postIds, meta: { isLastPage, pageNumber, totalElements, totalPages }, } = useSelector(selectExpertsData); @@ -109,13 +109,7 @@ const ExpertMaterialsContainer: React.FC = ({ }; }, []); - const allMaterials = Object.values(posts); - const materials = [...allMaterials].filter((el) => { - if (postIds.find((elem) => elem === el.id)) { - return true; - } - return false; - }); + const materials = selectExpertPostsByIds(postIds); const postTypes = useSelector(selectPostTypes); diff --git a/src/old/modules/posts/components/PostView.tsx b/src/old/modules/posts/components/PostView.tsx index 7c0a3a7b0..bdb6c0f1f 100644 --- a/src/old/modules/posts/components/PostView.tsx +++ b/src/old/modules/posts/components/PostView.tsx @@ -21,9 +21,11 @@ export interface IPostViewProps { post: IPost; modificationAllowed?: boolean; onDelete?: () => void; + isPreview?:boolean; } const PostView: React.FC = ({ + isPreview, post, modificationAllowed, onDelete, @@ -63,7 +65,7 @@ const PostView: React.FC = ({ )} - {permission && ( + {!isPreview && permission && ( diff --git a/src/old/modules/posts/styles/PostView.styles.ts b/src/old/modules/posts/styles/PostView.styles.ts index 640a594b4..0a299b892 100644 --- a/src/old/modules/posts/styles/PostView.styles.ts +++ b/src/old/modules/posts/styles/PostView.styles.ts @@ -6,6 +6,7 @@ export const useStyles = makeStyles( position: 'relative', minHeight: '550px', padding: '70px 225px 150px', + wordBreak:'break-word', }, wrapper: { display: 'flex', diff --git a/src/views/postCreation/TextPostCreation.tsx b/src/views/postCreation/TextPostCreation.tsx index 7c5eae2f7..7ac2752b5 100644 --- a/src/views/postCreation/TextPostCreation.tsx +++ b/src/views/postCreation/TextPostCreation.tsx @@ -30,7 +30,10 @@ import { } from '../../old/lib/utilities/API/types'; import { createPost, getAllExperts } from '../../old/lib/utilities/API/api'; import { + CHECK_REG_EXP, CLEAR_HTML_REG_EXP, CONTENT_DEBOUNCE_TIMEOUT, + MIN_CONTENT_LENGTH, + MIN_TITLE_LENGTH, PREVIEW_DEBOUNCE_TIMEOUT, } from '../../old/lib/constants/editors'; import PostView from '../../old/modules/posts/components/PostView'; @@ -194,13 +197,9 @@ export const TextPostCreation: React.FC = ({ setAuthors([]); return; } - getAllExperts({ params: { userName: searchValue.trim() } }) - .then((res) => { - setAuthors(res.data.content); - }) - .catch((e) => { - console.error(e); - }); + getAllExperts({ params: { userName: searchValue.trim() } }).then((res) => { + setAuthors(res.data.content); + }); }, [searchValue]); const onAuthorTableClick = (value: number, item: ExpertResponseType) => { @@ -248,15 +247,17 @@ export const TextPostCreation: React.FC = ({ type: { id: postType.type }, }; - const regExp = /^[а-яєїіґ]*\d*\s*\W*$/i; + const contentText = newPost.content.replaceAll(CLEAR_HTML_REG_EXP, ''); const isEmpty = !newPost.title || !newPost.directions.length || !newPost.content; const isEnoughLength = - newPost.content.length < 15 || newPost.title.length < 10; + contentText.length < MIN_CONTENT_LENGTH || + newPost.title.length < MIN_TITLE_LENGTH; - const isHasUASymbols = !regExp.test(newPost.title); + const isHasUASymbols = + !CHECK_REG_EXP.test(newPost.title) || !CHECK_REG_EXP.test(contentText); const previewPost = React.useMemo( () => @@ -413,7 +414,7 @@ export const TextPostCreation: React.FC = ({ ) : ( - + )} = ({ type: { id: PostTypeEnum.VIDEO }, }; - const regExp = /^[а-яєїіґ]*\d*\s*\W*$/i; + const contentText = newPost.content.replaceAll(CLEAR_HTML_REG_EXP, ''); const isEmpty = !newPost.title || !newPost.directions.length || !newPost.content; const isEnoughLength = - newPost.content.length < 15 || newPost.title.length < 10; + contentText.length < MIN_CONTENT_LENGTH || newPost.title.length < MIN_TITLE_LENGTH; - const isHasUASymbols = !regExp.test(newPost.title); + const isHasUASymbols = !CHECK_REG_EXP.test(newPost.title)|| + !CHECK_REG_EXP.test(contentText); const isVideoEmpty = !newPost.videoUrl; @@ -386,7 +389,7 @@ export const VideoPostCreation: React.FC = ({ ) : ( - + )} = ({ const authorities = useSelector(selectAuthorities); const isAdmin = authorities.data?.includes('SET_IMPORTANCE'); + const [autoChanges, setAutoChanges] = useState(true); + const [selectedDirections, setSelectedDirections] = useState( post.directions, ); @@ -156,7 +162,7 @@ export const TextPostUpdation: React.FC = ({ authorId: authorId ?? post.author.id, }; - const regExp = /^[а-яєїіґ]*\d*\s*\W*$/i; + const contentText = updatedPost.content.replaceAll(CLEAR_HTML_REG_EXP, ''); const isEmpty = !updatedPost.title || @@ -164,9 +170,11 @@ export const TextPostUpdation: React.FC = ({ !updatedPost.directions.length; const isEnoughLength = - updatedPost.content.length < 15 || updatedPost.title.length < 10; + contentText.length < MIN_CONTENT_LENGTH || + updatedPost.title.length < MIN_TITLE_LENGTH; - const isHasUASymbols = !regExp.test(updatedPost.title); + const isHasUASymbols = + !CHECK_REG_EXP.test(updatedPost.title) || !CHECK_REG_EXP.test(contentText); const previewPost: IPost = { ...post, @@ -263,8 +271,12 @@ export const TextPostUpdation: React.FC = ({ onHtmlContentChange={(value) => { setTyping({ ...typing, content: true }); handleHtmlContentChange(value); + if (!htmlContent.replaceAll(CLEAR_HTML_REG_EXP, '').length) { + setAutoChanges(false); + } }} - initialWasPreviewManuallyChanged + initialWasPreviewManuallyChanged={autoChanges} + disableAutoChanges={() => setAutoChanges(true)} onPreviewChange={(value) => { setTyping({ ...typing, preview: true }); handlePreviewChange(value); @@ -274,7 +286,7 @@ export const TextPostUpdation: React.FC = ({ ) : ( - + )} = ({ const authorities = useSelector(selectAuthorities); const isAdmin = authorities.data?.includes('SET_IMPORTANCE'); + const [autoChanges, setAutoChanges] = useState(true); const [selectedDirections, setSelectedDirections] = useState( post.directions, ); @@ -130,7 +135,7 @@ export const VideoPostUpdation: React.FC = ({ authorId: authorId ?? post.author.id, }; - const regExp = /^[а-яєїіґ]*\d*\s*\W*$/i; + const contentText = updatedPost.content.replaceAll(CLEAR_HTML_REG_EXP, ''); const isEmpty = !updatedPost.title || @@ -138,9 +143,11 @@ export const VideoPostUpdation: React.FC = ({ !updatedPost.content; const isEnoughLength = - updatedPost.content.length < 15 || updatedPost.title.length < 10; + contentText.length < MIN_CONTENT_LENGTH || + updatedPost.title.length < MIN_TITLE_LENGTH; - const isHasUASymbols = !regExp.test(updatedPost.title); + const isHasUASymbols = + !CHECK_REG_EXP.test(updatedPost.title) || !CHECK_REG_EXP.test(contentText); const isVideoEmpty = !updatedPost.videoUrl; @@ -237,8 +244,12 @@ export const VideoPostUpdation: React.FC = ({ onHtmlContentChange={(value) => { setTyping({ ...typing, content: true }); handleHtmlContentChange(value); + if (!htmlContent.replaceAll(CLEAR_HTML_REG_EXP, '').length) { + setAutoChanges(false); + } }} - initialWasPreviewManuallyChanged + initialWasPreviewManuallyChanged={autoChanges} + disableAutoChanges={() => setAutoChanges(true)} onPreviewChange={(value) => { setTyping({ ...typing, preview: true }); handlePreviewChange(value); @@ -248,7 +259,7 @@ export const VideoPostUpdation: React.FC = ({ ) : ( - + )}