- {showSuccess && (
-
-
-
- Your message was sent
-
Thank you for contacting us. We’ll get back to you soon.
-
-
- )}
- {showError && (
-
-
-
- Your message was not sent
-
An error occured while trying to send your message. Please try again.
-
-
- )}
-
- Give feedback
-
- We are constantly improving Oodikone. Please share your thoughts using the form below, or contact us at{' '}
- grp-toska@helsinki.fi.
-
You can write in Finnish or English.
-
-
-
-
- )
-}
diff --git a/services/frontend/src/components/Routes/index.jsx b/services/frontend/src/components/Routes/index.jsx
index 5d5aec6ca6..328cc7a78a 100644
--- a/services/frontend/src/components/Routes/index.jsx
+++ b/services/frontend/src/components/Routes/index.jsx
@@ -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'
@@ -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 = {
diff --git a/services/frontend/src/components/material/PageTitle/index.tsx b/services/frontend/src/components/material/PageTitle/index.tsx
new file mode 100644
index 0000000000..f15d785757
--- /dev/null
+++ b/services/frontend/src/components/material/PageTitle/index.tsx
@@ -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 (
+
+
+ {title}
+
+
+ )
+}
diff --git a/services/frontend/src/components/material/StatusNotification/index.tsx b/services/frontend/src/components/material/StatusNotification/index.tsx
new file mode 100644
index 0000000000..adc0517946
--- /dev/null
+++ b/services/frontend/src/components/material/StatusNotification/index.tsx
@@ -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 (
+
+
+ {message}
+
+
+ )
+}
diff --git a/services/frontend/src/pages/Feedback/index.tsx b/services/frontend/src/pages/Feedback/index.tsx
new file mode 100644
index 0000000000..9f4afdd4d5
--- /dev/null
+++ b/services/frontend/src/pages/Feedback/index.tsx
@@ -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 (
+
+ setShowSuccess(false)}
+ open={showSuccess}
+ severity="success"
+ />
+ setShowError(false)}
+ open={showError}
+ severity="error"
+ />
+
+
+
+ We are constantly improving Oodikone. Please share your thoughts using the form below, or contact us at
+
+ grp-toska@helsinki.fi. You can write in Finnish or English.
+
+
+
+
+
+
+
+
+
+ setModalOpen(false)} open={modalOpen}>
+
+
+ Sending feedback to Toska
+
+
+
+ }
+ onClick={handleSubmit}
+ variant="contained"
+ >
+ Send
+
+
+
+
+
+ )
+}