@@ -3,6 +3,8 @@ import { useTheme } from '../contexts/ThemeContext';
33import { Button , CircularProgress } from '@mui/material' ;
44import MonacoEditor from '@monaco-editor/react' ;
55import { updateDocument , getSingleDocument } from '../services/dbService' ;
6+ import { isEqual , omit } from 'lodash' ;
7+ import SaveConflictDialog from './SaveConflictDialog' ;
68
79
810interface DocumentEditViewProps {
@@ -26,17 +28,47 @@ const DocumentEditView = forwardRef<DocumentEditViewRef, DocumentEditViewProps>(
2628 const [ feedback , setFeedback ] = useState < { type : 'success' | 'error' ; message : string } | null > ( null ) ;
2729 const { theme } = useTheme ( ) ;
2830 const [ isSaving , setIsSaving ] = useState ( false ) ;
31+ const [ isConflictDialogOpen , setIsConflictDialogOpen ] = useState ( false ) ;
32+ const [ conflictServerDocStr , setConflictServerDocStr ] = useState < string > ( '' ) ;
2933
3034 useImperativeHandle ( ref , ( ) => ( {
3135 getCurrentValue : ( ) => jsonValue ,
3236 setCurrentValue : ( val : string ) => setJsonValue ( val )
3337 } ) ) ;
3438
35- const handleSave = async ( ) => {
39+ const handleSave = async ( forceSave = false ) => {
3640 setIsSaving ( true ) ;
3741 try {
3842 const parsed = JSON . parse ( jsonValue ) ;
3943 if ( ! accountId || ! databaseName || ! collection || ! docId ) throw new Error ( 'Missing DB info' ) ;
44+
45+ if ( ! forceSave ) {
46+ // Fetch the latest document from DB
47+ const refreshed = await getSingleDocument ( accountId , databaseName , collection , docId ) ;
48+
49+ // Compare with the original document prop
50+ const ignoredKeys = [ '_id' , 'datetime_creation' , 'datetime_last_modified' ] ;
51+ const oldWithoutIgnored = omit ( document , ignoredKeys ) ;
52+ const newWithoutIgnored = omit ( refreshed , ignoredKeys ) ;
53+
54+ if ( ! isEqual ( oldWithoutIgnored , newWithoutIgnored ) ) {
55+ // Sync ignored fields to match user's expected view without highlighting them
56+ const displayServerDoc = { ...refreshed } ;
57+ ignoredKeys . forEach ( key => {
58+ if ( key in parsed ) {
59+ displayServerDoc [ key ] = parsed [ key ] ;
60+ } else {
61+ delete displayServerDoc [ key ] ;
62+ }
63+ } ) ;
64+
65+ setConflictServerDocStr ( JSON . stringify ( displayServerDoc , null , 2 ) ) ;
66+ setIsConflictDialogOpen ( true ) ;
67+ setIsSaving ( false ) ;
68+ return ;
69+ }
70+ }
71+
4072 await updateDocument ( accountId , databaseName , collection , docId , parsed ) ;
4173 // Fetch the latest document after update
4274 const refreshed = await getSingleDocument ( accountId , databaseName , collection , docId ) ;
@@ -56,7 +88,7 @@ const DocumentEditView = forwardRef<DocumentEditViewRef, DocumentEditViewProps>(
5688 < div className = "flex items-center justify-between mb-2" >
5789 < h3 className = "text-lg font-bold text-slate-800 dark:text-slate-100" > Edit Document</ h3 >
5890 < div className = "flex gap-2" >
59- < Button variant = "contained" color = "success" size = "small" onClick = { handleSave } disabled = { loading || isSaving } startIcon = { isSaving ? < CircularProgress size = { 18 } color = "inherit" /> : undefined } >
91+ < Button variant = "contained" color = "success" size = "small" onClick = { ( ) => handleSave ( false ) } disabled = { loading || isSaving } startIcon = { isSaving ? < CircularProgress size = { 18 } color = "inherit" /> : undefined } >
6092 { isSaving ? 'Saving...' : 'Save' }
6193 </ Button >
6294 </ div >
@@ -96,6 +128,16 @@ const DocumentEditView = forwardRef<DocumentEditViewRef, DocumentEditViewProps>(
96128 { feedback . message }
97129 </ div >
98130 ) }
131+ < SaveConflictDialog
132+ open = { isConflictDialogOpen }
133+ serverValue = { conflictServerDocStr }
134+ localValue = { jsonValue }
135+ onClose = { ( ) => setIsConflictDialogOpen ( false ) }
136+ onOverwrite = { ( ) => {
137+ setIsConflictDialogOpen ( false ) ;
138+ handleSave ( true ) ;
139+ } }
140+ />
99141 </ div >
100142 ) ;
101143} ) ;
0 commit comments