remove unused pages & update links

This commit is contained in:
Romulus21
2025-10-08 14:16:27 +02:00
parent e7a45dc46c
commit 3bf77add88
9 changed files with 120 additions and 294 deletions

View File

@@ -1,12 +1,18 @@
# install
```
cargo install sqlx-cli
wasm-pack build --target=web --debug --no-default-features --features=hydrate
cargo run --no-default-features --features=ssr
```
# dev
```
source .env
cargo sqlx migrate run
cargo leptos watch
```
npx tailwindcss -i ./input.css -o ./style/output.css --watch
```npx tailwindcss -i ./input.css -o ./style/output.css --watch```
# deploy

View File

@@ -20,8 +20,6 @@ pub fn App() -> impl IntoView {
<Routes>
<Route path="/" view=move || view! { <Home/> }/>
<Route path="/liens" view=move || view! { <Links/> }/>
<Route path="/formulaire" view=move || view! { <FormValues/> }/>
<Route path="/donnees" view=move || view! { <Data/> }/>
<Route path="/volets" view=move || view! { <Shutters/> }/>
//<Route path="/*any" view=move || view! { <NotFound/> }/>
</Routes>
@@ -54,12 +52,12 @@ pub fn Navigation() -> impl IntoView {
<a href="/liens"
class="hover:text-third hover:border-b border-third inline-block transition-colors"
class:active-link=move || location.pathname.get() == "/liens">Liens</a>
<a href="/formulaire"
class="hover:text-third hover:border-b border-third inline-block transition-colors"
class:active-link=move || location.pathname.get() == "/formulaire">Formulaire</a>
<a href="/donnees"
class="hover:text-third hover:border-b border-third inline-block transition-colors"
class:active-link=move || location.pathname.get() == "/donnees">Données</a>
//<a href="/formulaire"
// class="hover:text-third hover:border-b border-third inline-block transition-colors"
// class:active-link=move || location.pathname.get() == "/formulaire">Formulaire</a>
//<a href="/donnees"
// class="hover:text-third hover:border-b border-third inline-block transition-colors"
// class:active-link=move || location.pathname.get() == "/donnees">Données</a>
<a href="/volets"
class="hover:text-third hover:border-b border-third inline-block transition-colors"
class:active-link=move || location.pathname.get() == "/volets">Volets</a>

View File

@@ -94,6 +94,24 @@ impl Link {
}
}
#[cfg(feature = "ssr")]
pub async fn update(
id: String,
name: String,
link: String,
icon: String,
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
sqlx::query!(
"UPDATE links SET name = ?, link = ?, icon = ? WHERE id = ?",
name,
link,
icon,
id
)
.execute(crate::database::get_db())
.await
}
#[cfg(feature = "ssr")]
pub async fn destroy(id: String) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
sqlx::query!("DELETE FROM links WHERE id = ? ", id,)

View File

@@ -1,5 +1,3 @@
mod link;
mod value;
pub use link::Link;
pub use value::Value;
pub use value::OPTIONS;

View File

@@ -1,122 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct Option {
pub value: &'static str,
pub name: &'static str,
service: &'static str,
device: &'static str,
r_type: &'static str,
}
pub const OPTIONS: &[Option] = &[
Option {
value: "citerne",
name: "Citerne",
service: "home",
device: "citerne",
r_type: "height",
},
Option {
value: "eau",
name: "Eau",
service: "home",
device: "eau",
r_type: "volume",
},
Option {
value: "elec-hight",
name: "Electricité heure pleine",
service: "home",
device: "elec",
r_type: "pleine",
},
Option {
value: "elec-low",
name: "Electricité heure creuse",
service: "home",
device: "elec",
r_type: "creuse",
},
];
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Value {
id: i64,
service: String,
capteur: String,
type_donnee: String,
pub donnee: String,
pub date_donnee: String,
}
impl Value {
#[cfg(feature = "ssr")]
pub async fn insert(
device: String,
value: String,
) -> Result<sqlx::mysql::MySqlQueryResult, sqlx::Error> {
let option = match find_option(&device) {
Ok(option) => option,
Err(err) => panic!("{}", err),
};
sqlx::query!(
"INSERT INTO donnees(service, capteur, type, donnee, date_donnee) VALUES (?, ?, ?, ?, ?)",
option.service,
option.device,
option.r_type,
value,
chrono::Local::now().naive_local(),
)
.execute(crate::database::get_db())
.await
}
#[cfg(feature = "ssr")]
pub async fn get_one(topic: String) -> Result<Self, sqlx::Error> {
let split: Vec<&str> = topic.split("/").collect();
let unknow_value = Value {
id: 0,
service: "".to_string(),
capteur: "".to_string(),
type_donnee: "".to_string(),
donnee: "--".to_string(),
date_donnee: "--:--".to_string(),
};
if split.len() == 3 {
let res = sqlx::query!(
"SELECT * FROM donnees WHERE service = ? AND capteur = ? AND type = ? ORDER BY date_donnee DESC LIMIT 1",
split[0],
split[1],
split[2],
//chrono::Local::now().naive_local(),
)
.map(|x| Self {
id: x.id,
service: x.service,
capteur: x.capteur,
type_donnee: x.r#type,
donnee: x.donnee,
date_donnee: x.date_donnee.format("%H:%M").to_string(),
})
.fetch_one(crate::database::get_db())
.await;
match res {
Ok(res) => Ok(res),
Err(_) => Ok(unknow_value)
}
} else {
Ok(unknow_value)
}
}
}
fn find_option(value: &str) -> Result<&Option, &str> {
OPTIONS
.iter()
.find(|&option| option.value == value)
.ok_or("No option found with the given value")
}

View File

@@ -1,94 +0,0 @@
use leptos::*;
use leptos_meta::*;
use crate::models::Value;
#[component]
pub fn Data() -> impl IntoView {
view! {
<Title text="Capteurs"/>
<ul class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-3 m-5">
<ValueCard location="Bureau".to_string()
sensor="Température".to_string()
unity="°C".to_string()
color="text-third".to_string()
topic="maison/bureau/temperature".to_string() />
<ValueCard location="Bureau".to_string()
sensor="CO2".to_string()
unity="ppm".to_string()
color="text-green".to_string()
topic="maison/bureau/co2".to_string() />
<ValueCard location="Bureau".to_string()
sensor="Pression".to_string()
unity="hPa".to_string()
color="text-fourth".to_string()
topic="maison/bureau/pression".to_string() />
<ValueCard location="Extérieur".to_string()
sensor="Température".to_string()
unity="°C".to_string()
color="text-third".to_string()
topic="meteo/exterieur/temperature".to_string() />
<ValueCard location="Véranda".to_string()
sensor="Température".to_string()
unity="°C".to_string()
color="text-third".to_string()
topic="meteo/veranda/temperature".to_string() />
</ul>
}
}
#[server(ValueAction, "/api")]
pub async fn get_value(topic: String) -> Result<crate::models::Value, ServerFnError> {
crate::models::Value::get_one(topic).await.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]
pub fn ValueCard(topic: String, location: String, sensor: String, unity: String, color: String) -> impl IntoView {
let (sensor_props, _) = create_signal((location, sensor, unity, color));
let value = create_resource(
move || topic.clone(),
|topic| async { get_value(topic).await },
);
view! {
<li class="flex p-3 bg-prim-light rounded-lg justify-between">
<Suspense fallback=move || view! { <p>"Loading Value"</p> }>
<ErrorBoundary fallback=|_| {
view! { <p class="error-messages text-xs-center">"Something went wrong, please try again later."</p>}
}>
{move || value.get().map(move |x| {
x.map(move |result| {
view! {
<Card sensor_props value=result />
}
})
})
}
</ErrorBoundary>
</Suspense>
</li>
}
}
#[component]
pub fn Card(sensor_props: ReadSignal<(String, String, String, String)>, value: Value) -> impl IntoView {
let (location, sensor, unity, color) = sensor_props.get();
let value_class = ["text-4xl flex ml-5 -my-2 text-center ", color.as_str()].join(" ");
view! {
<div class="flex flex-col">
<div class="text-xl flex-1">{location}</div>
<div class="text-second-dark">{sensor}</div>
<div class="text-second-dark text-sm">{value.date_donnee}</div>
</div>
<h2 class=value_class>
<strong>{value.donnee}</strong>
<span class="text-2xl mt-5">{unity}</span>
</h2>
}
}

View File

@@ -45,7 +45,7 @@ pub fn Links() -> impl IntoView {
show.get().then(|| {
view! { <div class="my-0 mx-auto w-72 p-6 bg-prim-light rounded-lg">
<h2 class="pb-6 text-2xl text-center">"Ajout d'un lien"</h2>
<ActionForm action=link_action attr:class="">
<ActionForm action=link_action>
<div>
<label class="block mt-3 mb-1">"Nom"</label>
<input type="text"
@@ -130,6 +130,8 @@ fn Link<T: 'static + Clone, S: 'static>(
edit: ReadSignal<bool>,
links: Resource<T, S>,
) -> impl IntoView {
let (show_edit, set_show_edit) = create_signal(false);
view! {
<li class="mx-auto w-44 lg:w-60">
<a class="bg-prim-light w-full hover:bg-prim-lightest border-b hover:border-third border-transparent text-xl rounded-lg text-center hover:text-third transition-colors px-5 py-4 inline-block"
@@ -148,13 +150,18 @@ fn Link<T: 'static + Clone, S: 'static>(
view! { <div class="bg-third border border-transparent rounded-b-lg flex justify-between">
<MoveButton id=link.with(|x| x.id.to_string()) value="prev".to_string() label="<".to_string() links=links />
<button class="block px-2 py-1 flex-1 hover:bg-third-light"
//on:click=move |_| {set_show.set(true)}
on:click=move |_| {set_show_edit.set(true)}
>"Editer"</button>
<DeleteButton id=link.with(|x| x.id.to_string()) links=links />
<MoveButton id=link.with(|x| x.id.to_string()) value="next".to_string() label=">".to_string() links=links />
</div> }
})
}}
{move || {
show_edit.get().then(|| {
view! { <EditLinkForm link=link set_show=set_show_edit links=links /> }
})
}}
</li>
}
}
@@ -213,3 +220,82 @@ fn DeleteButton<T: 'static + Clone, S: 'static>(
</ActionForm>
}
}
#[server(UpdateLinkAction, "/api")]
pub async fn update_link(id: String, name: String, link: String, icon: String) -> Result<(), ServerFnError> {
crate::models::Link::update(id, name, link, icon)
.await
.map(|_| ())
.map_err(|x| {
let err = format!("Error while updating a link: {x:?}");
tracing::error!("{err}");
ServerFnError::ServerError("Could not update the link, try again later".into())
})
}
#[component]
fn EditLinkForm<T: 'static + Clone, S: 'static>(
link: RwSignal<crate::models::Link>,
set_show: WriteSignal<bool>,
links: Resource<T, S>,
) -> impl IntoView {
let update_action = create_server_action::<UpdateLinkAction>();
let (name, set_name) = create_signal(link.with(|x| x.name.clone()));
let (link_url, set_link_url) = create_signal(link.with(|x| x.link.clone()));
let (icon, set_icon) = create_signal(link.with(|x| x.icon.clone()));
create_effect(move |_| {
if update_action.value().get().is_some() {
links.refetch();
set_show.set(false);
}
});
view! {
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-prim-light p-6 rounded-lg w-96">
<h2 class="pb-6 text-2xl text-center">"Édition du lien"</h2>
<ActionForm action=update_action>
<input name="id" type="hidden" value={move || link.with(|x| x.id.to_string())} />
<div>
<label class="block mt-3 mb-1">"Nom"</label>
<input type="text"
name="name"
prop:value=move || name.get()
on:input=move |ev| set_name.set(event_target_value(&ev))
class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-full" />
</div>
<div>
<label class="block mt-3 mb-1">"Lien"</label>
<input type="url"
name="link"
prop:value=move || link_url.get()
on:input=move |ev| set_link_url.set(event_target_value(&ev))
class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-full" />
</div>
<div>
<label class="block mt-3 mb-1">"Icône"</label>
<input type="url"
name="icon"
prop:value=move || icon.get()
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" />
</div>
<div class="flex gap-2 mt-5">
<button type="button"
on:click=move |_| set_show.set(false)
class="bg-prim hover:bg-prim-light rounded-lg transition-colors px-2 py-1 flex-1">"Annuler"</button>
<button type="submit"
class="bg-third hover:bg-third-light rounded-lg transition-colors px-2 py-1 flex-1">"Valider"</button>
</div>
</ActionForm>
</div>
</div>
}
}

View File

@@ -1,9 +1,5 @@
mod data;
mod link;
mod value;
mod shutters;
pub use data::*;
pub use link::*;
pub use value::*;
pub use shutters::*;

View File

@@ -1,60 +0,0 @@
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
use crate::models::OPTIONS;
#[server(ValueAction, "/api")]
pub async fn add_value(device: String, value: String) -> Result<(), ServerFnError> {
crate::models::Value::insert(device, value)
.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]
pub fn FormValues() -> impl IntoView {
let value_action = create_server_action::<ValueAction>();
let result = value_action.version();
let reset_value = create_rw_signal("");
let _ = create_resource(
move || (result.get()),
move |_| async move {
reset_value.set("");
},
);
view! {
<Title text="Formulaire"/>
<div class="my-5 mx-auto w-72 p-6 bg-prim-light rounded-lg">
<h2 class="pb-6 text-2xl text-center">"Formulaire"</h2>
<ActionForm action=value_action attr:class="">
<div>
<label class="block mt-3 mb-1">Capteur</label>
<select name="device" class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-60">
{OPTIONS.into_iter()
.map(|option| view! { <option value={option.value} class="px-2 py-1">{option.name}</option>})
.collect::<Vec<_>>()}
</select>
</div>
<div>
<label class="block mt-3 mb-1">Valeur</label>
<input name="value"
type="number"
step="0.01"
prop:value=move || reset_value.get()
class="text-center bg-prim border border-transparent rounded-lg focus:border-third px-2 py-2 w-60" />
</div>
<div>
<button type="submit" class="bg-third hover:bg-third-light rounded-lg transition-colors px-2 py-1 w-60 my-5">Valider</button>
</div>
</ActionForm>
</div>
}
}