Compare commits

..

No commits in common. "a75df84dcb55d4368a5491d16cf28c04a436f2ae" and "ec195dfbf7f135c7cf2e83834d92ad9f814f3047" have entirely different histories.

10 changed files with 1525 additions and 1278 deletions

2363
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,40 +7,28 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0.197", features = ["derive"] }
dioxus = { version = "0.6", features = ["fullstack", "router"] }
dioxus-cli-config = { version = "0.6", optional = true }
web-sys = { version = "0.3", features = ["Window", "Location"] }
dioxus = { version = "0.5", features = ["fullstack", "router"] }
web-sys = { version = "0.3.70", features = ["Window", "Location"] }
tokio = { version = "1.42", features = ["macros", "rt-multi-thread"], optional = true }
tokio = { version = "1.38", features = ["macros", "rt-multi-thread"], optional = true }
axum = { version = "0.7", optional = true }
axum-extra = { version = "0.9", features = ["cookie"], optional = true }
time = { version = "0.3", optional = true }
once_cell = { version = "1.20", optional = true }
surrealdb = { version = "2.1", optional = true }
thiserror = { version = "2.0" }
once_cell = { version = "1.19", optional = true }
surrealdb = { version = "2.0", optional = true }
thiserror = { version = "1.0" }
strum = { version = "0.26", features = ["derive"] }
csv = { version = "1.3", optional = true }
# Debug
tracing = "0.1"
manganis = "0.6"
dioxus-logger = "0.5"
manganis = "0.2"
[features]
default = []
server = [ "dioxus/server", "dioxus-cli-config", "tokio", "axum", "axum-extra", "time", "once_cell", "surrealdb", "csv" ]
server = [ "dioxus/axum", "tokio", "axum", "axum-extra", "time", "once_cell", "surrealdb", "csv" ]
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,10 +3352,6 @@ details.collapse summary::-webkit-details-marker {
display: contents;
}
.hidden {
display: none;
}
.size-6 {
width: 1.5rem;
height: 1.5rem;
@ -3396,12 +3392,12 @@ details.collapse summary::-webkit-details-marker {
min-height: 4rem;
}
.w-1\/3 {
width: 33.333333%;
.w-1\/2 {
width: 50%;
}
.w-2\/3 {
width: 66.666667%;
.w-1\/4 {
width: 25%;
}
.w-80 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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