Skip to content

Commit

Permalink
Merge pull request #4677 from UniversityOfHelsinkiCS/material-ui-feed…
Browse files Browse the repository at this point in the history
…back

Material UI: Feedback
  • Loading branch information
rikurauhala authored Nov 12, 2024
2 parents bdda5dc + af9ceda commit 3d0b6cf
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 98 deletions.
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default [
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'with-single-extends' }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-floating-promises': 'off', // TODO: Disable only for .jsx and .tsx files
'@typescript-eslint/no-misused-promises': 'off', // Most of these errors come from Express route handlers that are handled correctly by express-async-errors
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
Expand Down
97 changes: 0 additions & 97 deletions services/frontend/src/components/Feedback/index.jsx

This file was deleted.

2 changes: 1 addition & 1 deletion services/frontend/src/components/Routes/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { CustomPopulation } from '@/components/CustomPopulation'
import { EvaluationOverview } from '@/components/EvaluationOverview'
import { UniversityViewPage } from '@/components/EvaluationOverview/UniversityView'
import { FacultyStatistics } from '@/components/FacultyStatistics'
import { Feedback } from '@/components/Feedback'
import { FrontPage } from '@/components/Frontpage'
import { LanguageCenterView } from '@/components/LanguageCenterView'
import { PopulationStatistics } from '@/components/PopulationStatistics'
Expand All @@ -22,6 +21,7 @@ import { Teachers } from '@/components/Teachers'
import { Updater } from '@/components/Updater'
import { Users } from '@/components/Users'
import { languageCenterViewEnabled } from '@/conf'
import { Feedback } from '@/pages/Feedback'
import { ProtectedRoute } from './ProtectedRoute'

const routes = {
Expand Down
16 changes: 16 additions & 0 deletions services/frontend/src/components/material/PageTitle/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Box, Typography } from '@mui/material'

/**
* A title text displayed at the top of the page.
*
* @param {string} title - The main title of the page.
*/
export const PageTitle = ({ title }: { title: string }) => {
return (
<Box my={3}>
<Typography component="h1" variant="h4">
{title}
</Typography>
</Box>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Alert, Snackbar } from '@mui/material'

/**
* A temporary notification message to give feedback on the status of an action.
*
* @param message - The message to display to the user.
* @param onClose - The function to call when the notification is closed.
* @param open - Whether the notification is open or not.
* @param severity - The severity of the notification. Changes the color and icon of the notification.
*/
export const StatusNotification = ({
message,
onClose,
open,
severity,
}: {
message: string
onClose: () => void
open: boolean
severity: 'success' | 'info' | 'warning' | 'error'
}) => {
return (
<Snackbar
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
autoHideDuration={10000}
onClose={onClose}
open={open}
>
<Alert onClose={onClose} severity={severity}>
{message}
</Alert>
</Snackbar>
)
}
144 changes: 144 additions & 0 deletions services/frontend/src/pages/Feedback/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/* eslint-disable consistent-return */
import { Send } from '@mui/icons-material'
import { Box, Button, Container, Link, Modal, TextField, Tooltip, Typography } from '@mui/material'
import { useEffect, useState } from 'react'

import { useTitle } from '@/common/hooks'
import { PageTitle } from '@/components/material/PageTitle'
import { StatusNotification } from '@/components/material/StatusNotification'
import { useGetAuthorizedUserQuery } from '@/redux/auth'
import { useSendFeedbackMutation } from '@/redux/feedback'

export const Feedback = () => {
useTitle('Feedback')

const [feedback, setFeedback] = useState('')
const [showError, setShowError] = useState(false)
const [showSuccess, setShowSuccess] = useState(false)
const [modalOpen, setModalOpen] = useState(false)

const { email } = useGetAuthorizedUserQuery()
const [sendFeedback, { isError, isLoading, isSuccess }] = useSendFeedbackMutation()

useEffect(() => {
if (isSuccess) {
setFeedback('')
setShowSuccess(true)
const timer = setTimeout(() => setShowSuccess(false), 10000)
return () => clearTimeout(timer)
}
}, [isSuccess])

useEffect(() => {
if (isError) {
setShowError(true)
const timer = setTimeout(() => setShowError(false), 10000)
return () => clearTimeout(timer)
}
}, [isError])

const handleTyping = event => {
setFeedback(event.target.value)
}

const handleSubmit = () => {
sendFeedback({ content: feedback })
setModalOpen(false)
}

return (
<Container maxWidth="md">
<StatusNotification
message="Your message was sent. Thank you for contacting us. We will get back to you soon."
onClose={() => setShowSuccess(false)}
open={showSuccess}
severity="success"
/>
<StatusNotification
message="Your message was not sent. An error occurred while trying to send your message. Please try again."
onClose={() => setShowError(false)}
open={showError}
severity="error"
/>
<Box textAlign="center">
<PageTitle title="Feedback" />
<Typography>
We are constantly improving Oodikone. Please share your thoughts using the form below, or contact us at
<br />
<Link href="mailto:[email protected]">[email protected]</Link>. You can write in Finnish or English.
</Typography>
</Box>
<Box
autoComplete="off"
component="form"
noValidate
sx={{ alignItems: 'center', display: 'flex', flexDirection: 'column' }}
>
<Tooltip arrow title="Your email address is based on your user account. We will reply to this address.">
<TextField
disabled
fullWidth
label="Your email address"
placeholder="Your email address"
sx={{ mt: 2 }}
value={email}
variant="outlined"
/>
</Tooltip>
<TextField
fullWidth
label="Your feedback"
multiline
onChange={handleTyping}
placeholder="Write your feedback here"
rows={15}
sx={{ my: 2 }}
value={feedback}
variant="outlined"
/>
<Button
color="primary"
disabled={!feedback.trim().length || isLoading}
onClick={() => setModalOpen(true)}
variant="contained"
>
Submit
</Button>
</Box>
<Modal onClose={() => setModalOpen(false)} open={modalOpen}>
<Box
bgcolor="background.paper"
borderRadius={2}
boxShadow={24}
maxWidth="400px"
p={4}
position="absolute"
sx={{
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
}}
width="100%"
>
<Typography component="h3" mb={2} variant="h6">
Sending feedback to Toska
</Typography>
<Box display="flex" justifyContent="space-between">
<Button disabled={isLoading} onClick={() => setModalOpen(false)}>
Cancel
</Button>
<Button
color="primary"
disabled={!feedback.trim().length || isLoading}
endIcon={<Send />}
onClick={handleSubmit}
variant="contained"
>
Send
</Button>
</Box>
</Box>
</Modal>
</Container>
)
}

0 comments on commit 3d0b6cf

Please sign in to comment.