Refactored Rating component
Separated annDetails, added to userPage, made actually operating
This commit is contained in:
parent
f432193508
commit
d9925647c6
@ -4,19 +4,13 @@ import { CSSProperties, useState } from 'react'
|
|||||||
import { LatLng } from 'leaflet'
|
import { LatLng } from 'leaflet'
|
||||||
|
|
||||||
import LineDot from './LineDot'
|
import LineDot from './LineDot'
|
||||||
import Rating from './Rating'
|
|
||||||
import { categoryNames } from '../assets/category'
|
import { categoryNames } from '../assets/category'
|
||||||
import { useBook, useRemoveAnnouncement } from '../hooks/api'
|
import { useBook, useRemoveAnnouncement } from '../hooks/api'
|
||||||
import { Announcement } from '../api/announcement/types'
|
import { Announcement } from '../api/announcement/types'
|
||||||
import { iconItem } from '../utils/markerIcons'
|
import { iconItem } from '../utils/markerIcons'
|
||||||
import { useId } from '../hooks'
|
import { useId } from '../hooks'
|
||||||
import SelectDisposalTrashbox from './SelectDisposalTrashbox'
|
import SelectDisposalTrashbox from './SelectDisposalTrashbox'
|
||||||
|
import StarRating from './StarRating'
|
||||||
type AnnouncementDetailsProps = {
|
|
||||||
close: () => void,
|
|
||||||
refresh: () => void,
|
|
||||||
announcement: Announcement,
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
container: {
|
container: {
|
||||||
@ -30,9 +24,13 @@ const styles = {
|
|||||||
} as CSSProperties,
|
} as CSSProperties,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ViewProps = {
|
||||||
|
announcement: Announcement,
|
||||||
|
}
|
||||||
|
|
||||||
const View = ({
|
const View = ({
|
||||||
announcement: { name, category, bestBy, description, lat, lng, address, metro, userId },
|
announcement: { name, category, bestBy, description, lat, lng, address, metro, userId },
|
||||||
}: { announcement: Announcement }) => (
|
}: ViewProps) => (
|
||||||
<>
|
<>
|
||||||
<h1>{name}</h1>
|
<h1>{name}</h1>
|
||||||
|
|
||||||
@ -42,7 +40,9 @@ const View = ({
|
|||||||
|
|
||||||
<p className='mb-0'>{description}</p>
|
<p className='mb-0'>{description}</p>
|
||||||
|
|
||||||
<Rating userId={userId} className='mb-3' />
|
<p className='mb-3'>
|
||||||
|
Рейтинг пользователя: <StarRating dynamic userId={userId} />
|
||||||
|
</p>
|
||||||
|
|
||||||
<MapContainer style={styles.map} center={[lat, lng]} zoom={16} >
|
<MapContainer style={styles.map} center={[lat, lng]} zoom={16} >
|
||||||
<TileLayer
|
<TileLayer
|
||||||
@ -80,7 +80,7 @@ function Control({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>Забронировали {bookedBy} чел.</p>
|
<p>Забронировали {bookedBy + (bookButton.disabled ? 1 : 0)} чел.</p>
|
||||||
{(myId === userId) ? (
|
{(myId === userId) ? (
|
||||||
<>
|
<>
|
||||||
<Button variant='success' onClick={showDispose}>Утилизировать</Button>
|
<Button variant='success' onClick={showDispose}>Утилизировать</Button>
|
||||||
@ -93,6 +93,12 @@ function Control({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AnnouncementDetailsProps = {
|
||||||
|
close: () => void,
|
||||||
|
refresh: () => void,
|
||||||
|
announcement: Announcement,
|
||||||
|
}
|
||||||
|
|
||||||
function AnnouncementDetails({
|
function AnnouncementDetails({
|
||||||
close,
|
close,
|
||||||
refresh,
|
refresh,
|
||||||
|
@ -1,78 +0,0 @@
|
|||||||
import { useState } from 'react'
|
|
||||||
|
|
||||||
import { gotError, gotResponse } from '../hooks/useFetch'
|
|
||||||
import { useUserRating, useSendRate } from '../hooks/api'
|
|
||||||
|
|
||||||
import styles from '../styles/Rating.module.css'
|
|
||||||
|
|
||||||
type StarProps = {
|
|
||||||
filled: boolean,
|
|
||||||
selected: boolean,
|
|
||||||
setMyRate: () => void,
|
|
||||||
sendMyRate: () => void,
|
|
||||||
}
|
|
||||||
|
|
||||||
function Star({ filled, selected, setMyRate, sendMyRate }: StarProps) {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className={`${styles.star} ${filled ? styles.starFilled : ''} ${selected ? styles.starSelected : ''}`}
|
|
||||||
onMouseEnter={setMyRate}
|
|
||||||
onFocus={setMyRate}
|
|
||||||
onClick={sendMyRate}
|
|
||||||
>★</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type RatingProps = {
|
|
||||||
userId: number,
|
|
||||||
className: string | undefined,
|
|
||||||
}
|
|
||||||
|
|
||||||
function Rating({ userId, className }: RatingProps) {
|
|
||||||
const rating = useUserRating(userId)
|
|
||||||
|
|
||||||
const [myRate, setMyRate] = useState(0)
|
|
||||||
|
|
||||||
const { doSendRate } = useSendRate()
|
|
||||||
|
|
||||||
async function sendMyRate() {
|
|
||||||
const res = await doSendRate(myRate)
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
rating.refetch()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<p className={className}>
|
|
||||||
Рейтинг пользователя:{' '}
|
|
||||||
{gotResponse(rating) ? (
|
|
||||||
gotError(rating) ? (
|
|
||||||
<span className='text-danger'>{rating.error}</span>
|
|
||||||
) : (
|
|
||||||
<span
|
|
||||||
className={styles.starContainer}
|
|
||||||
onMouseLeave={() => setMyRate(0)}
|
|
||||||
>
|
|
||||||
{...Array(5).fill(5).map(
|
|
||||||
(_, i) =>
|
|
||||||
<Star
|
|
||||||
key={i}
|
|
||||||
filled={i < rating.data}
|
|
||||||
selected={i < myRate}
|
|
||||||
setMyRate={() => setMyRate(i + 1)}
|
|
||||||
sendMyRate={() => void sendMyRate()}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<span>Загрузка...</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Rating
|
|
77
front/src/components/StarRating.tsx
Normal file
77
front/src/components/StarRating.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import { useSendRate, useUserRating } from '../hooks/api'
|
||||||
|
import { gotError, gotResponse } from '../hooks/useFetch'
|
||||||
|
|
||||||
|
import styles from '../styles/StarRating.module.css'
|
||||||
|
|
||||||
|
type StarProps = {
|
||||||
|
filled: boolean,
|
||||||
|
selected: boolean,
|
||||||
|
setMyRate?: () => void,
|
||||||
|
sendMyRate?: () => void,
|
||||||
|
disabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function Star({ filled, selected, setMyRate, sendMyRate, disabled }: StarProps) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`${styles.star} ${filled ? styles.starFilled : ''} ${selected ? styles.starSelected : ''}`}
|
||||||
|
onMouseEnter={setMyRate}
|
||||||
|
onFocus={setMyRate}
|
||||||
|
onClick={sendMyRate}
|
||||||
|
disabled={disabled}
|
||||||
|
>★</button> // star
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type StarRatingProps = {
|
||||||
|
userId: number,
|
||||||
|
dynamic?: boolean,
|
||||||
|
style?: React.CSSProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
function StarRating({ userId, dynamic = false }: StarRatingProps) {
|
||||||
|
const rating = useUserRating(userId)
|
||||||
|
|
||||||
|
const [myRate, setMyRate] = useState(0)
|
||||||
|
|
||||||
|
const { doSendRate } = useSendRate()
|
||||||
|
|
||||||
|
async function sendMyRate() {
|
||||||
|
const res = await doSendRate(myRate, userId)
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
rating.refetch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gotResponse(rating)) {
|
||||||
|
return (
|
||||||
|
<span>Загрузка...</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gotError(rating)) {
|
||||||
|
return (
|
||||||
|
<span className='text-danger'>{rating.error}</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={styles.starContainer} onMouseLeave={() => setMyRate(0)}>
|
||||||
|
{...Array(5).fill(5).map((_, i) => (
|
||||||
|
<Star
|
||||||
|
key={i}
|
||||||
|
filled={i < Math.round(rating.data)}
|
||||||
|
selected={i < myRate}
|
||||||
|
setMyRate={() => dynamic && setMyRate(i + 1)}
|
||||||
|
sendMyRate={() => dynamic && void sendMyRate()}
|
||||||
|
disabled={!dynamic}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</span >
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StarRating
|
@ -14,4 +14,4 @@ export { default as Points } from './Points'
|
|||||||
export { default as SignOut } from './SignOut'
|
export { default as SignOut } from './SignOut'
|
||||||
export { default as Poetry } from './Poetry'
|
export { default as Poetry } from './Poetry'
|
||||||
export { default as SelectDisposalTrashbox } from './SelectDisposalTrashbox'
|
export { default as SelectDisposalTrashbox } from './SelectDisposalTrashbox'
|
||||||
export { default as Rating } from './Rating'
|
export { default as StarRating } from './StarRating'
|
||||||
|
@ -11,10 +11,11 @@ function useSendRate() {
|
|||||||
processSendRate,
|
processSendRate,
|
||||||
)
|
)
|
||||||
|
|
||||||
const doSendRate = (rate: number) => (
|
const doSendRate = (rate: number, user_id: number) => (
|
||||||
doSend({}, {
|
doSend({}, {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
rate,
|
rate,
|
||||||
|
user_id,
|
||||||
}),
|
}),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
@ -3,28 +3,14 @@ import { UserRating, isUserRatingResponse } from '../../api/userRating/types'
|
|||||||
import useFetch, { UseFetchReturn } from '../useFetch'
|
import useFetch, { UseFetchReturn } from '../useFetch'
|
||||||
|
|
||||||
const useUserRating = (userId: number): UseFetchReturn<UserRating> => (
|
const useUserRating = (userId: number): UseFetchReturn<UserRating> => (
|
||||||
// useFetch(
|
useFetch(
|
||||||
// composeUserRatingURL(userId),
|
composeUserRatingURL(userId),
|
||||||
// 'GET',
|
'GET',
|
||||||
// false,
|
false,
|
||||||
// isUserRatingResponse,
|
isUserRatingResponse,
|
||||||
// processUserRating,
|
processUserRating,
|
||||||
// initialUserRating
|
initialUserRating
|
||||||
// )
|
)
|
||||||
|
|
||||||
{
|
|
||||||
data: 3,
|
|
||||||
loading: false,
|
|
||||||
error: null,
|
|
||||||
refetch: () => { return },
|
|
||||||
}
|
|
||||||
|
|
||||||
// {
|
|
||||||
// data: undefined,
|
|
||||||
// loading: true,
|
|
||||||
// error: null,
|
|
||||||
// refetch: () => { return },
|
|
||||||
// }
|
|
||||||
)
|
)
|
||||||
|
|
||||||
export default useUserRating
|
export default useUserRating
|
||||||
|
@ -2,7 +2,7 @@ import { Container } from 'react-bootstrap'
|
|||||||
|
|
||||||
import { useUser } from '../hooks/api'
|
import { useUser } from '../hooks/api'
|
||||||
import { userCategories } from '../assets/userCategories'
|
import { userCategories } from '../assets/userCategories'
|
||||||
import { BackHeader, CategoryPreview, Poetry, Points, SignOut } from '../components'
|
import { BackHeader, CategoryPreview, Poetry, Points, SignOut, StarRating } from '../components'
|
||||||
import { gotError, gotResponse } from '../hooks/useFetch'
|
import { gotError, gotResponse } from '../hooks/useFetch'
|
||||||
|
|
||||||
import styles from '../styles/UserPage.module.css'
|
import styles from '../styles/UserPage.module.css'
|
||||||
@ -34,6 +34,13 @@ function UserPage() {
|
|||||||
)
|
)
|
||||||
} />
|
} />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h5 className={styles.userRating}>
|
||||||
|
Ваша оценка:
|
||||||
|
<StarRating userId={user.data?.id || 1} />
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
{userCategories.map(cat => (
|
{userCategories.map(cat => (
|
||||||
<CategoryPreview key={cat} category={cat} />
|
<CategoryPreview key={cat} category={cat} />
|
||||||
))}
|
))}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
|
.starContainer {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.star {
|
.star {
|
||||||
-webkit-text-stroke: 2px var(--bs-body-color);
|
-webkit-text-stroke: 2px var(--bs-body-color);
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
color: var(--bs-modal-bg);
|
color: var(--bs-body-bg);
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0 3px;
|
padding: 0 3px;
|
@ -1,3 +1,10 @@
|
|||||||
|
.userRating {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.sixteenXnine {
|
.sixteenXnine {
|
||||||
max-width: calc(100vh * 9/16) !important;
|
max-width: calc(100vh * 9/16) !important;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user