diff --git a/quotesdb/src/bin/ui/pages/quote.rs b/quotesdb/src/bin/ui/pages/quote.rs
index 6932388..7a3f4c0 100644
--- a/quotesdb/src/bin/ui/pages/quote.rs
+++ b/quotesdb/src/bin/ui/pages/quote.rs
@@ -1,4 +1,4 @@
-//! Quote detail page — view, edit, delete, and report a single quote.
+//! Quote detail page — view, edit, delete, hide, and report a single quote.
use crate::api::{self, ApiError};
use crate::components::auth_modal::AuthModal;
@@ -23,6 +23,8 @@ enum Action {
EditForm,
/// Delete auth modal shown.
DeleteAuth,
+ /// Hide/unhide auth modal shown.
+ HideAuth,
/// Report modal shown.
Report,
}
@@ -37,10 +39,11 @@ pub struct QuotePageProps {
/// Quote detail page.
///
/// Fetches a quote by ID, renders it with [`QuoteCard`], and provides
-/// Edit, Delete, and Report actions.
+/// Edit, Delete, Hide/Unhide, and Report actions.
///
/// - Edit: shows auth modal → edit form → `POST /api/quotes/:id` → re-fetch
/// - Delete: shows auth modal → `DELETE /api/quotes/:id` → navigate to `/browse`
+/// - Hide/Unhide: shows auth modal → `POST /api/quotes/:id` `{ hidden: bool }` → update state
/// - Report: shows [`ReportModal`] → `POST /api/quotes/:id/report` → success message
/// - 403 errors clear the stored auth code and display an error message.
/// - 404 errors display a user-friendly "not found" message.
@@ -239,6 +242,50 @@ pub fn quote_page(props: &QuotePageProps) -> Html {
})
};
+ // --- Hide/unhide auth modal submitted ---
+ let on_hide_auth = {
+ let id = id.clone();
+ let action = action.clone();
+ let action_error = action_error.clone();
+ let quote = quote.clone();
+ Callback::from(move |code: String| {
+ let id = id.clone();
+ let action = action.clone();
+ let action_error = action_error.clone();
+ let quote = quote.clone();
+ // Determine the desired hidden state from current quote value.
+ let new_hidden = (*quote).as_ref().map(|q| !q.hidden).unwrap_or(true);
+ storage::set_auth_code(&id, &code);
+ spawn_local(async move {
+ let input = UpdateQuoteInput {
+ text: None,
+ author: None,
+ source: None,
+ date: None,
+ tags: None,
+ hidden: Some(new_hidden),
+ };
+ match api::update_quote(&id, &input, &code).await {
+ Ok(updated) => {
+ storage::set_auth_code(&id, &code);
+ quote.set(Some(updated));
+ action.set(Action::None);
+ action_error.set(None);
+ }
+ Err(ApiError::Server { status: 403, .. }) => {
+ storage::clear_auth_code(&id);
+ action_error.set(Some("Wrong auth code. Please try again.".to_string()));
+ action.set(Action::HideAuth);
+ }
+ Err(e) => {
+ action_error.set(Some(e.to_string()));
+ action.set(Action::None);
+ }
+ }
+ });
+ })
+ };
+
let on_cancel = {
let action = action.clone();
let action_error = action_error.clone();
@@ -284,6 +331,12 @@ pub fn quote_page(props: &QuotePageProps) -> Html {
} else if let Some(err) = (*error).clone() {