Save point, Sidebar prevents navigation without first saving or cancelling current changes

This commit is contained in:
Daniel S. Hansen
2025-07-31 12:23:45 -07:00
parent 90704879e1
commit 364ce0b752
4 changed files with 51 additions and 18 deletions
+10 -2
View File
@@ -12,7 +12,7 @@ import SettingsCanvas from "./views/SettingsCanvas"
import { DashboardCanvas } from "./views/DashboardCanvas"; import { DashboardCanvas } from "./views/DashboardCanvas";
import { DASHBOARD_VIEWS } from "@/constants/viewKeys"; import { DASHBOARD_VIEWS } from "@/constants/viewKeys";
export default function Canvas({ view, setView, layout, setLayout, onLayoutChange, darkMode }) { export default function Canvas({ isEditable, setIsEditable, isDelete, setIsDelete, view, setView, layout, setLayout, originalLayout, setOriginalLayout, onLayoutChange, onChangeView, darkMode }) {
const recipe = useRecipe({ key: "canvas" }); const recipe = useRecipe({ key: "canvas" });
const styles = recipe(); const styles = recipe();
@@ -21,11 +21,19 @@ export default function Canvas({ view, setView, layout, setLayout, onLayoutChang
{/* Check if view is a tab inside the Canvas Header */} {/* Check if view is a tab inside the Canvas Header */}
{ DASHBOARD_VIEWS.includes(view) && ( { DASHBOARD_VIEWS.includes(view) && (
<DashboardCanvas <DashboardCanvas
darkMode={ darkMode }
view={ view } view={ view }
setView={ setView } setView={ setView }
layout={ layout } layout={ layout }
setLayout={ setLayout } setLayout={ setLayout }
originalLayout={ originalLayout }
setOriginalLayout={ setOriginalLayout }
onLayoutChange={ onLayoutChange } onLayoutChange={ onLayoutChange }
onChangeView={ onChangeView }
isEditable={ isEditable }
setIsEditable={ setIsEditable }
isDelete={ isDelete }
setIsDelete={ setIsDelete }
/> />
)} )}
{view === "global" && <GlobalMapCanvas />} {view === "global" && <GlobalMapCanvas />}
@@ -11,11 +11,11 @@ import { CancelButton } from "../components/CancelButton";
import { SaveButton } from "../components/SaveButton"; import { SaveButton } from "../components/SaveButton";
import DeleteAllButton from "../components/DeleteAllButton"; import DeleteAllButton from "../components/DeleteAllButton";
export function DashboardCanvas({ view, setView, layout, setLayout, onLayoutChange }) { export function DashboardCanvas({ isEditable, setIsEditable, isDelete, setIsDelete, view, setView, layout, setLayout, originalLayout, setOriginalLayout, onLayoutChange, onChangeView }) {
const [isEditable, setIsEditable] = useState(false); // editable=true means static=false (vice versa) // const [isEditable, setIsEditable] = useState(false); // editable=true means static=false (vice versa)
const [isDelete, setIsDelete] = useState(false); // const [isDelete, setIsDelete] = useState(false);
const [newCounter, setNewCounter] = useState(3); const [newCounter, setNewCounter] = useState(3);
const [originalLayout, setOriginalLayout] = useState([]); // const [originalLayout, setOriginalLayout] = useState([]);
const onAddWidget = () => { const onAddWidget = () => {
const newId = newCounter.toString(); const newId = newCounter.toString();
@@ -100,15 +100,15 @@ export function DashboardCanvas({ view, setView, layout, setLayout, onLayoutChan
// Prevent navigation away from 'My Dashboard' while in Edit mode // Prevent navigation away from 'My Dashboard' while in Edit mode
onValueChange={(e) => { onValueChange={(e) => {
const nextView = e.value; const nextView = e.value;
onChangeView(nextView);
if (view === "widget" && isEditable && nextView !== "widget") { // if (view === "widget" && isEditable && nextView !== "widget") {
const confirmed = window.confirm( // const confirmed = window.confirm(
"You have unsaved changes. Do you wish to discard them and continue?" // "You have unsaved changes. Do you wish to discard them and continue?"
); // );
if (!confirmed) return; // if (!confirmed) return;
onCancel(); // onCancel();
} // }
setView(nextView); // setView(nextView);
}} }}
> >
<Flex <Flex
+26 -2
View File
@@ -10,7 +10,9 @@ export default function Dashboard() {
const styles = recipe(); const styles = recipe();
const [darkMode, setDarkMode] = useState(false); const [darkMode, setDarkMode] = useState(false);
// Lifted State - defaults to widget dashboard const [isEditable, setIsEditable] = useState(false); // editable=true means static=false (vice versa)
const [isDelete, setIsDelete] = useState(false);
const [originalLayout, setOriginalLayout] = useState([]);
const [view, setView] = useState("widget"); const [view, setView] = useState("widget");
const [layout, setLayout] = useState([ const [layout, setLayout] = useState([
{ i: '0', x: 0, y: 0, w: 3, h: 3, minH: 3, minW: 1, static: true }, { i: '0', x: 0, y: 0, w: 3, h: 3, minH: 3, minW: 1, static: true },
@@ -18,6 +20,18 @@ export default function Dashboard() {
{ i: '2', x: 6, y: 0, w: 3, h: 3, minH: 3, minW: 1, static: true } { i: '2', x: 6, y: 0, w: 3, h: 3, minH: 3, minW: 1, static: true }
]); ]);
// Prevent navigation while in Edit mode without saving or cancelling
const handleViewChange = (nextView) => {
if (view === "widget" && isEditable) {
const confirmed = window.confirm(
"You have unsaved changes. Discard them and continue?"
);
if (!confirmed) return;
onCancel(); // Discards the layout
}
setView(nextView);
};
return ( return (
<> <>
<Grid css={styles} <Grid css={styles}
@@ -30,6 +44,7 @@ export default function Dashboard() {
<Header <Header
view={ view } view={ view }
setView={ setView } setView={ setView }
onChangeView={ handleViewChange }
/> />
</GridItem> </GridItem>
<GridItem colSpan={1}> <GridItem colSpan={1}>
@@ -37,7 +52,9 @@ export default function Dashboard() {
darkMode={ darkMode } darkMode={ darkMode }
setDarkMode={ setDarkMode } setDarkMode={ setDarkMode }
view={ view } view={ view }
onChangeView={ setView } onChangeView={ handleViewChange }
isEditabe={ isEditable }
// onChangeView={ setView }
/> />
</GridItem> </GridItem>
<GridItem colSpan={1} width="100%" overflow="auto"> <GridItem colSpan={1} width="100%" overflow="auto">
@@ -47,7 +64,14 @@ export default function Dashboard() {
setView={ setView } setView={ setView }
layout={ layout } layout={ layout }
setLayout={ setLayout } setLayout={ setLayout }
originalLayout={ originalLayout }
setOriginalLayout={ setOriginalLayout }
onLayoutChange={( newLayout ) => setLayout( newLayout )} onLayoutChange={( newLayout ) => setLayout( newLayout )}
onChangeView={ handleViewChange }
isEditable={ isEditable }
setIsEditable={ setIsEditable }
isDelete={ isDelete }
setIsDelete={ setIsDelete }
/> />
</GridItem> </GridItem>
</Grid> </Grid>
@@ -8,7 +8,7 @@ const user = {
// avatar: "src\\assets\\user_profile.svg" // avatar: "src\\assets\\user_profile.svg"
} }
export default function Header({ view, setView }) { export default function Header({ view, setView, onChangeView }) {
const recipe = useRecipe({ key: "header" }); const recipe = useRecipe({ key: "header" });
const styles = recipe(); const styles = recipe();
@@ -25,6 +25,7 @@ export default function Header({ view, setView }) {
avatar={user.avatar} avatar={user.avatar}
view={ view } view={ view }
setView={ setView } setView={ setView }
onChangeView={ onChangeView }
/> />
</Flex> </Flex>
</chakra.div> </chakra.div>