Compare commits

...

2 Commits

Author SHA1 Message Date
a75df84dcb
Migrated to dioxus 0.6 2024-12-10 18:38:58 +01:00
fdd029ffd7
Commit before update of dioxus 2024-12-10 07:35:09 +01:00
10 changed files with 1275 additions and 1522 deletions

2359
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,28 +7,40 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
dioxus = { version = "0.5", features = ["fullstack", "router"] } dioxus = { version = "0.6", features = ["fullstack", "router"] }
web-sys = { version = "0.3.70", features = ["Window", "Location"] } dioxus-cli-config = { version = "0.6", optional = true }
web-sys = { version = "0.3", features = ["Window", "Location"] }
tokio = { version = "1.38", features = ["macros", "rt-multi-thread"], optional = true } tokio = { version = "1.42", features = ["macros", "rt-multi-thread"], optional = true }
axum = { version = "0.7", optional = true } axum = { version = "0.7", optional = true }
axum-extra = { version = "0.9", features = ["cookie"], optional = true } axum-extra = { version = "0.9", features = ["cookie"], optional = true }
time = { version = "0.3", optional = true } time = { version = "0.3", optional = true }
once_cell = { version = "1.19", optional = true } once_cell = { version = "1.20", optional = true }
surrealdb = { version = "2.0", optional = true } surrealdb = { version = "2.1", optional = true }
thiserror = { version = "1.0" } thiserror = { version = "2.0" }
strum = { version = "0.26", features = ["derive"] } strum = { version = "0.26", features = ["derive"] }
csv = { version = "1.3", optional = true } csv = { version = "1.3", optional = true }
# Debug # Debug
tracing = "0.1" tracing = "0.1"
dioxus-logger = "0.5" manganis = "0.6"
manganis = "0.2"
[features] [features]
default = [] default = []
server = [ "dioxus/axum", "tokio", "axum", "axum-extra", "time", "once_cell", "surrealdb", "csv" ] server = [ "dioxus/server", "dioxus-cli-config", "tokio", "axum", "axum-extra", "time", "once_cell", "surrealdb", "csv" ]
web = ["dioxus/web"] web = ["dioxus/web"]
[profile]
[profile.wasm-dev]
inherits = "dev"
opt-level = 1
[profile.server-dev]
inherits = "dev"
[profile.android-dev]
inherits = "dev"

View File

@ -3352,6 +3352,10 @@ details.collapse summary::-webkit-details-marker {
display: contents; display: contents;
} }
.hidden {
display: none;
}
.size-6 { .size-6 {
width: 1.5rem; width: 1.5rem;
height: 1.5rem; height: 1.5rem;
@ -3392,12 +3396,12 @@ details.collapse summary::-webkit-details-marker {
min-height: 4rem; min-height: 4rem;
} }
.w-1\/2 { .w-1\/3 {
width: 50%; width: 33.333333%;
} }
.w-1\/4 { .w-2\/3 {
width: 25%; width: 66.666667%;
} }
.w-80 { .w-80 {

View File

@ -2,8 +2,7 @@ use crate::util::model::{
member::{Member, MembersMigration}, member::{Member, MembersMigration},
session::Session, session::Session,
}; };
use dioxus::prelude::{dioxus_elements::FileEngine, *}; use dioxus::prelude::*;
use std::sync::Arc;
#[derive(Debug)] #[derive(Debug)]
struct UploadedFile { struct UploadedFile {
@ -31,12 +30,12 @@ pub fn Migration() -> Element {
li { class: "step step-primary", "Uploaden" }, li { class: "step step-primary", "Uploaden" },
li { li {
class: "step", class: "step",
class: if let Steps::Verify | Steps::Done = *step.read() { { "step-primary" } }, class: if let Steps::Verify | Steps::Done = *step.read() { "step-primary" },
"Controleren" "Controleren"
}, },
li { li {
class: "step", class: "step",
class: if let Steps::Done = *step.read() { { "step-primary" } }, class: if let Steps::Done = *step.read() { "step-primary" },
"Klaar" "Klaar"
}, },
}, },
@ -51,26 +50,23 @@ pub fn Migration() -> Element {
#[component] #[component]
fn Upload(step: Signal<Steps>, members_migration: Signal<(u16, MembersMigration)>) -> Element { fn Upload(step: Signal<Steps>, members_migration: Signal<(u16, MembersMigration)>) -> Element {
let mut file_uploaded = use_signal(|| None); let mut file_uploaded: Signal<Option<UploadedFile>> = use_signal(|| None);
let mut loading = use_signal(|| false); let mut loading = use_signal(|| false);
let read_files = move |file_engine: Arc<dyn FileEngine>| async move {
let files = file_engine.files();
for file_name in &files {
if let Some(contents) = file_engine.read_file_to_string(file_name).await {
file_uploaded.set(Some(UploadedFile {
name: file_name.clone(),
contents,
}));
}
}
};
let upload_files = move |evt: FormEvent| async move { let upload_files = move |evt: FormEvent| async move {
if let Some(file_engine) = evt.files() { // TODO: Uncomment after bugfix dioxus
read_files(file_engine).await; // if let Some(file_engine) = &evt.files() {
} // let files = file_engine.files();
// for file_name in &files {
// if let Some(file) = file_engine.read_file_to_string(file_name).await {
// file_uploaded.set(Some(UploadedFile {
// name: file_name.to_owned(),
// contents: file,
// }))
// }
// }
// }
}; };
let sumbit = move |_evt: FormEvent| async move { let sumbit = move |_evt: FormEvent| async move {

View File

@ -1,3 +1,4 @@
use crate::util::model::news::{Target, TargetKind};
use std::collections::HashMap; use std::collections::HashMap;
use dioxus::prelude::*; use dioxus::prelude::*;
@ -49,27 +50,36 @@ pub fn NewsCreate() -> Element {
li { class: "step step-primary", "Inhoud" }, li { class: "step step-primary", "Inhoud" },
li { li {
class: "step", class: "step",
class: if let Steps::Targets | Steps::Done | Steps::Verify = *step.read() { { "step-primary" } }, class: if let Steps::Targets | Steps::Done | Steps::Verify = *step.read() { "step-primary" },
"Naar" "Naar"
} }
li { li {
class: "step", class: "step",
class: if let Steps::Verify | Steps::Done = *step.read() { { "step-primary" } }, class: if let Steps::Verify | Steps::Done = *step.read() { "step-primary" },
"Controleren" "Controleren"
} }
li { li {
class: "step", class: "step",
class: if let Steps::Done = *step.read() { { "step-primary" } }, class: if let Steps::Done = *step.read() { "step-primary" },
"Klaar" "Klaar"
} }
} }
div { div {
class: "flex flex-col gap-y-5", class: "flex flex-col gap-y-5",
match *step.read() { div {
Steps::Message => rsx! { Message { step: step, title: form.title, body: form.body } }, class: if let Steps::Message = *step.read() { "" } else { "hidden" },
Steps::Targets => rsx! { TargetSelect { step: step, targets: form.targets } }, Message {
Steps::Verify => rsx! { { } }, step: step,
Steps::Done => rsx! { { } }, title: form.title,
body: form.body,
}
}
div {
class: if let Steps::Targets = *step.read() { "" } else { "hidden" },
TargetSelect {
step: step,
targets: form.targets,
}
} }
} }
} }
@ -81,9 +91,6 @@ fn Message(step: Signal<Steps>, title: Signal<String>, body: Signal<String>) ->
let submit = move |event: FormEvent| { let submit = move |event: FormEvent| {
title.set(event.values()["title"].as_value()); title.set(event.values()["title"].as_value());
body.set(event.values()["body"].as_value()); body.set(event.values()["body"].as_value());
tracing::info!("{title}");
step.set(Steps::Targets); step.set(Steps::Targets);
}; };
@ -101,7 +108,6 @@ fn Message(step: Signal<Steps>, title: Signal<String>, body: Signal<String>) ->
r#type: "text", r#type: "text",
required: true, required: true,
name: "title", name: "title",
value: "{title}",
class: "input input-bordered w-full", class: "input input-bordered w-full",
} }
} }
@ -113,7 +119,6 @@ fn Message(step: Signal<Steps>, title: Signal<String>, body: Signal<String>) ->
}, },
textarea { textarea {
name: "body", name: "body",
value: "{body}",
class: "textarea textarea-bordered w-full", class: "textarea textarea-bordered w-full",
required: true, required: true,
} }
@ -130,39 +135,6 @@ fn Message(step: Signal<Steps>, title: Signal<String>, body: Signal<String>) ->
} }
} }
#[derive(PartialEq, Clone, Copy, Debug)]
enum TargetKind {
None,
All,
Group,
Hourgroup,
Hour,
Member,
Account,
Day,
}
#[derive(PartialEq, Debug)]
struct Target {
kind: TargetKind,
value: String,
}
impl TargetKind {
fn from_string(input: &str) -> Self {
match input {
"all" => Self::All,
"group" => Self::Group,
"hourgroup" => Self::Hourgroup,
"hour" => Self::Hour,
"member" => Self::Member,
"account" => Self::Account,
"day" => Self::Day,
_ => Self::None,
}
}
}
#[component] #[component]
fn TargetSelect(step: Signal<Steps>, targets: Signal<HashMap<u32, Target>>) -> Element { fn TargetSelect(step: Signal<Steps>, targets: Signal<HashMap<u32, Target>>) -> Element {
let mut target_id = use_signal(|| 1); let mut target_id = use_signal(|| 1);
@ -172,12 +144,17 @@ fn TargetSelect(step: Signal<Steps>, targets: Signal<HashMap<u32, Target>>) -> E
filtered_targets.sort_unstable(); filtered_targets.sort_unstable();
tracing::info!("targets updated");
filtered_targets filtered_targets
}); });
let submit = move |_| {
step.set(Steps::Verify);
};
rsx! { rsx! {
form {
class: "w-full",
onsubmit: submit,
label { label {
class: "form-control w-full", class: "form-control w-full",
div { div {
@ -207,6 +184,7 @@ fn TargetSelect(step: Signal<Steps>, targets: Signal<HashMap<u32, Target>>) -> E
class: "w-full flex gap-x-3 justify-end", class: "w-full flex gap-x-3 justify-end",
button { button {
class: "btn", class: "btn",
r#type: "button",
onclick: move |_| { onclick: move |_| {
step.set(Steps::Message) step.set(Steps::Message)
}, },
@ -214,32 +192,29 @@ fn TargetSelect(step: Signal<Steps>, targets: Signal<HashMap<u32, Target>>) -> E
} }
button { button {
class: "btn btn-primary", class: "btn btn-primary",
onclick: move |_| {
step.set(Steps::Verify)
},
"Volgende", "Volgende",
} }
} }
} }
}
} }
#[component] #[component]
fn TargetEntry(mut targets: Signal<HashMap<u32, Target>>, id: u32) -> Element { fn TargetEntry(mut targets: Signal<HashMap<u32, Target>>, id: u32) -> Element {
let kind = use_memo(move || targets.read().get(&id).unwrap().kind); let kind = use_memo(move || targets.read().get(&id).unwrap().kind);
tracing::info!("Comonent rendered!");
rsx! { rsx! {
div { div {
class: "join w-full mt-3", class: "join w-full mt-3",
select { select {
class: "select select-bordered join-item w-1/2", class: "select select-bordered join-item w-1/3",
required: true,
oninput: move |event| { oninput: move |event| {
let target_kind = TargetKind::from_string(&event.value()); let target_kind = TargetKind::from_string(&event.value());
targets.write().get_mut(&id).unwrap().kind = target_kind; targets.write().get_mut(&id).unwrap().kind = target_kind;
targets.write().get_mut(&id).unwrap().value = String::new(); targets.write().get_mut(&id).unwrap().value = String::from("");
}, },
option { disabled: true, selected: true, "Selecteer een type" }, option { disabled: true, selected: true, value: "", "Selecteer een type" },
option { value: "all", "Iedereen" } option { value: "all", "Iedereen" }
option { value: "group", "Groep" } option { value: "group", "Groep" }
option { value: "day", "Dag" } option { value: "day", "Dag" }
@ -262,16 +237,19 @@ fn TargetValueInput(
let groups = use_context::<crate::Groups>(); let groups = use_context::<crate::Groups>();
let value = use_memo(move || targets.read().get(&id).unwrap().value.clone()); let value = use_memo(move || targets.read().get(&id).unwrap().value.clone());
tracing::info!("Input rendered");
rsx! { rsx! {
match target_kind() { match target_kind() {
TargetKind::Group => { TargetKind::Group => {
rsx! { rsx! {
select { select {
class: "select select-bordered join-item w-1/2", class: "select select-bordered join-item w-2/3",
required: true,
oninput: move |event| { oninput: move |event| {
targets.write().get_mut(&id).unwrap().value = event.value(); targets.write().get_mut(&id).unwrap().value = event.value();
}, },
option { disabled: true, selected: true, "Selecteer een groep" } option { disabled: true, selected: true, value: "", "Selecteer een groep" }
for (group_id, group_name) in groups.0 { for (group_id, group_name) in groups.0 {
option { value: group_id, "{group_name}" } option { value: group_id, "{group_name}" }
} }
@ -281,11 +259,12 @@ fn TargetValueInput(
TargetKind::Day => { TargetKind::Day => {
rsx! { rsx! {
select { select {
class: "select select-bordered join-item w-1/2", class: "select select-bordered join-item w-2/3",
required: true,
oninput: move |event| { oninput: move |event| {
targets.write().get_mut(&id).unwrap().value = event.value(); targets.write().get_mut(&id).unwrap().value = event.value();
}, },
option { disabled: true, selected: true, "Selecteer een dag" } option { disabled: true, selected: true, value: "", "Selecteer een dag" }
option { option {
value: "friday", value: "friday",
"Vrijdag", "Vrijdag",
@ -300,8 +279,9 @@ fn TargetValueInput(
TargetKind::Hourgroup => { TargetKind::Hourgroup => {
rsx! { rsx! {
select { select {
class: "select select-bordered join-item w-1/4", class: "select select-bordered join-item w-1/3",
option { disabled: true, selected: true, "Selecteer een uur" } required: true,
option { disabled: true, selected: true, value: "", "Selecteer een uur" }
option { option {
value: "a", value: "a",
"A", "A",
@ -328,8 +308,9 @@ fn TargetValueInput(
} }
} }
select { select {
class: "select select-bordered join-item w-1/4", class: "select select-bordered join-item w-1/3",
option { disabled: true, selected: true, "Selecteer een groep" } required: true,
option { disabled: true, selected: true, value: "", "Selecteer een groep" }
for i in 1..7 { for i in 1..7 {
option { "{i}" } option { "{i}" }
} }
@ -339,8 +320,9 @@ fn TargetValueInput(
TargetKind::Hour => { TargetKind::Hour => {
rsx! { rsx! {
select { select {
class: "select select-bordered join-item w-1/2", class: "select select-bordered join-item w-2/3",
option { disabled: true, selected: true, "Selecteer een uur" } required: true,
option { disabled: true, selected: true, value: "", "Selecteer een uur" }
option { option {
value: "a", value: "a",
"A", "A",
@ -371,8 +353,8 @@ fn TargetValueInput(
TargetKind::Member | TargetKind::Account => { TargetKind::Member | TargetKind::Account => {
rsx! { rsx! {
input { input {
class: "input input-bordered join-item w-1/2", class: "input input-bordered join-item w-2/3",
value: "{value}", required: true,
oninput: move |event| { oninput: move |event| {
targets.write().get_mut(&id).unwrap().value = event.value(); targets.write().get_mut(&id).unwrap().value = event.value();
}, },
@ -380,7 +362,7 @@ fn TargetValueInput(
} }
}, },
_ => rsx! { _ => rsx! {
div { class: "input input-bordered w-1/2 join-item" } div { class: "input input-bordered w-2/3 join-item" }
} }
} }
} }

View File

@ -8,7 +8,6 @@ mod util;
pub use err::Error; pub use err::Error;
use dioxus::prelude::*; use dioxus::prelude::*;
use tracing::Level;
// Use routes // Use routes
use components::admin::members::migration::Migration; use components::admin::members::migration::Migration;
@ -22,6 +21,7 @@ use components::layout::Global;
use components::news::create::NewsCreate; use components::news::create::NewsCreate;
use components::news::News; use components::news::News;
use components::settings::Settings; use components::settings::Settings;
use tracing::Level;
#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[rustfmt::skip] #[rustfmt::skip]
@ -49,43 +49,29 @@ pub enum Route {
Migration {}, Migration {},
} }
const _TAILWIND_URL: &str = manganis::mg!(file("assets/tailwind.css")); #[cfg(feature = "server")]
#[tokio::main]
async fn main() {
dioxus::logger::init(Level::INFO).expect("Failed to init logger");
fn main() {
// Init logger
dioxus_logger::init(Level::INFO).expect("failed to init logger");
#[cfg(feature = "server")]
{
use axum::routing::*;
// Spawn main tokio runtime
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async move {
// Initialize surrealdb connection // Initialize surrealdb connection
util::surrealdb::initialize() util::surrealdb::initialize()
.await .await
.expect("Error while initializing surrealdb"); .expect("Error while initializing surrealdb");
// Create axum app let address = dioxus_cli_config::fullstack_address_or_localhost();
let app = Router::new()
.serve_dioxus_application(ServeConfig::builder().build(), || {
VirtualDom::new(App)
})
.await;
// Run axum app let router = axum::Router::new().serve_dioxus_application(ServeConfigBuilder::default(), App);
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service()) let router = router.into_make_service();
.await let listener = tokio::net::TcpListener::bind(address).await.unwrap();
.unwrap();
});
}
launch(App); axum::serve(listener, router).await.unwrap();
}
#[cfg(not(feature = "server"))]
fn main() {
dioxus::launch(App);
} }
#[derive(Clone)] #[derive(Clone)]
@ -117,6 +103,10 @@ fn App() -> Element {
use_context_provider(|| hours); use_context_provider(|| hours);
rsx! { rsx! {
document::Stylesheet {
href: asset!("assets/tailwind.css")
}
div { div {
class: "h-screen flex flex-col", class: "h-screen flex flex-col",
Router::<Route> {} Router::<Route> {}

View File

@ -1,3 +1,4 @@
pub mod member; pub mod member;
pub mod news;
pub mod session; pub mod session;
pub mod user; pub mod user;

68
src/util/model/news.rs Normal file
View File

@ -0,0 +1,68 @@
use serde::{Deserialize, Serialize};
#[cfg(feature = "server")]
use crate::util::surrealdb::{thing_to_string, DB};
#[cfg(feature = "server")]
use surrealdb::sql::{
statements::{BeginStatement, CommitStatement},
Thing,
};
#[derive(PartialEq, Clone, Copy, Debug, Deserialize, Serialize, Eq)]
pub enum TargetKind {
None,
All,
Group,
Hourgroup,
Hour,
Member,
Account,
Day,
}
impl TargetKind {
pub fn from_string(input: &str) -> Self {
match input {
"all" => Self::All,
"group" => Self::Group,
"hourgroup" => Self::Hourgroup,
"hour" => Self::Hour,
"member" => Self::Member,
"account" => Self::Account,
"day" => Self::Day,
_ => Self::None,
}
}
}
#[derive(PartialEq, Debug, Deserialize, Serialize, Clone, Eq)]
pub struct Target {
pub kind: TargetKind,
pub value: String,
}
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
pub struct News {
#[cfg_attr(feature = "server", serde(deserialize_with = "thing_to_string"))]
pub id: String,
pub title: String,
pub body: String,
pub tagets: Vec<Target>,
}
#[cfg(feature = "server")]
impl News {
// pub async fn new(
// title: String,
// body: String,
// targets: Vec<Target>,
// ) -> Result<Self, crate::Error> {
// }
}
#[cfg(feature = "server")]
fn fetch_targets(targets: Vec<Target>) {
let mut transaction = DB.query(BeginStatement::default());
for target in targets {}
}

View File

@ -3,8 +3,6 @@ use crate::util::surrealdb::{thing_to_string, DB};
use dioxus::prelude::*; use dioxus::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "server")]
use surrealdb::sql::Thing;
use super::user::User; use super::user::User;
@ -51,11 +49,7 @@ impl Session {
cookie.set_same_site(SameSite::Strict); cookie.set_same_site(SameSite::Strict);
server_context() server_context().response_parts_mut().headers.insert(
.response_parts_mut()
.unwrap()
.headers
.insert(
header::SET_COOKIE, header::SET_COOKIE,
HeaderValue::from_str(&cookie.to_string())?, HeaderValue::from_str(&cookie.to_string())?,
); );
@ -104,11 +98,7 @@ impl Session {
cookie.set_same_site(SameSite::Strict); cookie.set_same_site(SameSite::Strict);
server_context() server_context().response_parts_mut().headers.insert(
.response_parts_mut()
.unwrap()
.headers
.insert(
header::SET_COOKIE, header::SET_COOKIE,
HeaderValue::from_str(&cookie.to_string())?, HeaderValue::from_str(&cookie.to_string())?,
); );

View File

@ -16,7 +16,11 @@ where
} }
pub async fn initialize() -> surrealdb::Result<()> { pub async fn initialize() -> surrealdb::Result<()> {
DB.connect::<Ws>("localhost:8000").await?; tracing::info!("Connectiong to surrealdb");
DB.connect::<Ws>("localhost:8000")
.await
.expect("Failed to connect to surrealdb");
DB.signin(Root { DB.signin(Root {
username: "root", username: "root",
@ -26,7 +30,7 @@ pub async fn initialize() -> surrealdb::Result<()> {
DB.use_ns("wrbapp").use_db("wrbapp").await?; DB.use_ns("wrbapp").use_db("wrbapp").await?;
apply_queries().await?; apply_queries().await.expect("Failed to apply queries");
Ok(()) Ok(())
} }
@ -47,7 +51,8 @@ async fn apply_queries() -> surrealdb::Result<()> {
DEFINE INDEX userEmailIndex ON TABLE user COLUMNS email UNIQUE; DEFINE INDEX userEmailIndex ON TABLE user COLUMNS email UNIQUE;
", ",
) )
.await?; .await
.expect("Failed to apply user query");
DB.query( DB.query(
" "