Added settings for company info. Added code for entity validation.
This commit is contained in:
Generated
+49
@@ -1504,6 +1504,12 @@ dependencies = [
|
|||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "if_chain"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.3"
|
version = "1.9.3"
|
||||||
@@ -2503,6 +2509,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
"validator",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
@@ -3558,6 +3565,48 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "validator"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd"
|
||||||
|
dependencies = [
|
||||||
|
"idna",
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"url",
|
||||||
|
"validator_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "validator_derive"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af"
|
||||||
|
dependencies = [
|
||||||
|
"if_chain",
|
||||||
|
"lazy_static",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"validator_types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "validator_types"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ chrono = "0.4.26"
|
|||||||
sqlx = { version = "0.7.1", optional = true, features = ["runtime-tokio-rustls", "postgres", "chrono", "rust_decimal"] }
|
sqlx = { version = "0.7.1", optional = true, features = ["runtime-tokio-rustls", "postgres", "chrono", "rust_decimal"] }
|
||||||
rust_decimal = "1.31.0"
|
rust_decimal = "1.31.0"
|
||||||
uuid = {version = "1.4.1", features = ["v4"]}
|
uuid = {version = "1.4.1", features = ["v4"]}
|
||||||
|
validator = {version = "0.16.1", features = ["derive"]}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
|
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
|
||||||
|
|||||||
+6
-10
@@ -3,6 +3,7 @@ use leptos_meta::*;
|
|||||||
use leptos_router::*;
|
use leptos_router::*;
|
||||||
use crate::locales::trl;
|
use crate::locales::trl;
|
||||||
use crate::pages::home_page::HomePage;
|
use crate::pages::home_page::HomePage;
|
||||||
|
use crate::pages::settings::Settings;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn App(cx: Scope) -> impl IntoView {
|
pub fn App(cx: Scope) -> impl IntoView {
|
||||||
@@ -98,21 +99,15 @@ pub fn App(cx: Scope) -> impl IntoView {
|
|||||||
|
|
||||||
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
|
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
|
||||||
//<!-- Search -->
|
//<!-- Search -->
|
||||||
<div class="navbar-nav align-items-center">
|
<div class="navbar-nav align-items-center ms-auto mt-auto">
|
||||||
<div class="nav-item d-flex align-items-center">
|
<div class="nav-item d-flex align-items-center mt-auto">
|
||||||
<i class="bx bx-search fs-4 lh-0"></i>
|
<h4 class="mt-3"><i class="bx bx-desktop fs-4 lh-0"></i>" Admin portal"</h4>
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control border-0 shadow-none"
|
|
||||||
placeholder={trl(cx, "Search...")}
|
|
||||||
aria-label={trl(cx, "Search...")}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
//<!-- /Search -->
|
//<!-- /Search -->
|
||||||
<ul class="navbar-nav flex-row align-items-center ms-auto">
|
<ul class="navbar-nav flex-row align-items-center ms-auto">
|
||||||
<li class="nav-item navbar-dropdown dropdown-user dropdown">
|
<li class="nav-item navbar-dropdown dropdown-user dropdown">
|
||||||
<a class="nav-link dropdown-toggle hide-arrow" href="#" data-bs-toggle="dropdown">
|
<a class="nav-link dropdown-toggle hide-arrow" href="/settings" data-bs-toggle="dropdown">
|
||||||
<i class="bx bx-cog fs-3 lh-0"></i>
|
<i class="bx bx-cog fs-3 lh-0"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -179,6 +174,7 @@ pub fn App(cx: Scope) -> impl IntoView {
|
|||||||
<main>
|
<main>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="" view=|cx| view! { cx, <HomePage/> }/>
|
<Route path="" view=|cx| view! { cx, <HomePage/> }/>
|
||||||
|
<Route path="settings" view=|cx| view! { cx, <Settings/> }/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</main>
|
</main>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
use crate::backend::data::Company;
|
||||||
|
use leptos::*;
|
||||||
|
|
||||||
|
#[server(GetCompany, "/api", "Url", "get_company")]
|
||||||
|
pub async fn get_company(cx: Scope) -> 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 cmp = sqlx::query_as::<_, Company>("SELECT * FROM company")
|
||||||
|
.fetch_one(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(cmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(UpdateCompany, "/api", "Url", "update_company")]
|
||||||
|
pub async fn update_company(cx: Scope, 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?;
|
||||||
|
|
||||||
|
sqlx::query(
|
||||||
|
"UPDATE company SET name = $1, street = $2, house_number = $3, zip_code = $4, city = $5 \
|
||||||
|
WHERE id = $6")
|
||||||
|
.bind(company.name.clone())
|
||||||
|
.bind(company.street.clone())
|
||||||
|
.bind(company.house_number.clone())
|
||||||
|
.bind(company.zip_code.clone())
|
||||||
|
.bind(company.city.clone())
|
||||||
|
.bind(company.id())
|
||||||
|
.execute(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
+19
-6
@@ -1,14 +1,27 @@
|
|||||||
use chrono::{NaiveDate, NaiveTime, Weekday};
|
use chrono::{NaiveDate, NaiveTime, Weekday};
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Validate)]
|
||||||
|
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]
|
||||||
pub struct Company {
|
pub struct Company {
|
||||||
id: u16,
|
id: i32,
|
||||||
name: String,
|
#[validate(length(min = 1,message = "Name cannot be empty"))]
|
||||||
street: String,
|
pub name: String,
|
||||||
house_number: String,
|
#[validate(length(min = 1,message = "Street cannot be empty"))]
|
||||||
zip_code: String,
|
pub street: String,
|
||||||
city: String,
|
#[validate(length(min = 1,message = "House number cannot be empty"))]
|
||||||
|
pub house_number: String,
|
||||||
|
pub zip_code: String,
|
||||||
|
pub city: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Company {
|
||||||
|
pub fn id(&self) -> i32 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
pub mod company;
|
||||||
|
|
||||||
cfg_if!{
|
cfg_if!{
|
||||||
if #[cfg(feature = "ssr")] {
|
if #[cfg(feature = "ssr")] {
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
pub mod modal_box;
|
pub mod modal_box;
|
||||||
|
pub mod server_err;
|
||||||
|
pub mod validation_err;
|
||||||
|
|
||||||
|
|||||||
@@ -75,3 +75,19 @@ pub fn ModalFooter(cx: Scope, children: Children) -> impl IntoView {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DlgNotLoaded(cx: Scope, opener: DialogOpener, title: &'static str) -> impl IntoView {
|
||||||
|
view! {cx,
|
||||||
|
<ModalDialog opener=opener title=title>
|
||||||
|
<ModalBody>
|
||||||
|
<div>{trl(cx, "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")}
|
||||||
|
</button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalDialog>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::components::modal_box::DialogOpener;
|
||||||
|
use leptos::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn ServerErr(
|
||||||
|
cx: Scope,
|
||||||
|
result: RwSignal<Option<Result<(), ServerFnError>>>,
|
||||||
|
opener: DialogOpener,
|
||||||
|
) -> impl IntoView {
|
||||||
|
view! {cx, {move || {
|
||||||
|
if let Some(val) = result.get() {
|
||||||
|
if let Err(e) = val {
|
||||||
|
view! {cx,
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
"Server error: " {e.to_string()}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opener.hide();
|
||||||
|
view! {cx, <div></div>}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view! {cx, <div></div>}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use crate::locales::trl;
|
||||||
|
use crate::validator::Validator;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn ValidationErr(
|
||||||
|
cx: Scope,
|
||||||
|
validator: Validator,
|
||||||
|
) -> impl IntoView {
|
||||||
|
view! {cx, {move || {
|
||||||
|
if !validator.is_valid() {
|
||||||
|
if let Some(msgs) = validator.messages() {
|
||||||
|
let out_msgs = msgs.into_iter().map(move |e| {
|
||||||
|
view! {cx,
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{trl(cx, &e)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}).collect_view(cx);
|
||||||
|
view! {cx,
|
||||||
|
<div>
|
||||||
|
{out_msgs}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view! {cx,
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
"Validation error"
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view! {cx, <div></div>}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ pub mod locales;
|
|||||||
pub mod backend;
|
pub mod backend;
|
||||||
mod pages;
|
mod pages;
|
||||||
mod components;
|
mod components;
|
||||||
|
mod validator;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ lazy_static! {
|
|||||||
("Settings", "Nastavení"),
|
("Settings", "Nastavení"),
|
||||||
("Search...", "Najít..."),
|
("Search...", "Najít..."),
|
||||||
("Close", "Zavřít"),
|
("Close", "Zavřít"),
|
||||||
("Save changes", "Uložit změny")
|
("Save changes", "Uložit změny"),
|
||||||
|
("Company info", "Organizace"),
|
||||||
|
("Name cannot be empty", "Jméno nesmí být prázdné"),
|
||||||
])),
|
])),
|
||||||
("sk", HashMap::from( [
|
("sk", HashMap::from( [
|
||||||
("Dashboard", "Prehlad"),
|
("Dashboard", "Prehlad"),
|
||||||
|
|||||||
+7
-3
@@ -1,15 +1,19 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::ops::Deref;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use crate::locales::catalogues::get_dictionary;
|
use crate::locales::catalogues::get_dictionary;
|
||||||
|
|
||||||
mod catalogues;
|
mod catalogues;
|
||||||
|
|
||||||
pub fn trl(cx: Scope, phrase: &'static str) -> impl Fn() -> &'static str {
|
pub fn trl(cx: Scope, phrase: &str) -> impl Fn() -> String {
|
||||||
let mut translated = phrase;
|
let mut translated = phrase;
|
||||||
if let Some(dict) = get_dictionary(cx) {
|
if let Some(dict) = get_dictionary(cx) {
|
||||||
if let Some(p) = dict.get(phrase) {
|
if let Some(p) = dict.get(phrase.to_string().as_str()) {
|
||||||
translated = *p;
|
translated = *p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|| { translated }
|
let out = translated.to_string();
|
||||||
|
|
||||||
|
move || { out.clone() }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
use crate::backend::data::Company;
|
||||||
|
use crate::backend::company::UpdateCompany;
|
||||||
|
use crate::components::modal_box::{
|
||||||
|
DialogOpener, DlgNotLoaded, ModalBody, ModalDialog, ModalFooter,
|
||||||
|
};
|
||||||
|
use crate::components::server_err::ServerErr;
|
||||||
|
use crate::locales::trl;
|
||||||
|
use crate::validator::Validator;
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_router::*;
|
||||||
|
use crate::components::validation_err::ValidationErr;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CompanyEdit(
|
||||||
|
cx: Scope,
|
||||||
|
company: ReadSignal<Option<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>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view! {cx,
|
||||||
|
<DlgNotLoaded opener=opener title="Edit company" />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use serde::de::Unexpected::Option;
|
||||||
|
use crate::backend::company::get_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);
|
||||||
|
|
||||||
|
view! {cx,
|
||||||
|
<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>
|
||||||
|
<p class="card-text">
|
||||||
|
<Transition fallback=move || view! {cx, <p>{trl(cx, "Loading...")}</p> }>
|
||||||
|
{move || {
|
||||||
|
company.read(cx).map(|c| match c {
|
||||||
|
Err(e) => {view! {cx, <p>{trl(cx, "Error loading data")}</p>
|
||||||
|
<p>{e.to_string()}</p>
|
||||||
|
}}
|
||||||
|
Ok(c) => {
|
||||||
|
set_cmp.update(|cmp| *cmp = Some(c.clone()));
|
||||||
|
view! {
|
||||||
|
cx, <p><b>{c.name}</b></p>
|
||||||
|
<p>{c.street}" "{c.house_number}<br/>
|
||||||
|
{c.zip_code}" "{c.city}
|
||||||
|
</p>
|
||||||
|
}}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</Transition>
|
||||||
|
</p>
|
||||||
|
<a href="javascript:void(0)" class="card-link" on:click = move |_| editor.show()>
|
||||||
|
<i class="bx bx-pencil fs-4 lh-0"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,4 @@
|
|||||||
pub mod home_page;
|
pub mod home_page;
|
||||||
|
pub mod settings;
|
||||||
|
pub mod company_info;
|
||||||
|
mod company_edit;
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
use leptos::*;
|
||||||
|
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>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<div class="col-md-6 col-lg-4 mb-3">
|
||||||
|
<CompanyInfo/>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4 mb-3">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">"Card title"</h5>
|
||||||
|
<h6 class="card-subtitle text-muted">"Support card subtitle"</h6>
|
||||||
|
</div>
|
||||||
|
<img class="img-fluid" src="../assets/img/elements/13.jpg" alt="Card image cap" />
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="card-text">"Bear claw sesame snaps gummies chocolate."</p>
|
||||||
|
<a href="javascript:void(0);" class="card-link">"Card link"</a>
|
||||||
|
<a href="javascript:void(0);" class="card-link">"Another link"</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-lg-4 mb-3">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">"Card title"</h5>
|
||||||
|
<h6 class="card-subtitle text-muted">"Support card subtitle"</h6>
|
||||||
|
<img
|
||||||
|
class="img-fluid d-flex mx-auto my-4"
|
||||||
|
src="../assets/img/elements/4.jpg"
|
||||||
|
alt="Card image cap"
|
||||||
|
/>
|
||||||
|
<p class="card-text">"Bear claw sesame snaps gummies chocolate."</p>
|
||||||
|
<a href="javascript:void(0);" class="card-link">Card link</a>
|
||||||
|
<a href="javascript:void(0);" class="card-link">Another link</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Validator {
|
||||||
|
message: ReadSignal<Option<String>>,
|
||||||
|
set_message: WriteSignal<Option<String>>,
|
||||||
|
valid: ReadSignal<bool>,
|
||||||
|
set_valid: WriteSignal<bool>,
|
||||||
|
messages: ReadSignal<Option<Vec<String>>>,
|
||||||
|
set_messages: WriteSignal<Option<Vec<String>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
Self {
|
||||||
|
message,
|
||||||
|
set_message,
|
||||||
|
valid,
|
||||||
|
set_valid,
|
||||||
|
messages,
|
||||||
|
set_messages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&self, entity: &impl Validate, ev: &web_sys::Event) {
|
||||||
|
if let Err(val_err) = entity.validate() {
|
||||||
|
ev.prevent_default();
|
||||||
|
self.set_message.update(|m| *m = Some(val_err.to_string().clone()));
|
||||||
|
self.set_messages.update(|m| *m = {
|
||||||
|
let mut out: Vec<String> = vec![];
|
||||||
|
val_err.field_errors().drain().for_each(|e| {
|
||||||
|
e.1.to_vec().into_iter().for_each(|err| {
|
||||||
|
if let Some(msg) = err.message {
|
||||||
|
out.push(msg.to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Some(out)
|
||||||
|
});
|
||||||
|
self.set_valid.update(|v| *v = false);
|
||||||
|
} else {
|
||||||
|
self.set_valid.update(|v| *v = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
self.valid.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&self) -> Option<String> {
|
||||||
|
self.message.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn messages(&self) -> Option<Vec<String>> {
|
||||||
|
self.messages.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user