admin page
This commit is contained in:
2
client/src/pages/admin/main/mod.rs
Normal file
2
client/src/pages/admin/main/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
// src/pages/admin/main/mod.rs
|
||||
pub mod state;
|
||||
302
client/src/pages/admin/main/state.rs
Normal file
302
client/src/pages/admin/main/state.rs
Normal file
@@ -0,0 +1,302 @@
|
||||
// src/pages/admin/main/state.rs
|
||||
|
||||
use ratatui::widgets::ListState;
|
||||
use crate::state::pages::add_table::AddTableState;
|
||||
use crate::state::pages::add_logic::AddLogicState;
|
||||
use crate::movement::{move_focus, MovementAction};
|
||||
use crate::state::app::state::AppState;
|
||||
|
||||
// Define the focus states for the admin panel panes
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum AdminFocus {
|
||||
#[default] // Default focus is on the profiles list
|
||||
ProfilesPane,
|
||||
InsideProfilesList,
|
||||
Tables,
|
||||
InsideTablesList,
|
||||
Button1,
|
||||
Button2,
|
||||
Button3,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct AdminState {
|
||||
pub profiles: Vec<String>, // Holds profile names (used by non-admin view)
|
||||
pub profile_list_state: ListState, // Tracks navigation highlight (>) in profiles
|
||||
pub table_list_state: ListState, // Tracks navigation highlight (>) in tables
|
||||
pub selected_profile_index: Option<usize>, // Index with [*] in profiles (persistent)
|
||||
pub selected_table_index: Option<usize>, // Index with [*] in tables (persistent)
|
||||
pub current_focus: AdminFocus, // Tracks which pane is focused
|
||||
pub add_table_state: AddTableState,
|
||||
pub add_logic_state: AddLogicState,
|
||||
}
|
||||
|
||||
impl AdminState {
|
||||
/// Gets the index of the currently selected item.
|
||||
pub fn get_selected_index(&self) -> Option<usize> {
|
||||
self.profile_list_state.selected()
|
||||
}
|
||||
|
||||
/// Gets the name of the currently selected profile.
|
||||
pub fn get_selected_profile_name(&self) -> Option<&String> {
|
||||
self.profile_list_state.selected().and_then(|i| self.profiles.get(i))
|
||||
}
|
||||
|
||||
/// Populates the profile list and updates/resets the selection.
|
||||
pub fn set_profiles(&mut self, new_profiles: Vec<String>) {
|
||||
let current_selection_index = self.profile_list_state.selected();
|
||||
self.profiles = new_profiles;
|
||||
|
||||
if self.profiles.is_empty() {
|
||||
self.profile_list_state.select(None);
|
||||
} else {
|
||||
let new_selection = match current_selection_index {
|
||||
Some(index) => Some(index.min(self.profiles.len() - 1)),
|
||||
None => Some(0),
|
||||
};
|
||||
self.profile_list_state.select(new_selection);
|
||||
}
|
||||
}
|
||||
|
||||
/// Selects the next profile in the list, wrapping around.
|
||||
pub fn next(&mut self) {
|
||||
if self.profiles.is_empty() {
|
||||
self.profile_list_state.select(None);
|
||||
return;
|
||||
}
|
||||
let i = match self.profile_list_state.selected() {
|
||||
Some(i) => if i >= self.profiles.len() - 1 { 0 } else { i + 1 },
|
||||
None => 0,
|
||||
};
|
||||
self.profile_list_state.select(Some(i));
|
||||
}
|
||||
|
||||
/// Selects the previous profile in the list, wrapping around.
|
||||
pub fn previous(&mut self) {
|
||||
if self.profiles.is_empty() {
|
||||
self.profile_list_state.select(None);
|
||||
return;
|
||||
}
|
||||
let i = match self.profile_list_state.selected() {
|
||||
Some(i) => if i == 0 { self.profiles.len() - 1 } else { i - 1 },
|
||||
None => self.profiles.len() - 1,
|
||||
};
|
||||
self.profile_list_state.select(Some(i));
|
||||
}
|
||||
|
||||
/// Gets the index of the currently selected profile.
|
||||
pub fn get_selected_profile_index(&self) -> Option<usize> {
|
||||
self.profile_list_state.selected()
|
||||
}
|
||||
|
||||
/// Gets the index of the currently selected table.
|
||||
pub fn get_selected_table_index(&self) -> Option<usize> {
|
||||
self.table_list_state.selected()
|
||||
}
|
||||
|
||||
/// Selects a profile by index and resets table selection.
|
||||
pub fn select_profile(&mut self, index: Option<usize>) {
|
||||
self.profile_list_state.select(index);
|
||||
self.table_list_state.select(None);
|
||||
}
|
||||
|
||||
/// Selects a table by index.
|
||||
pub fn select_table(&mut self, index: Option<usize>) {
|
||||
self.table_list_state.select(index);
|
||||
}
|
||||
|
||||
/// Selects the next profile, wrapping around.
|
||||
/// `profile_count` should be the total number of profiles available.
|
||||
pub fn next_profile(&mut self, profile_count: usize) {
|
||||
if profile_count == 0 {
|
||||
return;
|
||||
}
|
||||
let i = match self.get_selected_profile_index() {
|
||||
Some(i) => {
|
||||
if i >= profile_count - 1 {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
self.select_profile(Some(i)); // Use the helper method
|
||||
}
|
||||
|
||||
/// Selects the previous profile, wrapping around.
|
||||
/// `profile_count` should be the total number of profiles available.
|
||||
pub fn previous_profile(&mut self, profile_count: usize) {
|
||||
if profile_count == 0 {
|
||||
return;
|
||||
}
|
||||
let i = match self.get_selected_profile_index() {
|
||||
Some(i) => {
|
||||
if i == 0 {
|
||||
profile_count - 1
|
||||
} else {
|
||||
i - 1
|
||||
}
|
||||
}
|
||||
None => 0, // Or profile_count - 1 if you prefer wrapping from None
|
||||
};
|
||||
self.select_profile(Some(i)); // Use the helper method
|
||||
}
|
||||
|
||||
/// Selects the next table, wrapping around.
|
||||
/// `table_count` should be the number of tables in the *currently selected* profile.
|
||||
pub fn next_table(&mut self, table_count: usize) {
|
||||
if table_count == 0 {
|
||||
return;
|
||||
}
|
||||
let i = match self.get_selected_table_index() {
|
||||
Some(i) => {
|
||||
if i >= table_count - 1 {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
self.select_table(Some(i));
|
||||
}
|
||||
|
||||
/// Selects the previous table, wrapping around.
|
||||
/// `table_count` should be the number of tables in the *currently selected* profile.
|
||||
pub fn previous_table(&mut self, table_count: usize) {
|
||||
if table_count == 0 {
|
||||
return;
|
||||
}
|
||||
let i = match self.get_selected_table_index() {
|
||||
Some(i) => {
|
||||
if i == 0 {
|
||||
table_count - 1
|
||||
} else {
|
||||
i - 1
|
||||
}
|
||||
}
|
||||
None => 0, // Or table_count - 1
|
||||
};
|
||||
self.select_table(Some(i));
|
||||
}
|
||||
}
|
||||
|
||||
impl AdminState {
|
||||
pub fn handle_movement(
|
||||
&mut self,
|
||||
app: &AppState,
|
||||
action: MovementAction,
|
||||
) -> bool {
|
||||
use AdminFocus::*;
|
||||
|
||||
const ORDER: [AdminFocus; 5] = [
|
||||
ProfilesPane,
|
||||
Tables,
|
||||
Button1,
|
||||
Button2,
|
||||
Button3,
|
||||
];
|
||||
|
||||
// Enter "inside" on Select
|
||||
match (self.current_focus, action) {
|
||||
(ProfilesPane, MovementAction::Select) => {
|
||||
if !app.profile_tree.profiles.is_empty()
|
||||
&& self.profile_list_state.selected().is_none()
|
||||
{
|
||||
self.profile_list_state.select(Some(0));
|
||||
}
|
||||
self.current_focus = InsideProfilesList;
|
||||
return true;
|
||||
}
|
||||
(Tables, MovementAction::Select) => {
|
||||
let p_idx = self
|
||||
.selected_profile_index
|
||||
.or_else(|| self.profile_list_state.selected());
|
||||
if let Some(pi) = p_idx {
|
||||
let len = app
|
||||
.profile_tree
|
||||
.profiles
|
||||
.get(pi)
|
||||
.map(|p| p.tables.len())
|
||||
.unwrap_or(0);
|
||||
if len > 0 && self.table_list_state.selected().is_none() {
|
||||
self.table_list_state.select(Some(0));
|
||||
}
|
||||
}
|
||||
self.current_focus = InsideTablesList;
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Inside navigation + Esc to exit; don't consume Select (admin_nav handles it)
|
||||
match self.current_focus {
|
||||
InsideProfilesList => match action {
|
||||
MovementAction::Up => {
|
||||
if !app.profile_tree.profiles.is_empty() {
|
||||
let curr = self.profile_list_state.selected().unwrap_or(0);
|
||||
let next = curr.saturating_sub(1);
|
||||
self.profile_list_state.select(Some(next));
|
||||
}
|
||||
true
|
||||
}
|
||||
MovementAction::Down => {
|
||||
let len = app.profile_tree.profiles.len();
|
||||
if len > 0 {
|
||||
let curr = self.profile_list_state.selected().unwrap_or(0);
|
||||
let next = if curr + 1 < len { curr + 1 } else { curr };
|
||||
self.profile_list_state.select(Some(next));
|
||||
}
|
||||
true
|
||||
}
|
||||
MovementAction::Esc => {
|
||||
self.current_focus = ProfilesPane;
|
||||
true
|
||||
}
|
||||
MovementAction::Next | MovementAction::Previous => true, // block outer moves
|
||||
MovementAction::Select => false,
|
||||
_ => false,
|
||||
},
|
||||
InsideTablesList => {
|
||||
let tables_len = {
|
||||
let p_idx = self
|
||||
.selected_profile_index
|
||||
.or_else(|| self.profile_list_state.selected());
|
||||
p_idx.and_then(|pi| app.profile_tree.profiles.get(pi))
|
||||
.map(|p| p.tables.len())
|
||||
.unwrap_or(0)
|
||||
};
|
||||
match action {
|
||||
MovementAction::Up => {
|
||||
if tables_len > 0 {
|
||||
let curr = self.table_list_state.selected().unwrap_or(0);
|
||||
let next = curr.saturating_sub(1);
|
||||
self.table_list_state.select(Some(next));
|
||||
}
|
||||
true
|
||||
}
|
||||
MovementAction::Down => {
|
||||
if tables_len > 0 {
|
||||
let curr = self.table_list_state.selected().unwrap_or(0);
|
||||
let next = if curr + 1 < tables_len { curr + 1 } else { curr };
|
||||
self.table_list_state.select(Some(next));
|
||||
}
|
||||
true
|
||||
}
|
||||
MovementAction::Esc => {
|
||||
self.current_focus = Tables;
|
||||
true
|
||||
}
|
||||
MovementAction::Next | MovementAction::Previous => true, // block outer moves
|
||||
MovementAction::Select => false,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Default: outer navigation via helper
|
||||
return move_focus(&ORDER, &mut self.current_focus, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
client/src/pages/admin/mod.rs
Normal file
4
client/src/pages/admin/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
// src/pages/admin/mod.rs
|
||||
pub mod main;
|
||||
|
||||
pub use main::state::{AdminState, AdminFocus};
|
||||
@@ -5,3 +5,4 @@ pub mod intro;
|
||||
pub mod login;
|
||||
pub mod register;
|
||||
pub mod forms;
|
||||
pub mod admin;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// src/pages/routing/router.rs
|
||||
use crate::state::pages::{
|
||||
admin::AdminState,
|
||||
auth::AuthState,
|
||||
add_logic::AddLogicState,
|
||||
add_table::AddTableState,
|
||||
};
|
||||
use crate::pages::admin::AdminState;
|
||||
use crate::pages::forms::FormState;
|
||||
use crate::pages::login::LoginState;
|
||||
use crate::pages::register::RegisterState;
|
||||
|
||||
Reference in New Issue
Block a user