diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 05699b4..3ac6a81 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -26,6 +26,7 @@ use canvas::DataProvider; use crate::state::app::state::AppState; use crate::pages::admin::AdminState; use crate::state::pages::auth::AuthState; +use crate::state::pages::auth::UserRole; use crate::pages::login::LoginState; use crate::pages::register::RegisterState; use crate::pages::intro::IntroState; @@ -528,7 +529,7 @@ impl EventHandler { } } Page::Admin(state) => { - if auth_state.role.as_deref() == Some("admin") { + if matches!(auth_state.role, Some(UserRole::Admin)) { if state.handle_movement(app_state, ma) { return Ok(EventOutcome::Ok(String::new())); } @@ -545,7 +546,7 @@ impl EventHandler { // Optional page-specific handlers (non-movement or rich actions) if let Page::Admin(admin_state) = &mut router.current { - if auth_state.role.as_deref() == Some("admin") { + if matches!(auth_state.role, Some(UserRole::Admin)) { // Full admin navigation if handle_admin_navigation( key_event, @@ -961,7 +962,7 @@ impl EventHandler { "exit_highlight_mode" | "save" | "quit" | - "force_quit" | + "Force_quit" | "save_and_quit" | "revert" | "enter_decider" | diff --git a/client/src/pages/admin/main/ui.rs b/client/src/pages/admin/main/ui.rs index 4fc6e25..6f39d1a 100644 --- a/client/src/pages/admin/main/ui.rs +++ b/client/src/pages/admin/main/ui.rs @@ -12,6 +12,7 @@ use ratatui::{ widgets::{Block, BorderType, Borders, List, ListItem, Paragraph, Wrap}, Frame, }; +use crate::state::pages::auth::UserRole; use crate::pages::admin::admin::ui::render_admin_panel_admin; pub fn render_admin_panel( @@ -44,23 +45,20 @@ pub fn render_admin_panel( .constraints([Constraint::Percentage(30), Constraint::Percentage(70)]) .split(chunks[1]); - if auth_state.role.as_deref() != Some("admin") { - render_admin_panel_non_admin( - f, - admin_state, - &content_chunks, - theme, - profile_tree, - selected_profile, - ); - } else { - render_admin_panel_admin( - f, - chunks[1], - app_state, - admin_state, - theme, - ); + match auth_state.role { + Some(UserRole::Admin) => { + render_admin_panel_admin(f, chunks[1], app_state, admin_state, theme); + } + _ => { + render_admin_panel_non_admin( + f, + admin_state, + &content_chunks, + theme, + profile_tree, + selected_profile, + ); + } } } diff --git a/client/src/pages/login/logic.rs b/client/src/pages/login/logic.rs index 436e41c..d424df4 100644 --- a/client/src/pages/login/logic.rs +++ b/client/src/pages/login/logic.rs @@ -8,6 +8,7 @@ use crate::config::storage::storage::{StoredAuthData, save_auth_data}; use crate::ui::handlers::context::DialogPurpose; use common::proto::komp_ac::auth::LoginResponse; use crate::pages::login::LoginFormState; +use crate::state::pages::auth::UserRole; use anyhow::{Context, Result, anyhow}; use tokio::spawn; use tokio::sync::mpsc; @@ -56,7 +57,7 @@ pub async fn save( // Store authentication details auth_state.auth_token = Some(response.access_token.clone()); auth_state.user_id = Some(response.user_id.clone()); - auth_state.role = Some(response.role.clone()); + auth_state.role = Some(UserRole::from_str(&response.role)); auth_state.decoded_username = Some(response.username.clone()); login_state.set_has_unsaved_changes(false); @@ -180,7 +181,7 @@ pub fn handle_login_result( LoginResult::Success(response) => { auth_state.auth_token = Some(response.access_token.clone()); auth_state.user_id = Some(response.user_id.clone()); - auth_state.role = Some(response.role.clone()); + auth_state.role = Some(UserRole::from_str(&response.role)); auth_state.decoded_username = Some(response.username.clone()); let data_to_store = StoredAuthData { diff --git a/client/src/state/pages/auth.rs b/client/src/state/pages/auth.rs index 665648b..3ac3ac2 100644 --- a/client/src/state/pages/auth.rs +++ b/client/src/state/pages/auth.rs @@ -2,12 +2,44 @@ use canvas::{DataProvider, AppMode}; +/// Strongly typed user roles +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UserRole { + Admin, + Moderator, + Accountant, + Viewer, + Unknown(String), // fallback for unexpected roles +} + +impl UserRole { + pub fn from_str(s: &str) -> Self { + match s.trim().to_lowercase().as_str() { + "admin" => UserRole::Admin, + "moderator" => UserRole::Moderator, + "accountant" => UserRole::Accountant, + "viewer" => UserRole::Viewer, + other => UserRole::Unknown(other.to_string()), + } + } + + pub fn as_str(&self) -> &str { + match self { + UserRole::Admin => "admin", + UserRole::Moderator => "moderator", + UserRole::Accountant => "accountant", + UserRole::Viewer => "viewer", + UserRole::Unknown(s) => s.as_str(), + } + } +} + /// Represents the authenticated session state #[derive(Default)] pub struct AuthState { pub auth_token: Option, pub user_id: Option, - pub role: Option, + pub role: Option, pub decoded_username: Option, } diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index 8cc36f4..9225af9 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -9,6 +9,7 @@ use crate::modes::common::commands::CommandHandler; use crate::modes::handlers::event::{EventHandler, EventOutcome}; use crate::modes::handlers::mode_manager::{AppMode, ModeManager}; use crate::state::pages::auth::AuthState; +use crate::state::pages::auth::UserRole; use crate::pages::login::LoginFormState; use crate::pages::register::RegisterFormState; use crate::pages::admin::AdminState; @@ -84,7 +85,7 @@ pub async fn run_ui() -> Result<()> { Ok(Some(stored_data)) => { auth_state.auth_token = Some(stored_data.access_token); auth_state.user_id = Some(stored_data.user_id); - auth_state.role = Some(stored_data.role); + auth_state.role = Some(UserRole::from_str(&stored_data.role)); auth_state.decoded_username = Some(stored_data.username); auto_logged_in = true; info!("Auth data loaded from file. User is auto-logged in.");