mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
Guard finalize action when entry unchanged
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { Button, Box, Typography, CircularProgress, Divider, alpha, List, ListItemText, Tooltip } from '@mui/material'
|
||||
import SaveIcon from '@mui/icons-material/Save'
|
||||
import { useDetailContext } from './Context/DetailContext'
|
||||
import { EditDataType, Editable, Reference, Species } from '@/shared/types'
|
||||
import { useState, useEffect, Fragment } from 'react'
|
||||
import SaveIcon from '@mui/icons-material/Save'
|
||||
import { referenceValidator } from '@/shared/validators/validator'
|
||||
import { checkSpeciesTaxonomy, convertSpeciesTaxonomyFields, hasTaxonomyChanges } from '../../util/taxonomyUtilities'
|
||||
import { useGetAllSpeciesQuery, useGetAllSynonymsQuery } from '@/redux/speciesReducer'
|
||||
@@ -12,6 +12,7 @@ import { countryBoundingBoxes } from '@/country_data/countryBoundingBoxes'
|
||||
import { boundingBoxSplit, isPointInBoxes } from '@/util/isPointInBox'
|
||||
import { OutOfBoundsWarningModal, OutOfBoundsWarningModalState } from './OutOfBoundsWarningModal'
|
||||
import { skipToken } from '@reduxjs/toolkit/query'
|
||||
import { finalizeEntry } from '@/services/entryApi'
|
||||
export { ReturnButton } from '@/components/common/ReturnButton'
|
||||
|
||||
export const WriteButton = <T,>({
|
||||
@@ -111,7 +112,7 @@ export const WriteButton = <T,>({
|
||||
// @ts-expect-error Reason: Typescript doesn't recognise that references do exist. Unable to find a way around it. Fix if extra time
|
||||
}, [mode, editData.references])
|
||||
|
||||
const writeWithTaxonomyCheck = () => {
|
||||
const writeWithTaxonomyCheck = async () => {
|
||||
let speciesEditData: EditDataType<Species> | undefined = undefined
|
||||
|
||||
if (!mode.staging && taxonomy) {
|
||||
@@ -154,37 +155,37 @@ export const WriteButton = <T,>({
|
||||
}
|
||||
}
|
||||
|
||||
void onWrite((speciesEditData as EditDataType<T>) ?? editData, setEditData).then(() => {
|
||||
setMode('read')
|
||||
return
|
||||
})
|
||||
await onWrite((speciesEditData as EditDataType<T>) ?? editData, setEditData)
|
||||
setMode('read')
|
||||
}
|
||||
|
||||
const handleWriteButtonClick = () => {
|
||||
const dirtyBlocked = !mode.staging && !isDirty
|
||||
const handleWriteButtonClick = async () => {
|
||||
const finalize = async () => {
|
||||
if (taxonomy) {
|
||||
setLoading(true)
|
||||
await writeWithTaxonomyCheck()
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (dirtyBlocked) {
|
||||
notify('Please make changes before finalizing the entry.', 'info')
|
||||
return
|
||||
}
|
||||
if (!mode.staging && hasStagingMode) {
|
||||
setMode(mode.new ? 'staging-new' : 'staging-edit')
|
||||
return
|
||||
}
|
||||
|
||||
if (taxonomy) {
|
||||
setLoading(true)
|
||||
writeWithTaxonomyCheck()
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (!mode.staging && hasStagingMode) {
|
||||
setMode(mode.new ? 'staging-new' : 'staging-edit')
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
void onWrite(editData, setEditData).then(() => {
|
||||
await onWrite(editData, setEditData)
|
||||
setLoading(false)
|
||||
setMode('read')
|
||||
}
|
||||
|
||||
const result = await finalizeEntry({
|
||||
isDirty,
|
||||
finalize,
|
||||
onBlocked: () => notify('Please make changes before finalizing the entry.', 'info'),
|
||||
})
|
||||
|
||||
if (result.blocked) return
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -221,13 +222,13 @@ export const WriteButton = <T,>({
|
||||
!('dec_long' in (editData as object)) ||
|
||||
!('country' in (editData as object))
|
||||
) {
|
||||
handleWriteButtonClick()
|
||||
void handleWriteButtonClick()
|
||||
return
|
||||
}
|
||||
|
||||
const localityObject = editData as unknown as { dec_lat: number; dec_long: number; country: string }
|
||||
if (!isCoordinateOutOfBounds(localityObject.dec_lat, localityObject.dec_long, localityObject.country)) {
|
||||
handleWriteButtonClick()
|
||||
void handleWriteButtonClick()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export type FinalizeEntryParams = {
|
||||
isDirty: boolean
|
||||
finalize: () => Promise<void> | void
|
||||
onBlocked?: () => void
|
||||
}
|
||||
|
||||
export const finalizeEntry = async ({ isDirty, finalize, onBlocked }: FinalizeEntryParams) => {
|
||||
if (!isDirty) {
|
||||
onBlocked?.()
|
||||
return { blocked: true as const }
|
||||
}
|
||||
|
||||
await finalize()
|
||||
return { blocked: false as const }
|
||||
}
|
||||
@@ -38,6 +38,7 @@ const mockUseGetAllSynonymsQuery = useGetAllSynonymsQuery as jest.MockedFunction
|
||||
const mockCheckSpeciesTaxonomy = checkSpeciesTaxonomy as jest.MockedFunction<typeof checkSpeciesTaxonomy>
|
||||
|
||||
const modeEdit = { read: false, staging: false, new: false, option: 'edit' as const }
|
||||
const modeStagingEdit = { read: false, staging: true, new: false, option: 'staging-edit' as const }
|
||||
|
||||
const textField: DetailContextType<Species>['textField'] = () => <span />
|
||||
const bigTextField: DetailContextType<Species>['bigTextField'] = () => <span />
|
||||
@@ -198,4 +199,21 @@ describe('WriteButton taxonomy handling', () => {
|
||||
const button = screen.getByRole('button', { name: /save changes/i })
|
||||
expect((button as HTMLButtonElement).disabled).toBe(false)
|
||||
})
|
||||
|
||||
it('blocks finalize handler and shows feedback when dirty flag is false', async () => {
|
||||
const onWrite = jest.fn(() => Promise.resolve())
|
||||
|
||||
renderButton(baseSpecies as EditDataType<Species>, onWrite, {
|
||||
isDirty: false,
|
||||
mode: modeStagingEdit,
|
||||
})
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /complete and save/i }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onWrite).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
expect(mockNotify).toHaveBeenCalledWith('Please make changes before finalizing the entry.', 'info')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user