|
@@ -1,11 +1,13 @@
|
|
|
-/*
|
|
|
-Tiny CRM: A port of the Yew CRM example to Dioxus.
|
|
|
-*/
|
|
|
+//! Tiny CRM: A port of the Yew CRM example to Dioxus.
|
|
|
+#![allow(non_snake_case)]
|
|
|
+
|
|
|
+use std::sync::{Arc, Mutex};
|
|
|
+
|
|
|
use dioxus::prelude::*;
|
|
|
-use dioxus_router::{Link, Route, Router};
|
|
|
+use dioxus_router::prelude::*;
|
|
|
|
|
|
fn main() {
|
|
|
- dioxus_desktop::launch(app);
|
|
|
+ dioxus_desktop::launch(App);
|
|
|
}
|
|
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
@@ -15,92 +17,185 @@ pub struct Client {
|
|
|
pub description: String,
|
|
|
}
|
|
|
|
|
|
-fn app(cx: Scope) -> Element {
|
|
|
- let clients = use_ref(cx, || vec![] as Vec<Client>);
|
|
|
- let firstname = use_state(cx, String::new);
|
|
|
- let lastname = use_state(cx, String::new);
|
|
|
- let description = use_state(cx, String::new);
|
|
|
-
|
|
|
- cx.render(rsx!(
|
|
|
- body {
|
|
|
- margin_left: "35%",
|
|
|
- link {
|
|
|
- rel: "stylesheet",
|
|
|
- href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css",
|
|
|
- integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5",
|
|
|
- crossorigin: "anonymous",
|
|
|
+type ClientContext = Arc<Mutex<Vec<Client>>>;
|
|
|
+
|
|
|
+fn App(cx: Scope) -> Element {
|
|
|
+ use_router(cx, &|| RouterConfiguration::default(), &|| {
|
|
|
+ Segment::content(comp(ClientList))
|
|
|
+ .fixed(
|
|
|
+ "new",
|
|
|
+ Route::content(comp(ClientAdd)).name::<ClientAddName>(),
|
|
|
+ )
|
|
|
+ .fixed(
|
|
|
+ "settings",
|
|
|
+ Route::content(comp(Settings)).name::<SettingsName>(),
|
|
|
+ )
|
|
|
+ });
|
|
|
+
|
|
|
+ use_context_provider::<ClientContext>(&cx, Default::default);
|
|
|
+
|
|
|
+ render! {
|
|
|
+ link {
|
|
|
+ rel: "stylesheet",
|
|
|
+ href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css",
|
|
|
+ integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5",
|
|
|
+ crossorigin: "anonymous",
|
|
|
+ }
|
|
|
+
|
|
|
+ style { "
|
|
|
+ .red {{
|
|
|
+ background-color: rgb(202, 60, 60) !important;
|
|
|
+ }}
|
|
|
+ " }
|
|
|
+
|
|
|
+ h1 {"Dioxus CRM Example"}
|
|
|
+
|
|
|
+ Outlet { }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn ClientList(cx: Scope) -> Element {
|
|
|
+ let clients = use_context::<ClientContext>(cx).unwrap();
|
|
|
+
|
|
|
+ cx.render(rsx! {
|
|
|
+ h2 { "List of Clients" }
|
|
|
+
|
|
|
+ Link {
|
|
|
+ target: named::<ClientAddName>(),
|
|
|
+ class: "pure-button pure-button-primary",
|
|
|
+ "Add Client"
|
|
|
+ }
|
|
|
+ Link {
|
|
|
+ target: named::<SettingsName>(),
|
|
|
+ class: "pure-button",
|
|
|
+ "Settings"
|
|
|
+ }
|
|
|
+
|
|
|
+ clients.lock().unwrap().iter().map(|client| rsx! {
|
|
|
+ div {
|
|
|
+ class: "client",
|
|
|
+ style: "margin-bottom: 50px",
|
|
|
+
|
|
|
+ p { "Name: {client.first_name} {client.last_name}" }
|
|
|
+ p { "Description: {client.description}" }
|
|
|
}
|
|
|
- h1 {"Dioxus CRM Example"}
|
|
|
- Router {
|
|
|
- Route { to: "/",
|
|
|
- div { class: "crm",
|
|
|
- h2 { margin_bottom: "10px", "List of clients" }
|
|
|
- div { class: "clients", margin_left: "10px",
|
|
|
- clients.read().iter().map(|client| rsx!(
|
|
|
- div { class: "client", style: "margin-bottom: 50px",
|
|
|
- p { "First Name: {client.first_name}" }
|
|
|
- p { "Last Name: {client.last_name}" }
|
|
|
- p {"Description: {client.description}"}
|
|
|
- })
|
|
|
- )
|
|
|
- }
|
|
|
- Link { to: "/new", class: "pure-button pure-button-primary", "Add New" }
|
|
|
- Link { to: "/new", class: "pure-button", "Settings" }
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+struct ClientAddName;
|
|
|
+fn ClientAdd(cx: Scope) -> Element {
|
|
|
+ let clients = use_context::<ClientContext>(cx).unwrap();
|
|
|
+ let first_name = use_state(&cx, String::new);
|
|
|
+ let last_name = use_state(&cx, String::new);
|
|
|
+ let description = use_state(&cx, String::new);
|
|
|
+
|
|
|
+ let navigator = use_navigate(&cx).unwrap();
|
|
|
+
|
|
|
+ cx.render(rsx! {
|
|
|
+ h2 { "Add new Client" }
|
|
|
+
|
|
|
+ form {
|
|
|
+ class: "pure-form pure-form-aligned",
|
|
|
+ onsubmit: move |_| {
|
|
|
+ let mut clients = clients.lock().unwrap();
|
|
|
+
|
|
|
+ clients.push(Client {
|
|
|
+ first_name: first_name.to_string(),
|
|
|
+ last_name: last_name.to_string(),
|
|
|
+ description: description.to_string(),
|
|
|
+ });
|
|
|
+
|
|
|
+ navigator.push(named::<RootIndex>());
|
|
|
+ },
|
|
|
+
|
|
|
+ fieldset {
|
|
|
+ div {
|
|
|
+ class: "pure-control-group",
|
|
|
+ label {
|
|
|
+ "for": "first_name",
|
|
|
+ "First Name"
|
|
|
+ }
|
|
|
+ input {
|
|
|
+ id: "first_name",
|
|
|
+ "type": "text",
|
|
|
+ placeholder: "First Name…",
|
|
|
+ required: "",
|
|
|
+ value: "{first_name}",
|
|
|
+ oninput: move |e| first_name.set(e.value.clone())
|
|
|
}
|
|
|
}
|
|
|
- Route { to: "/new",
|
|
|
- div { class: "crm",
|
|
|
- h2 { margin_bottom: "10px", "Add new client" }
|
|
|
- form { class: "pure-form",
|
|
|
- input {
|
|
|
- class: "new-client firstname",
|
|
|
- placeholder: "First name",
|
|
|
- value: "{firstname}",
|
|
|
- oninput: move |e| firstname.set(e.value.clone())
|
|
|
- }
|
|
|
- input {
|
|
|
- class: "new-client lastname",
|
|
|
- placeholder: "Last name",
|
|
|
- value: "{lastname}",
|
|
|
- oninput: move |e| lastname.set(e.value.clone())
|
|
|
- }
|
|
|
- textarea {
|
|
|
- class: "new-client description",
|
|
|
- placeholder: "Description",
|
|
|
- value: "{description}",
|
|
|
- oninput: move |e| description.set(e.value.clone())
|
|
|
- }
|
|
|
- }
|
|
|
- button {
|
|
|
- class: "pure-button pure-button-primary",
|
|
|
- onclick: move |_| {
|
|
|
- clients.write().push(Client {
|
|
|
- description: description.to_string(),
|
|
|
- first_name: firstname.to_string(),
|
|
|
- last_name: lastname.to_string(),
|
|
|
- });
|
|
|
- description.set(String::new());
|
|
|
- firstname.set(String::new());
|
|
|
- lastname.set(String::new());
|
|
|
- },
|
|
|
- "Add New"
|
|
|
- }
|
|
|
- Link { to: "/", class: "pure-button", "Go Back" }
|
|
|
+
|
|
|
+ div {
|
|
|
+ class: "pure-control-group",
|
|
|
+ label {
|
|
|
+ "for": "last_name",
|
|
|
+ "Last Name"
|
|
|
+ }
|
|
|
+ input {
|
|
|
+ id: "last_name",
|
|
|
+ "type": "text",
|
|
|
+ placeholder: "Last Name…",
|
|
|
+ required: "",
|
|
|
+ value: "{last_name}",
|
|
|
+ oninput: move |e| last_name.set(e.value.clone())
|
|
|
}
|
|
|
}
|
|
|
- Route { to: "/settings",
|
|
|
- div {
|
|
|
- h2 { margin_bottom: "10px", "Settings" }
|
|
|
- button {
|
|
|
- background: "rgb(202, 60, 60)",
|
|
|
- class: "pure-button pure-button-primary",
|
|
|
- onclick: move |_| clients.write().clear(),
|
|
|
- "Remove all clients"
|
|
|
- }
|
|
|
- Link { to: "/", class: "pure-button pure-button-primary", "Go Back" }
|
|
|
+
|
|
|
+ div {
|
|
|
+ class: "pure-control-group",
|
|
|
+ label {
|
|
|
+ "for": "description",
|
|
|
+ "Description"
|
|
|
+ }
|
|
|
+ textarea {
|
|
|
+ id: "description",
|
|
|
+ placeholder: "Description…",
|
|
|
+ value: "{description}",
|
|
|
+ oninput: move |e| description.set(e.value.clone())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ div {
|
|
|
+ class: "pure-controls",
|
|
|
+ button {
|
|
|
+ "type": "submit",
|
|
|
+ class: "pure-button pure-button-primary",
|
|
|
+ "Save"
|
|
|
+ }
|
|
|
+ Link {
|
|
|
+ target: named::<RootIndex>(),
|
|
|
+ class: "pure-button pure-button-primary red",
|
|
|
+ "Cancel"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+struct SettingsName;
|
|
|
+fn Settings(cx: Scope) -> Element {
|
|
|
+ let clients = use_context::<ClientContext>(&cx).unwrap();
|
|
|
+
|
|
|
+ cx.render(rsx! {
|
|
|
+ h2 { "Settings" }
|
|
|
+
|
|
|
+ button {
|
|
|
+ class: "pure-button pure-button-primary red",
|
|
|
+ onclick: move |_| {
|
|
|
+ let mut clients = clients.lock().unwrap();
|
|
|
+ clients.clear();
|
|
|
+ },
|
|
|
+ "Remove all Clients"
|
|
|
+ }
|
|
|
+
|
|
|
+ Link {
|
|
|
+ target: named::<RootIndex>(),
|
|
|
+ class: "pure-button",
|
|
|
+ "Go back"
|
|
|
}
|
|
|
- ))
|
|
|
+ })
|
|
|
}
|