215 lines
7.1 KiB
Rust
215 lines
7.1 KiB
Rust
use crate::azure::models::Application;
|
|
use crate::state::AppState;
|
|
use crate::ui::components::{
|
|
show_error_message, show_loading_spinner, show_section_header, show_success_message, show_text,
|
|
};
|
|
use egui::{Context, ScrollArea, Ui};
|
|
|
|
pub fn show_app_list_view(ctx: &Context, state: &mut AppState, config: &crate::config::Config) -> Option<AppListAction> {
|
|
let mut action = None;
|
|
|
|
// Handle keyboard navigation for app list
|
|
// Check early, before text edits might consume the input
|
|
let any_text_focused = ctx.memory(|m| m.focused().is_some());
|
|
|
|
if !any_text_focused {
|
|
let filtered_apps = state.filtered_applications();
|
|
|
|
if !filtered_apps.is_empty() {
|
|
let current_idx = state.selected_app.as_ref()
|
|
.and_then(|sel| filtered_apps.iter().position(|a| a.id == sel.id));
|
|
|
|
ctx.input(|i| {
|
|
let mut target_idx = None;
|
|
|
|
if i.key_pressed(egui::Key::ArrowDown) {
|
|
target_idx = Some(current_idx.map_or(0, |idx| (idx + 1) % filtered_apps.len()));
|
|
}
|
|
|
|
if i.key_pressed(egui::Key::ArrowUp) {
|
|
target_idx = Some(current_idx.map_or(
|
|
filtered_apps.len() - 1,
|
|
|idx| if idx > 0 { idx - 1 } else { filtered_apps.len() - 1 }
|
|
));
|
|
}
|
|
|
|
if let Some(idx) = target_idx {
|
|
if let Some(app) = filtered_apps.get(idx) {
|
|
action = Some(AppListAction::SelectApp(app.clone()));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
egui::TopBottomPanel::top("top_panel")
|
|
.frame(egui::Frame::none()
|
|
.fill(egui::Color32::TRANSPARENT)
|
|
.stroke(egui::Stroke::NONE)
|
|
.inner_margin(egui::Margin::symmetric(20.0, 10.0)))
|
|
.show_separator_line(false)
|
|
.show(ctx, |ui| {
|
|
ui.horizontal(|ui| {
|
|
show_section_header(ui, "App Registrations");
|
|
|
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
|
if ui.button("Sign Out").clicked() {
|
|
action = Some(AppListAction::SignOut);
|
|
}
|
|
|
|
ui.add_space(10.0); // Replace separator with space
|
|
|
|
if ui.button("🔄 Refresh").clicked() {
|
|
action = Some(AppListAction::Refresh);
|
|
}
|
|
});
|
|
});
|
|
|
|
if let Some(user) = &state.user_info {
|
|
show_text(ui, &format!("Logged in as: {}", user.display_name));
|
|
}
|
|
});
|
|
|
|
egui::CentralPanel::default()
|
|
.frame(egui::Frame::none()
|
|
.fill(egui::Color32::TRANSPARENT)
|
|
.inner_margin(egui::Margin {
|
|
left: 20.0,
|
|
right: 20.0,
|
|
top: 20.0,
|
|
bottom: 80.0, // Extra margin so cards don't show behind bottom panel
|
|
}))
|
|
.show(ctx, |ui| {
|
|
// Show messages
|
|
if let Some(error) = &state.error_message {
|
|
show_error_message(ui, error);
|
|
ui.add_space(10.0);
|
|
}
|
|
|
|
if let Some(success) = &state.success_message {
|
|
show_success_message(ui, success);
|
|
ui.add_space(10.0);
|
|
}
|
|
|
|
// Show loading spinner if loading
|
|
if state.operations.load_applications.is_some() {
|
|
show_loading_spinner(ui, "Loading applications...");
|
|
return;
|
|
}
|
|
|
|
// Search box
|
|
ui.horizontal(|ui| {
|
|
show_text(ui, "Search:");
|
|
ui.text_edit_singleline(&mut state.app_search_filter);
|
|
});
|
|
ui.add_space(10.0);
|
|
|
|
let filtered_apps = state.filtered_applications();
|
|
|
|
if filtered_apps.is_empty() {
|
|
ui.centered_and_justified(|ui| {
|
|
if state.applications.is_empty() {
|
|
show_text(ui, "No app registrations found. Click Refresh to load.");
|
|
} else {
|
|
show_text(ui, "No matching applications found.");
|
|
}
|
|
});
|
|
} else {
|
|
show_text(ui, &format!("Found {} applications", filtered_apps.len()));
|
|
ui.add_space(5.0);
|
|
|
|
ScrollArea::vertical()
|
|
.auto_shrink([false, false])
|
|
.show(ui, |ui| {
|
|
ui.set_width(ui.available_width() - 20.0); // Add margin for scrollbar
|
|
for app in filtered_apps {
|
|
show_app_card(ui, &app, &state.selected_app, &mut action, config);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
egui::TopBottomPanel::bottom("bottom_panel")
|
|
.frame(egui::Frame::none()
|
|
.fill(egui::Color32::TRANSPARENT)
|
|
.stroke(egui::Stroke::NONE)
|
|
.inner_margin(egui::Margin::symmetric(20.0, 15.0)))
|
|
.show_separator_line(false)
|
|
.show(ctx, |ui| {
|
|
ui.horizontal(|ui| {
|
|
let is_app_selected = state.selected_app.is_some();
|
|
|
|
if ui
|
|
.add_enabled(is_app_selected, egui::Button::new("Create Secret").min_size(egui::vec2(120.0, 32.0)))
|
|
.clicked()
|
|
{
|
|
action = Some(AppListAction::CreateSecret);
|
|
}
|
|
|
|
if !is_app_selected {
|
|
show_text(ui, "Select an app registration to create a secret");
|
|
}
|
|
});
|
|
});
|
|
|
|
action
|
|
}
|
|
|
|
fn show_app_card(
|
|
ui: &mut Ui,
|
|
app: &Application,
|
|
selected_app: &Option<Application>,
|
|
action: &mut Option<AppListAction>,
|
|
config: &crate::config::Config,
|
|
) {
|
|
let is_selected = selected_app
|
|
.as_ref()
|
|
.map(|selected| selected.id == app.id)
|
|
.unwrap_or(false);
|
|
|
|
// Get card colors from config with fallback to defaults
|
|
let card_colors = config.colors.as_ref()
|
|
.map(|c| &c.cards.app_list);
|
|
|
|
let frame = if is_selected {
|
|
let bg = card_colors.map(|c| c.selected_bg).unwrap_or([60, 60, 100, 180]);
|
|
let border = card_colors.map(|c| c.selected_border).unwrap_or([100, 150, 255]);
|
|
egui::Frame::none()
|
|
.fill(egui::Color32::from_rgba_unmultiplied(bg[0], bg[1], bg[2], bg[3]))
|
|
.inner_margin(10.0)
|
|
.rounding(5.0)
|
|
.stroke(egui::Stroke::new(2.0, egui::Color32::from_rgb(border[0], border[1], border[2])))
|
|
} else {
|
|
let bg = card_colors.map(|c| c.unselected_bg).unwrap_or([40, 40, 60, 150]);
|
|
egui::Frame::none()
|
|
.fill(egui::Color32::from_rgba_unmultiplied(bg[0], bg[1], bg[2], bg[3]))
|
|
.inner_margin(10.0)
|
|
.rounding(5.0)
|
|
};
|
|
|
|
let response = frame.show(ui, |ui| {
|
|
ui.vertical(|ui| {
|
|
ui.set_width(ui.available_width());
|
|
ui.strong(&app.display_name);
|
|
ui.label(format!("App ID: {}", app.app_id));
|
|
if let Some(created) = &app.created_date_time {
|
|
ui.label(format!("Created: {}", created));
|
|
}
|
|
});
|
|
});
|
|
|
|
// Make the entire card clickable
|
|
if response.response.interact(egui::Sense::click()).clicked() && !is_selected {
|
|
*action = Some(AppListAction::SelectApp(app.clone()));
|
|
}
|
|
|
|
ui.add_space(5.0);
|
|
}
|
|
|
|
pub enum AppListAction {
|
|
Refresh,
|
|
SelectApp(Application),
|
|
CreateSecret,
|
|
SignOut,
|
|
}
|