Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions backend/fastAPI.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import List, Optional
from pydantic import BaseModel
import devices_json as dj
import users

Expand All @@ -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
Expand All @@ -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."""
Expand Down Expand Up @@ -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):
Expand Down
44 changes: 34 additions & 10 deletions backend/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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()
Expand All @@ -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

Expand All @@ -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)

Expand All @@ -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.
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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[:]
Expand All @@ -155,4 +179,4 @@ def getUpdates():

# DEBUGGING SHIT DONT MIND
# load_users()
# add_user("Aditya S", "0000")
# add_user("Aditya S", "0000", [1, 2, 3, 4])
45 changes: 30 additions & 15 deletions frontend/src/app/devices/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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) => {
Expand Down Expand Up @@ -259,19 +271,21 @@ const Devices = () => {
marginBottom: 3,
}}
>
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={handleAddDeviceClick}
sx={{
fontFamily: "JetBrains Mono",
fontWeight: 600,
textTransform: "none",
color: "white",
}}
>
Add Device
</Button>
{isSuperUser && (
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={handleAddDeviceClick}
sx={{
fontFamily: "JetBrains Mono",
fontWeight: 600,
textTransform: "none",
color: "white",
}}
>
Add Device
</Button>
)}
</Box>

<Grid container spacing={3}>
Expand Down Expand Up @@ -336,13 +350,14 @@ const Devices = () => {
}}
onClick={() => handleEdit(device.id, device.name)}
>
<EditIcon sx={{ fontSize: 20 }} />
{isSuperUser && <EditIcon sx={{ fontSize: 20 }} />}
</IconButton>

<IconButton
sx={{ position: "absolute", bottom: 8, right: 8 }}
onClick={() => handleDeleteClick(device.id)}
>
<DeleteIcon sx={{ fontSize: 20 }} />
{isSuperUser && <DeleteIcon sx={{ fontSize: 20 }} />}
</IconButton>
</CardContent>
</Card>
Expand Down
34 changes: 25 additions & 9 deletions frontend/src/app/ui/dashboard/accountMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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);

Expand All @@ -42,7 +46,6 @@ export default function AccountMenu() {
}
};

// Fetch selected user from the backend
React.useEffect(() => {
const fetchSelectedUser = async () => {
try {
Expand All @@ -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");
Expand All @@ -61,6 +66,9 @@ export default function AccountMenu() {
fetchSelectedUser();
}, []);

// Log just before rendering
// console.log("isSuperUser:", isSuperUser);

return (
<React.Fragment>
<Box sx={{ display: "flex", alignItems: "center", textAlign: "center" }}>
Expand All @@ -73,7 +81,7 @@ export default function AccountMenu() {
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
>
<Avatar sx={{ width: 32, height: 32 }}>{selectedUser.charAt(0)}</Avatar>
<Avatar sx={{ width: 32, height: 32, fontFamily: "Jetbrains Mono" }}>{selectedUser.charAt(0)}</Avatar>
</IconButton>
</Tooltip>
</Box>
Expand Down Expand Up @@ -122,15 +130,17 @@ export default function AccountMenu() {
color: "primary.main",
}}
>
<Avatar /> {selectedUser}
<Avatar sx={{ fontFamily: "Jetbrains Mono" }}>{selectedUser.charAt(0)}</Avatar> {selectedUser}
</MenuItem>
<Divider />
<MenuItem onClick={handleClose} sx={{ fontFamily: "JetBrains Mono" }}>
<ListItemIcon>
<PersonAdd fontSize="small" sx={{ color: "primary.main" }} />
</ListItemIcon>
Add another account
</MenuItem>
{isSuperUser && (
<MenuItem onClick={() => setOpenUserDialog(true)} sx={{ fontFamily: "JetBrains Mono" }}>
<ListItemIcon>
<PersonAdd fontSize="small" sx={{ color: "primary.main" }} />
</ListItemIcon>
Add another account
</MenuItem>
)}
<MenuItem onClick={handleClose} sx={{ fontFamily: "JetBrains Mono" }}>
<ListItemIcon>
<Settings fontSize="small" sx={{ color: "primary.main" }} />
Expand All @@ -144,6 +154,12 @@ export default function AccountMenu() {
Logout
</MenuItem>
</Menu>

<AddUserDialog
open={openUserDialog}
onClose={() => setOpenUserDialog(false)}
// onSave={handleSaveUser}
/>
</React.Fragment>
);
}
Loading