Skip to content

Commit 0946efb

Browse files
committed
Added a dialog regarding unsaved changes
1 parent 45aa000 commit 0946efb

File tree

6 files changed

+186
-59
lines changed

6 files changed

+186
-59
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { FileTreeItemContextMenuStyledDialog } from '@/features/editor/components/fileTreeView/fileTreeItem';
2+
import { useStatusContext } from '@/hooks';
3+
import { Close as CloseIcon } from '@mui/icons-material';
4+
import {
5+
Box,
6+
Button,
7+
DialogActions,
8+
DialogContent,
9+
DialogTitle,
10+
Grid,
11+
IconButton,
12+
Typography,
13+
useTheme,
14+
} from '@mui/material';
15+
import { useCallback } from 'react';
16+
17+
interface EditorConfirmLeaveDialogProps {
18+
onConfirm: () => void;
19+
isOpen: boolean;
20+
onClose: () => void;
21+
}
22+
23+
export const EditorConfirmLeave: React.FC<EditorConfirmLeaveDialogProps> = ({ onConfirm, isOpen, onClose }) => {
24+
const { unsavedStateUpdate } = useStatusContext();
25+
const Theme = useTheme();
26+
27+
const handleConfirm = useCallback(() => {
28+
unsavedStateUpdate(false);
29+
onConfirm();
30+
onClose();
31+
}, [onConfirm, onClose, unsavedStateUpdate]);
32+
33+
return (
34+
<FileTreeItemContextMenuStyledDialog open={isOpen} onClose={onClose}>
35+
<Grid container spacing={2} justifyContent='center' alignItems='center'>
36+
<Grid item xs={8}>
37+
<DialogTitle sx={{ color: Theme.palette.primary.main, pl: '1.5rem', pt: '1.5rem', fontWeight: '700' }}>
38+
Unsaved changes
39+
</DialogTitle>
40+
</Grid>
41+
<Grid item xs={4}>
42+
<Box display='flex' justifyContent='flex-end'>
43+
<IconButton
44+
aria-label='close'
45+
onClick={onClose}
46+
sx={{
47+
color: Theme.palette.primary.main,
48+
mt: '0.5rem',
49+
mr: '1.5rem',
50+
}}
51+
>
52+
<CloseIcon />
53+
</IconButton>
54+
</Box>
55+
</Grid>
56+
</Grid>
57+
<DialogContent sx={{ py: 0 }}>
58+
<Typography sx={{ color: Theme.palette.text.primary, fontSize: '1rem', mb: '1rem' }}>
59+
You have unsaved changes. If you continue, your <b>changes will be lost</b>. <br />
60+
Do you wish to continue?
61+
</Typography>
62+
</DialogContent>
63+
<DialogActions>
64+
<Button onClick={onClose}>
65+
<Typography sx={{ fontSize: '1rem', color: Theme.palette.text.secondary }}>Cancel</Typography>
66+
</Button>
67+
<Button
68+
onClick={handleConfirm}
69+
variant='outlined'
70+
sx={{
71+
borderColor: Theme.palette.primary.main,
72+
':hover': { borderColor: Theme.palette.primary.main, bgcolor: Theme.palette.secondary.main },
73+
'& .MuiTouchRipple-root': {
74+
color: Theme.palette.primary.main,
75+
},
76+
}}
77+
>
78+
Continue
79+
</Button>
80+
</DialogActions>
81+
</FileTreeItemContextMenuStyledDialog>
82+
);
83+
};

app/front-end/src/features/editor/components/editorView/editorToolbar.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useStatusContext } from '@/hooks';
22
import { socket } from '@/lib';
33
import { Events } from '@/types';
44
import { Done as DoneIcon, Error as ErrorIcon } from '@mui/icons-material';
5-
import { Box, Button, CircularProgress, useTheme } from '@mui/material';
5+
import { alpha, Box, Button, CircularProgress, Typography, useTheme } from '@mui/material';
66
import {
77
GridToolbarColumnsButton,
88
GridToolbarContainer,
@@ -52,7 +52,7 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({ handleSave }) => {
5252
const [saveStatus, setSaveStatus] = useState(true);
5353

5454
const Theme = useTheme();
55-
const { blocked } = useStatusContext();
55+
const { blocked, unsavedStateUpdate, unsaved } = useStatusContext();
5656

5757
useEffect(() => {
5858
const handleWorkspaceFileSaveFeedback = (data: { status: 'success' | 'error' }) => {
@@ -73,10 +73,17 @@ export const EditorToolbar: React.FC<EditorToolbarProps> = ({ handleSave }) => {
7373
<GridToolbarDensitySelector />
7474
{/* <GridToolbarExport /> */}
7575
<Box sx={{ flexGrow: 1 }} />
76+
{unsaved && (
77+
<Typography sx={{ fontSize: '0.9rem', color: alpha(Theme.palette.text.primary, 0.5), pr: '0.5rem' }}>
78+
Changes not saved
79+
</Typography>
80+
)}
81+
7682
<Button
7783
onClick={() => {
7884
setIsSaving(true);
7985
handleSave();
86+
unsavedStateUpdate(false);
8087
}}
8188
disabled={blocked}
8289
startIcon={

app/front-end/src/features/editor/components/editorView/editorView.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ export const EditorView: React.FC = () => {
127127

128128
const onCellEditStart = () => {
129129
unsavedStateUpdate(true);
130-
console.log(unsaved);
131130
};
132131

133132
const getWorkspaceFile = useCallback(async () => {

app/front-end/src/features/editor/components/editorView/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export { EditorColumnMenu } from './editorColumnMenu';
22
export { EditorColumnMenuAggregationItem } from './editorColumnMenuAggregationItem';
33
export type { EditorColumnMenuAggregationItemProps } from './editorColumnMenuAggregationItem';
4+
export { EditorConfirmLeave } from './editorConfirmLeave';
45
export { EditorHeader } from './editorHeader';
56
export type { EditorHeaderProps } from './editorHeader';
67
export { EditorToolbar } from './editorToolbar';

app/front-end/src/features/editor/components/fileTreeView/fileTreeItem/fileTreeItem.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EditorConfirmLeave } from '@/features/editor/components/editorView';
12
import { FileTreeItemContextMenu, FileTreeItemLabel } from '@/features/editor/components/fileTreeView/fileTreeItem';
23
import { useWorkspaceContext } from '@/features/editor/hooks';
34
import { FileTypes } from '@/features/editor/types';
@@ -166,6 +167,13 @@ export const FileTreeItem = React.forwardRef(function CustomTreeItem(
166167
setContextMenuPosition({ top: 0, left: 0 });
167168
};
168169

170+
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);
171+
const { unsaved } = useStatusContext();
172+
const handleConfirm = () => {
173+
handleClick(item.id, item.label, item.fileType);
174+
setIsConfirmDialogOpen(false);
175+
};
176+
169177
return (
170178
<TreeItem2Provider itemId={itemId}>
171179
<StyledFileTreeItemRoot {...getRootProps(other)}>
@@ -174,7 +182,11 @@ export const FileTreeItem = React.forwardRef(function CustomTreeItem(
174182
onClick: (event) => {
175183
if (!blocked) {
176184
if (getContentProps().onClick) getContentProps().onClick(event);
177-
handleClick(item.id, item.label, item.fileType);
185+
if (unsaved) {
186+
setIsConfirmDialogOpen(true);
187+
} else {
188+
handleClick(item.id, item.label, item.fileType);
189+
}
178190
}
179191
},
180192

@@ -204,6 +216,11 @@ export const FileTreeItem = React.forwardRef(function CustomTreeItem(
204216
onClose={handleCloseContextMenu}
205217
/>
206218
{children && <TransitionComponent {...getGroupTransitionProps()} />}
219+
<EditorConfirmLeave
220+
isOpen={isConfirmDialogOpen}
221+
onClose={() => setIsConfirmDialogOpen(false)}
222+
onConfirm={handleConfirm}
223+
/>
207224
</StyledFileTreeItemRoot>
208225
</TreeItem2Provider>
209226
);
Lines changed: 75 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import { EditorConfirmLeave } from '@/features/editor/components/editorView';
12
import { useWorkspaceContext } from '@/features/editor/hooks';
23
import { FileModel } from '@/features/editor/types';
34
import { useStatusContext } from '@/hooks';
45
import { Close as CloseIcon } from '@mui/icons-material';
56
import { alpha, Box, IconButton, Typography, useTheme } from '@mui/material';
7+
import { useCallback, useState } from 'react';
68

79
/**
810
* `FilebarGroupItem` is a functional component that represents an individual item in the file bar group.
@@ -26,68 +28,86 @@ import { alpha, Box, IconButton, Typography, useTheme } from '@mui/material';
2628
export const FilebarGroupItem: React.FC<FileModel> = (file) => {
2729
const Theme = useTheme();
2830
const Workspace = useWorkspaceContext();
29-
const { blocked } = useStatusContext();
31+
const { blocked, unsaved } = useStatusContext();
3032

3133
const { id, label } = file;
3234

35+
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);
36+
const handleConfirm = useCallback(() => {
37+
Workspace.fileStateUpdate(file);
38+
Workspace.filesHistoryStateUpdate(file);
39+
setIsConfirmDialogOpen(false);
40+
}, [file, Workspace]);
41+
3342
return (
34-
<Box
35-
id={id}
36-
sx={{
37-
height: '100%',
38-
pl: '1rem',
39-
pr: '0.5rem',
40-
bgcolor: Workspace.file.id === id ? Theme.palette.background.default : Theme.palette.action.selected,
41-
borderRadius: '0rem',
42-
':hover': {
43-
backgroundColor:
44-
Workspace.file.id === id
45-
? Theme.palette.background.default
46-
: blocked
47-
? Theme.palette.action.selected
48-
: alpha(Theme.palette.background.default, 0.5),
49-
},
50-
cursor: blocked ? 'default' : 'pointer',
51-
display: 'flex',
52-
flexDirection: 'row',
53-
alignItems: 'center',
54-
gap: '0.5rem',
55-
}}
56-
onClick={() => {
57-
// Update the workspace to the selected file
58-
if (!blocked) {
59-
Workspace.fileStateUpdate(file);
60-
Workspace.filesHistoryStateUpdate(file);
61-
}
62-
}}
63-
>
64-
<Typography
43+
<>
44+
<Box
45+
id={id}
6546
sx={{
66-
fontSize: 12,
67-
fontWeight: 'bold',
68-
textTransform: 'none',
69-
maxWidth: '10rem',
70-
overflow: 'hidden',
71-
textOverflow: 'ellipsis',
72-
whiteSpace: 'nowrap',
73-
}}
74-
>
75-
{label}
76-
</Typography>
77-
<IconButton
78-
size='small'
79-
disabled={blocked}
80-
onClick={(event) => {
81-
event.stopPropagation();
47+
height: '100%',
48+
pl: '1rem',
49+
pr: '0.5rem',
50+
bgcolor: Workspace.file.id === id ? Theme.palette.background.default : Theme.palette.action.selected,
51+
borderRadius: '0rem',
52+
':hover': {
53+
backgroundColor:
54+
Workspace.file.id === id
55+
? Theme.palette.background.default
56+
: blocked
57+
? Theme.palette.action.selected
58+
: alpha(Theme.palette.background.default, 0.5),
59+
},
60+
cursor: blocked ? 'default' : 'pointer',
61+
display: 'flex',
62+
flexDirection: 'row',
63+
alignItems: 'center',
64+
gap: '0.5rem',
8265
}}
83-
onMouseDown={(event) => {
84-
event.stopPropagation();
85-
// Remove the file from the workspace
86-
Workspace.filesHistoryStateUpdate(undefined, file);
66+
onClick={() => {
67+
// Update the workspace to the selected file
68+
if (!blocked) {
69+
if (unsaved) {
70+
setIsConfirmDialogOpen(true);
71+
} else {
72+
Workspace.fileStateUpdate(file);
73+
Workspace.filesHistoryStateUpdate(file);
74+
}
75+
}
8776
}}
8877
>
89-
<CloseIcon sx={{ fontSize: 12, color: Theme.palette.text.primary }} />
90-
</IconButton>
91-
</Box>
78+
<Typography
79+
sx={{
80+
fontSize: 12,
81+
fontWeight: 'bold',
82+
textTransform: 'none',
83+
maxWidth: '10rem',
84+
overflow: 'hidden',
85+
textOverflow: 'ellipsis',
86+
whiteSpace: 'nowrap',
87+
}}
88+
>
89+
{label}
90+
</Typography>
91+
<IconButton
92+
size='small'
93+
disabled={blocked}
94+
onClick={(event) => {
95+
event.stopPropagation();
96+
}}
97+
onMouseDown={(event) => {
98+
event.stopPropagation();
99+
// Remove the file from the workspace
100+
Workspace.filesHistoryStateUpdate(undefined, file);
101+
}}
102+
>
103+
<CloseIcon sx={{ fontSize: 12, color: Theme.palette.text.primary }} />
104+
</IconButton>
105+
</Box>
106+
<EditorConfirmLeave
107+
isOpen={isConfirmDialogOpen}
108+
onClose={() => setIsConfirmDialogOpen(false)}
109+
onConfirm={handleConfirm}
110+
/>
111+
</>
92112
);
93113
};

0 commit comments

Comments
 (0)