Skip to content
Merged
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
31 changes: 25 additions & 6 deletions frontend/src/components/Channels/ChannelsBody.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const ChannelsBody = ({
const renderButton = (name, id) => (
<button
type="button"
className={`w-100 text-start rounded-0 btn ${currentChannelName === name ? 'btn-secondary' : ''}`}
className={`w-100 text-start rounded-0 btn ${
currentChannelName === name ? 'btn-secondary' : ''
}`}
onClick={onSetCurrentChannel(name, id)}
>
<span className="me-1">{t('chat.channels.marker')}</span>
Expand All @@ -38,21 +40,38 @@ const ChannelsBody = ({
{name}
</Button>

<Dropdown.Toggle split variant={currentChannelName === name ? 'secondary' : 'none'}>
<Dropdown.Toggle
split
variant={currentChannelName === name ? 'secondary' : 'none'}
>
<span className="visually-hidden">{t('chat.channels.editButton')}</span>
</Dropdown.Toggle>

<Dropdown.Menu className="w-100">
<Dropdown.Item onClick={onDeleteChannel}>{t('chat.channels.removeButton')}</Dropdown.Item>
<Dropdown.Item onClick={onRenameChannel}>{t('chat.channels.renameButton')}</Dropdown.Item>
<Dropdown.Item onClick={onDeleteChannel}>
{t('chat.channels.removeButton')}
</Dropdown.Item>
<Dropdown.Item onClick={onRenameChannel}>
{t('chat.channels.renameButton')}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);

return (
<ul id="channels-box" className="nav flex-column nav-pills nav-fill px-2 mb-3 overflow-auto h-100 d-block">
<ul
id="channels-box"
className="nav flex-column nav-pills nav-fill px-2 mb-3 overflow-auto h-100 d-block"
>
{data?.map(({ id, name, removable }) => (
<li key={id} id={`${id}`} name={`${name}`} className="nav-item w-100" onClick={onClickChannel} role="presentation">
<li
key={id}
id={`${id}`}
name={`${name}`}
className="nav-item w-100"
onClick={onClickChannel}
role="presentation"
>
{removable ? renderDropdown(name, id) : renderButton(name, id)}
</li>
))}
Expand Down
18 changes: 7 additions & 11 deletions frontend/src/components/Channels/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import SocketContext from '../../contexts';
const Channels = () => {
const { data } = useGetChannelsQuery();
const dispatch = useDispatch();
const currentChannelName = useSelector(({
ui,
}) => ui.currentChannel.name ?? ui.defaultChannel.name);
const { name: modalName, show: modalShow } = useSelector((state) => state.ui.currentModal);
const { currentChannel, clickedChannel, defaultChannel } = useSelector((state) => state.ui);
const currentChannelName = useSelector(
({ ui }) => ui.currentChannel.name ?? ui.defaultChannel.name,
);
const { currentChannel, clickedChannel, defaultChannel } = useSelector(
(state) => state.ui,
);
const socket = useContext(SocketContext);

const channelsNames = data?.map(({ name }) => name);
Expand Down Expand Up @@ -93,12 +94,7 @@ const Channels = () => {
onRenameChannel={handleRenameChannel}
onSetCurrentChannel={handleSetCurrentChannel}
/>
<ChatModal
name={modalName}
show={modalShow}
onHide={handleModalHide}
validationData={channelsNames}
/>
<ChatModal onHide={handleModalHide} validationData={channelsNames} />
</div>
);
};
Expand Down
10 changes: 4 additions & 6 deletions frontend/src/components/ChatModal/index.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Modal from 'react-bootstrap/Modal';
import { useSelector } from 'react-redux';
import RenameChannelModal from './RenameChannelModal';
import DeleteChannelModal from './DeleteChannelModal';
import AddChannelModal from './AddChannelModal';
Expand All @@ -9,13 +10,10 @@ const mapModal = {
addChannel: AddChannelModal,
};

const ChatModal = ({
name,
show,
onHide,
validationData,
}) => {
const ChatModal = ({ onHide, validationData }) => {
const { name, show } = useSelector((state) => state.ui.currentModal);
const ModalBody = mapModal[name] ?? (() => null);

return (
<Modal
show={show}
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@ import { resetAuthData } from '../store/slices/authDataSlice';
const Header = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const { t } = useTranslation();
const { t, i18n } = useTranslation();

const currentUserName = useSelector(({ authData }) => authData.username);
const handleClick = () => {
resetAuthDataInLocalStorage();
dispatch(resetAuthData());
navigate('/login', { replace: false });
};
const handleChangeLang = () => {
i18n.changeLanguage(i18n.language === 'en' ? 'ru' : 'en');
};

return (
<Navbar className="shadow-sm navbar bg-white">
<Container>
<Navbar.Brand href="/">{t('header.title')}</Navbar.Brand>
<Navbar.Collapse className="justify-content-end">
<Button variant="outline-secondary" className="mx-1" onClick={handleChangeLang}>{t('header.lang')}</Button>
{currentUserName
&& (
<>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/LoginForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const LoginForm = () => {
onChange={formik.handleChange}
className={classNames('form-control', { 'is-invalid': !!formik.errors.username || !!formik.errors.unathorized })}
/>
<label htmlFor="username">Ваш ник</label>
<label htmlFor="username">{ t('login.form.username') }</label>
{formik.errors.username && <div className="invalid-feedback">{formik.errors.username}</div>}
</div>
<div className="form-floating mb-4">
Expand All @@ -80,12 +80,12 @@ const LoginForm = () => {
onChange={formik.handleChange}
className={classNames('form-control', { 'is-invalid': !!formik.errors.password || !!formik.errors.unathorized })}
/>
<label htmlFor="password">Пароль</label>
<label htmlFor="password">{ t('login.form.password') }</label>
{formik.errors.password && <div className="invalid-feedback">{formik.errors.password}</div>}
{formik.errors.unathorized && <div className="invalid-tooltip" style={{ display: 'block' }}>{t('login.form.errors.invalidRequest')}</div>}
</div>
<Button type="submit" className="w-100 mb-3" variant="outline-primary" disabled={formik.isSubmitting}>
Войти
{ t('login.form.submit') }
</Button>
</form>
);
Expand Down
14 changes: 7 additions & 7 deletions frontend/src/components/SignupForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ const SignupForm = () => {
name="username"
autoComplete="username"
required=""
placeholder="Имя пользователя"
placeholder={t('signup.form.username')}
id="username"
value={formik.values.username}
onChange={formik.handleChange}
className={classNames('form-control', { 'is-invalid': !!formik.errors.username || !!formik.errors.isNotUniq })}
/>
<label htmlFor="username">Имя пользователя</label>
<label htmlFor="username">{t('signup.form.username')}</label>
{formik.errors.username && <div className="invalid-feedback">{formik.errors.username}</div>}
</div>

Expand All @@ -90,14 +90,14 @@ const SignupForm = () => {
name="password"
autoComplete="password"
required=""
placeholder="Пароль"
placeholder={t('signup.form.password')}
type="password"
id="password"
value={formik.values.password}
onChange={formik.handleChange}
className={classNames('form-control', { 'is-invalid': !!formik.errors.password || !!formik.errors.isNotUniq })}
/>
<label htmlFor="password">Пароль</label>
<label htmlFor="password">{t('signup.form.password')}</label>
{formik.errors.password && <div className="invalid-feedback">{formik.errors.password}</div>}
</div>

Expand All @@ -106,19 +106,19 @@ const SignupForm = () => {
name="confirmPassword"
autoComplete="confirmPassword"
required=""
placeholder="Подтвердите пароль"
placeholder={t('signup.form.confirmPassword')}
type="password"
id="confirmPassword"
value={formik.values.confirmPassword}
onChange={formik.handleChange}
className={classNames('form-control', { 'is-invalid': !!formik.errors.confirmPassword || !!formik.errors.isNotUniq })}
/>
<label htmlFor="confirmPassword">Подтвердите пароль</label>
<label htmlFor="confirmPassword">{t('signup.form.confirmPassword')}</label>
{formik.errors.confirmPassword && <div className="invalid-feedback">{formik.errors.confirmPassword}</div>}
{formik.errors.isNotUniq && <div className="invalid-tooltip" style={{ display: 'block' }}>{t('signup.form.errors.invalidRequest')}</div>}
</div>
<Button type="submit" className="w-100 mb-3" variant="outline-primary" disabled={formik.isSubmitting}>
Войти
{t('signup.form.submit')}
</Button>
</form>
);
Expand Down
113 changes: 113 additions & 0 deletions frontend/src/locales/en.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const en = {
translation: {
header: {
title: 'Hexlet chat',
signedAs: 'Signed as: ',
lang: 'Change lang',
exit: 'Exit',
},
errorPage: {
title: 'Oops!',
notFound: 'Page not found',
goMain: {
text: 'But you can ',
link: 'go to main page',
},
},
login: {
title: 'Login',
noAccount: 'No acc?',
signupLink: 'Registration',
form: {
username: 'Your nickname',
password: 'Password',
submit: 'Login',
errors: {
required: 'Required to fill',
invalidRequest: 'Not valid username or password',
},
},
},
signup: {
title: 'Registration',
form: {
username: 'User name',
password: 'Password',
confirmPassword: 'Confirm password',
submit: 'Registration',
errors: {
required: 'Required to fill',
usernameLength: 'From 3 to 20 symbols',
passwordLength: 'More 6 symbols',
confirmPassword: 'Passwords must match',
invalidRequest: 'This user already exists',
},
},
},
chat: {
channels: {
title: 'Channels',
removeButton: 'Delete',
renameButton: 'Rename',
addButton: '+',
editButton: 'Channel management',
marker: '#',
},
messages: {
marker: '#',
count_one: '{{count}} message',
count_other: '{{count}} messages',
form: {
newMessage: 'Enter message...',
},
},
},
notifications: {
add: 'Channel created',
remove: 'Channel deleted',
rename: 'Channel renamed',
errors: {
network: 'Connection error',
server: 'Data loading error',
unknown: 'Unknown error',
parsing: 'Data loading error',
},
},
modals: {
add: {
title: 'Add channel',
form: {
name: 'Channel name',
submit: 'Submit',
cancel: 'Cancel',
errors: {
required: 'Required field',
length: '3 to 20 characters',
uniq: 'Must be unique',
},
},
},
remove: {
title: 'Remove channel',
description: 'Are you sure?',
submit: 'Remove',
cancel: 'Cancel',
},
rename: {
title: 'Rename channel',
form: {
name: 'New channel name',
submit: 'Submit',
cancel: 'Cancel',
errors: {
required: 'Required field',
length: '3 to 20 characters',
uniq: 'Must be unique',
},
},
},
},
},
};

export default en;
3 changes: 2 additions & 1 deletion frontend/src/locales/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ru from './ru';
import en from './en';

const resources = { ru };
const resources = { ru, en };

export default resources;
1 change: 1 addition & 0 deletions frontend/src/locales/ru.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const ru = {
title: 'Hexlet chat',
signedAs: 'Вошли как: ',
exit: 'Выйти',
lang: 'Сменить язык',
},
errorPage: {
title: 'Упс!',
Expand Down
Loading