mirror of
https://github.com/nowcommunity/nowdatabase.git
synced 2026-10-12 19:13:52 +03:00
Address field update history review
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import HistoryIcon from '@mui/icons-material/History'
|
||||
import { Box, Card, Divider, IconButton, Popover, Tooltip, Typography } from '@mui/material'
|
||||
import { createContext, useContext, useMemo, useState, type Context, type MouseEvent } from 'react'
|
||||
import { useContext, useMemo, useState, type Context, type MouseEvent } from 'react'
|
||||
import { DetailContext, type DetailContextType } from '@/components/DetailView/Context/DetailContext'
|
||||
import type { AnyReference, UpdateLog } from '@/shared/types'
|
||||
import { ReferenceList } from './ReferenceList'
|
||||
@@ -14,15 +14,13 @@ type FieldUpdate = {
|
||||
container: UpdateContainer
|
||||
}
|
||||
|
||||
const SafeDetailContext =
|
||||
(DetailContext as unknown as Context<DetailContextType<Record<string, unknown>> | null> | undefined) ??
|
||||
createContext<DetailContextType<Record<string, unknown>> | null>(null)
|
||||
const SafeDetailContext = DetailContext as unknown as Context<DetailContextType<Record<string, unknown>> | null>
|
||||
|
||||
const isObject = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
|
||||
const isUpdateLog = (value: unknown): value is UpdateLog =>
|
||||
isObject(value) && ('column_name' in value || 'old_data' in value || 'new_data' in value || 'log_action' in value)
|
||||
isObject(value) && 'column_name' in value && 'log_action' in value
|
||||
|
||||
const isUpdateContainer = (value: unknown): value is UpdateContainer =>
|
||||
isObject(value) && Array.isArray(value.updates) && value.updates.every(isUpdateLog)
|
||||
@@ -53,13 +51,6 @@ const getFieldUpdates = (data: unknown, field: string): FieldUpdate[] =>
|
||||
}))
|
||||
)
|
||||
|
||||
const formatDate = (value: unknown): string => {
|
||||
if (!value) return 'No date'
|
||||
const date = new Date(value as string | number | Date)
|
||||
if (Number.isNaN(date.getTime())) return formatValue(value)
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
const formatValue = (value: unknown): string => {
|
||||
if (value === null || value === undefined) return ''
|
||||
if (typeof value === 'string') return value
|
||||
@@ -68,6 +59,13 @@ const formatValue = (value: unknown): string => {
|
||||
return JSON.stringify(value)
|
||||
}
|
||||
|
||||
const formatDate = (value: unknown): string => {
|
||||
if (!value) return 'No date'
|
||||
const date = new Date(value as string | number | Date)
|
||||
if (Number.isNaN(date.getTime())) return formatValue(value)
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
const findFirstBySuffix = (container: UpdateContainer, suffix: string): unknown => {
|
||||
const entry = Object.entries(container).find(([key]) => key.endsWith(suffix))
|
||||
return entry?.[1]
|
||||
@@ -149,7 +147,7 @@ export const FieldUpdateHistory = ({ field, label }: { field: string; label: str
|
||||
{updates.map(({ log, container }, index) => {
|
||||
const references = collectReferences(container)
|
||||
return (
|
||||
<Card key={`${log.log_id ?? index}-${log.column_name ?? field}`} sx={{ p: 1.5 }}>
|
||||
<Card key={`${index}-${log.log_id ?? ''}-${log.column_name ?? field}`} sx={{ p: 1.5 }}>
|
||||
<Typography variant="body2">
|
||||
<b>Date:</b> {formatDate(getDate(container))}
|
||||
</Typography>
|
||||
|
||||
@@ -18,8 +18,7 @@ const reference = {
|
||||
},
|
||||
}
|
||||
|
||||
const contextValue = {
|
||||
data: {
|
||||
const detailData = {
|
||||
body_mass: 10,
|
||||
now_sau: [
|
||||
{
|
||||
@@ -39,11 +38,18 @@ const contextValue = {
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const contextValue = {
|
||||
data: detailData,
|
||||
mode: { read: true, staging: false, new: false, option: 'read' },
|
||||
} as unknown as ContextType<typeof DetailContext>
|
||||
|
||||
describe('FieldUpdateHistory', () => {
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('shows field-specific update history from detail data', async () => {
|
||||
const user = userEvent.setup()
|
||||
|
||||
@@ -77,4 +83,60 @@ describe('FieldUpdateHistory', () => {
|
||||
|
||||
expect(screen.queryByLabelText('Show update history for Diet')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not show the history icon in edit mode', () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<DetailContext.Provider
|
||||
value={{ ...contextValue, mode: { read: false, staging: false, new: false, option: 'edit' } }}
|
||||
>
|
||||
<ArrayToTable array={[['Body mass', <FieldElement key="body_mass" field="body_mass" />]]} />
|
||||
</DetailContext.Provider>
|
||||
</MemoryRouter>
|
||||
)
|
||||
|
||||
expect(screen.queryByLabelText('Show update history for Body mass')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders multiple same-field updates without duplicate key warnings', async () => {
|
||||
const user = userEvent.setup()
|
||||
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => undefined)
|
||||
const duplicateLogContext = {
|
||||
...contextValue,
|
||||
data: {
|
||||
...detailData,
|
||||
now_sau: [
|
||||
detailData.now_sau[0],
|
||||
{
|
||||
...detailData.now_sau[0],
|
||||
sau_date: '2026-05-30',
|
||||
sau_comment: 'Updated body mass again',
|
||||
updates: [
|
||||
{
|
||||
log_id: 1,
|
||||
column_name: 'body_mass',
|
||||
log_action: 3,
|
||||
old_data: '12',
|
||||
new_data: '14',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
} as unknown as ContextType<typeof DetailContext>
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<DetailContext.Provider value={duplicateLogContext}>
|
||||
<ArrayToTable array={[['Body mass', <FieldElement key="body_mass" field="body_mass" />]]} />
|
||||
</DetailContext.Provider>
|
||||
</MemoryRouter>
|
||||
)
|
||||
|
||||
await user.click(screen.getByLabelText('Show update history for Body mass'))
|
||||
|
||||
expect(screen.getByText('Updated body mass')).toBeInTheDocument()
|
||||
expect(screen.getByText('Updated body mass again')).toBeInTheDocument()
|
||||
expect(consoleError).not.toHaveBeenCalledWith(expect.stringContaining('Encountered two children with the same key'))
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user