Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,430 changes: 1,104 additions & 326 deletions kpoint-react/package-lock.json

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions kpoint-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,21 @@
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@lexical/react": "^0.13.1",
"@mdi/js": "^7.4.47",
"@mui/icons-material": "^5.15.8",
"@mui/lab": "^5.0.0-alpha.167",
"@mui/material": "^5.15.6",
"@react-oauth/google": "^0.12.1",
"@reduxjs/toolkit": "^1.9.7",
"i18next": "^23.7.18",
"lexical": "^0.13.1",
"mdi-material-ui": "^7.8.0",
"query-string": "^8.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-google-button": "^0.7.2",
"react-hook-form": "^7.49.3",
"react-hook-form": "^7.51.2",
"react-i18next": "^14.0.1",
"react-markdown": "^9.0.1",
"react-redux": "^8.1.0",
"react-responsive-masonry": "^2.2.0",
"react-router-dom": "^6.22.0",
Expand Down
6 changes: 1 addition & 5 deletions kpoint-react/src/common/types/projects/projects-edit.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,5 @@ export type ProjectsEditType = {
collectDeadline: string;
goalSum: number;
goalDeadline: string;
networksLinks: {
FACEBOOK: string | null;
INSTAGRAM: string | null;
YOUTUBE: string | null;
};
networksLinks: object;
};
4 changes: 2 additions & 2 deletions kpoint-react/src/common/types/projects/testRequest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type TestRequest = {
export type PostProject = {
file: File | string,
createdProject:{
title:string,
Expand All @@ -9,6 +9,6 @@ export type TestRequest = {
goalDeadline:string,
collectDeadline:string,
startSum:number,
networksLinks:{ FACEBOOK: string }
networksLinks: object,
}
};
1 change: 1 addition & 0 deletions kpoint-react/src/common/types/sign-up/sign-up.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type SignUpType = {
username: string,
email: string,
password: string,
confirmPassword?: string,
avatarImgUrl: string,
description: string,
tags: string[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const ProjectsPage: FC = () => {

const projects = useAppSelector((state) => state.project.projects);

const isAuthenticated = useAppSelector((state) => state.token.isloggedIn);
const isAuthenticated = useAppSelector((state) => state.token.isLoggedIn);

const [page, setPage] = useState(1);
useLayoutEffect(() => {
Expand Down
33 changes: 17 additions & 16 deletions kpoint-react/src/components/all-projects-page/subscribe-button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import AddIcon from '@mui/icons-material/Add';
// import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline';
import { Typography } from '@mui/material';
import { CircularProgress, Typography } from '@mui/material';
import Button from '@mui/material/Button';
import { useAppSelector } from 'hooks/use-app-selector/use-app-selector.hook';
import { FC, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { toast } from 'react-toastify';
Expand All @@ -10,10 +10,6 @@ import { projectAction } from 'store/actions';
import { StorageKey } from '../../common/enums/enums';
import { useAppDispatch } from '../../hooks/use-app-dispatch/use-app-dispatch.hook';
import { storage } from '../../services/services';
import {
subscribeToProjectLocally,
unsubscribeFromProjectLocally,
} from '../../store/projects/reducer';

interface SubscribeButtonProps {
projectId: string;
Expand All @@ -33,6 +29,7 @@ const SubscribeButton: FC<SubscribeButtonProps> = ({
const dispatch = useAppDispatch();
const user = storage.getItem(StorageKey.TOKEN);
const [isProcessing, setIsProcessing] = useState(false);
const isSubscribePending = useAppSelector((state) => state.project.isSubscribePending);

const handleButtonSubClick = async (): Promise<void> => {
if (isProcessing) return;
Expand All @@ -46,7 +43,6 @@ const SubscribeButton: FC<SubscribeButtonProps> = ({
await dispatch(
projectAction.subscribeToProject({ projectId: projectId }),
);
dispatch(subscribeToProjectLocally(projectId));
toast.success(t('buttons.user_subscribed'));
dispatch(
projectAction.getAllProjectsDefault({
Expand All @@ -56,7 +52,6 @@ const SubscribeButton: FC<SubscribeButtonProps> = ({
);
} else if (user && isFollowed) {
dispatch(projectAction.unSubscribe({ projectId: projectId }));
dispatch(unsubscribeFromProjectLocally(!isFollowed));
toast.success(t('buttons.user_unsubscribed'));
}
setIsProcessing(false);
Expand Down Expand Up @@ -85,14 +80,20 @@ const SubscribeButton: FC<SubscribeButtonProps> = ({
}}
onClick={handleButtonSubClick}
>
{isFollowed ? (
<Typography textTransform={'none'} sx={{ color: '#21272A' }}>
{t('buttons.unfollow')}
</Typography>
) : (
<Typography textTransform={'none'} sx={{ color: '#21272A' }}>
{t('buttons.follow')}
</Typography>
{isSubscribePending ? <CircularProgress size={20} sx={{
color: '#535365',
}}/> : (
<>
{isFollowed ? (
<Typography textTransform={'none'} sx={{ color: '#21272A' }}>
{t('buttons.unfollow')}
</Typography>
) : (
<Typography textTransform={'none'} sx={{ color: '#21272A' }}>
{t('buttons.follow')}
</Typography>
)}
</>
)}
</Button>
);
Expand Down
6 changes: 1 addition & 5 deletions kpoint-react/src/components/auth-page/oauth2.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import './oauth2.css';

import { useGoogleLogin } from '@react-oauth/google';
import { FC, useEffect } from 'react';
import { FC } from 'react';
import GoogleButton from 'react-google-button';
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';
Expand Down Expand Up @@ -41,10 +41,6 @@ const OAuth2: FC = () => {
},
});

useEffect(() => {
login();
}, [login]);

return (
<div
className="oauth2">
Expand Down
106 changes: 27 additions & 79 deletions kpoint-react/src/components/auth-page/sign-in-page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import { Divider, FormLabel } from '@mui/material';
import Avatar from '@mui/material/Avatar';
Expand All @@ -13,18 +14,16 @@ import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import { GoogleOAuthProvider } from '@react-oauth/google';
import * as React from 'react';
import { FC, useState } from 'react';
import { FC, useEffect } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';
import { authAction } from 'store/actions';

import { ENV, StorageKey } from '../../common/enums/enums';
import { ResponseType } from '../../common/types/response/response';
import { ENV } from '../../common/enums/enums';
import { SignInType } from '../../common/types/sign-in/sign-in';
import { useAppDispatch } from '../../hooks/hooks';
import { storage } from '../../services/services';
import { EmailRegx } from '../common/inputField/email-regx';
import { OAuth2 } from './oauth2';

const defaultTheme = createTheme();
Expand All @@ -33,69 +32,27 @@ const SignInPage: FC = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const navigate = useNavigate();
const [loginError, setLoginError] = useState<string | null>(null);
const [errors, setErrors] = useState<Record<string, string>>({});

const [formData, setFormData] = useState<SignInType>({
email: '',
password: '',
});

React.useEffect(() => {
const { register, handleSubmit } = useForm<SignInType>();
useEffect(() => {
document.body.style.backgroundColor = '#E4E5E9';
}, []);

const validateForm = (data: SignInType): Record<string, string> => {
const errors: Record<string, string> = {};

if(!EmailRegx.test(data.email) || data.email.trim() === '') {
toast.error(t('errors.invalid_email'));
errors.email = t('errors.invalid_email');
}

if (data.password.trim().length === 0) {
errors.password = t('errors.password_short');
errors.confirmPassword = t('errors.password_short');
toast.error(t('errors.password_short'), { position: 'top-right' });
}

return errors;

const onSubmit = ( e: React.FormEvent<HTMLFormElement> ): void => {
e.preventDefault();

handleSubmit(signIn)(e);
};

const handleFieldFocus = (field: string): void => {
setErrors((prevErrors) => ({ ...prevErrors, [field]: '' }));
};

const handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
event.preventDefault();

const formErrors = validateForm(formData);
setErrors(formErrors);

if (Object.keys(formErrors).length > 0) {
return;
}

dispatch(authAction.login(formData))
.then((action) => {
const responseType: ResponseType = action.payload as ResponseType;
const user = responseType.user;
storage.setItem(StorageKey.TOKEN, responseType.token);
storage.setItem(StorageKey.USER, JSON.stringify(user));
const signIn: SubmitHandler<SignInType> = async (data): Promise<void> => {

await dispatch(authAction.login(data)).then((requestStatus) => {
if (requestStatus.type === 'login/fulfilled') {
navigate('/');
})
.catch(() => {
setLoginError('Невірний логін або пароль');
});
};

const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
event.preventDefault();
const { name, value } = event.target;
setFormData((formData) => ({
...formData,
[name]: value,
}));
}
}).catch(() => {
toast.error(t('errors.can_not_login'));
});

};

return (
Expand Down Expand Up @@ -128,39 +85,30 @@ const SignInPage: FC = () => {
{t('welcome')}
</Typography>
<Typography>{t('sign_in_to_continue')}</Typography>
<Box component="form" sx={{ mt: 1, width: '100%' }} onSubmit={handleSubmit}>
<Box component="form" sx={{ mt: 1, width: '100%' }}
onSubmit={(e: React.MouseEvent<HTMLFormElement>): void => onSubmit(e)}>
<Grid item xs={3} md={6} marginTop={2}>
<FormLabel required>{t('email')}</FormLabel>
<TextField
required
fullWidth
id="email"
name="email"
value={formData.email}
autoComplete="email"
onChange={handleOnChange}
onFocus={(): void => handleFieldFocus('email')}
autoComplete="email"
autoFocus
error={!!errors.email}
helperText={errors.email}
{...register('email')}
/>
</Grid>
<Grid item xs={3} md={6} marginTop={2}>
<FormLabel required>{t('password')}</FormLabel>
<TextField
required
fullWidth
name="password"
fullWidth
type="password"
id="password"
value={formData.password}
autoComplete="current-password"
onChange={handleOnChange}
onFocus={(): void => handleFieldFocus('password')}
error={!!errors.password}
autoComplete="current-password"
{...register('password')}
/>
</Grid>
{loginError && <Typography color="error">{loginError}</Typography>}
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<FormControlLabel
Expand Down
Loading