💄(frontend) refactor feedbacks screen
Enhance initial screen. Allow user returning to the meeting. Also, add basic and non-functional rating component to mock, what the form would be.
This commit is contained in:
committed by
aleb_the_flash
parent
b07e4c58b4
commit
fb8c0fd1b5
114
src/frontend/src/features/rooms/components/Rating.tsx
Normal file
114
src/frontend/src/features/rooms/components/Rating.tsx
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { Button, H, Text } from '@/primitives'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { cva } from '@/styled-system/css'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { styled } from '@/styled-system/jsx'
|
||||||
|
|
||||||
|
const Card = styled('div', {
|
||||||
|
base: {
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: 'gray.300',
|
||||||
|
padding: '1rem',
|
||||||
|
marginTop: '1.5rem',
|
||||||
|
borderRadius: '0.25rem',
|
||||||
|
boxShadow: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const Bar = styled('div', {
|
||||||
|
base: {
|
||||||
|
display: 'flex',
|
||||||
|
border: '2px solid',
|
||||||
|
borderColor: 'gray.300',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflowY: 'hidden',
|
||||||
|
scrollbar: 'hidden',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const ratingButtonRecipe = cva({
|
||||||
|
base: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
color: 'initial',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: 0,
|
||||||
|
padding: '0.5rem 0.85rem',
|
||||||
|
flexGrow: '1',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
selected: {
|
||||||
|
true: {
|
||||||
|
backgroundColor: '#1d4ed8',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderLeft: {
|
||||||
|
true: {
|
||||||
|
borderLeft: '1px solid',
|
||||||
|
borderColor: 'gray.300',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const labelRecipe = cva({
|
||||||
|
base: {
|
||||||
|
color: 'gray.600',
|
||||||
|
paddingTop: '0.25rem',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const Rating = ({ maxRating = 8, ...props }) => {
|
||||||
|
const { t } = useTranslation('rooms', { keyPrefix: 'rating' })
|
||||||
|
const [selectedRating, setSelectedRating] = useState<number | null>(null)
|
||||||
|
|
||||||
|
const handleRatingClick = (rating: number) => {
|
||||||
|
setSelectedRating((prevRating) => (prevRating === rating ? null : rating))
|
||||||
|
}
|
||||||
|
|
||||||
|
const minLabel = props?.minLabel ?? t('levels.min')
|
||||||
|
const maxLabel = props?.maxLabel ?? t('levels.max')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<H lvl={3}>{t('question')}</H>
|
||||||
|
<Bar>
|
||||||
|
{[...Array(maxRating)].map((_, index) => (
|
||||||
|
<Button
|
||||||
|
key={index}
|
||||||
|
onPress={() => handleRatingClick(index + 1)}
|
||||||
|
className={ratingButtonRecipe({
|
||||||
|
selected: selectedRating === index + 1,
|
||||||
|
borderLeft: index != 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{index + 1}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</Bar>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text variant="sm" className={labelRecipe()}>
|
||||||
|
{minLabel}
|
||||||
|
</Text>
|
||||||
|
<Text variant="sm" className={labelRecipe()}>
|
||||||
|
{maxLabel}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="sm"
|
||||||
|
fullWidth
|
||||||
|
isDisabled={!selectedRating}
|
||||||
|
>
|
||||||
|
{t('submit')}
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,17 +1,47 @@
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Text } from '@/primitives'
|
import { Button } from '@/primitives'
|
||||||
import { Screen } from '@/layout/Screen'
|
import { Screen } from '@/layout/Screen'
|
||||||
import { CenteredContent } from '@/layout/CenteredContent'
|
import { Center, HStack, styled, VStack } from '@/styled-system/jsx'
|
||||||
|
import { Rating } from '@/features/rooms/components/Rating.tsx'
|
||||||
|
import { useLocation } from 'wouter'
|
||||||
|
|
||||||
|
// fixme - duplicated with home, refactor in a proper style
|
||||||
|
const Heading = styled('h1', {
|
||||||
|
base: {
|
||||||
|
fontWeight: '500',
|
||||||
|
fontStyle: 'normal',
|
||||||
|
fontStretch: 'normal',
|
||||||
|
fontOpticalSizing: 'auto',
|
||||||
|
fontSize: '2.3rem',
|
||||||
|
lineHeight: '2.5rem',
|
||||||
|
letterSpacing: '0',
|
||||||
|
paddingBottom: '2rem',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
export const FeedbackRoute = () => {
|
export const FeedbackRoute = () => {
|
||||||
const { t } = useTranslation('rooms')
|
const { t } = useTranslation('rooms')
|
||||||
|
const [, setLocation] = useLocation()
|
||||||
return (
|
return (
|
||||||
<Screen layout="centered">
|
<Screen layout="centered">
|
||||||
<CenteredContent title={t('feedback.heading')} withBackButton>
|
<Center>
|
||||||
<Text as="p" variant="h3" centered>
|
<VStack>
|
||||||
{t('feedback.body')}
|
<Heading>{t('feedback.heading')}</Heading>
|
||||||
</Text>
|
<HStack>
|
||||||
</CenteredContent>
|
<Button
|
||||||
|
outline
|
||||||
|
variant="primary"
|
||||||
|
onPress={() => window.history.back()}
|
||||||
|
>
|
||||||
|
{t('feedback.back')}
|
||||||
|
</Button>
|
||||||
|
<Button variant="primary" onPress={() => setLocation('/')}>
|
||||||
|
{t('feedback.home')}
|
||||||
|
</Button>
|
||||||
|
</HStack>
|
||||||
|
<Rating />
|
||||||
|
</VStack>
|
||||||
|
</Center>
|
||||||
</Screen>
|
</Screen>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"feedback": {
|
"feedback": {
|
||||||
"body": "",
|
"heading": "",
|
||||||
"heading": ""
|
"home": "",
|
||||||
|
"back": ""
|
||||||
},
|
},
|
||||||
"join": {
|
"join": {
|
||||||
"videoinput": {
|
"videoinput": {
|
||||||
@@ -109,6 +110,14 @@
|
|||||||
"chat": {
|
"chat": {
|
||||||
"disclaimer": ""
|
"disclaimer": ""
|
||||||
},
|
},
|
||||||
|
"rating": {
|
||||||
|
"submit": "",
|
||||||
|
"question": "",
|
||||||
|
"levels": {
|
||||||
|
"min": "",
|
||||||
|
"max": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"participants": {
|
"participants": {
|
||||||
"subheading": "",
|
"subheading": "",
|
||||||
"contributors": "",
|
"contributors": "",
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"feedback": {
|
"feedback": {
|
||||||
"body": "Please fill out the form available in the header to give us your precious feedback! Thanks.",
|
"heading": "You have left the meeting",
|
||||||
"heading": "Help us improve Visio"
|
"home": "Return to home",
|
||||||
|
"back": "Rejoin the meeting"
|
||||||
},
|
},
|
||||||
"join": {
|
"join": {
|
||||||
"videoinput": {
|
"videoinput": {
|
||||||
@@ -107,6 +108,14 @@
|
|||||||
"chat": {
|
"chat": {
|
||||||
"disclaimer": "The messages are visible to participants only at the time they are sent. All messages are deleted at the end of the call."
|
"disclaimer": "The messages are visible to participants only at the time they are sent. All messages are deleted at the end of the call."
|
||||||
},
|
},
|
||||||
|
"rating": {
|
||||||
|
"submit": "Submit",
|
||||||
|
"question": "What do you think about the quality of your call?",
|
||||||
|
"levels": {
|
||||||
|
"min": "very poor",
|
||||||
|
"max": "excellent"
|
||||||
|
}
|
||||||
|
},
|
||||||
"participants": {
|
"participants": {
|
||||||
"subheading": "In room",
|
"subheading": "In room",
|
||||||
"you": "You",
|
"you": "You",
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"feedback": {
|
"feedback": {
|
||||||
"body": "Remplissez le formulaire disponible dans l'entête du site pour nous donner votre avis sur l'outil. Vos retours sont précieux ! Merci.",
|
"heading": "Vous avez quitté la réunion",
|
||||||
"heading": "Aidez-nous à améliorer Visio"
|
"home": "Retourner à l'accueil",
|
||||||
|
"back": "Réintégrer la réunion"
|
||||||
},
|
},
|
||||||
"join": {
|
"join": {
|
||||||
"videoinput": {
|
"videoinput": {
|
||||||
@@ -107,6 +108,14 @@
|
|||||||
"chat": {
|
"chat": {
|
||||||
"disclaimer": "Les messages sont visibles par les participants uniquement au moment de\nleur envoi. Tous les messages sont supprimés à la fin de l'appel."
|
"disclaimer": "Les messages sont visibles par les participants uniquement au moment de\nleur envoi. Tous les messages sont supprimés à la fin de l'appel."
|
||||||
},
|
},
|
||||||
|
"rating": {
|
||||||
|
"submit": "Envoyer",
|
||||||
|
"question": "Que pensez-vous de la qualité de votre appel ?",
|
||||||
|
"levels": {
|
||||||
|
"min": "très mauvaise",
|
||||||
|
"max": "excellente"
|
||||||
|
}
|
||||||
|
},
|
||||||
"participants": {
|
"participants": {
|
||||||
"subheading": "Dans la réunion",
|
"subheading": "Dans la réunion",
|
||||||
"you": "Vous",
|
"you": "Vous",
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ export const buttonRecipe = cva({
|
|||||||
},
|
},
|
||||||
primary: {
|
primary: {
|
||||||
colorPalette: 'primary',
|
colorPalette: 'primary',
|
||||||
|
'&[data-disabled]': {
|
||||||
|
opacity: 0.3,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// @TODO: better handling of colors… this is a mess
|
// @TODO: better handling of colors… this is a mess
|
||||||
success: {
|
success: {
|
||||||
|
|||||||
Reference in New Issue
Block a user