//! promo-screen key-input handler (split out of tui/input.rs). use super::{ App, AppEvent, ConfirmAction, DataPayload, KeyCode, KeyEvent, MnwApiClient, PromoCreateStep, Screen, load_promo_codes, mpsc, }; pub(crate) async fn handle_promo_input( key: KeyEvent, app: &mut App, screen: &mut Screen, api: &MnwApiClient, tx: &mpsc::Sender, ) { // Cancel pending confirmation on any key other than the confirmation key if app.confirm_action.is_some() && !matches!(key.code, KeyCode::Char('d' | 'D')) { app.confirm_action = None; app.promo_status = None; } // Creating mode if let Some(step) = app.promo_editing_step { match key.code { KeyCode::Esc => { app.promo_editing_step = None; app.promo_create_code.clear(); app.promo_create_discount.clear(); app.edit_buffer.clear(); app.promo_status = None; } KeyCode::Enter => match step { PromoCreateStep::Code => { if !app.edit_buffer.is_empty() { app.promo_create_code = app.edit_buffer.clone(); app.edit_buffer.clear(); app.promo_editing_step = Some(PromoCreateStep::Discount); app.promo_status = Some("Discount % (e.g. 25): _".to_string()); } } PromoCreateStep::Discount => { let code = app.promo_create_code.clone(); let discount: i32 = app.edit_buffer.parse().unwrap_or(0); let api = api.clone(); let user_id = app.user.user_id.clone(); let tx = tx.clone(); app.promo_status = Some("Creating...".to_string()); tokio::spawn(async move { match api .create_promo_code(&user_id, &code, "percentage", discount, None, None) .await { Ok(_) => { load_promo_codes(&api, &user_id, &tx).await; } Err(e) => { let _ = tx .send(AppEvent::DataLoaded(DataPayload::GenericError { error: e.to_string(), })) .await; } } }); app.promo_editing_step = None; app.promo_create_code.clear(); app.promo_create_discount.clear(); app.edit_buffer.clear(); } }, KeyCode::Backspace => { app.edit_buffer.pop(); } KeyCode::Char(c) => { app.edit_buffer.push(c); } _ => {} } return; } // Normal mode match key.code { KeyCode::Char('j') | KeyCode::Down => app.move_down(screen), KeyCode::Char('k') | KeyCode::Up => app.move_up(screen), KeyCode::Esc => { *screen = Screen::Home; app.promo_codes.clear(); app.promo_status = None; app.selected_index = 0; } KeyCode::Char('n' | 'N') => { app.promo_editing_step = Some(PromoCreateStep::Code); app.edit_buffer.clear(); app.promo_status = Some("Code: _".to_string()); } KeyCode::Char('d' | 'D') if !app.promo_codes.is_empty() => { let idx = app.selected_index; if matches!(app.confirm_action, Some(ConfirmAction::DeletePromoCode { code_idx }) if code_idx == idx) { // Confirmed — execute delete app.confirm_action = None; let code_id = app.promo_codes[idx].id.clone(); let code_value = app.promo_codes[idx].code.clone(); let user_id = app.user.user_id.clone(); tracing::info!( user_id = %user_id, code_id = %code_id, code = %code_value, "delete promo code confirmed" ); let api = api.clone(); let tx = tx.clone(); app.promo_status = Some("Deleting...".to_string()); tokio::spawn(async move { match api.delete_promo_code(&user_id, &code_id).await { Ok(()) => { load_promo_codes(&api, &user_id, &tx).await; } Err(e) => { let _ = tx .send(AppEvent::DataLoaded(DataPayload::GenericError { error: e.to_string(), })) .await; } } }); } else { // First press — ask for confirmation app.confirm_action = Some(ConfirmAction::DeletePromoCode { code_idx: idx }); app.promo_status = Some(format!( "Delete '{}'? Press d again to confirm", app.promo_codes[idx].code )); } } KeyCode::Char('r' | 'R') => { app.loading = true; let api = api.clone(); let user_id = app.user.user_id.clone(); let tx = tx.clone(); tokio::spawn(async move { load_promo_codes(&api, &user_id, &tx).await; }); } _ => {} } }