This commit is contained in:
Primakov Alexandr Alexandrovich 2025-01-14 18:28:30 +03:00
parent 0f211115cf
commit 05b5843c08
10 changed files with 243 additions and 76 deletions

View File

@ -26,6 +26,6 @@ module.exports = {
}, },
}, },
config: { config: {
'nav2.api': '/api', "dry-wash.api.url": "/api"
}, },
} }

View File

@ -1,39 +1,45 @@
import globals from "globals" import globals from 'globals'
import pluginJs from "@eslint/js" import pluginJs from '@eslint/js'
import tseslint from "typescript-eslint" import tseslint from 'typescript-eslint'
import pluginReact from "eslint-plugin-react" import pluginReact from 'eslint-plugin-react'
import stylistic from '@stylistic/eslint-plugin' import stylistic from '@stylistic/eslint-plugin'
export default [ export default [
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] }, { files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'] },
{ languageOptions: { globals: globals.browser } }, { languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended, pluginJs.configs.recommended,
...tseslint.configs.recommended, ...tseslint.configs.recommended,
pluginReact.configs.flat.recommended, pluginReact.configs.flat.recommended,
{ {
plugins: { plugins: {
'@stylistic': stylistic '@stylistic': stylistic,
}, },
"rules": { rules: {
"no-unused-vars": "off", 'no-unused-vars': 'off',
"react/prop-types": "off", 'react/prop-types': 'off',
"@typescript-eslint/no-unused-vars": [ '@typescript-eslint/no-unused-vars': [
"warn", // or "error" 'warn', // or "error"
{ {
"argsIgnorePattern": "^_", argsIgnorePattern: '^_',
"varsIgnorePattern": "^_", varsIgnorePattern: '^_',
"caughtErrorsIgnorePattern": "^_" caughtErrorsIgnorePattern: '^_',
} },
], ],
"sort-imports": ["error", { 'sort-imports': [
"ignoreCase": false, 'error',
"ignoreDeclarationSort": true, {
"ignoreMemberSort": false, ignoreCase: false,
"memberSyntaxSortOrder": ["none", "all", "multiple", "single"], ignoreDeclarationSort: true,
"allowSeparatedGroups": true ignoreMemberSort: false,
}], memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
semi: ["error", "never"], allowSeparatedGroups: true,
},
],
semi: ['error', 'never'],
'@stylistic/indent': ['error', 2], '@stylistic/indent': ['error', 2],
}, },
} },
] {
ignores: ['stubs/'],
},
]

View File

@ -1,5 +1,5 @@
import { useForm, Controller } from 'react-hook-form' import { useForm, Controller } from 'react-hook-form'
import { Box, Input } from '@chakra-ui/react' import { Box, Button, Input } from '@chakra-ui/react'
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
type Inputs = { type Inputs = {
@ -7,7 +7,7 @@ type Inputs = {
age: string age: string
} }
export const FormTest = () => { export const FormTest = ({ name }) => {
const { const {
register, register,
control, control,
@ -16,45 +16,51 @@ export const FormTest = () => {
reset, reset,
setValue, setValue,
formState: { errors }, formState: { errors },
} = useForm<Inputs>() } = useForm<Inputs>({
mode: 'onChange',
const [name, setName] = React.useState('') defaultValues: {
const [age, setAge] = React.useState('12') name: name,
const ageRef = useRef(null) age: '12',
},
useEffect(() => { })
ageRef.current.focus()
}, [])
const onSibmit = ({ name, age }) => { const onSibmit = ({ name, age }) => {
// console.log(1111111, name, age) // console.log(1111111, name, age)
} }
return ( return (
<Box <>
as="form" <Box
flexDirection="column" as="form"
display="flex" flexDirection="column"
onSubmit={handleSubmit(onSibmit)} display="flex"
> onSubmit={handleSubmit(onSibmit)}
<label> >
Name: <label>
<Controller Name:
control={control} <Controller
name="name" control={control}
rules={{ name="name"
required: 'required 4 now', rules={{
minLength: { value: 4, message: 'min 4 now' }, required: 'required 4 now',
}} minLength: { value: 4, message: 'min 4 now' },
render={({ field, fieldState, formState }) => <Input {...field} />} }}
/> render={({ field, fieldState, formState }) => <Input {...field} />}
</label> />
</label>
<label> <label>
Age: Age:
<input type="number" ref={ageRef} defaultValue={age} /> <Controller
</label> control={control}
<button type="submit">Submit</button> name="age"
</Box> render={({ field, fieldState, formState }) => (
<Input type="number" {...field} />
)}
/>
</label>
<Button type="submit">Submit</Button>
</Box>
</>
) )
} }

View File

@ -41,10 +41,12 @@ export const Profile = ({
title?: string title?: string
}) => { }) => {
// const [rated, setRated] = useState(user.rated || 0) // const [rated, setRated] = useState(user.rated || 0)
const [editProfile, setEditProfile] = useState(false)
return ( return (
<Box mt={3} borderWidth="1px" p={3} overflowX="hidden"> <Box mt={3} borderWidth="1px" p={3} overflowX="hidden">
{!isLink && <FormTest />} {!isLink && editProfile && <FormTest name={user.name} />}
{!editProfile && <Button onClick={() => setEditProfile(true)}>Редактировать</Button>}
<Heading as="h2">{title || 'Данные профиля'}</Heading> <Heading as="h2">{title || 'Данные профиля'}</Heading>
<Box m={3}> <Box m={3}>
<Card width={'fit-content'} shadow="2xl"> <Card width={'fit-content'} shadow="2xl">
@ -59,7 +61,7 @@ export const Profile = ({
<CardFooter> <CardFooter>
{features['stars'] && ( {features['stars'] && (
<Stars <Stars
count={Number(features['stars']?.value) * 2} count={Number(features['stars']?.value)}
userId={user.id} userId={user.id}
// rated={rated} // rated={rated}
// setRated={setRated} // setRated={setRated}
@ -68,15 +70,15 @@ export const Profile = ({
</CardFooter> </CardFooter>
</Card> </Card>
</Box> </Box>
{!isLink && {/* {!isLink &&
features['buttons'] && features['buttons'] &&
user.friends?.map((friend) => ( user.friends?.map((friend) => ( */}
<Counter <Counter
key={friend.id} key={user.id}
// value={rated} setValue={setRated} // value={rated} setValue={setRated}
userId={friend.id} userId={user.id}
/> />
))} {/* ))} */}
</Box> </Box>
) )
} }

View File

@ -1,6 +1,7 @@
import { HStack, Icon } from '@chakra-ui/react' import { HStack, Icon } from '@chakra-ui/react'
import { FaRegStar, FaStar } from 'react-icons/fa6' import { FaRegStar, FaStar } from 'react-icons/fa6'
import React, { useMemo, useState } from 'react' import React, { useMemo, useState } from 'react'
import { getConfigValue } from '@brojs/cli'
import { stars } from '../__data__/context' import { stars } from '../__data__/context'
import { useUsers } from '../hooks' import { useUsers } from '../hooks'
@ -34,6 +35,19 @@ export const Stars = ({
// setRated: starsSetRated // setRated: starsSetRated
// } = useStars(rated, count) // } = useStars(rated, count)
const { rate, setUserRate } = useUsers(state => state[userId].rated) const { rate, setUserRate } = useUsers(state => state[userId].rated)
const handleStarsClick = (stars: number) => {
setUserRate(userId, stars)
fetch(getConfigValue('dry-wash.api.url') + '/user-rate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId,
stars,
}),
})
}
return ( return (
<HStack> <HStack>
@ -43,7 +57,7 @@ export const Stars = ({
key={index} key={index}
color="orange.400" color="orange.400"
cursor="pointer" cursor="pointer"
onClick={() => setUserRate(userId, index + 1)} onClick={() => handleStarsClick(index + 1)}
> >
<FaRegStar /> <FaRegStar />
</Icon> </Icon>
@ -52,7 +66,7 @@ export const Stars = ({
key={index} key={index}
color="orange.400" color="orange.400"
cursor="pointer" cursor="pointer"
onClick={() => setUserRate(userId, index + 1)} onClick={() => handleStarsClick(index + 1)}
> >
<FaStar /> <FaStar />
</Icon> </Icon>

View File

@ -1,5 +1,5 @@
import { Container, Heading } from '@chakra-ui/react' import { Container, Heading } from '@chakra-ui/react'
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { Profile } from '../components' import { Profile } from '../components'
@ -47,6 +47,24 @@ const users = {
export const ByPage = () => { export const ByPage = () => {
const params = useParams() const params = useParams()
const [isLoading, setIsLoading] = useState(null)
const [data, setData] = useState(null)
const [error, setError] = useState(null)
useEffect(() => {
const getUser = async () => {
try {
const response = await fetch('/api/users/' + params.userId)
const data = await response.json()
} catch (e) {
alert(e.message)
}
}
getUser()
}, [])
if (!users[params.userId]) { if (!users[params.userId]) {
return <Heading as="h2">Пользователь не найден</Heading> return <Heading as="h2">Пользователь не найден</Heading>

View File

@ -1,14 +1,43 @@
import { HStack } from '@chakra-ui/react' import { HStack } from '@chakra-ui/react'
import React, { memo } from 'react' import React, { memo, useEffect, useState } from 'react'
import { Profile } from '../components' import { Profile } from '../components'
import { users } from '../__data__/users' import { users } from '../__data__/users'
export const Friends = memo(() => { export const Friends = memo(() => {
const [isLoading, setIsLoading] = useState(false)
const [data, setData] = useState(null)
const [error, setError] = useState(null)
useEffect(() => {
const getUser = async () => {
setIsLoading(true)
try {
const response = await fetch('/api/users/')
if (response.ok) {
setData((await response.json()).body)
} else {
throw 'Что-то пошло не так'
}
} catch (e) {
setError(e.message)
} finally {
setIsLoading(false)
}
}
getUser()
}, [])
if(!data || isLoading) return <h1>loading...</h1>
return ( return (
<HStack> <HStack>
<Profile user={users['some-user-id']} /> <Profile user={data['some-user-id']} />
<Profile user={users['2']} /> <Profile user={data['2']} />
</HStack> </HStack>
) )
}) })

View File

@ -1,8 +1,53 @@
const router = require('express').Router(); const router = require('express').Router();
const router2 = require('express').Router();
const path = require('node:path')
const fs = require('node:fs')
const timer = (time = 300) => (req, res, next) => setTimeout(next, time); let stubs = {
users: 'success'
}
router.use(timer()); const timer = (time) => (req, res, next) => {
setTimeout(next, time)
}
timer.slow = timer(5000)
timer.fast = timer(300)
// router.use(timer.fast)
router.post('/user-rate', (req, res) => {
console.log(req.body)
res.status(500).send({ ok: false })
})
router.use('/admin', router2)
router.get('/users',
(req, res, next) => {
res.status(stubs.users.includes('error') ? 400 : 200).send(require(`../json/users/${stubs.users}.json`))
})
router2.get('/', (req, res) => {
res.send(`
<h2>Users</h2>
<ul>
<li><button onclick="fetch('/api/admin/users/success')" style="background-color: ${stubs.users === 'success' ? 'green' : '#ccc'}">success</button></li>
<li><button onclick="fetch('/api/admin/users/error')" style="background-color: ${stubs.users === 'error' ? 'green' : '#ccc'}">error</button></li>
</ul>
<h2>Users</h2>
<ul>
<li><button onclick="fetch('/api/admin/users/success')" style="background-color: ${stubs.users === 'success' ? 'green' : '#ccc'}">success</button></li>
<li><button onclick="fetch('/api/admin/users/error')" style="background-color: ${stubs.users === 'error' ? 'green' : '#ccc'}">error</button></li>
</ul>
`)
})
router2.get('/:stubName/:value', (req, res) => {
const { stubName, value } = req.params
stubs[stubName] = value
})
module.exports = router; module.exports = router;

View File

@ -0,0 +1,6 @@
{
"success": false,
"body": {
}
}

View File

@ -0,0 +1,41 @@
{
"success": false,
"body": {
"some-user-id": {
"id": "some-user-id",
"name": "alexandr",
"surname": null,
"email": null,
"rated": 3,
"avatar": "https://www.gravatar.com/avatar/6529e885535ef67a3fad810ad71167c2c03f79480936e9b3a714731753cbb47e?d=robohash",
"friends": [
{
"id": "2",
"name": "not alexandr",
"surname": null,
"email": null,
"rated": 2,
"avatar": "https://www.gravatar.com/avatar/6e?d=robohash"
}
]
},
"2": {
"id": "2",
"name": "not alexandr",
"surname": null,
"email": null,
"rated": 2,
"avatar": "https://www.gravatar.com/avatar/6e?d=robohash",
"friends": [
{
"id": "some-user-id",
"name": "alexandr",
"surname": null,
"email": null,
"rated": 3,
"avatar": "https://www.gravatar.com/avatar/6529e885535ef67a3fad810ad71167c2c03f79480936e9b3a714731753cbb47e?d=robohash"
}
]
}
}
}