Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kushbak2 #2

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
30 changes: 30 additions & 0 deletions locales/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,33 @@ msgstr "Navigate to..."

msgid "submit"
msgstr "Submit"

msgid "users page"
msgstr "Users"

msgid "firstName"
msgstr "First name"

msgid "lastName"
msgstr "Last name"

msgid "email"
msgstr "Email"

msgid "phone"
msgstr "Phone"

msgid "new user"
msgstr "New user"

msgid "users"
msgstr "Users"

msgid "add user"
msgstr "Add user"

msgid "edit user"
msgstr "Edit"

msgid "remove user"
msgstr "Remove"
30 changes: 30 additions & 0 deletions locales/ky.po
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,33 @@ msgstr "...баракка өтүү"

msgid "submit"
msgstr "Тапшыруу"

msgid "users page"
msgstr "Колдонуучулар"

msgid "firstName"
msgstr "Аты"

msgid "lastName"
msgstr "Фамилиясы"

msgid "email"
msgstr "Почтасы"

msgid "phone"
msgstr "Телефон номери"

msgid "new user"
msgstr "Жаңы колдонуучу"

msgid "users"
msgstr "Колдонуучулар"

msgid "add user"
msgstr "Кошуу"

msgid "edit user"
msgstr "Өзгөртүү"

msgid "remove user"
msgstr "Өчүрүү"
1 change: 1 addition & 0 deletions nav-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const NavMenu = ({locale, setLocale}: Props) => {
<Nav>
<MyLink to="a">{t`go to page a`}</MyLink>
<MyLink to="b">{t`go to page b`}</MyLink>
<MyLink to="users">{t`users page`}</MyLink>
<MyTextField
size="small"
onChange={(event) => (navRef.current = event.target.value)}
Expand Down
29 changes: 29 additions & 0 deletions pages/UserPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useParams } from "react-router-dom"
import type { IUser } from "./Users"
import { t } from '@lingui/macro'

export const UserPage = () => {
const { id } = useParams()
const item = localStorage.getItem('users')
const currentUser: IUser = item && JSON.parse(item).filter((user: IUser) => user.id === id)[0]
return (
<div>
<p>
<strong>{t`firstName`}:</strong>
{currentUser.firstName}
</p>
<p>
<strong>{t`lastName`}:</strong>
{currentUser.lastName}
</p>
<p>
<strong>{t`email`}:</strong>
{currentUser.email}
</p>
<p>
<strong>{t`phone`}:</strong>
{currentUser.phone}
</p>
</div>
)
}
214 changes: 214 additions & 0 deletions pages/Users.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import { Button, Modal, TextField } from '@material-ui/core'
import styled from '@emotion/styled'
import { useEffect, useRef, useState } from 'react'
import { H1 } from 'atoms'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { defineMessage, t } from '@lingui/macro'
import { NavLink } from 'react-router-dom'

export interface IUser {
id: string
firstName: string
lastName: string
email: string
phone?: string
avatar?: string
}

const generateID = () => {
const randomHex = () => Math
.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
return `${randomHex()}-${randomHex()}-${randomHex()}-${randomHex()}`
}

export const Users = () => {
const [usersList, setUsersList] = useState<IUser[]>([])
const [isUserCreationModalOpen, setIsUserCreationModalOpen] = useState(false)
const [currentUserData, setCurrentUserData] = useState<IUser | null>(null)

const form = useForm({ resolver: zodResolver(schema) })

const handleModalClose = () => {
setCurrentUserData(null)
setIsUserCreationModalOpen(false)
}
const handleModalOpen = () => setIsUserCreationModalOpen(true)

const getUsers = () => {
const usersFromLS = localStorage.getItem('users')
if (usersFromLS) setUsersList(JSON.parse(usersFromLS))
}

const createUser = form.handleSubmit(async formData => {
setUsersList([...usersList, { ...formData, id: generateID() }])
handleModalClose()
})

const openModalToEdit = (user: IUser) => {
setCurrentUserData(user)
handleModalOpen()
}

const editUser = form.handleSubmit(formData => {
currentUserData && setUsersList(
usersList.map(user => {
return (user.id === currentUserData.id) ? { ...formData, id: currentUserData.id } : user
})
)
handleModalClose()
})

const removeUser = (id: string) => {
setUsersList(usersList.filter(user => user.id !== id))
}

const onSubmit = currentUserData ? editUser : createUser

useEffect(() => {
getUsers()
}, [])
useEffect(() => {
if (usersList) {
return localStorage.setItem('users', JSON.stringify(usersList))
}
}, [usersList])

return (
<div>
<PageHeader>
{t`users`}({usersList?.length || 0})
<Button variant='contained' onClick={handleModalOpen}>{t`add user`}</Button>
</PageHeader>

<div>
{usersList?.map((user, index) => (
<User key={index}>
<img src={user.avatar || 'https://filkiniada-4sc.ucoz.org/80781_3.jpg'} alt="avatar" />
<NavLink to={'/user/' + user.id}>
<p>{user.firstName} {user.lastName} </p>
</NavLink>
<Button variant='contained' color='primary' onClick={() => openModalToEdit(user)}>{t`edit user`}</Button>
<Button variant='contained' color='secondary' onClick={() => removeUser(user.id)}>{t`remove user`}</Button>
</User>
))}
</div>
<UserModal
isUserCreationModalOpen={isUserCreationModalOpen}
handleModalClose={handleModalClose}
currentUserData={currentUserData}
onSubmit={onSubmit}
form={form}
/>
</div>
)
}

type ModalProps = {
isUserCreationModalOpen: boolean
handleModalClose: () => void
currentUserData: IUser | null
onSubmit: any
form: any
}

const UserModal = (props: ModalProps) => {
const { isUserCreationModalOpen, handleModalClose, onSubmit, form, currentUserData } = props
return (
<Modal
open={isUserCreationModalOpen}
onClose={handleModalClose}
style={{
display: 'grid',
placeItems: 'center',
}}
>
<Form onSubmit={onSubmit}>
<H1>{t`new user`}</H1>
<TextField
inputRef={form.register}
name="firstName"
type="text"
error={!!form.errors.firstName}
helperText={form.errors.firstName?.message}
label={t`firstName`}
defaultValue={currentUserData?.firstName}
/>
<TextField
inputRef={form.register}
name="lastName"
type="text"
error={!!form.errors.lastName}
helperText={form.errors.lastName?.message}
label={t`lastName`}
defaultValue={currentUserData?.lastName}
/>
<TextField
inputRef={form.register}
name="email"
type="email"
error={!!form.errors.email}
helperText={form.errors.email?.message}
label={t`email`}
defaultValue={currentUserData?.email}
/>
<TextField
inputRef={form.register}
name="phone"
type="tel"
error={!!form.errors.phone}
helperText={form.errors.phone?.message}
label={t`phone`}
placeholder='0555 555 555'
defaultValue={currentUserData?.phone}
/>
<Button type="submit" variant="outlined">{t`submit`}</Button>
</Form>
</Modal>
)
}

const schema = z
.object({
firstName: z.string().min(3, defineMessage({ id: 'firstName is too short' }).id),
lastName: z.string().min(3, defineMessage({ id: 'lastName is too short' }).id),
email: z.string().email('Incorrect email address'),
phone: z.string().refine((value) => /^0\d{9}$/i.test(value), { message: 'Неправильно введен номер телефона' }),
})

const User = styled.div`
display: flex;
cursor: pointer;
margin-bottom: 10px;
& img {
max-width: 50px;
width: 100%;
border-radius: 100%;
margin-right: 10px;
}
& > * {
margin-right: 10px;
}
`
const PageHeader = styled.div`
display: flex;
align-items: center;
`
const Form = styled.form`
display: flex;
flex-direction: column;
width: 500px;
background: #fff;
padding: 20px 40px;

& > *:not(:last-child) {
margin-bottom: 10px;
}

& > button[type='submit'] {
align-self: flex-end;
}
`
2 changes: 2 additions & 0 deletions pages/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export {PageA} from './page-a'
export {PageB} from './page-b'
export {NotFound} from './not-found'
export {Users} from './Users'
export {UserPage} from './UserPage'
4 changes: 3 additions & 1 deletion route-config.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {NotFound, PageA, PageB} from 'pages'
import {NotFound, PageA, PageB, UserPage, Users} from 'pages'

export const routeConfig = [
{path: 'a', element: <PageA />},
{path: 'b', element: <PageB />},
{path: 'users', element: <Users />},
{path: 'user/:id', element: <UserPage />},
{path: '*', element: <NotFound />},
]