ajout de l'option dashboard pour afficher les liens importants sur home
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE links DROP COLUMN dashboard;
|
||||||
1
migrations/20260412150652_add_dashboard_to_links.up.sql
Normal file
1
migrations/20260412150652_add_dashboard_to_links.up.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE links ADD COLUMN dashboard BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
@@ -59,13 +59,6 @@ pub fn Navigation() -> impl IntoView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
|
||||||
pub fn Home() -> impl IntoView {
|
|
||||||
view! {
|
|
||||||
<h1 class="m-2">Home</h1>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn NotFound() -> impl IntoView {
|
pub fn NotFound() -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub struct Link {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
pub alternate_link: String,
|
pub alternate_link: String,
|
||||||
|
pub dashboard: bool,
|
||||||
position: i64,
|
position: i64,
|
||||||
created_at: String,
|
created_at: String,
|
||||||
}
|
}
|
||||||
@@ -18,13 +19,15 @@ impl Link {
|
|||||||
link: String,
|
link: String,
|
||||||
icon: String,
|
icon: String,
|
||||||
alternate_link: String,
|
alternate_link: String,
|
||||||
|
dashboard: bool,
|
||||||
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO links (name, link, icon, alternate_link, position, created_at) VALUES (?, ?, ?, ?, (SELECT COALESCE(MAX(position) + 1, 1) FROM links lin), ?)",
|
"INSERT INTO links (name, link, icon, alternate_link, dashboard, position, created_at) VALUES (?, ?, ?, ?, ?, (SELECT COALESCE(MAX(position) + 1, 1) FROM links lin), ?)",
|
||||||
name,
|
name,
|
||||||
link,
|
link,
|
||||||
icon,
|
icon,
|
||||||
alternate_link,
|
alternate_link,
|
||||||
|
dashboard,
|
||||||
chrono::Local::now().naive_local(),
|
chrono::Local::now().naive_local(),
|
||||||
)
|
)
|
||||||
.execute(crate::database::get_db())
|
.execute(crate::database::get_db())
|
||||||
@@ -34,7 +37,7 @@ impl Link {
|
|||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub async fn get_all() -> Result<Vec<Self>, sqlx::Error> {
|
pub async fn get_all() -> Result<Vec<Self>, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT id, name, link, icon, alternate_link, position, created_at FROM links ORDER BY position"
|
"SELECT id, name, link, icon, alternate_link, dashboard, position, created_at FROM links ORDER BY position"
|
||||||
)
|
)
|
||||||
.map(|x| Self {
|
.map(|x| Self {
|
||||||
id: x.id,
|
id: x.id,
|
||||||
@@ -42,6 +45,7 @@ impl Link {
|
|||||||
link: x.link,
|
link: x.link,
|
||||||
icon: x.icon,
|
icon: x.icon,
|
||||||
alternate_link: x.alternate_link,
|
alternate_link: x.alternate_link,
|
||||||
|
dashboard: x.dashboard != 0,
|
||||||
position: x.position,
|
position: x.position,
|
||||||
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
||||||
})
|
})
|
||||||
@@ -55,7 +59,7 @@ impl Link {
|
|||||||
direction: String,
|
direction: String,
|
||||||
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
||||||
let link = sqlx::query!(
|
let link = sqlx::query!(
|
||||||
"SELECT id, name, link, icon, alternate_link, position, created_at FROM links WHERE id = ?",
|
"SELECT id, name, link, icon, alternate_link, dashboard, position, created_at FROM links WHERE id = ?",
|
||||||
link_id
|
link_id
|
||||||
)
|
)
|
||||||
.map(|x| Self {
|
.map(|x| Self {
|
||||||
@@ -64,6 +68,7 @@ impl Link {
|
|||||||
link: x.link,
|
link: x.link,
|
||||||
icon: x.icon,
|
icon: x.icon,
|
||||||
alternate_link: x.alternate_link,
|
alternate_link: x.alternate_link,
|
||||||
|
dashboard: x.dashboard != 0,
|
||||||
position: x.position,
|
position: x.position,
|
||||||
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
created_at: x.created_at.format("%d/%m/%Y %H:%M").to_string(),
|
||||||
})
|
})
|
||||||
@@ -108,13 +113,15 @@ impl Link {
|
|||||||
link: String,
|
link: String,
|
||||||
icon: String,
|
icon: String,
|
||||||
alternate_link: String,
|
alternate_link: String,
|
||||||
|
dashboard: bool,
|
||||||
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE links SET name = ?, link = ?, icon = ?, alternate_link = ? WHERE id = ?",
|
"UPDATE links SET name = ?, link = ?, icon = ?, alternate_link = ?, dashboard = ? WHERE id = ?",
|
||||||
name,
|
name,
|
||||||
link,
|
link,
|
||||||
icon,
|
icon,
|
||||||
alternate_link,
|
alternate_link,
|
||||||
|
dashboard,
|
||||||
id
|
id
|
||||||
)
|
)
|
||||||
.execute(crate::database::get_db())
|
.execute(crate::database::get_db())
|
||||||
|
|||||||
57
src/routes/home.rs
Normal file
57
src/routes/home.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use leptos_meta::*;
|
||||||
|
|
||||||
|
#[server(GetDashboardLinksAction, "/api", "GetJson")]
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn get_dashboard_links() -> Result<Vec<crate::models::Link>, ServerFnError> {
|
||||||
|
crate::models::Link::get_all()
|
||||||
|
.await
|
||||||
|
.map(|links| links.into_iter().filter(|l| l.dashboard).collect())
|
||||||
|
.map_err(|x| {
|
||||||
|
let err = format!("Error while fetching dashboard links: {x:?}");
|
||||||
|
tracing::error!("{err}");
|
||||||
|
ServerFnError::ServerError("Could not fetch dashboard links, try again later".into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Home() -> impl IntoView {
|
||||||
|
let links = create_resource(|| (), |_| async move { get_dashboard_links().await });
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Title text="Bienvenue à la maison"/>
|
||||||
|
<ul class="flex gap-5 m-5 flex-wrap justify-center">
|
||||||
|
<Suspense fallback=move || view! { <p>"Chargement..."</p> }>
|
||||||
|
<ErrorBoundary fallback=|_| {
|
||||||
|
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)| {
|
||||||
|
view! { <DashboardLink link /> }
|
||||||
|
}/>
|
||||||
|
}
|
||||||
|
}))}
|
||||||
|
</ErrorBoundary>
|
||||||
|
</Suspense>
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn DashboardLink(link: crate::models::Link) -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<li class="w-44 lg:w-fit">
|
||||||
|
<a class="bg-prim-light border-b border-third container flex flex-col lg:gap-2 lg:flex-row w-full item-center hover:scale-110 transition hover:bg-prim-lightest rounded-lg text-center hover:text-third px-5 py-4"
|
||||||
|
target="_blank"
|
||||||
|
href=link.link.clone()>
|
||||||
|
<img src=link.icon.clone()
|
||||||
|
alt=link.name.clone()
|
||||||
|
class="size-32 lg:size-10 object-cover overflow-hidden" />
|
||||||
|
<span class="flex-1 text-2xl mt-2 lg:mt-0 flex justify-center items-center">{link.name.clone()}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,8 +13,8 @@ pub async fn get_links() -> Result<Vec<crate::models::Link>, ServerFnError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[server(LinkAction, "/api")]
|
#[server(LinkAction, "/api")]
|
||||||
pub async fn add_value(name: String, link: String, icon: String, alternate_link: String) -> Result<(), ServerFnError> {
|
pub async fn add_value(name: String, link: String, icon: String, alternate_link: String, dashboard: bool) -> Result<(), ServerFnError> {
|
||||||
crate::models::Link::insert(name, link, icon, alternate_link)
|
crate::models::Link::insert(name, link, icon, alternate_link, dashboard)
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|x| {
|
.map_err(|x| {
|
||||||
@@ -120,7 +120,7 @@ fn Link<T: 'static + Clone, S: 'static>(
|
|||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<li class="w-44 lg:w-fit">
|
<li class="w-44 lg:w-fit">
|
||||||
<a class="bg-prim-light container flex flex-col lg:gap-2 lg:flex-row w-full item-center hover:scale-110 transition hover:bg-prim-lightest border-b hover:border-third border-transparent rounded-lg text-center hover:text-third px-5 py-4"
|
<a class=move || format!("bg-prim-light container flex flex-col lg:gap-2 lg:flex-row w-full item-center hover:scale-110 transition hover:bg-prim-lightest border-b hover:border-third rounded-lg text-center hover:text-third px-5 py-4 {}", if link.with(|x| x.dashboard) { "border-third" } else { "border-transparent" })
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={move || link.with(|x| if use_alternate.get() { x.alternate_link.clone() } else { x.link.clone() })}>
|
href={move || link.with(|x| if use_alternate.get() { x.alternate_link.clone() } else { x.link.clone() })}>
|
||||||
<img src={move || link.with(|x| x.icon.to_string())}
|
<img src={move || link.with(|x| x.icon.to_string())}
|
||||||
@@ -210,8 +210,9 @@ pub async fn update_link(
|
|||||||
link: String,
|
link: String,
|
||||||
icon: String,
|
icon: String,
|
||||||
alternate_link: String,
|
alternate_link: String,
|
||||||
|
dashboard: bool,
|
||||||
) -> Result<(), ServerFnError> {
|
) -> Result<(), ServerFnError> {
|
||||||
crate::models::Link::update(id, name, link, icon, alternate_link)
|
crate::models::Link::update(id, name, link, icon, alternate_link, dashboard)
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|x| {
|
.map_err(|x| {
|
||||||
@@ -235,6 +236,8 @@ fn LinkFormModal(
|
|||||||
let (icon, set_icon) = create_signal(link.as_ref().map(|x| x.icon.clone()).unwrap_or_default());
|
let (icon, set_icon) = create_signal(link.as_ref().map(|x| x.icon.clone()).unwrap_or_default());
|
||||||
let (alternate_link, set_alternate_link) =
|
let (alternate_link, set_alternate_link) =
|
||||||
create_signal(link.as_ref().map(|x| x.alternate_link.clone()).unwrap_or_default());
|
create_signal(link.as_ref().map(|x| x.alternate_link.clone()).unwrap_or_default());
|
||||||
|
let (dashboard, set_dashboard) =
|
||||||
|
create_signal(link.as_ref().map(|x| x.dashboard).unwrap_or(false));
|
||||||
let link_id = link.as_ref().map(|x| x.id.to_string());
|
let link_id = link.as_ref().map(|x| x.id.to_string());
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
@@ -257,6 +260,8 @@ fn LinkFormModal(
|
|||||||
set_icon=set_icon
|
set_icon=set_icon
|
||||||
alternate_link=alternate_link
|
alternate_link=alternate_link
|
||||||
set_alternate_link=set_alternate_link
|
set_alternate_link=set_alternate_link
|
||||||
|
dashboard=dashboard
|
||||||
|
set_dashboard=set_dashboard
|
||||||
/>
|
/>
|
||||||
<FormButtons set_show=set_show />
|
<FormButtons set_show=set_show />
|
||||||
</ActionForm>
|
</ActionForm>
|
||||||
@@ -273,6 +278,8 @@ fn LinkFormModal(
|
|||||||
set_icon=set_icon
|
set_icon=set_icon
|
||||||
alternate_link=alternate_link
|
alternate_link=alternate_link
|
||||||
set_alternate_link=set_alternate_link
|
set_alternate_link=set_alternate_link
|
||||||
|
dashboard=dashboard
|
||||||
|
set_dashboard=set_dashboard
|
||||||
/>
|
/>
|
||||||
<FormButtons set_show=set_show />
|
<FormButtons set_show=set_show />
|
||||||
</ActionForm>
|
</ActionForm>
|
||||||
@@ -293,6 +300,8 @@ fn LinkFormFields(
|
|||||||
set_icon: WriteSignal<String>,
|
set_icon: WriteSignal<String>,
|
||||||
alternate_link: ReadSignal<String>,
|
alternate_link: ReadSignal<String>,
|
||||||
set_alternate_link: WriteSignal<String>,
|
set_alternate_link: WriteSignal<String>,
|
||||||
|
dashboard: ReadSignal<bool>,
|
||||||
|
set_dashboard: WriteSignal<bool>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
view! {
|
view! {
|
||||||
<div>
|
<div>
|
||||||
@@ -332,6 +341,15 @@ fn LinkFormFields(
|
|||||||
on:input=move |ev| set_icon.set(event_target_value(&ev))
|
on:input=move |ev| set_icon.set(event_target_value(&ev))
|
||||||
class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-full" />
|
class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-full" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" name="dashboard" prop:value=move || dashboard.get().to_string() />
|
||||||
|
<label class="flex items-center gap-2 mt-4 cursor-pointer select-none">
|
||||||
|
<input type="checkbox"
|
||||||
|
prop:checked=move || dashboard.get()
|
||||||
|
on:change=move |ev| set_dashboard.set(event_target_checked(&ev))
|
||||||
|
class="accent-third size-4 cursor-pointer" />
|
||||||
|
"Tableau de bord"
|
||||||
|
</label>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
mod home;
|
||||||
mod link;
|
mod link;
|
||||||
mod shutters;
|
mod shutters;
|
||||||
|
|
||||||
|
pub use home::*;
|
||||||
pub use link::*;
|
pub use link::*;
|
||||||
pub use shutters::*;
|
pub use shutters::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user