Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add snackbar after creating an offer in CompleteProfileBlock #2635

Merged
merged 17 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/constants/translations/en/offer-page.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
"buttonTitles": {
"tutor": "Create offer",
"student": "Create request"
}
},
"extendedSuccessMessage": {
"tutor": "You successfuly created an offer!;You can review all your offers on My Offers page.",
"student": "You successfuly created a request!;You can review all your requests on My Requests page."
},
"seeAll": "See all"
},
"editOffer": {
"title": {
Expand Down
7 changes: 6 additions & 1 deletion src/constants/translations/uk/offer-page.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
"buttonTitles": {
"tutor": "Створити пропозицію",
"student": "Створити запит"
}
},
"extendedSuccessMessage": {
"tutor": "Ви успішно створили пропозицію!;Ви можете переглянути всі свої пропозиції на сторінці \"Мої пропозиції\".",
"student": "Ви успішно створили запит!;Ви можете переглянути всі свої запити на сторінці \"Мої запити\"."
},
"seeAll": "Переглянути всі"
},
"editOffer": {
"title": {
Expand Down
13 changes: 13 additions & 0 deletions src/containers/layout/app-snackbar/AppSnackbar.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const styles = {
alert: {
color: 'basic.white'
},
action: {
p: '4px 8px 0 30px',
cursor: 'pointer'
},
secondMessage: {
fontSize: '12px',
fontWeight: '300'
}
}
42 changes: 36 additions & 6 deletions src/containers/layout/app-snackbar/AppSnackbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,63 @@ import { useAppDispatch, useAppSelector } from '~/hooks/use-redux'
import Snackbar from '@mui/material/Snackbar'
import Alert from '@mui/material/Alert'
import Box from '@mui/material/Box'

import { closeAlert, snackbarSelector } from '~/redux/features/snackbarSlice'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
import { styles } from '~/containers/layout/app-snackbar/AppSnackbar.styles'

const AppSnackbar = () => {
const { isOpened, message, duration, severity } =
const { isOpened, message, duration, severity, isExtended, route } =
useAppSelector(snackbarSelector)

const { t } = useTranslation()

const dispatch = useAppDispatch()
const navigate = useNavigate()

const handleClose = () => dispatch(closeAlert())

const translatedMessage =
typeof message === 'string' ? t(message) : t(message.text, message.options)

const actionBody = translatedMessage
.split(', ')
.map((line) => <Box key={line}>{line}</Box>)

const handleButtonClick = () => {
navigate(route!)
handleClose()
}

const actionButton = (
<Box onClick={handleButtonClick} sx={styles.action}>
{t('offerPage.createOffer.seeAll')}
</Box>
)

const [firstMessage, secondMessage] = translatedMessage.split(';')

const extendedBody = (
<>
<Box>{firstMessage}</Box>
<Box sx={styles.secondMessage}>{secondMessage}</Box>
</>
)

return (
<Snackbar
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
autoHideDuration={duration}
onClose={handleClose}
open={isOpened}
>
<Alert severity={severity} sx={{ color: 'basic.white' }} variant='filled'>
{translatedMessage.split(', ').map((line) => (
<Box key={line}>{line}</Box>
))}
<Alert
action={isExtended && actionButton}
severity={severity}
sx={styles.alert}
variant='filled'
>
{isExtended ? extendedBody : actionBody}
</Alert>
</Snackbar>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
ButtonVariantEnum,
ComponentEnum,
CreateOrUpdateOfferData,
ErrorResponse,
Offer,
OfferActionsEnum,
ServiceFunction,
Expand All @@ -35,7 +34,6 @@ import {
} from '~/types'
import { styles } from '~/containers/offer-page/OfferPage.styles'
import { openAlert } from '~/redux/features/snackbarSlice'
import { getErrorKey } from '~/utils/get-error-key'

interface CreateOrUpdateOfferProps {
existingOffer?: Offer | null
Expand All @@ -61,22 +59,28 @@ const CreateOrEditOffer: FC<CreateOrUpdateOfferProps> = ({
? OfferActionsEnum.Edit
: OfferActionsEnum.Create

const onResponseError = (error?: ErrorResponse) => {
dispatch(
openAlert({
severity: snackbarVariants.error,
message: getErrorKey(error)
})
)
}
const onResponse = (response: Offer | null) => {
const isHash = hash === '#offer' ? true : undefined
ShadowOfTheSpace marked this conversation as resolved.
Show resolved Hide resolved

dispatch(
openAlert({
severity: snackbarVariants.success,
message: `offerPage.${offerAction}.successMessage`
})
openAlert(
isHash
? {
severity: snackbarVariants.success,
message: `offerPage.createOffer.extendedSuccessMessage.${userRole}`,
duration: 10000,
isExtended: true,
route: authRoutes.myOffers.path
}
: {
severity: snackbarVariants.success,
message: `offerPage.${offerAction}.successMessage`
}
)
)

closeDrawer()

if (hash == '#offer') {
navigate(`${authRoutes.myProfile.path}#complete`)
updateOffer!(true)
Expand All @@ -97,8 +101,7 @@ const CreateOrEditOffer: FC<CreateOrUpdateOfferProps> = ({
service,
fetchOnMount: false,
defaultResponse: null,
onResponse,
onResponseError
onResponse
})

const {
Expand Down
10 changes: 9 additions & 1 deletion src/redux/features/snackbarSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ interface SnackbarState {
severity: AlertColor
message: SnackbarMessage
duration: number
isExtended?: boolean
route?: string
}

interface SnackbarOpenParams {
severity: AlertColor
message: SnackbarMessage
duration?: number
isExtended?: boolean
route?: string
}

type OpenSnackbarAction = PayloadAction<SnackbarOpenParams>
Expand All @@ -30,7 +34,9 @@ const initialState: SnackbarState = {
isOpened: false,
severity: 'info',
message: '',
duration: 0
duration: 0,
isExtended: false,
route: ''
}

const snackbarSlice = createSlice({
Expand All @@ -42,6 +48,8 @@ const snackbarSlice = createSlice({
state.severity = action.payload.severity
state.message = action.payload.message
state.duration = action.payload.duration || 4000
state.isExtended = action.payload.isExtended ?? false
state.route = action.payload.route ?? ''
},
closeAlert: (state) => {
state.isOpened = false
Expand Down
2 changes: 1 addition & 1 deletion src/types/offer/interfaces/offer.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface Offer extends CommonEntityFields {
proficiencyLevel: ProficiencyLevelEnum[]
description: string
languages: LanguagesEnum[]
enrolledUsers: string[]
enrolledUsers?: string[]
dudchakk marked this conversation as resolved.
Show resolved Hide resolved
authorRole: UserRoleEnum.Tutor | UserRoleEnum.Student
author: Pick<
UserResponse,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { screen, fireEvent, waitFor } from '@testing-library/react'
import { renderWithProviders } from '~tests/test-utils'
import LoginDialog from '~/containers/guest-home-page/login-dialog/LoginDialog'
import { TestSnackbar } from '~tests/test-utils'
import { vi } from 'vitest'
import { useAppSelector } from '~/hooks/use-redux'

const preloadedState = {
appMain: { loading: false, authLoading: false, userRole: '', error: '' }
}
const unwrap = vi.fn().mockRejectedValue({ data: { code: 'error' } })
const loginUser = vi.fn().mockReturnValue({ unwrap })

vi.mock('~/containers/guest-home-page/google-button/GoogleButton', () => ({
__esModule: true,
default: function () {
return <button>Google</button>
}
}))

vi.mock('~/services/auth-service', async () => {
return {
__esModule: true,
authService: {
endpoint: { matchFulfilled: vi.fn(), matchPending: vi.fn() }
},
useLoginMutation: () => [loginUser]
}
})

vi.mock('~/hooks/use-redux', async () => {
const actual = await vi.importActual('~/hooks/use-redux')
return {
...actual,
useAppSelector: vi.fn()
}
})

describe('snackbar with extended', () => {
beforeEach(async () => {
vi.mocked(useAppSelector).mockReturnValue({
isOpened: true,
message: 'Test message; Additional info',
duration: 3000,
severity: 'info',
isExtended: true,
route: '/test-route'
})

renderWithProviders(
<TestSnackbar>
<LoginDialog />
</TestSnackbar>,
preloadedState
)
})

it('should render extended content when isExtended is true', async () => {
const mainMessage = await screen.findByText('Test message')
const additionalInfo = await screen.findByText('Additional info')
const actionButton = await screen.findByText('offerPage.createOffer.seeAll')

expect(mainMessage).toBeInTheDocument()
expect(additionalInfo).toBeInTheDocument()
expect(actionButton).toBeInTheDocument()
})
})
Loading
Loading