Temp
This commit is contained in:
parent
44745b252a
commit
59eeabc888
|
@ -14,6 +14,7 @@ api_routes!(
|
||||||
update_event,
|
update_event,
|
||||||
get_all_events,
|
get_all_events,
|
||||||
event_outcome,
|
event_outcome,
|
||||||
|
delete_event,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub async fn apply_outcome(outcome: &EventOutcome, db: &Connection<Db>) -> Result<(), PartyError> {
|
pub async fn apply_outcome(outcome: &EventOutcome, db: &Connection<Db>) -> Result<(), PartyError> {
|
||||||
|
@ -164,3 +165,16 @@ pub async fn stop_event(
|
||||||
|
|
||||||
Ok(Json(outcome))
|
Ok(Json(outcome))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Delete event by name
|
||||||
|
#[openapi(tag = "Event")]
|
||||||
|
#[delete("/<name>")]
|
||||||
|
pub async fn delete_event(
|
||||||
|
_api_key: ApiKey,
|
||||||
|
db: Connection<Db>,
|
||||||
|
name: String,
|
||||||
|
) -> Result<Status, PartyError> {
|
||||||
|
db.events().delete_one(doc! { "name": name }, None).await?;
|
||||||
|
|
||||||
|
Ok(Status::Ok)
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::util::PartyError;
|
||||||
#[cfg(feature = "sycamore")]
|
#[cfg(feature = "sycamore")]
|
||||||
use crate::view::prelude::*;
|
use crate::view::prelude::*;
|
||||||
#[cfg(feature = "sycamore")]
|
#[cfg(feature = "sycamore")]
|
||||||
use lan_party_macros::{WebEdit, WebView};
|
use lan_party_macros::{web_view_attr, WebEdit, WebView};
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
#[cfg(feature = "openapi")]
|
#[cfg(feature = "openapi")]
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
|
@ -26,6 +26,7 @@ pub struct EventOutcome {
|
||||||
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
#[cfg_attr(feature = "openapi", derive(JsonSchema))]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "sycamore", derive(WebView))]
|
#[cfg_attr(feature = "sycamore", derive(WebView))]
|
||||||
|
#[cfg_attr(feature = "sycamore", web_view_attr(title = "name"))]
|
||||||
pub struct Event {
|
pub struct Event {
|
||||||
/// Has this event concluded?
|
/// Has this event concluded?
|
||||||
#[cfg_attr(feature = "serde", serde(default))]
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
|
|
|
@ -174,6 +174,7 @@ impl<'a, G: Html, A: for<'b> Viewable<'b, G> + Clone, B: for<'b> Viewable<'b, G>
|
||||||
view! { cx,
|
view! { cx,
|
||||||
Block(title="Tuple".into()) {
|
Block(title="Tuple".into()) {
|
||||||
(props.state.0.view(cx))
|
(props.state.0.view(cx))
|
||||||
|
br()
|
||||||
(props.state.1.view(cx))
|
(props.state.1.view(cx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,16 @@ mod edit;
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::{__private::TokenStream as TokenStream2, format_ident, quote};
|
use quote::{__private::TokenStream as TokenStream2, format_ident, quote};
|
||||||
use syn::{token::Do, Attribute, DataEnum, DataStruct, Fields, Ident, Path, Type};
|
use syn::{
|
||||||
|
parse::Parse, parse_str, token::Do, Attribute, DataEnum, DataStruct, Fields, Ident, Lit,
|
||||||
|
MetaNameValue, Path, Type,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ParsedAttribute {
|
||||||
|
Documentation(Documentation),
|
||||||
|
View(ViewAttribute),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Documentation {
|
enum Documentation {
|
||||||
|
@ -12,6 +21,11 @@ enum Documentation {
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ViewAttribute {
|
||||||
|
Title(Ident),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
impl Documentation {
|
impl Documentation {
|
||||||
fn parse(attr: &Attribute) -> Documentation {
|
fn parse(attr: &Attribute) -> Documentation {
|
||||||
if !attr.path.is_ident("doc") {
|
if !attr.path.is_ident("doc") {
|
||||||
|
@ -30,14 +44,57 @@ impl Documentation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_title_description(attrs: &[Attribute]) -> (Option<String>, Option<String>) {
|
impl ViewAttribute {
|
||||||
let docs: Vec<_> = attrs.iter().map(Documentation::parse).collect();
|
fn parse(attr: &Attribute) -> ViewAttribute {
|
||||||
|
if !attr.path.is_ident("web_view_attr") {
|
||||||
|
return Self::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed: Result<MetaNameValue, _> = parse_str(
|
||||||
|
attr.tokens
|
||||||
|
.to_string()
|
||||||
|
.trim_matches(|c: char| c == '(' || c == ')'),
|
||||||
|
);
|
||||||
|
|
||||||
|
dbg!(&parsed);
|
||||||
|
|
||||||
|
match parsed {
|
||||||
|
Ok(p) => match (p.path.get_ident().unwrap().to_string().as_str(), p.lit) {
|
||||||
|
("title", Lit::Str(ls)) => Self::Title(format_ident!("{}", ls.value())),
|
||||||
|
_ => Self::None,
|
||||||
|
},
|
||||||
|
Err(_) => Self::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsedAttribute {
|
||||||
|
fn parse(attr: &Attribute) -> ParsedAttribute {
|
||||||
|
match attr.path.get_ident() {
|
||||||
|
Some(i) if i.to_string() == "doc" => Self::Documentation(Documentation::parse(attr)),
|
||||||
|
Some(i) if i.to_string() == "web_view_attr" => Self::View(ViewAttribute::parse(attr)),
|
||||||
|
_ => Self::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Attributes {
|
||||||
|
title: Option<String>,
|
||||||
|
title_field: Option<Ident>,
|
||||||
|
description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attributes {
|
||||||
|
pub fn parse(attrs: &[Attribute]) -> Self {
|
||||||
|
let parsed: Vec<_> = attrs.iter().map(ParsedAttribute::parse).collect();
|
||||||
|
|
||||||
let mut title = None;
|
let mut title = None;
|
||||||
|
let mut title_field = None;
|
||||||
let mut description: Option<String> = None;
|
let mut description: Option<String> = None;
|
||||||
|
|
||||||
for doc in docs {
|
for attr in parsed {
|
||||||
match doc {
|
match attr {
|
||||||
|
ParsedAttribute::Documentation(doc) => match doc {
|
||||||
Documentation::Title(t) => title = Some(t),
|
Documentation::Title(t) => title = Some(t),
|
||||||
Documentation::Description(d) => {
|
Documentation::Description(d) => {
|
||||||
if description.is_some() {
|
if description.is_some() {
|
||||||
|
@ -48,15 +105,32 @@ fn get_title_description(attrs: &[Attribute]) -> (Option<String>, Option<String>
|
||||||
description.as_mut().unwrap().push_str(&d);
|
description.as_mut().unwrap().push_str(&d);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
},
|
||||||
|
ParsedAttribute::View(v) => match v {
|
||||||
|
ViewAttribute::Title(t) => title_field = Some(t),
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(title, description)
|
Self {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
title_field,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn web_view_attr(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
item
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ItemProps {
|
struct ItemProps {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
title: String,
|
title: String,
|
||||||
|
title_field: Option<Ident>,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +154,7 @@ fn struct_edit(props: &ItemProps, s: DataStruct) -> TokenStream2 {
|
||||||
name,
|
name,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
|
..
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
let fields = s.fields.iter().map(|f| {
|
let fields = s.fields.iter().map(|f| {
|
||||||
|
@ -89,7 +164,11 @@ fn struct_edit(props: &ItemProps, s: DataStruct) -> TokenStream2 {
|
||||||
.expect("each struct field must be named")
|
.expect("each struct field must be named")
|
||||||
.clone();
|
.clone();
|
||||||
let name_str = name.to_string();
|
let name_str = name.to_string();
|
||||||
let (title, description) = get_title_description(&f.attrs);
|
let Attributes {
|
||||||
|
title,
|
||||||
|
title_field: _,
|
||||||
|
description,
|
||||||
|
} = Attributes::parse(&f.attrs);
|
||||||
|
|
||||||
StructField {
|
StructField {
|
||||||
name_str,
|
name_str,
|
||||||
|
@ -161,6 +240,7 @@ fn enum_edit(props: &ItemProps, e: DataEnum) -> TokenStream2 {
|
||||||
name,
|
name,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
|
..
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
let variants = e.variants.iter().map(|v| {
|
let variants = e.variants.iter().map(|v| {
|
||||||
|
@ -171,7 +251,11 @@ fn enum_edit(props: &ItemProps, e: DataEnum) -> TokenStream2 {
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (title, description) = get_title_description(&v.attrs);
|
let Attributes {
|
||||||
|
title,
|
||||||
|
title_field: _,
|
||||||
|
description,
|
||||||
|
} = Attributes::parse(&v.attrs);
|
||||||
let variant_lower = format_ident!("{}", variant.to_string().to_case(Case::Snake));
|
let variant_lower = format_ident!("{}", variant.to_string().to_case(Case::Snake));
|
||||||
EnumVariant {
|
EnumVariant {
|
||||||
variant_lower,
|
variant_lower,
|
||||||
|
@ -288,6 +372,7 @@ fn struct_view(props: &ItemProps, s: DataStruct) -> TokenStream2 {
|
||||||
name,
|
name,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
|
title_field,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
let fields = s.fields.iter().map(|f| {
|
let fields = s.fields.iter().map(|f| {
|
||||||
|
@ -297,7 +382,11 @@ fn struct_view(props: &ItemProps, s: DataStruct) -> TokenStream2 {
|
||||||
.expect("each struct field must be named")
|
.expect("each struct field must be named")
|
||||||
.clone();
|
.clone();
|
||||||
let name_str = name.to_string();
|
let name_str = name.to_string();
|
||||||
let (title, description) = get_title_description(&f.attrs);
|
let Attributes {
|
||||||
|
title,
|
||||||
|
title_field: _,
|
||||||
|
description,
|
||||||
|
} = Attributes::parse(&f.attrs);
|
||||||
|
|
||||||
StructField {
|
StructField {
|
||||||
name_str,
|
name_str,
|
||||||
|
@ -334,6 +423,16 @@ fn struct_view(props: &ItemProps, s: DataStruct) -> TokenStream2 {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let title = if let Some(title_field) = title_field {
|
||||||
|
quote! {
|
||||||
|
state.#title_field
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#title
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
let state = props.state;
|
let state = props.state;
|
||||||
|
|
||||||
|
@ -355,6 +454,7 @@ fn enum_view(props: &ItemProps, e: DataEnum) -> TokenStream2 {
|
||||||
name,
|
name,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
|
..
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
let variants = e.variants.iter().map(|v| {
|
let variants = e.variants.iter().map(|v| {
|
||||||
|
@ -365,7 +465,11 @@ fn enum_view(props: &ItemProps, e: DataEnum) -> TokenStream2 {
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (title, description) = get_title_description(&v.attrs);
|
let Attributes {
|
||||||
|
title,
|
||||||
|
title_field: _,
|
||||||
|
description,
|
||||||
|
} = Attributes::parse(&v.attrs);
|
||||||
let variant_lower = format_ident!("{}", variant.to_string().to_case(Case::Snake));
|
let variant_lower = format_ident!("{}", variant.to_string().to_case(Case::Snake));
|
||||||
EnumVariant {
|
EnumVariant {
|
||||||
variant_lower,
|
variant_lower,
|
||||||
|
@ -429,7 +533,11 @@ pub fn web_edit(tokens: TokenStream) -> TokenStream {
|
||||||
let name = input.ident;
|
let name = input.ident;
|
||||||
let edit_ident = format_ident!("{}Edit", name);
|
let edit_ident = format_ident!("{}Edit", name);
|
||||||
|
|
||||||
let (t, d) = get_title_description(&input.attrs);
|
let Attributes {
|
||||||
|
title: t,
|
||||||
|
title_field,
|
||||||
|
description: d,
|
||||||
|
} = Attributes::parse(&input.attrs);
|
||||||
|
|
||||||
let title = t.unwrap_or(name.to_string());
|
let title = t.unwrap_or(name.to_string());
|
||||||
let description = d;
|
let description = d;
|
||||||
|
@ -438,6 +546,7 @@ pub fn web_edit(tokens: TokenStream) -> TokenStream {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
title: title.clone(),
|
title: title.clone(),
|
||||||
description: description.clone(),
|
description: description.clone(),
|
||||||
|
title_field: title_field.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = match input.data {
|
let inner = match input.data {
|
||||||
|
@ -470,7 +579,11 @@ pub fn web_view(tokens: TokenStream) -> TokenStream {
|
||||||
let name = input.ident;
|
let name = input.ident;
|
||||||
let view_ident = format_ident!("{}View", name);
|
let view_ident = format_ident!("{}View", name);
|
||||||
|
|
||||||
let (t, d) = get_title_description(&input.attrs);
|
let Attributes {
|
||||||
|
title: t,
|
||||||
|
title_field,
|
||||||
|
description: d,
|
||||||
|
} = Attributes::parse(&input.attrs);
|
||||||
|
|
||||||
let title = t.unwrap_or(name.to_string());
|
let title = t.unwrap_or(name.to_string());
|
||||||
let description = d;
|
let description = d;
|
||||||
|
@ -479,6 +592,7 @@ pub fn web_view(tokens: TokenStream) -> TokenStream {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
title: title.clone(),
|
title: title.clone(),
|
||||||
description: description.clone(),
|
description: description.clone(),
|
||||||
|
title_field: title_field.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = match input.data {
|
let inner = match input.data {
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<!--<link data-trunk href="tailwind.css" rel="css">-->
|
<!--<link data-trunk href="tailwind.css" rel="css">-->
|
||||||
<link rel="stylesheet" href="/simple.min-d15f5ff500b4c62a.css">
|
<link rel="stylesheet" href="/simple.min-d15f5ff500b4c62a.css">
|
||||||
<link rel="stylesheet" href="/style-d252283caa890f82.css">
|
<link rel="stylesheet" href="/style-2f979acf99c8ad73.css">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet">
|
||||||
<title>LAN Party</title>
|
<title>LAN Party</title>
|
||||||
|
|
||||||
<link rel="preload" href="/index-50210a46cec614af_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
<link rel="preload" href="/index-b0d435005316ee2a_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||||
<link rel="modulepreload" href="/index-50210a46cec614af.js"></head>
|
<link rel="modulepreload" href="/index-b0d435005316ee2a.js"></head>
|
||||||
<body>
|
<body>
|
||||||
<script type="module">import init from '/index-50210a46cec614af.js';init('/index-50210a46cec614af_bg.wasm');</script></body></html>
|
<script type="module">import init from '/index-b0d435005316ee2a.js';init('/index-b0d435005316ee2a_bg.wasm');</script></body></html>
|
|
@ -1,6 +1,6 @@
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
|
|
||||||
use sycamore::prelude::*;
|
use sycamore::{builder::prelude::*, prelude::*};
|
||||||
use web_sys::Event;
|
use web_sys::Event;
|
||||||
|
|
||||||
#[derive(Prop)]
|
#[derive(Prop)]
|
||||||
|
@ -74,3 +74,48 @@ pub fn Block<'a, G: Html>(cx: Scope<'a>, props: BlockProps<'a, G>) -> View<G> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[derive(Prop)]
|
||||||
|
pub struct TestProps<'a> {
|
||||||
|
pub text: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Test<'a, G: Html>(cx: Scope<'a>, props: TestProps<'a>) -> View<G> {
|
||||||
|
let text = create_ref(cx, props.text.clone());
|
||||||
|
|
||||||
|
// This is okay, but I don't know why
|
||||||
|
create_child_scope(cx, move |_| {
|
||||||
|
println!("{}", props.text);
|
||||||
|
drop(props.text);
|
||||||
|
});
|
||||||
|
|
||||||
|
// This is fine
|
||||||
|
create_child_scope(cx, move |_| {
|
||||||
|
println!("{}", text);
|
||||||
|
drop(text);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Builders always seem to work just fine
|
||||||
|
let _: View<G> = div().c(p().t(text)).view(cx);
|
||||||
|
let _: View<G> = div().dyn_c_scoped(|cx| p().t(text).view(cx)).view(cx);
|
||||||
|
let _: View<G> = div()
|
||||||
|
.dyn_c_scoped(|cx| p().t(props.text).view(cx))
|
||||||
|
.t(props.text)
|
||||||
|
.view(cx);
|
||||||
|
|
||||||
|
// error[E0521]: borrowed data escapes outside of function
|
||||||
|
let _: View<G> = view! { cx,
|
||||||
|
p { (text) }
|
||||||
|
};
|
||||||
|
|
||||||
|
// error[E0521]: borrowed data escapes outside of function
|
||||||
|
let _: View<G> = view! { cx,
|
||||||
|
p { (props.text) }
|
||||||
|
p { (props.text) }
|
||||||
|
};
|
||||||
|
|
||||||
|
view! { cx, }
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use lan_party_core::{
|
use lan_party_core::{
|
||||||
|
components::Block,
|
||||||
edit::IntoEdit,
|
edit::IntoEdit,
|
||||||
event::{Event, EventSpec, EventUpdate},
|
event::{Event, EventSpec, EventUpdate},
|
||||||
view::IntoView,
|
view::IntoView,
|
||||||
|
@ -8,7 +9,7 @@ use reqwasm::http::Method;
|
||||||
use sycamore::{futures::spawn_local_scoped, prelude::*};
|
use sycamore::{futures::spawn_local_scoped, prelude::*};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{messages::Messenger, Block, Button},
|
components::{messages::Messenger, Button},
|
||||||
util::api_request,
|
util::api_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,17 +19,20 @@ pub fn EventsPage<'a, G: Html>(cx: Scope<'a>) -> View<G> {
|
||||||
|
|
||||||
let event_spec = create_signal(cx, EventSpec::default());
|
let event_spec = create_signal(cx, EventSpec::default());
|
||||||
let event_update = create_signal(cx, EventUpdate::default());
|
let event_update = create_signal(cx, EventUpdate::default());
|
||||||
|
let event_update_name = create_signal(cx, String::new());
|
||||||
|
|
||||||
let events: &'a Signal<Vec<Event>> = create_signal(cx, Vec::<Event>::new());
|
let events: &'a Signal<Vec<Event>> = create_signal(cx, Vec::<Event>::new());
|
||||||
|
|
||||||
spawn_local_scoped(cx, async move {
|
let update_events = move || async move {
|
||||||
events.set(
|
events.set(
|
||||||
api_request::<_, Vec<Event>>(Method::GET, "/event", Option::<()>::None)
|
api_request::<_, Vec<Event>>(Method::GET, "/event", Option::<()>::None)
|
||||||
.await
|
.await
|
||||||
.map(|inner| inner.unwrap())
|
.map(|inner| inner.unwrap())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
|
spawn_local_scoped(cx, update_events());
|
||||||
|
|
||||||
let onadd = move |_| {
|
let onadd = move |_| {
|
||||||
spawn_local_scoped(cx, async move {
|
spawn_local_scoped(cx, async move {
|
||||||
|
@ -54,29 +58,146 @@ pub fn EventsPage<'a, G: Html>(cx: Scope<'a>) -> View<G> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let onupdate = move |_| {
|
||||||
|
spawn_local_scoped(cx, async move {
|
||||||
|
let res = api_request::<EventUpdate, ()>(
|
||||||
|
Method::POST,
|
||||||
|
&format!("/event/{}", event_update_name),
|
||||||
|
Some((*event_update).get().as_ref().clone()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Ok(_) = res {
|
||||||
|
update_events().await;
|
||||||
|
messenger.info(
|
||||||
|
"Updated event",
|
||||||
|
format!(
|
||||||
|
"Successfully updated event with name \"{}\"",
|
||||||
|
event_update_name
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
messenger.info(
|
||||||
|
"Error when updating event",
|
||||||
|
format!("Unable to update event with name \"{}\"", event_update_name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let onviewoutcome = move |event_name: String| {
|
||||||
|
move |_| {
|
||||||
|
let event_name = event_name.clone();
|
||||||
|
spawn_local_scoped(cx, async move {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let onstop = move |event_name: String| {
|
||||||
|
move |_| {
|
||||||
|
let event_name = event_name.clone();
|
||||||
|
spawn_local_scoped(cx, async move {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ondelete = move |event_name: String| {
|
||||||
|
move |_| {
|
||||||
|
let event_name = event_name.clone();
|
||||||
|
spawn_local_scoped(cx, async move {
|
||||||
|
let res =
|
||||||
|
api_request::<(), ()>(Method::DELETE, &format!("/event/{}", event_name), None)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Ok(_) = res {
|
||||||
|
update_events().await;
|
||||||
|
messenger.info(
|
||||||
|
"Removed event",
|
||||||
|
format!("Successfully removed event with name \"{}\"", event_name),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
messenger.info(
|
||||||
|
"Error when removing event",
|
||||||
|
format!("Unable to remove event with name \"{}\"", event_name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
view! { cx,
|
view! { cx,
|
||||||
|
div(class="events-cols") {
|
||||||
|
div {
|
||||||
Block(title="Events".into()) {
|
Block(title="Events".into()) {
|
||||||
Keyed(
|
Indexed(
|
||||||
iterable=&events,
|
iterable=&events,
|
||||||
view=move |cx, event| {
|
view=move |cx, event| {
|
||||||
let event = create_ref(cx, event);
|
let event = create_ref(cx, event);
|
||||||
view! { cx,
|
view! { cx,
|
||||||
(event.view(cx))
|
//(event.view(cx))
|
||||||
|
EventView(event=event, ondelete=ondelete(event.name.clone()), onviewoutcome=onviewoutcome(event.name.clone()), onstop=onstop(event.name.clone()))
|
||||||
br()
|
br()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
key=|event| (event.name.clone()),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
br()
|
}
|
||||||
|
div(class="events-right") {
|
||||||
Block(title="Create new event".into()) {
|
Block(title="Create new event".into()) {
|
||||||
(event_spec.edit(cx))
|
(event_spec.edit(cx))
|
||||||
Button(icon="mdi-check".into(), onclick=onadd)
|
Button(icon="mdi-check".into(), onclick=onadd)
|
||||||
}
|
}
|
||||||
br()
|
br()
|
||||||
Block(title="Update an event".into()) {
|
Block(title="Update an event".into()) {
|
||||||
|
label { "Event name" }
|
||||||
|
select(bind:value=event_update_name) {
|
||||||
|
Indexed(
|
||||||
|
iterable=&events,
|
||||||
|
view=move |cx, event| {
|
||||||
|
let event = create_ref(cx, event);
|
||||||
|
view! { cx,
|
||||||
|
option(value=event.name) { (event.name) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
(event_update.edit(cx))
|
(event_update.edit(cx))
|
||||||
Button(icon="mdi-check".into(), onclick=move |_| debug!("{:#?}", event_update.get()))
|
Button(icon="mdi-check".into(), onclick=onupdate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Prop)]
|
||||||
|
struct EventViewProps<'a, F1, F2, F3>
|
||||||
|
where
|
||||||
|
F1: FnMut(web_sys::Event),
|
||||||
|
F2: FnMut(web_sys::Event),
|
||||||
|
F3: FnMut(web_sys::Event),
|
||||||
|
{
|
||||||
|
pub event: &'a Event,
|
||||||
|
|
||||||
|
pub ondelete: F1,
|
||||||
|
pub onviewoutcome: F2,
|
||||||
|
pub onstop: F3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn EventView<'a, G, F1, F2, F3>(cx: Scope<'a>, props: EventViewProps<'a, F1, F2, F3>) -> View<G>
|
||||||
|
where
|
||||||
|
F1: FnMut(web_sys::Event) + 'a,
|
||||||
|
F2: FnMut(web_sys::Event) + 'a,
|
||||||
|
F3: FnMut(web_sys::Event) + 'a,
|
||||||
|
G: Html,
|
||||||
|
{
|
||||||
|
view! { cx,
|
||||||
|
Block(title=props.event.name.clone()) {
|
||||||
|
(props.event.description)
|
||||||
|
br()
|
||||||
|
Button(text="Delete".into(), icon="mdi-delete".into(), onclick=props.ondelete)
|
||||||
|
Button(text="View outcome".into(), onclick=props.onviewoutcome)
|
||||||
|
Button(text="Finish".into(), onclick=props.onstop)
|
||||||
|
br()
|
||||||
|
(props.event.event_type.view(cx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,15 @@ pub async fn api_request<B: Serialize, R: for<'a> Deserialize<'a>>(
|
||||||
|
|
||||||
let res = req.send().await?;
|
let res = req.send().await?;
|
||||||
|
|
||||||
|
if res.ok() {
|
||||||
if let Ok(json) = res.json().await {
|
if let Ok(json) = res.json().await {
|
||||||
Ok(Some(json))
|
Ok(Some(json))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("Request failed"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
|
@ -64,7 +64,7 @@ textarea:focus, input:focus{
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
grid-template-columns: 1fr min(60rem, 90%) 1fr;
|
grid-template-columns: 1fr min(100rem, 90%) 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messages {
|
.messages {
|
||||||
|
@ -91,3 +91,21 @@ body {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.events-cols {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.events-cols > * {
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.events-cols:nth-child(1) {
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.events-cols:nth-child(2) {
|
||||||
|
grid-column: 2;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue