-
- Current Version ({selectedVersion.versionName})
+ {selectedVersion && (
+
+
+
-
- {versions.length} Saved History
-
-
-
-
- {/* Versions List */}
-
- {versions.map((version, index) => (
-
);
diff --git a/app/src/components/layout/sidebarRight/versionHisory/VersionSaved.tsx b/app/src/components/layout/sidebarRight/versionHisory/VersionSaved.tsx
new file mode 100644
index 0000000..fb998fa
--- /dev/null
+++ b/app/src/components/layout/sidebarRight/versionHisory/VersionSaved.tsx
@@ -0,0 +1,186 @@
+import React, { useState, useEffect, useRef } from "react";
+import { useVersionStore } from "../../../../store/builder/store";
+import {
+ CloseIcon,
+ SaveIcon,
+ SaveVersionIcon,
+} from "../../../icons/ExportCommonIcons";
+import { RenameIcon } from "../../../icons/ContextMenuIcons";
+import RenderOverlay from "../../../templates/Overlay";
+
+const VersionSaved = () => {
+ const { versions, updateVersion } = useVersionStore();
+ const [isEditing, setIsEditing] = useState(false);
+ const [shouldDismiss, setShouldDismiss] = useState(false);
+ const [showNotification, setShowNotification] = useState(false);
+ const [newName, setNewName] = useState("");
+ const [description, setDescription] = useState("");
+ const prevVersionCount = useRef(versions.length);
+ const dismissTimerRef = useRef
(null);
+
+ const latestVersion = versions?.[0];
+
+ // Clear dismiss timer when component unmounts
+ useEffect(() => {
+ return () => {
+ if (dismissTimerRef.current) clearTimeout(dismissTimerRef.current);
+ };
+ }, []);
+
+ // Handle new version notification and setup dismiss timer
+ useEffect(() => {
+ if (versions.length > prevVersionCount.current) {
+ setShowNotification(true);
+ setShouldDismiss(false);
+ setIsEditing(false);
+ setNewName(versions[0].versionName ?? "");
+ setDescription(versions[0]?.description ?? "");
+
+ // Only start dismiss timer if not in edit mode
+ if (!isEditing) {
+ startDismissTimer();
+ }
+
+ prevVersionCount.current = versions.length;
+ } else if (versions.length < prevVersionCount.current) {
+ prevVersionCount.current = versions.length;
+ }
+ }, [versions, isEditing]);
+
+ // Start or restart the dismiss timer
+ const startDismissTimer = (delay = 5000) => {
+ if (dismissTimerRef.current) clearTimeout(dismissTimerRef.current);
+ dismissTimerRef.current = setTimeout(() => {
+ console.log("isEditing: ", isEditing);
+ setShouldDismiss(true);
+ }, delay);
+ };
+
+ // Hide notification after dismiss animation delay
+ useEffect(() => {
+ if (shouldDismiss) {
+ const timer = setTimeout(() => setShowNotification(false), 200);
+ return () => clearTimeout(timer);
+ }
+ }, [shouldDismiss]);
+
+ const handleEditName = () => {
+ setIsEditing(true);
+ setNewName(latestVersion?.versionName ?? "");
+ setDescription(latestVersion?.description ?? "");
+
+ // Clear any existing dismiss timer when editing starts
+ if (dismissTimerRef.current) {
+ clearTimeout(dismissTimerRef.current);
+ dismissTimerRef.current = null;
+ }
+ };
+
+ const handleFinishEdit = () => {
+ if (latestVersion) {
+ updateVersion(latestVersion.id, {
+ versionName: newName,
+ description,
+ });
+ console.log("saved");
+ startDismissTimer(); // Restart 5s timer after save
+ }
+ startDismissTimer(); // Restart 5s timer after save
+ setIsEditing(false);
+ };
+
+ const handleCancel = () => {
+ setIsEditing(false);
+ startDismissTimer(); // Restart 5s timer after cancel
+ };
+
+ const handleClose = () => {
+ setShouldDismiss(true);
+ if (dismissTimerRef.current) clearTimeout(dismissTimerRef.current);
+ };
+
+ if (!showNotification || !latestVersion) return null;
+
+ return (
+
+
+
+
+
+
+
Saved New Version
+
+
+
+
+
+
+
+
+
+
+ New Version Created {latestVersion.versionLabel}{" "}
+ {latestVersion.timestamp.toUpperCase()}
+
+
Edit name
+
+
+
+ {isEditing && (
+
+
+
+
+
+
+
setNewName(e.target.value)}
+ placeholder="Enter new version name"
+ />
+
+ by @{latestVersion.savedBy}{" "}
+ {new Date(latestVersion.timestamp).toLocaleString("en-US", {
+ month: "short",
+ day: "numeric",
+ year: "2-digit",
+ hour: "numeric",
+ minute: "2-digit",
+ })}
+
+
+
+ {/* setDescription(e.target.value)}
+ placeholder="Add description"
+ /> */}
+
+
+
+
+ Cancel
+
+
+ Save
+
+
+
+
+
+ )}
+
+ );
+};
+
+export default VersionSaved;
diff --git a/app/src/components/ui/compare/compare.tsx b/app/src/components/ui/compareVersion/Compare.tsx
similarity index 95%
rename from app/src/components/ui/compare/compare.tsx
rename to app/src/components/ui/compareVersion/Compare.tsx
index 6306520..f3d0b0b 100644
--- a/app/src/components/ui/compare/compare.tsx
+++ b/app/src/components/ui/compareVersion/Compare.tsx
@@ -1,9 +1,8 @@
-import React, { useState } from "react";
+import React from "react";
import { InfoIcon } from "../../icons/ShortcutIcons";
import { SaveDiskIcon } from "../../icons/ExportCommonIcons";
import { useCompareStore } from "../../../store/builder/store";
import OuterClick from "../../../utils/outerClick";
-import useToggleStore from "../../../store/useUIToggleStore";
interface ComparePopUpProps {
onClose: () => void;
diff --git a/app/src/components/ui/compare/CompareLayOut.tsx b/app/src/components/ui/compareVersion/CompareLayOut.tsx
similarity index 100%
rename from app/src/components/ui/compare/CompareLayOut.tsx
rename to app/src/components/ui/compareVersion/CompareLayOut.tsx
diff --git a/app/src/components/ui/menu/menu.tsx b/app/src/components/ui/menu/menu.tsx
index 745176b..d4f7989 100644
--- a/app/src/components/ui/menu/menu.tsx
+++ b/app/src/components/ui/menu/menu.tsx
@@ -4,6 +4,7 @@ import { ArrowIcon } from "../../icons/ExportCommonIcons";
import { toggleTheme } from "../../../utils/theme";
import useVersionHistoryStore, {
useShortcutStore,
+ useVersionStore,
} from "../../../store/builder/store";
import { useSubModuleStore } from "../../../store/useModuleStore";
@@ -20,6 +21,8 @@ interface MenuItem {
}
const MenuBar: React.FC = ({ setOpenMenu }) => {
+ const userName = localStorage.getItem("userName") ?? "Anonymous";
+
const navigate = useNavigate();
const [activeMenu, setActiveMenu] = useState(null);
const [activeSubMenu, setActiveSubMenu] = useState(null);
@@ -59,7 +62,32 @@ const MenuBar: React.FC = ({ setOpenMenu }) => {
File: [
{ label: "New File", shortcut: "Ctrl + N" },
{ label: "Open Local File", shortcut: "Ctrl + O" },
- { label: "Save Version" },
+ {
+ label: "Save Version",
+ action: () => {
+ const versionStore = useVersionStore.getState();
+ const versionCount = versionStore.versions.length;
+
+ const newVersion = {
+ id: crypto.randomUUID(),
+ versionLabel: `v${versionCount + 1}.0`,
+ timestamp: `${new Date().toLocaleTimeString("en-US", {
+ hour: "numeric",
+ minute: "2-digit",
+ hour12: true,
+ })} ${new Date().toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "2-digit",
+ })}`,
+
+ savedBy: userName,
+ };
+
+ console.log("newVersion: ", newVersion);
+ versionStore.addVersion(newVersion);
+ },
+ },
{ label: "Make a Copy" },
{ label: "Share" },
{ label: "Rename" },
diff --git a/app/src/modules/visualization/widgets/panel/AddButtons.tsx b/app/src/modules/visualization/widgets/panel/AddButtons.tsx
index 973154f..1055131 100644
--- a/app/src/modules/visualization/widgets/panel/AddButtons.tsx
+++ b/app/src/modules/visualization/widgets/panel/AddButtons.tsx
@@ -244,7 +244,7 @@ const AddButtons: React.FC = ({
{/* "+" Button */}
= ({
? "active"
: ""
}`}
- id="hide-panel-visulization"
+ id={`${side}-hide-panel-visulization`}
title={
hiddenPanels[selectedZone.zoneId]?.includes(side)
? "Show Panel"
@@ -301,7 +301,7 @@ const AddButtons: React.FC = ({
cleanPanel(side)}
style={{
cursor:
@@ -319,7 +319,7 @@ const AddButtons: React.FC = ({
className={`icon ${
selectedZone.lockedPanels.includes(side) ? "active" : ""
}`}
- id="lock-panel-visulization"
+ id={`${side}-lock-panel-visulization`}
title={
selectedZone.lockedPanels.includes(side)
? "Unlock Panel"
diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx
index 6a129ad..f8d0f29 100644
--- a/app/src/pages/Project.tsx
+++ b/app/src/pages/Project.tsx
@@ -15,12 +15,13 @@ import {
useLoadingProgress,
useWidgetSubOption,
useSaveVersion,
+ useVersionStore,
} from "../store/builder/store";
import { useNavigate } from "react-router-dom";
import { usePlayButtonStore } from "../store/usePlayButtonStore";
import MarketPlace from "../modules/market/MarketPlace";
import LoadingPage from "../components/templates/LoadingPage";
-import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
+import SimulationPlayer from "../components/ui/simulation/SimulationPlayer";
import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys";
import { useSelectedUserStore } from "../store/useCollabStore";
import FollowPerson from "../components/templates/FollowPerson";
@@ -34,9 +35,10 @@ import LogList from "../components/ui/log/LogList";
import Footer from "../components/footer/Footer";
import SelectFloorPlan from "../components/temporary/SelectFloorPlan";
import ControlsPlayer from "../components/layout/controls/ControlsPlayer";
-import CompareLayOut from "../components/ui/compare/CompareLayOut";
+import CompareLayOut from "../components/ui/compareVersion/CompareLayOut";
import useToggleStore from "../store/useUIToggleStore";
import RegularDropDown from "../components/ui/inputs/RegularDropDown";
+import VersionSaved from "../components/layout/sidebarRight/versionHisory/VersionSaved";
const Project: React.FC = () => {
let navigate = useNavigate();
@@ -95,6 +97,7 @@ const Project: React.FC = () => {
const { setFloatingWidget } = useFloatingWidget();
const [selectedLayout, setSelectedLayout] = useState(null); // Track selected layout
+ const { versions } = useVersionStore();
const dummyLayouts = [
{ id: 1, name: "Layout 1" },
@@ -176,6 +179,7 @@ const Project: React.FC = () => {
>
)}
+
);
};
diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts
index 34108dc..e93078a 100644
--- a/app/src/store/builder/store.ts
+++ b/app/src/store/builder/store.ts
@@ -477,15 +477,52 @@ export const useCompareStore = create