Leptos updated to 0.5

This commit is contained in:
2023-09-15 13:55:01 +02:00
parent 9dd0993204
commit bbad2bb183
18 changed files with 234 additions and 281 deletions
+7 -8
View File
@@ -6,13 +6,12 @@ use crate::pages::home_page::HomePage;
use crate::pages::settings::Settings;
#[component]
pub fn App(cx: Scope) -> impl IntoView {
pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
provide_meta_context(cx);
//provide_context(cx, DialogOpener::new(cx));
provide_meta_context();
//provide_context(DialogOpener::new());
view! {
cx,
<Html
lang="cz"
dir="ltr"
@@ -61,7 +60,7 @@ pub fn App(cx: Scope) -> impl IntoView {
<li class="menu-item">
<a href="/" class="menu-link">
<i class="menu-icon tf-icons bx bx-home-circle"></i>
<div data-i18n="Analytics">{trl(cx, "Dashboard")}</div>
<div data-i18n="Analytics">{trl("Dashboard")}</div>
</a>
</li>
<li class="menu-item">
@@ -173,8 +172,8 @@ pub fn App(cx: Scope) -> impl IntoView {
<Router>
<main>
<Routes>
<Route path="" view=|cx| view! { cx, <HomePage/> }/>
<Route path="settings" view=|cx| view! { cx, <Settings/> }/>
<Route path="" view=|| view! { <HomePage/> }/>
<Route path="settings" view=|| view! { <Settings/> }/>
</Routes>
</main>
</Router>
@@ -212,7 +211,7 @@ pub fn App(cx: Scope) -> impl IntoView {
<Router>
<main>
<Routes>
<Route path="" view=|cx| view! { cx, <HomePage/> }/>
<Route path="" view=|| view! { <HomePage/> }/>
</Routes>
</main>
</Router>
+4 -12
View File
@@ -2,16 +2,12 @@ use crate::backend::data::Company;
use leptos::*;
#[server(GetCompany, "/api", "Url", "get_company")]
pub async fn get_company(cx: Scope) -> Result<Company, ServerFnError> {
pub async fn get_company() -> Result<Company, ServerFnError> {
use crate::backend::AppData;
use actix_web::web::Data;
use leptos_actix::extract;
let pool = extract(
cx,
|data: Data<AppData>| async move { data.db_pool().clone() },
)
.await?;
let pool = extract(|data: Data<AppData>| async move { data.db_pool().clone() }).await?;
let cmp = sqlx::query_as::<_, Company>("SELECT * FROM company")
.fetch_one(&pool)
@@ -21,16 +17,12 @@ pub async fn get_company(cx: Scope) -> Result<Company, ServerFnError> {
}
#[server(UpdateCompany, "/api", "Url", "update_company")]
pub async fn update_company(cx: Scope, company: Company) -> Result<(), ServerFnError> {
pub async fn update_company(company: Company) -> Result<(), ServerFnError> {
use crate::backend::AppData;
use actix_web::web::Data;
use leptos_actix::extract;
let pool = extract(
cx,
|data: Data<AppData>| async move { data.db_pool().clone() },
)
.await?;
let pool = extract(|data: Data<AppData>| async move { data.db_pool().clone() }).await?;
sqlx::query(
"UPDATE company SET name = $1, street = $2, house_number = $3, zip_code = $4, city = $5 \
+1 -1
View File
@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use uuid::Uuid;
use validator::Validate;
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate)]
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate, Default)]
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
pub struct Company {
id: i32,
+15 -16
View File
@@ -8,8 +8,8 @@ pub struct DialogOpener {
}
impl DialogOpener {
pub fn new(cx: Scope) -> Self {
let (visible, set_visible) = create_signal(cx, false);
pub fn new() -> Self {
let (visible, set_visible) = create_signal(false);
DialogOpener {
visible,
set_visible,
@@ -31,19 +31,18 @@ impl DialogOpener {
#[component]
pub fn ModalDialog(
cx: Scope,
opener: DialogOpener,
title: &'static str,
children: Children,
) -> impl IntoView {
view! {cx,
view! {
<div class={ move || if opener.visible() {"modal fade show"} else {"modal fade"}}
style={ move || if opener.visible() {"display: block;"} else {""}}
id="modalCenter" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalCenterTitle">{trl(cx, title)}</h5>
<h5 class="modal-title" id="modalCenterTitle">{trl(title)}</h5>
<button
type="button"
class="btn-close"
@@ -51,7 +50,7 @@ pub fn ModalDialog(
aria-label="Close"
on:click=move |_| opener.hide()/>
</div>
{children(cx)}
{children()}
</div>
</div>
</div>
@@ -59,33 +58,33 @@ pub fn ModalDialog(
}
#[component]
pub fn ModalBody(cx: Scope, children: Children) -> impl IntoView {
view! {cx,
pub fn ModalBody(children: Children) -> impl IntoView {
view! {
<div class="modal-body">
{children(cx)}
{children()}
</div>
}
}
#[component]
pub fn ModalFooter(cx: Scope, children: Children) -> impl IntoView {
view! {cx,
pub fn ModalFooter(children: Children) -> impl IntoView {
view! {
<div class="modal-footer">
{children(cx)}
{children()}
</div>
}
}
#[component]
pub fn DlgNotLoaded(cx: Scope, opener: DialogOpener, title: &'static str) -> impl IntoView {
view! {cx,
pub fn DlgNotLoaded(opener: DialogOpener, title: &'static str) -> impl IntoView {
view! {
<ModalDialog opener=opener title=title>
<ModalBody>
<div>{trl(cx, "Entity not loaded")}</div>
<div>{trl("Entity not loaded")}</div>
</ModalBody>
<ModalFooter>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal" on:click=move |_| opener.hide()>
{trl(cx, "Close")}
{trl("Close")}
</button>
</ModalFooter>
</ModalDialog>
+4 -5
View File
@@ -3,24 +3,23 @@ use leptos::*;
#[component]
pub fn ServerErr(
cx: Scope,
result: RwSignal<Option<Result<(), ServerFnError>>>,
opener: DialogOpener,
) -> impl IntoView {
view! {cx, {move || {
view! {{move || {
if let Some(val) = result.get() {
if let Err(e) = val {
view! {cx,
view! {
<div class="alert alert-danger">
"Server error: " {e.to_string()}
</div>
}
} else {
opener.hide();
view! {cx, <div></div>}
view! {<div></div>}
}
} else {
view! {cx, <div></div>}
view! {<div></div>}
}
}}
}
+7 -8
View File
@@ -4,33 +4,32 @@ use crate::validator::Validator;
#[component]
pub fn ValidationErr(
cx: Scope,
validator: Validator,
) -> impl IntoView {
view! {cx, {move || {
view! {{move || {
if !validator.is_valid() {
if let Some(msgs) = validator.messages() {
let out_msgs = msgs.into_iter().map(move |e| {
view! {cx,
view! {
<div class="alert alert-danger">
{trl(cx, &e)}
{trl(&e)}
</div>
}
}).collect_view(cx);
view! {cx,
}).collect_view();
view! {
<div>
{out_msgs}
</div>
}
} else {
view! {cx,
view! {
<div class="alert alert-danger">
"Validation error"
</div>
}
}
} else {
view! {cx, <div></div>}
view! {<div></div>}
}
}}
}
+2 -2
View File
@@ -20,8 +20,8 @@ if #[cfg(feature = "hydrate")] {
console_error_panic_hook::set_once();
leptos::mount_to_body(move |cx| {
view! { cx, <App/> }
leptos::mount_to_body(move || {
view! { <App/> }
});
}
}
+5 -5
View File
@@ -24,10 +24,10 @@ lazy_static! {
};
}
fn get_locales(cx: Scope) -> Vec<Option<String>> {
let (loc, set_loc) = create_signal(cx, Vec::new());
fn get_locales() -> Vec<Option<String>> {
let (loc, set_loc) = create_signal(Vec::new());
create_effect(cx, move |_| {
create_effect(move |_| {
let js_locales = window().navigator().languages();
set_loc.update(|l| *l = js_locales.into_iter().map(|val| val.as_string()).collect::<Vec<_>>());
});
@@ -35,8 +35,8 @@ fn get_locales(cx: Scope) -> Vec<Option<String>> {
loc.get_untracked()
}
pub fn get_dictionary(cx: Scope) -> Option<&'static HashMap<&'static str, &'static str>> {
let locs = get_locales(cx);
pub fn get_dictionary() -> Option<&'static HashMap<&'static str, &'static str>> {
let locs = get_locales();
for loc in locs {
if let Some(key) = loc {
+2 -2
View File
@@ -5,9 +5,9 @@ use crate::locales::catalogues::get_dictionary;
mod catalogues;
pub fn trl(cx: Scope, phrase: &str) -> impl Fn() -> String {
pub fn trl(phrase: &str) -> impl Fn() -> String {
let mut translated = phrase;
if let Some(dict) = get_dictionary(cx) {
if let Some(dict) = get_dictionary() {
if let Some(p) = dict.get(phrase.to_string().as_str()) {
translated = *p;
}
+5 -4
View File
@@ -1,3 +1,4 @@
use actix_web::middleware::DefaultHeaders;
use sqlx::migrate;
use sqlx::postgres::PgPoolOptions;
@@ -18,7 +19,7 @@ async fn main() -> std::io::Result<()> {
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(|cx| view! { cx, <App/> });
let routes = generate_route_list(|| view! { <App/> });
let key = Key::generate();
let pool = PgPoolOptions::new()
@@ -41,7 +42,7 @@ async fn main() -> std::io::Result<()> {
.leptos_routes(
leptos_options.to_owned(),
routes.to_owned(),
|cx| view! { cx, <App/> },
|| view! { <App/> },
)
.service(Files::new("/", site_root))
//.wrap(middleware::Compress::default())
@@ -70,9 +71,9 @@ pub fn main() {
console_error_panic_hook::set_once();
leptos::mount_to_body(move |cx| {
leptos::mount_to_body(move || {
// note: for testing it may be preferrable to replace this with a
// more specific component, although leptos_router should still work
view! {cx, <App/> }
view! {<App/> }
});
}
+90 -100
View File
@@ -12,108 +12,98 @@ use crate::components::validation_err::ValidationErr;
#[component]
pub fn CompanyEdit(
cx: Scope,
company: ReadSignal<Option<Company>>,
company: ReadSignal<Company>,
opener: DialogOpener,
) -> impl IntoView {
view! {cx,
{move ||
if let Some(c) = company.get() {
let update_company = create_server_action::<UpdateCompany>(cx);
let upd_val = update_company.value();
let validator = Validator::new(cx);
view! {cx,
<ActionForm
on:submit=move |ev| {
let act = UpdateCompany::from_event(&ev);
if !act.is_err() {
validator.check(&act.unwrap().company, &ev);
}
}
action=update_company>
<ModalDialog opener=opener title="Edit company">
<ModalBody>
<ServerErr result={upd_val} opener=opener/>
<ValidationErr validator=validator />
<input type="hidden" value=c.id() name="company[id]"/>
<div class="row">
<div class="col mb-3">
<label for="nameWithTitle" class="form-label">"Name"</label>
<input
type="text"
id="nameWithTitle"
class="form-control"
placeholder="Enter Name"
value=c.name
name="company[name]"
/>
</div>
</div>
<div class="row">
<div class="col mb-3">
<label for="street" class="form-label">"Street"</label>
<input
type="text"
id="street"
class="form-control"
placeholder="Enter Street"
value=c.street
name="company[street]"
/>
</div>
<div class="col-4 mb-3">
<label for="houseNumber" class="form-label">"House number"</label>
<input
type="text"
id="houseNumber"
class="form-control"
placeholder="Enter House number"
value=c.house_number
name="company[house_number]"
/>
</div>
</div>
<div class="row">
<div class="col-4 mb-3">
<label for="zip" class="form-label">"ZIP code"</label>
<input
type="text"
id="zip"
class="form-control"
placeholder="Enter ZIP code"
value=c.zip_code
name="company[zip_code]"
/>
</div>
<div class="col mb-3">
<label for="city" class="form-label">"City"</label>
<input
type="text"
id="city"
class="form-control"
placeholder="Enter City"
value=c.city
name="company[city]"
/>
</div>
</div>
</ModalBody>
<ModalFooter>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal" on:click=move |_| opener.hide()>
{trl(cx, "Close")}
</button>
<button type="submit" class="btn btn-primary">
{trl(cx, "Save changes")}
</button>
</ModalFooter>
</ModalDialog>
</ActionForm>
let update_company = create_server_action::<UpdateCompany>();
let upd_val = update_company.value();
let validator = Validator::new();
view! {
<ActionForm
on:submit=move |ev| {
let act = UpdateCompany::from_event(&ev);
if !act.is_err() {
validator.check(&act.unwrap().company, &ev);
}
}
} else {
view! {cx,
<DlgNotLoaded opener=opener title="Edit company" />
}
}
}
action=update_company>
<ModalDialog opener=opener title="Edit company">
<ModalBody>
<ServerErr result={upd_val} opener=opener/>
<ValidationErr validator=validator />
<input type="hidden" value={move || company.get().id()} name="company[id]"/>
<div class="row">
<div class="col mb-3">
<label for="nameWithTitle" class="form-label">"Name"</label>
<input
type="text"
id="nameWithTitle"
class="form-control"
placeholder="Enter Name"
value={move || company.get().name}
name="company[name]"
/>
</div>
</div>
<div class="row">
<div class="col mb-3">
<label for="street" class="form-label">"Street"</label>
<input
type="text"
id="street"
class="form-control"
placeholder="Enter Street"
value={move || company.get().street}
name="company[street]"
/>
</div>
<div class="col-4 mb-3">
<label for="houseNumber" class="form-label">"House number"</label>
<input
type="text"
id="houseNumber"
class="form-control"
placeholder="Enter House number"
value={move || company.get().house_number}
name="company[house_number]"
/>
</div>
</div>
<div class="row">
<div class="col-4 mb-3">
<label for="zip" class="form-label">"ZIP code"</label>
<input
type="text"
id="zip"
class="form-control"
placeholder="Enter ZIP code"
value={move || company.get().zip_code}
name="company[zip_code]"
/>
</div>
<div class="col mb-3">
<label for="city" class="form-label">"City"</label>
<input
type="text"
id="city"
class="form-control"
placeholder="Enter City"
value={move || company.get().city}
name="company[city]"
/>
</div>
</div>
</ModalBody>
<ModalFooter>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal" on:click=move |_| opener.hide()>
{trl("Close")}
</button>
<button type="submit" class="btn btn-primary">
{trl("Save changes")}
</button>
</ModalFooter>
</ModalDialog>
</ActionForm>
}
}
+12 -11
View File
@@ -1,32 +1,33 @@
use leptos::*;
use serde::de::Unexpected::Option;
use crate::backend::company::get_company;
use crate::backend::data::Company;
use crate::components::modal_box::DialogOpener;
use crate::locales::trl;
use crate::pages::company_edit::CompanyEdit;
#[component]
pub fn CompanyInfo(cx: Scope) -> impl IntoView {
let editor = DialogOpener::new(cx);
let company = create_resource(cx, move|| editor.visible(), move |_| { get_company(cx) });
let (cmp, set_cmp) = create_signal(cx, None);
pub fn CompanyInfo() -> impl IntoView {
let editor = DialogOpener::new();
let company = create_resource(move|| editor.visible(), move |_| { get_company() });
let (cmp, set_cmp) = create_signal(Company::default());
view! {cx,
view! {
<CompanyEdit company={cmp} opener=editor/>
<div class="card">
<div class="card-body">
<h5 class="card-title"><i class="bx bx-buildings"></i>" "{trl(cx, "Company info")}</h5>
<h5 class="card-title"><i class="bx bx-buildings"></i>" "{trl("Company info")}</h5>
<p class="card-text">
<Transition fallback=move || view! {cx, <p>{trl(cx, "Loading...")}</p> }>
<Transition fallback=move || view! {<p>{trl("Loading...")}</p> }>
{move || {
company.read(cx).map(|c| match c {
Err(e) => {view! {cx, <p>{trl(cx, "Error loading data")}</p>
company.read().map(|c| match c {
Err(e) => {view! {<p>{trl("Error loading data")}</p>
<p>{e.to_string()}</p>
}}
Ok(c) => {
set_cmp.update(|cmp| *cmp = Some(c.clone()));
set_cmp.update(|cmp| *cmp = c.clone());
view! {
cx, <p><b>{c.name}</b></p>
<p><b>{c.name}</b></p>
<p>{c.street}" "{c.house_number}<br/>
{c.zip_code}" "{c.city}
</p>
+9 -9
View File
@@ -5,21 +5,21 @@ use crate::locales::trl;
/// Renders the home page of your application.
#[component]
pub fn HomePage(cx: Scope) -> impl IntoView {
pub fn HomePage() -> impl IntoView {
// Creates a reactive value to update the button
let (count, set_count) = create_signal(cx, 0);
let (count, set_count) = create_signal(0);
let on_click = move |_| set_count.update(|count| *count += 1);
let dialog = DialogOpener::new(cx);
let dialog = DialogOpener::new();
//let (dialog, set_dialog) = create_signal(cx, false);
//let (dialog, set_dialog) = create_signal(false);
//let on_dialog = move |_| dialog.set_visible.update(|dialog| {*dialog = true});
//let pok = use_context::<Request>(cx);
//let pok = use_context::<Request>();
//log!("{:?}", pok);
view! { cx,
view! {
<ModalDialog opener={dialog} title="Titulek">
<ModalBody>
<div class="row">
@@ -67,14 +67,14 @@ pub fn HomePage(cx: Scope) -> impl IntoView {
<button on:click=move |_| dialog.show()>"Dialog"</button>
<button on:click=move |_| {
spawn_local(async move {
set_session(cx).await;
set_session().await;
});
}>"Session"</button>
<button on:click=move |_| {
spawn_local(async move {
get_session(cx).await;
get_session().await;
});
}>"Session get"</button>
<p>{trl(cx, "testik!")}</p>
<p>{trl("testik!")}</p>
}
}
+3 -3
View File
@@ -3,9 +3,9 @@ use crate::locales::trl;
use crate::pages::company_info::CompanyInfo;
#[component]
pub fn Settings(cx: Scope) -> impl IntoView {
view! {cx,
<h1>{trl(cx, "Settings")}</h1>
pub fn Settings() -> impl IntoView {
view! {
<h1>{trl("Settings")}</h1>
<div class="row mb-5">
<div class="col-md-6 col-lg-4 mb-3">
<CompanyInfo/>
+8 -6
View File
@@ -1,14 +1,15 @@
use leptos::*;
#[server(SetSession, "/api", "Url", "set_session")]
pub async fn set_session(cx: Scope) -> Result<(), ServerFnError> {
pub async fn set_session() -> Result<(), ServerFnError> {
use leptos_actix::extract;
use actix_session::*;
use actix_web::web::Data;
use leptos::logging::log;
//use crate::DataPok;
extract(cx, |session: Session| async move {
leptos::log!("extract");
extract(|session: Session| async move {
log!("extract");
let pok = session.insert("user", "uzivatel");
log!("{pok:?}");
}).await
@@ -17,12 +18,13 @@ pub async fn set_session(cx: Scope) -> Result<(), ServerFnError> {
}
#[server(GetSession, "/api")]
pub async fn get_session(cx: Scope) -> Result<(), ServerFnError> {
pub async fn get_session() -> Result<(), ServerFnError> {
use leptos_actix::extract;
use actix_session::*;
use leptos::logging::log;
extract(cx, |session: Session| async move {
leptos::log!("extract");
extract(|session: Session| async move {
log!("extract");
let pok = session.get::<String>("user");
log!("{pok:?}");
}).await
+4 -4
View File
@@ -12,10 +12,10 @@ pub struct Validator {
}
impl Validator {
pub fn new(cx: Scope) -> Self {
let (valid, set_valid) = create_signal(cx, true);
let (message, set_message) = create_signal(cx, None);
let (messages, set_messages) = create_signal(cx, None);
pub fn new() -> Self {
let (valid, set_valid) = create_signal(true);
let (message, set_message) = create_signal(None);
let (messages, set_messages) = create_signal(None);
Self {
message,
set_message,