@@ -17,7 +17,7 @@ def load_db():
1717 if not os .path .exists (DB_FILE ):
1818 default_data = {
1919 "role_db" : {"armasupplyguy@gmail.com" : "SUPER_ADMIN" },
20- "usernames" : {"armasupplyguy@gmail.com" : "ArmaSupplyGuy" }, # Default Username
20+ "usernames" : {"armasupplyguy@gmail.com" : "ArmaSupplyGuy" },
2121 "passwords" : {"armasupplyguy@gmail.com" : SYSTEM_PASSWORD },
2222 "mods" : [],
2323 "events" : [],
@@ -31,7 +31,6 @@ def load_db():
3131 try :
3232 with open (DB_FILE , 'r' ) as f :
3333 data = json .load (f )
34- # Migration check: Ensure 'usernames' exists for old databases
3534 if "usernames" not in data :
3635 data ["usernames" ] = {}
3736 return data
@@ -95,7 +94,6 @@ def save_db(data):
9594
9695 with signup_tab :
9796 with st .container (border = True ):
98- # NEW FIELD: USERNAME
9997 new_user = st .text_input ("Username" , key = "sign_user" )
10098 new_email = st .text_input ("New Email" , key = "sign_email" )
10199 new_pass = st .text_input ("New Password" , type = "password" , key = "sign_pwd" )
@@ -109,18 +107,17 @@ def save_db(data):
109107 elif new_email and new_pass and new_user :
110108 DB ['role_db' ][new_email ] = "staff"
111109 DB ['passwords' ][new_email ] = new_pass
112- DB ['usernames' ][new_email ] = new_user # Save Username
110+ DB ['usernames' ][new_email ] = new_user
113111 save_db (DB )
114112 st .success ("Account created! Please login." )
115113 else :
116- st .warning ("All fields (including Username) are required." )
114+ st .warning ("All fields are required." )
117115 st .stop ()
118116
119117# =========================================================
120118# MAIN APP
121119# =========================================================
122120USER_EMAIL = st .session_state .current_user
123- # Fallback for legacy users who might not have a username set
124121USER_NAME = DB ['usernames' ].get (USER_EMAIL , USER_EMAIL .split ('@' )[0 ])
125122user_role = DB ['role_db' ].get (USER_EMAIL , "staff" )
126123
@@ -134,7 +131,7 @@ def get_mod_status():
134131
135132# --- SIDEBAR ---
136133st .sidebar .title ("🛠 Staff Portal" )
137- st .sidebar .write (f"User: **{ USER_NAME } **" ) # Show Username, not email
134+ st .sidebar .write (f"User: **{ USER_NAME } **" )
138135if st .sidebar .button ("🚪 Logout" ):
139136 st .session_state .logged_in = False
140137 st .rerun ()
@@ -160,15 +157,28 @@ def get_mod_status():
160157 st .sidebar .divider ()
161158 st .sidebar .button ("🔑 Assign Roles" , on_click = navigate_to , args = ("roles" ,))
162159
163- # --- TOP NAV (HIDDEN FOR STAFF) ---
160+ # --- DYNAMIC TOP NAV ---
161+ # Logic: Staff sees NOTHING. Admins see EVERYTHING. CLP sees LIMITED.
164162if user_role != "staff" :
165- cols = st .columns (6 )
166- with cols [0 ]: st .button ("Broken Mods" , use_container_width = True , on_click = navigate_to , args = ("view_broken_mods" ,))
167- with cols [1 ]: st .button ("Fixed" , use_container_width = True , on_click = navigate_to , args = ("view_fixed_mods" ,))
168- with cols [2 ]: st .button ("Tutorials" , use_container_width = True , on_click = navigate_to , args = ("view_tutorials" ,))
169- with cols [3 ]: st .button ("Training Schedules" , use_container_width = True , on_click = navigate_to , args = ("view_events" ,))
170- with cols [4 ]: st .button ("Events" , use_container_width = True , on_click = navigate_to , args = ("view_events" ,))
171- with cols [5 ]: st .button ("Users" , use_container_width = True , on_click = navigate_to , args = ("view_users" ,))
163+ menu_items = []
164+
165+ # 1. Admin Only Items
166+ if user_role in ["admin" , "SUPER_ADMIN" ]:
167+ menu_items .append ({"label" : "Broken Mods" , "page" : "view_broken_mods" })
168+ menu_items .append ({"label" : "Fixed" , "page" : "view_fixed_mods" })
169+
170+ # 2. Common Items (CLP, CLPLEAD, Admin, Super Admin)
171+ menu_items .append ({"label" : "Tutorials" , "page" : "view_tutorials" })
172+ menu_items .append ({"label" : "Training Schedules" , "page" : "view_events" })
173+ menu_items .append ({"label" : "Events" , "page" : "view_events" })
174+ menu_items .append ({"label" : "Users" , "page" : "view_users" })
175+
176+ # Render the Menu
177+ cols = st .columns (len (menu_items ))
178+ for i , item in enumerate (menu_items ):
179+ with cols [i ]:
180+ st .button (item ["label" ], use_container_width = True , on_click = navigate_to , args = (item ["page" ],))
181+
172182 st .markdown ("---" )
173183
174184# --- PAGES ---
@@ -215,26 +225,34 @@ def get_mod_status():
215225 st .rerun ()
216226
217227elif st .session_state .page == "view_broken_mods" :
218- st .title ("Active Broken Mods" )
219- active = [m for m in DB ['mods' ] if not m ['complete' ]]
220- if not active : st .success ("No active issues." )
221- for m in active :
222- with st .container (border = True ):
223- c1 , c2 = st .columns ([5 ,1 ])
224- with c1 :
225- st .subheader (f"⚠️ { m ['name' ]} " )
226- st .caption (f"Severity: { m ['severity' ]} | Assigned: { m ['assignment' ]} " )
227- with c2 : st .button ("Details" , key = f"d_{ m ['id' ]} " , on_click = navigate_to , args = ("mod_detail" , m ['id' ]))
228+ # DOUBLE CHECK ACCESS (Security Layer)
229+ if user_role not in ["admin" , "SUPER_ADMIN" ]:
230+ st .error ("Access Denied." )
231+ else :
232+ st .title ("Active Broken Mods" )
233+ active = [m for m in DB ['mods' ] if not m ['complete' ]]
234+ if not active : st .success ("No active issues." )
235+ for m in active :
236+ with st .container (border = True ):
237+ c1 , c2 = st .columns ([5 ,1 ])
238+ with c1 :
239+ st .subheader (f"⚠️ { m ['name' ]} " )
240+ st .caption (f"Severity: { m ['severity' ]} | Assigned: { m ['assignment' ]} " )
241+ with c2 : st .button ("Details" , key = f"d_{ m ['id' ]} " , on_click = navigate_to , args = ("mod_detail" , m ['id' ]))
228242
229243elif st .session_state .page == "view_fixed_mods" :
230- st .title ("Fixed Mods Archive" )
231- fixed = [m for m in DB ['mods' ] if m ['complete' ]]
232- if not fixed : st .info ("Empty archive." )
233- for m in fixed :
234- with st .container (border = True ):
235- c1 , c2 = st .columns ([5 ,1 ])
236- with c1 : st .subheader (f"✅ { m ['name' ]} " )
237- with c2 : st .button ("Archive View" , key = f"a_{ m ['id' ]} " , on_click = navigate_to , args = ("mod_detail" , m ['id' ]))
244+ # DOUBLE CHECK ACCESS
245+ if user_role not in ["admin" , "SUPER_ADMIN" ]:
246+ st .error ("Access Denied." )
247+ else :
248+ st .title ("Fixed Mods Archive" )
249+ fixed = [m for m in DB ['mods' ] if m ['complete' ]]
250+ if not fixed : st .info ("Empty archive." )
251+ for m in fixed :
252+ with st .container (border = True ):
253+ c1 , c2 = st .columns ([5 ,1 ])
254+ with c1 : st .subheader (f"✅ { m ['name' ]} " )
255+ with c2 : st .button ("Archive View" , key = f"a_{ m ['id' ]} " , on_click = navigate_to , args = ("mod_detail" , m ['id' ]))
238256
239257elif st .session_state .page == "mod_detail" :
240258 m = next ((x for x in DB ['mods' ] if x ['id' ] == st .session_state .selected_mod_id ), None )
@@ -246,19 +264,24 @@ def get_mod_status():
246264 if m .get ('json_data' ): st .code (m ['json_data' ], language = 'json' )
247265 st .markdown (m ['description' ], unsafe_allow_html = True )
248266 st .divider ()
249- if not m ['complete' ]:
250- if st .button ("✅ Mark Resolved" , type = "primary" ):
251- m ['complete' ] = True
252- save_db (DB )
253- st .success ("Resolved!" )
254- st .session_state .page = "view_fixed_mods"
255- st .rerun ()
256- else :
257- st .success ("Resolved." )
258- if st .button ("Re-open" ):
259- m ['complete' ] = False
260- save_db (DB )
261- st .rerun ()
267+ # Only Admins can Resolve
268+ if user_role in ["admin" , "SUPER_ADMIN" ]:
269+ if not m ['complete' ]:
270+ if st .button ("✅ Mark Resolved" , type = "primary" ):
271+ m ['complete' ] = True
272+ save_db (DB )
273+ st .success ("Resolved!" )
274+ st .session_state .page = "view_fixed_mods"
275+ st .rerun ()
276+ else :
277+ st .success ("Resolved." )
278+ if st .button ("Re-open" ):
279+ m ['complete' ] = False
280+ save_db (DB )
281+ st .rerun ()
282+ elif m ['complete' ]:
283+ st .success ("This issue is Resolved." )
284+
262285 with c2 :
263286 st .subheader ("Discussion" )
264287 chat = st .container (height = 400 , border = True )
@@ -320,37 +343,47 @@ def get_mod_status():
320343 st .subheader (t ['title' ])
321344 st .markdown (t ['content' ], unsafe_allow_html = True )
322345
323- # --- VIEW USERS (UPDATED: USERNAMES & PRIVACY) ---
324346elif st .session_state .page == "view_users" :
325347 st .title ("Staff Roster" )
326348 for email , role in DB ['role_db' ].items ():
327349 with st .container (border = True ):
328350 c1 , c2 , c3 = st .columns ([1 ,4 ,2 ])
329-
330- # 1. Get Username (Default to 'Unknown' if missing)
331351 u_name = DB .get ('usernames' , {}).get (email , "Unknown User" )
332-
333352 with c1 : st .write ("👤" )
334353 with c2 :
335- # 2. Show Username Main
336354 st .subheader (u_name )
337- # 3. Restrict Email Visibility
338355 if user_role == "SUPER_ADMIN" :
339- st .caption (f"Email: { email } " ) # Only Super Admin sees this
356+ st .caption (f"Email: { email } " )
340357 st .caption (f"Role: { role } " )
341358 with c3 :
342359 st .write ("🟢 Online" if email == USER_EMAIL else "⚪ Offline" )
343360
344361elif st .session_state .page == "roles" :
345362 st .title ("Role Management" )
346- u_email = st .text_input ("Email to Update" )
347- u_role = st .selectbox ("New Role" , ["admin" , "CLPLEAD" , "CLP" , "staff" ])
348- if st .button ("Update" ):
349- if u_email in DB ['role_db' ]:
350- DB ['role_db' ][u_email ] = u_role
351- save_db (DB )
352- st .success ("Updated!" )
353- else :
354- st .error ("User not found." )
363+
364+ with st .container (border = True ):
365+ st .subheader ("Update User Role" )
366+ u_email = st .text_input ("User Email to Update" )
367+ u_role = st .selectbox ("New Role" , ["admin" , "CLPLEAD" , "CLP" , "staff" ])
368+ if st .button ("Update Role" ):
369+ if u_email in DB ['role_db' ]:
370+ DB ['role_db' ][u_email ] = u_role
371+ save_db (DB )
372+ st .success ("Updated!" )
373+ else :
374+ st .error ("User not found." )
375+
376+ with st .expander ("❌ Delete User (Danger Zone)" ):
377+ st .warning ("This action cannot be undone." )
378+ del_email = st .text_input ("Enter Email to Delete" )
379+ if st .button ("Permanently Delete User" , type = "primary" ):
380+ if del_email in DB ['role_db' ]:
381+ del DB ['role_db' ][del_email ]
382+ if del_email in DB ['passwords' ]: del DB ['passwords' ][del_email ]
383+ if del_email in DB ['usernames' ]: del DB ['usernames' ][del_email ]
384+ save_db (DB )
385+ st .success (f"User { del_email } deleted." )
386+ else :
387+ st .error ("User not found." )
355388
356389 st .table (pd .DataFrame (DB ['role_db' ].items (), columns = ["Email" , "Role" ]))
0 commit comments