diff --git a/backend/fastAPI.py b/backend/fastAPI.py index 849fe1a..17b25ff 100644 --- a/backend/fastAPI.py +++ b/backend/fastAPI.py @@ -1,3 +1,5 @@ +from typing import List, Optional +from pydantic import BaseModel import devices_json as dj import users @@ -12,7 +14,6 @@ USER_DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "database", "users_db.json") # print(USER_DB_PATH) # For testing purposes -# FastAPI initialization and routes app = FastAPI() # Add CORS Middleware @@ -24,6 +25,11 @@ allow_headers=["*"], ) +class UserRequest(BaseModel): + user_name: str + user_password: str + allocated_devices: Optional[List[str]] = None + @app.on_event("startup") async def startup_event(): """Starts device updates when the FastAPI server starts.""" @@ -94,10 +100,10 @@ def get_selected_user(): """Returns the selected user""" return users.get_selected_user() -@app.post("/add_user/{user_name}/{user_password}") -def add_new_user(user_name: str, user_password: str): - """Adds a new user with the given name and password.""" - return users.add_user(user_name, user_password) +@app.post("/add_user/") +def add_new_user(user: UserRequest): + """Adds a new user with the given name, password, and optional allocated devices.""" + return users.add_user(user.user_name, user.user_password, user.allocated_devices or []) @app.delete("/delete_user/{user_name}/{user_password}") def delete_user(user_name: str, user_password: str): diff --git a/backend/users.py b/backend/users.py index 55aa01d..cfc6805 100644 --- a/backend/users.py +++ b/backend/users.py @@ -18,6 +18,7 @@ def load_devices(): except json.JSONDecodeError: return [] + def load_users(): """Load user data from users_db.json.""" if os.path.exists(USER_DB_FILE): @@ -30,12 +31,14 @@ def load_users(): return [] return [] + def save_users(users): """Save updated user data back to users_db.json.""" with open(USER_DB_FILE, "w") as f: json.dump({"users": users}, f, indent=4) -def add_user(user_name: str, user_password: str): + +def add_user(user_name: str, user_password: str, allocated_device_ids: list = None): """Adds a new user with correct role and allocated devices.""" users = load_users() available_devices = load_devices() @@ -45,7 +48,10 @@ def add_user(user_name: str, user_password: str): allocated_devices = available_devices else: user_role = "sub_user" - allocated_devices = [] + if allocated_device_ids: + allocated_devices = [str(device_id) for device_id in allocated_device_ids] + else: + allocated_devices = [] new_user_id = (max(user["user_id"] for user in users) + 1) if users else 1 @@ -57,8 +63,6 @@ def add_user(user_name: str, user_password: str): "user_role": user_role } - # print(new_user) - users.append(new_user) save_users(users) @@ -67,6 +71,7 @@ def add_user(user_name: str, user_password: str): return {"success": message, "user": new_user} + def delete_user(user_name: str, user_password: str): """Deletes a user from the system if the given password matches. If deleting the super user, assign the position to the next user. Re-indexes user IDs to maintain sequential order. @@ -99,24 +104,42 @@ def delete_user(user_name: str, user_password: str): updates.append(message) return {"success": message} + def select_user(user: str): - """Set the selected user and persist it.""" + """Set the selected user and persist it along with their role.""" global selected_user selected_user = user + + users = load_users() + selected_user_data = next((u for u in users if u["user_name"] == selected_user), None) + + if selected_user_data: + user_role = selected_user_data["user_role"] + else: + user_role = "unknown" + with open(SELECTED_USER_FILE, "w") as f: - json.dump({"selected_user": selected_user}, f) + json.dump({"selected_user": selected_user, "user_role": user_role}, f) + message = f"Logged in as {selected_user}" updates.append(message) - return {"success": message} + + return {"success": message, "selected_user": selected_user, "user_role": user_role} + def get_selected_user(): - """Retrieve the currently selected user.""" + """Retrieve the currently selected user and their role.""" global selected_user + user_role = "" + if os.path.exists(SELECTED_USER_FILE): with open(SELECTED_USER_FILE, "r") as f: data = json.load(f) selected_user = data.get("selected_user", "") - return {"selected_user": selected_user} + user_role = data.get("user_role", "") + + return {"selected_user": selected_user, "user_role": user_role} + ## USERS DEVICE MANAGEMENT ## def create_selected_user_devices_json(): @@ -147,6 +170,7 @@ def create_selected_user_devices_json(): with open(SELECTED_USER_FILE, "w") as f: json.dump({"user"}) + def getUpdates(): global updates messages = updates[:] @@ -155,4 +179,4 @@ def getUpdates(): # DEBUGGING SHIT DONT MIND # load_users() -# add_user("Aditya S", "0000") \ No newline at end of file +# add_user("Aditya S", "0000", [1, 2, 3, 4]) \ No newline at end of file diff --git a/frontend/src/app/devices/page.jsx b/frontend/src/app/devices/page.jsx index 00b7323..e786385 100644 --- a/frontend/src/app/devices/page.jsx +++ b/frontend/src/app/devices/page.jsx @@ -38,6 +38,7 @@ const Devices = () => { const [openAddDialog, setOpenAddDialog] = useState(false); const [notConnectedDevices, setNotConnectedDevices] = useState([]); const [selectedDeviceId, setSelectedDeviceId] = useState(null); + const [isSuperUser, setIsSuperUser] = useState(false); // const [selectedDeviceName, setSelectedDeviceName] = useState(""); const disappearingStyle = { @@ -46,6 +47,17 @@ const Devices = () => { }; useEffect(() => { + fetch("http://localhost:8000/selected_user") + .then((res) => res.json()) + .then((data) => { + setIsSuperUser(data.user_role === "super_user"); + }) + .catch((err) => { + console.error("Error fetching user role:", err); + setIsSuperUser(false); + }); + + // Fetch devices information fetch("http://localhost:8000/device_info") .then((res) => res.json()) .then((data) => { @@ -259,19 +271,21 @@ const Devices = () => { marginBottom: 3, }} > - + {isSuperUser && ( + + )} @@ -336,13 +350,14 @@ const Devices = () => { }} onClick={() => handleEdit(device.id, device.name)} > - + {isSuperUser && } + handleDeleteClick(device.id)} > - + {isSuperUser && } diff --git a/frontend/src/app/ui/dashboard/accountMenu.jsx b/frontend/src/app/ui/dashboard/accountMenu.jsx index c1c27f1..6794e81 100644 --- a/frontend/src/app/ui/dashboard/accountMenu.jsx +++ b/frontend/src/app/ui/dashboard/accountMenu.jsx @@ -12,6 +12,8 @@ import PersonAdd from "@mui/icons-material/PersonAdd"; import Settings from "@mui/icons-material/Settings"; import Logout from "@mui/icons-material/Logout"; +import AddUserDialog from "../newUserDialogue"; + import { signOut } from "firebase/auth"; import { auth } from "@/app/firebase/config"; @@ -21,6 +23,8 @@ export default function AccountMenu() { const router = useRouter(); const [anchorEl, setAnchorEl] = React.useState(null); const [selectedUser, setSelectedUser] = React.useState("Loading..."); + const [isSuperUser, setIsSuperUser] = React.useState(false); + const [openUserDialog, setOpenUserDialog] = React.useState(false); const open = Boolean(anchorEl); @@ -42,7 +46,6 @@ export default function AccountMenu() { } }; - // Fetch selected user from the backend React.useEffect(() => { const fetchSelectedUser = async () => { try { @@ -52,6 +55,8 @@ export default function AccountMenu() { } const data = await response.json(); setSelectedUser(data.selected_user || "Unknown User"); + setIsSuperUser(data.user_role === "super_user"); + console.log("Fetched user role:", data.user_role); } catch (error) { console.error("Error fetching user:", error); setSelectedUser("Error fetching user"); @@ -61,6 +66,9 @@ export default function AccountMenu() { fetchSelectedUser(); }, []); + // Log just before rendering + // console.log("isSuperUser:", isSuperUser); + return ( @@ -73,7 +81,7 @@ export default function AccountMenu() { aria-haspopup="true" aria-expanded={open ? "true" : undefined} > - {selectedUser.charAt(0)} + {selectedUser.charAt(0)} @@ -122,15 +130,17 @@ export default function AccountMenu() { color: "primary.main", }} > - {selectedUser} + {selectedUser.charAt(0)} {selectedUser} - - - - - Add another account - + {isSuperUser && ( + setOpenUserDialog(true)} sx={{ fontFamily: "JetBrains Mono" }}> + + + + Add another account + + )} @@ -144,6 +154,12 @@ export default function AccountMenu() { Logout + + setOpenUserDialog(false)} + // onSave={handleSaveUser} + /> ); } diff --git a/frontend/src/app/ui/newUserDialogue.jsx b/frontend/src/app/ui/newUserDialogue.jsx new file mode 100644 index 0000000..7b41dcc --- /dev/null +++ b/frontend/src/app/ui/newUserDialogue.jsx @@ -0,0 +1,153 @@ +import { useState, useEffect } from "react"; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Button, + FormControl, + InputLabel, + Select, + MenuItem, + Checkbox, + ListItemText, + Chip, + Box, +} from "@mui/material"; + +const AddUserDialog = ({ open, onClose, onSave }) => { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [devices, setDevices] = useState([]); + const [selectedDevices, setSelectedDevices] = useState([]); + + useEffect(() => { + // Fetch available devices from the API + fetch("http://localhost:8000/device_info") + .then((res) => res.json()) + .then((data) => { + const allDevices = data.smart_home_devices || []; + setDevices(allDevices); + }) + .catch((err) => console.error("Error fetching devices:", err)); + }, []); + + const handleDeviceChange = (event) => { + const { value } = event.target; + setSelectedDevices(value); + }; + + const handleSubmit = () => { + const newUser = { + user_name: username, + user_password: password, + allocated_devices: selectedDevices.map(String), // Ensure they are strings + }; + + fetch("http://localhost:8000/add_user/", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(newUser), + }) + .then((res) => res.json()) + .then((data) => { + handleClose(); + }) + .catch((err) => console.error("Error adding user:", err)); + }; + + const handleClose = () => { + setUsername(""); + setPassword(""); + setSelectedDevices([]); + onClose(); + }; + + return ( + + + Add New User + + + setUsername(e.target.value)} + sx={{ fontFamily: "Jetbrains Mono" }} + /> + setPassword(e.target.value)} + sx={{ fontFamily: "Jetbrains Mono" }} + /> + + {/* Device Selection */} + + Select Devices + + + + {/* Selected Devices as Chips */} + + {selectedDevices.map((deviceId) => { + const device = devices.find((d) => d.id === deviceId); + return ( + + setSelectedDevices((prev) => + prev.filter((id) => id !== deviceId) + ) + } + sx={{ fontFamily: "Jetbrains Mono" }} + /> + ); + })} + + + + + + + + ); +}; + +export default AddUserDialog;