Add tags in post
This commit is contained in:
parent
cf65d5b8e9
commit
d353161c92
3 changed files with 74 additions and 30 deletions
|
@ -1,4 +1,5 @@
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
use leptos_router::A;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Title(
|
pub fn Title(
|
||||||
|
@ -8,7 +9,7 @@ pub fn Title(
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<header>
|
<header>
|
||||||
<a href=href>r"< Retour"</a>
|
<A href=href>r"< Retour"</A>
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<span></span>
|
<span></span>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -9,12 +9,18 @@ use crate::app::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn get_posts() -> Result<Vec<Post>, ServerFnError> {
|
pub async fn get_posts(
|
||||||
|
tag: Option<String>
|
||||||
|
) -> Result<Vec<Post>, ServerFnError> {
|
||||||
leptos_actix::extract(
|
leptos_actix::extract(
|
||||||
|data: actix_web::web::Data<crate::app::models::Data>| async move {
|
|data: actix_web::web::Data<crate::app::models::Data>| async move {
|
||||||
let data = data.into_inner();
|
let data = data.into_inner();
|
||||||
data.posts
|
let default = vec![];
|
||||||
.iter()
|
let posts = match tag {
|
||||||
|
Some(tag) => data.posts_by_tag.get(&tag).unwrap_or(&default),
|
||||||
|
None => &data.posts
|
||||||
|
};
|
||||||
|
posts.iter()
|
||||||
.map(|post| {
|
.map(|post| {
|
||||||
Post {
|
Post {
|
||||||
metadata: post.metadata.clone(),
|
metadata: post.metadata.clone(),
|
||||||
|
@ -44,44 +50,72 @@ pub async fn get_post(
|
||||||
.and_then(|post| post.ok_or_else(|| ServerFnError::ServerError("Post not found".to_string())))
|
.and_then(|post| post.ok_or_else(|| ServerFnError::ServerError("Post not found".to_string())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn PostTags(
|
||||||
|
tags: Vec<String>
|
||||||
|
) -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<div class="tags_list">
|
||||||
|
{
|
||||||
|
tags.into_iter().map(|tag| view! { <A class="tag" href=format!("/posts?tag={}", tag)>{tag}</A>}).collect_view()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn PostListCard(
|
||||||
|
post: Post
|
||||||
|
) -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<div>
|
||||||
|
<img src={post.metadata.image_path.clone()} alt=format!("Image {}", post.metadata.title)/>
|
||||||
|
|
||||||
|
{
|
||||||
|
if post.metadata.draft {
|
||||||
|
Some(view!{
|
||||||
|
<div class="warning">
|
||||||
|
<Icon icon=Icon::from(IoIcon::IoConstruct)/>
|
||||||
|
</div>
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<PostTags tags=post.metadata.tags.clone()/>
|
||||||
|
<h2>{post.metadata.title.clone()}</h2>
|
||||||
|
<p>{post.metadata.description.clone()}</p>
|
||||||
|
<span>{post.metadata.date.clone()}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn PostList() -> impl IntoView {
|
pub fn PostList() -> impl IntoView {
|
||||||
let posts = create_resource(|| (), |_| get_posts());
|
let query = use_query_map();
|
||||||
|
let tag = move || query.with(|query| query.get("tag").cloned());
|
||||||
|
let posts = create_resource(move || tag(), move |_| get_posts(tag()));
|
||||||
|
|
||||||
let posts_view = move || {
|
let posts_view = move || {
|
||||||
posts.and_then(|posts| {
|
posts.and_then(|posts| {
|
||||||
posts.iter()
|
posts.iter()
|
||||||
.map(|post| view! {
|
.map(|post| view! { <PostListCard post=post.clone() /> })
|
||||||
<a href=format!("posts/{}", post.metadata.slug.clone())>
|
|
||||||
<img src={post.metadata.image_path.clone()} alt=format!("Image {}", post.metadata.title)/>
|
|
||||||
|
|
||||||
{
|
|
||||||
if post.metadata.draft {
|
|
||||||
Some(view!{
|
|
||||||
<div class="warning">
|
|
||||||
<Icon icon=Icon::from(IoIcon::IoConstruct)/>
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h2>{post.metadata.title.clone()}</h2>
|
|
||||||
<p>{post.metadata.description.clone()}</p>
|
|
||||||
<span>{post.metadata.date.clone()}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
})
|
|
||||||
.collect_view()
|
.collect_view()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let title = move || match tag() {
|
||||||
|
Some(tag) => view! { <Title href="/posts".to_string() title=format!("Posts for {}", tag)/> },
|
||||||
|
None => view! { <Title href="/".to_string() title="Posts".to_string()/> }
|
||||||
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Suspense fallback=move || view! { <Loading title="Chargement des posts...".to_string() /> }>
|
<Suspense fallback=move || view! { <Loading title="Chargement des posts...".to_string() /> }>
|
||||||
<main class="posts">
|
<main class="posts">
|
||||||
<Title href="/".to_string() title="Posts".to_string()/>
|
{ title }
|
||||||
|
|
||||||
<div class="posts__cards">{posts_view}</div>
|
<div class="posts__cards">{posts_view}</div>
|
||||||
</main>
|
</main>
|
||||||
|
@ -101,6 +135,7 @@ pub fn PostElement() -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<>
|
<>
|
||||||
<Title href="/posts".to_string() title=post.metadata.title.clone()/>
|
<Title href="/posts".to_string() title=post.metadata.title.clone()/>
|
||||||
|
<PostTags tags=post.metadata.tags.clone()/>
|
||||||
{
|
{
|
||||||
if post.metadata.draft {
|
if post.metadata.draft {
|
||||||
Some(view!{
|
Some(view!{
|
||||||
|
|
|
@ -42,13 +42,21 @@
|
||||||
& ol li {
|
& ol li {
|
||||||
@apply list-decimal list-inside;
|
@apply list-decimal list-inside;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& blockquote {
|
||||||
|
@apply border-l-4 border-primary/50 dark:border-dark_primary/50 pl-3 my-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .tags_list {
|
||||||
|
@apply flex flex-row flex-wrap gap-2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.posts {
|
.posts {
|
||||||
& > .posts__cards {
|
& > .posts__cards {
|
||||||
@apply grid grid-cols-1 gap-5 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 m-5;
|
@apply grid grid-cols-1 gap-5 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 m-5;
|
||||||
|
|
||||||
& > a {
|
& > div {
|
||||||
@apply rounded-xl overflow-hidden relative;
|
@apply rounded-xl overflow-hidden relative;
|
||||||
@apply bg-primary/10 dark:bg-dark_primary/10;
|
@apply bg-primary/10 dark:bg-dark_primary/10;
|
||||||
@apply shadow-md shadow-primary/5 dark:shadow-dark_primary/5;
|
@apply shadow-md shadow-primary/5 dark:shadow-dark_primary/5;
|
||||||
|
|
Loading…
Reference in a new issue