From ddb06d299314ebd8de6ef06b386800e97bbe2165 Mon Sep 17 00:00:00 2001 From: Thomas Gideon Date: Sat, 7 Sep 2024 09:10:16 -0400 Subject: [PATCH] Rough out multi-pane layout - Introduce nested routing for series. - Rough out add and view routes. - Add buttons to access new routes. --- src/app.rs | 99 ------------------------------------------ src/app/home.rs | 18 ++++++++ src/app/mod.rs | 53 ++++++++++++++++++++++ src/app/series/add.rs | 15 +++++++ src/app/series/mod.rs | 71 ++++++++++++++++++++++++++++++ src/app/series/view.rs | 15 +++++++ 6 files changed, 172 insertions(+), 99 deletions(-) delete mode 100644 src/app.rs create mode 100644 src/app/home.rs create mode 100644 src/app/mod.rs create mode 100644 src/app/series/add.rs create mode 100644 src/app/series/mod.rs create mode 100644 src/app/series/view.rs diff --git a/src/app.rs b/src/app.rs deleted file mode 100644 index fa6d733..0000000 --- a/src/app.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::error_template::{AppError, ErrorTemplate}; -use leptos::*; -use leptos_meta::*; -use leptos_router::*; -use serde::{Deserialize, Serialize}; -#[cfg(feature = "ssr")] -use sqlx::FromRow; -use uuid::Uuid; - -#[component] -pub fn App() -> impl IntoView { - // Provides context that manages stylesheets, titles, meta tags, etc. - provide_meta_context(); - - view! { - - - // injects a stylesheet into the document - // id=leptos means cargo-leptos will hot-reload this stylesheet - - - - // sets the document title - - - // content for this welcome page - <Router fallback=|| { - let mut outside_errors = Errors::default(); - outside_errors.insert_with_default_key(AppError::NotFound); - view! { - <ErrorTemplate outside_errors/> - } - .into_view() - }> - <main class="container"> - <Routes> - <Route path="" view=HomePage/> - </Routes> - </main> - </Router> - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(feature = "ssr", derive(FromRow))] -struct Series { - id: Uuid, - name: String, -} - -#[server] -pub async fn get_all() -> Result<Vec<String>, ServerFnError> { - use sqlx::{Pool, Postgres}; - use tokio_stream::StreamExt; - - let pool: Pool<Postgres> = expect_context(); - - let mut r = sqlx::query_as::<_, Series>("select * from series;").fetch(&pool); - - let mut found = Vec::new(); - while let Some(series) = r.try_next().await? { - found.push(series.name); - } - - Ok(found) -} - -/// Renders the home page of your application. -#[component] -fn HomePage() -> impl IntoView { - // Creates a reactive value to update the button - let load = create_resource(|| (), |_| async move { get_all().await }); - - view! { - <h1>"What We're Watching"</h1> - <div class="card"> - <Suspense fallback=|| view!{ <div>"Loading..."</div> }> - <ul> - {load().map(format_series)} - </ul> - </Suspense> - </div> - } -} - -fn format_series(data: Result<Vec<String>, ServerFnError>) -> Vec<impl IntoView> { - data.unwrap_or_default() - .into_iter() - .map(|d| { - view! { - <li>{d}</li> - } - }) - .collect::<Vec<_>>() -} diff --git a/src/app/home.rs b/src/app/home.rs new file mode 100644 index 0000000..7b1b65a --- /dev/null +++ b/src/app/home.rs @@ -0,0 +1,18 @@ +use super::series::SeriesList; +use leptos::*; +use leptos_router::*; + +#[component] +pub fn HomePage() -> impl IntoView { + view! { + <h1 class="mt-3 mb-5">"What We're Watching"</h1> + <div class="row"> + <div class="col"> + <SeriesList /> + </div> + <div class="col"> + <Outlet /> + </div> + </div> + } +} diff --git a/src/app/mod.rs b/src/app/mod.rs new file mode 100644 index 0000000..c92f1b1 --- /dev/null +++ b/src/app/mod.rs @@ -0,0 +1,53 @@ +use self::{ + home::HomePage, + series::{add::AddSeries, view::ViewSeries}, +}; +use crate::error_template::{AppError, ErrorTemplate}; +use leptos::*; +use leptos_meta::*; +use leptos_router::*; + +mod home; +mod series; + +#[component] +pub fn App() -> impl IntoView { + // Provides context that manages stylesheets, titles, meta tags, etc. + provide_meta_context(); + + view! { + + + // injects a stylesheet into the document <head> + // id=leptos means cargo-leptos will hot-reload this stylesheet + <Stylesheet id="leptos" href="/pkg/watch.css"/> + <Link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" + rel="stylesheet" + integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" + crossorigin="anonymous" + /> + + // sets the document title + <Title text="Watch"/> + + // content for this welcome page + <Router fallback=|| { + let mut outside_errors = Errors::default(); + outside_errors.insert_with_default_key(AppError::NotFound); + view! { + <ErrorTemplate outside_errors/> + } + .into_view() + }> + <main class="container"> + <Routes> + <Route path="" view=HomePage> + <Route path="add" view=AddSeries /> + <Route path="view/:id" view=ViewSeries /> + <Route path="" view=|| () /> + </Route> + </Routes> + </main> + </Router> + } +} diff --git a/src/app/series/add.rs b/src/app/series/add.rs new file mode 100644 index 0000000..a25a28e --- /dev/null +++ b/src/app/series/add.rs @@ -0,0 +1,15 @@ +use leptos::*; + +#[component] +pub fn AddSeries() -> impl IntoView { + view! { + <div class="card p-3"> + <div class="card-heading"> + <h2>Add Series</h2> + </div> + <div class="card-body"> + To Do + </div> + </div> + } +} diff --git a/src/app/series/mod.rs b/src/app/series/mod.rs new file mode 100644 index 0000000..60015b2 --- /dev/null +++ b/src/app/series/mod.rs @@ -0,0 +1,71 @@ +use leptos::*; +use leptos_router::*; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "ssr")] +use sqlx::FromRow; +use uuid::Uuid; + +pub mod add; +pub mod view; + +#[component] +pub fn SeriesList() -> impl IntoView { + let load = create_resource(|| (), |_| async move { get_all().await }); + view! { + <Suspense fallback=|| view!{ <div>"Loading..."</div> }> + <ul class="list-group mb-3"> + {load().map(format_series)} + </ul> + </Suspense> + <div> + <A href="add" + attr:class="btn btn-primary" + > + Add Series + </A> + </div> + } +} + +fn format_series(data: Result<Vec<Series>, ServerFnError>) -> impl IntoView { + data.unwrap_or_default() + .into_iter() + .map(|d| { + view! { + <li class="list-group-item"> + <A href=format!("view/{}", d.id) + attr:class="btn btn-sm btn-secondary" + attr:style="float: right" + > + View Series + </A> + {d.name} + </li> + } + }) + .collect_view() +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "ssr", derive(FromRow))] +pub struct Series { + id: Uuid, + name: String, +} + +#[server] +pub async fn get_all() -> Result<Vec<Series>, ServerFnError> { + use sqlx::{Pool, Postgres}; + use tokio_stream::StreamExt; + + let pool: Pool<Postgres> = expect_context(); + + let mut r = sqlx::query_as::<_, Series>("select * from series;").fetch(&pool); + + let mut found = Vec::new(); + while let Some(series) = r.try_next().await? { + found.push(series); + } + + Ok(found) +} diff --git a/src/app/series/view.rs b/src/app/series/view.rs new file mode 100644 index 0000000..d9f6353 --- /dev/null +++ b/src/app/series/view.rs @@ -0,0 +1,15 @@ +use leptos::*; + +#[component] +pub fn ViewSeries() -> impl IntoView { + view! { + <div class="card p-3"> + <div class="card-heading"> + <h2>View Series</h2> + </div> + <div class="card-body"> + View Series + </div> + </div> + } +}