add links
This commit is contained in:
1
migrations/20241104213550_links.down.sql
Normal file
1
migrations/20241104213550_links.down.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS `links`;
|
||||||
7
migrations/20241104213550_links.up.sql
Normal file
7
migrations/20241104213550_links.up.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS links (
|
||||||
|
id BIGINT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
link TEXT NOT NULL,
|
||||||
|
position BIGINT NOT NULL DEFAULT 1,
|
||||||
|
created_at DATETIME NOT NULL
|
||||||
|
);
|
||||||
@@ -2,7 +2,40 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Link {
|
pub struct Link {
|
||||||
id: u16,
|
id: u64,
|
||||||
link: String,
|
pub link: String,
|
||||||
name: String,
|
pub name: String,
|
||||||
|
position: i64,
|
||||||
|
created_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Link {
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
pub async fn insert(
|
||||||
|
name: String,
|
||||||
|
link: String,
|
||||||
|
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO links (name, link, position, created_at) VALUES (?, ?, (SELECT COALESCE(MAX(position) + 1, 1) FROM links lin), ?)",
|
||||||
|
name,
|
||||||
|
link,
|
||||||
|
chrono::Local::now().naive_local(),
|
||||||
|
)
|
||||||
|
.execute(crate::database::get_db())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
pub async fn get_all() -> Result<Vec<Self>, sqlx::Error> {
|
||||||
|
sqlx::query!("SELECT id, name, link, position, created_at FROM links ORDER BY position")
|
||||||
|
.map(|x| Self {
|
||||||
|
id: x.id,
|
||||||
|
name: x.name,
|
||||||
|
link: x.link,
|
||||||
|
position: x.position,
|
||||||
|
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
||||||
|
})
|
||||||
|
.fetch_all(crate::database::get_db())
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
mod link;
|
mod link;
|
||||||
mod value;
|
mod value;
|
||||||
|
pub use link::Link;
|
||||||
pub use value::Value;
|
pub use value::Value;
|
||||||
pub use value::OPTIONS;
|
pub use value::OPTIONS;
|
||||||
|
|||||||
@@ -1,23 +1,108 @@
|
|||||||
use leptos::*;
|
use leptos::*;
|
||||||
//use leptos_meta::*;
|
//use leptos_meta::*;
|
||||||
//use leptos_router::*;
|
use leptos_router::*;
|
||||||
|
|
||||||
|
#[server(GetLinksAction, "/api", "GetJson")]
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn get_links() -> Result<Vec<crate::models::Link>, ServerFnError> {
|
||||||
|
crate::models::Link::get_all().await.map_err(|x| {
|
||||||
|
let err = format!("Error while posting a link: {x:?}");
|
||||||
|
tracing::error!("{err}");
|
||||||
|
ServerFnError::ServerError("Could not post a link, try again later".into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(LinkAction, "/api")]
|
||||||
|
pub async fn add_value(name: String, link: String) -> Result<(), ServerFnError> {
|
||||||
|
crate::models::Link::insert(name, link)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|x| {
|
||||||
|
let err = format!("Error while posting a comment: {x:?}");
|
||||||
|
tracing::error!("{err}");
|
||||||
|
ServerFnError::ServerError("Could not post a comment, try again later".into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Links() -> impl IntoView {
|
pub fn Links() -> impl IntoView {
|
||||||
|
let (show, set_show) = create_signal(false);
|
||||||
|
let link_action = create_server_action::<LinkAction>();
|
||||||
|
let result = link_action.version();
|
||||||
|
let reset_form = create_rw_signal("");
|
||||||
|
let links = create_resource(
|
||||||
|
move || (result.get()),
|
||||||
|
move |_| async move {
|
||||||
|
reset_form.set("");
|
||||||
|
set_show.set(false);
|
||||||
|
get_links().await
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let form = move || {
|
||||||
|
show.get().then(|| {
|
||||||
|
view! { <div class="my-0 mx-auto w-72 text-center">
|
||||||
|
<h2 class="p-6 text-4xl">"Ajout d'un lien"</h2>
|
||||||
|
<ActionForm action=link_action attr:class="border border-lime-500">
|
||||||
|
<div>
|
||||||
|
<label class="block mt-3 mb-1">"Nom"</label>
|
||||||
|
<input type="text"
|
||||||
|
name="name"
|
||||||
|
prop:value=move || reset_form.get()
|
||||||
|
class="text-center dark:bg-slate-800 focus:border-lime-500 dark:text-white px-2 py-2 w-60" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block mt-3 mb-1">"Lien"</label>
|
||||||
|
<input type="url"
|
||||||
|
name="link"
|
||||||
|
prop:value=move || reset_form.get()
|
||||||
|
class="text-center dark:bg-slate-800 focus:border-lime-500 dark:text-white px-2 py-2 w-60" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="submit" class="bg-lime-500 hover:bg-lime-400 text-black px-2 py-1 w-60 my-5">"Valider"</button>
|
||||||
|
</div>
|
||||||
|
</ActionForm>
|
||||||
|
</div> }
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<ul class="flex gap-5 mt-5 justify-center">
|
<ul class="flex gap-5 mt-5 justify-center">
|
||||||
<Link link="aa".to_string() name="Mon Lien".to_string() />
|
<Suspense fallback=move || view! {<p>"Loading Comments from the article"</p> }>
|
||||||
<Link link="aa".to_string() name="mon lien 2".to_string() />
|
<ErrorBoundary fallback=|_| {
|
||||||
<Link link="aa".to_string() name="mon lien 3".to_string() />
|
view! { <p class="error-messages text-xs-center">"Something went wrong."</p>}
|
||||||
|
}>
|
||||||
|
{move || links.get().map(move |x| x.map(move |c| {
|
||||||
|
view! {
|
||||||
|
<For each=move || c.clone().into_iter().enumerate()
|
||||||
|
key=|(i, _)| *i
|
||||||
|
children=move |(_, link)| {
|
||||||
|
let link = create_rw_signal(link);
|
||||||
|
view!{<Link link />}
|
||||||
|
}/>
|
||||||
|
}
|
||||||
|
}))}
|
||||||
|
</ErrorBoundary>
|
||||||
|
</Suspense>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button on:click=move |_| {set_show.set(true)}>"Add"</button>
|
||||||
|
{form}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn Link(link: String, name: String) -> impl IntoView {
|
fn Link(link: RwSignal<crate::models::Link>) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<li>
|
<li>
|
||||||
<a class="border border-lime-500 bg-lime-500 hover:bg-lime-400 text-black px-3 py-2 inline-block" href=link>{name}</a>
|
<a class="border border-lime-500 bg-lime-500 hover:bg-lime-400 text-black px-3 py-2 inline-block"
|
||||||
|
target="_blank"
|
||||||
|
href={move || link.with(|x| x.link.to_string())}>
|
||||||
|
{move || link.with(|x| x.name.to_string())}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,14 @@ pub async fn add_value(device: String, value: String) -> Result<(), ServerFnErro
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn FormValues() -> impl IntoView {
|
pub fn FormValues() -> impl IntoView {
|
||||||
let value_action = create_server_action::<ValueAction>();
|
let value_action = create_server_action::<ValueAction>();
|
||||||
//let value = value_action.value();
|
let result = value_action.version();
|
||||||
//let has_error = move || value.with(|val| matches!(val, Some(Err(_))));
|
let reset_value = create_rw_signal("");
|
||||||
|
let _ = create_resource(
|
||||||
|
move || (result.get()),
|
||||||
|
move |_| async move {
|
||||||
|
reset_value.set("");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="my-0 mx-auto w-72 text-center">
|
<div class="my-0 mx-auto w-72 text-center">
|
||||||
@@ -37,8 +43,10 @@ pub fn FormValues() -> impl IntoView {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block mt-3 mb-1">Valeur</label>
|
<label class="block mt-3 mb-1">Valeur</label>
|
||||||
<input type="text" name="value" type="number"
|
<input name="value"
|
||||||
class="text-center dark:bg-slate-800 focus:border-lime-500 dark:text-white px-2 py-2 w-60" />
|
type="number"
|
||||||
|
prop:value=move || reset_value.get()
|
||||||
|
class="text-center dark:bg-slate-800 focus:border-lime-500 dark:text-white px-2 py-2 w-60" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" class="bg-lime-500 hover:bg-lime-400 text-black px-2 py-1 w-60 my-5">Valider</button>
|
<button type="submit" class="bg-lime-500 hover:bg-lime-400 text-black px-2 py-1 w-60 my-5">Valider</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user