diff --git a/src/app.rs b/src/app.rs index 2dc3611..394aeb7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -7,7 +7,7 @@ use leptos_router::{ }; use url::Url; -use crate::pages::{AboutPage, HomePage, ProviderEntry}; +use crate::pages::{AboutPage, HomePage}; pub fn shell(options: LeptosOptions) -> impl IntoView { view! { @@ -67,7 +67,6 @@ pub fn App() -> impl IntoView { - diff --git a/src/common/expandable_info.rs b/src/common/expandable_info.rs index ebb0f8f..8f6c2b4 100644 --- a/src/common/expandable_info.rs +++ b/src/common/expandable_info.rs @@ -1,16 +1,16 @@ -use std::collections::HashMap; -use serde::{Deserialize, Serialize}; +use crate::common::ValueType; use macros::Reactive; -use crate::components::ValueType; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; /// This struct allows for completely open-ended data storage. -/// +/// /// The `HashMap` allows the application to store an arbitrary number of values which are accessible /// using a `String` as a key, allowing for simple shifting of stored values. All information is /// stored using the `ValueType` enum, which provides some simple methods to verify the type of the /// stored value. It *must* have a main key member name so that a database will know how to store /// the data. -/// +/// /// In the future, this should also be optimized to store properly within a DynamoDB table. As of /// now, the table will simply create a single massive `Map` field that contains all the stored /// information in the `HashMap`. We'd prefer to be able to get single values if possible, diff --git a/src/common/form_schema.rs b/src/common/form_schema.rs index 8655c94..84e2816 100644 --- a/src/common/form_schema.rs +++ b/src/common/form_schema.rs @@ -1,13 +1,14 @@ -use std::collections::HashMap; -use leptos::{view, IntoView}; +use crate::common::ValueType; +use crate::components::{CheckboxList, OutlinedTextField, RadioList, Row, Select}; use leptos::either::EitherOf5; use leptos::prelude::RwSignal; -use serde::{Serialize, Deserialize}; -use crate::components::{CheckboxList, OutlinedTextField, RadioList, Row, Select, ValueType}; +use leptos::{IntoView, view}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; /// Defines a series of options that allow a user to define a form. #[derive(Serialize, Deserialize, Clone, Debug)] -pub enum InputType{ +pub enum InputType { /// Represents a text input. Requires a `String` key, `String` label, and `String` placeholder. /// The entered value will be returned as a single `String` value. Text(String, String, String), @@ -23,7 +24,7 @@ pub enum InputType{ Checkbox(String, String, Vec), /// Represents a dropdown input. Requires a `String` key, `String` label, and a series /// of `String` options. The selected value will be returned as a `String`. - Select(String, String, Vec) + Select(String, String, Vec), } impl InputType { @@ -80,7 +81,7 @@ impl InputType { }} } } - + pub fn into_view_rows(self, data_map: RwSignal>) -> impl IntoView { view! { @@ -91,12 +92,12 @@ impl InputType { } /// Shorthand macro for creating `InputType` enums. -/// +/// /// Example usage: /// ``` /// // To create an InputType::Text /// let text_input = input!(Text, "member", "label", "placeholder"); -/// +/// /// // To create an InputType::Select /// let select_input = input!(Select, "member", "label", ["option1", "option2"]); /// ``` @@ -109,7 +110,7 @@ macro_rules! input { vec![$($option.to_string()), *] ) }; - + ($variant:ident, $key:expr, $label:expr, $placeholder:expr) => { $crate::common::InputType::$variant( $key.to_string(), diff --git a/src/common/mod.rs b/src/common/mod.rs index 7e04a46..fd320cb 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,7 +1,6 @@ -mod student_info; -mod user; -mod form_schema; mod expandable_info; +mod form_schema; +mod user; pub mod value_type; -pub use self::{expandable_info::*, form_schema::*, student_info::*, user::*}; +pub use self::{expandable_info::*, form_schema::*, user::*, value_type::*}; diff --git a/src/common/student_info.rs b/src/common/student_info.rs deleted file mode 100644 index 6f7b792..0000000 --- a/src/common/student_info.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::components::{MultiEntryData, ValueType}; -use macros::Reactive; -use serde::{Deserialize, Serialize}; -use std::default::Default; -use chrono::{DateTime, Utc}; -use reactive_stores::Store; - -/// Represents information about a student. -#[derive(Serialize, Deserialize, Debug, Clone, Default, Store, Reactive)] -pub struct StudentInfo { - /// The email address of the student. This was used in the previous version of the database, - /// and should now be deprecated. - /// - /// This is the primary key of the table, and should not have a default value. - pub Email: String, - /// The student's first name. - #[serde(default)] - pub first_name: ValueType, // String - /// The student's last name. - #[serde(default)] - pub last_name: ValueType, // String - /// The student's highest math score on the SAT. - #[serde(default = "default_number")] - pub math_sat: ValueType, // Number - /// The student's desired contact email. - #[serde(default)] - pub contact_email: ValueType, // String - /// The student's address. - #[serde(default)] - pub address: ValueType, // String - /// The student's town of residence. - #[serde(default)] - pub town: ValueType, // String - /// The student's entered phone number. - #[serde(default)] - pub phone_number: ValueType, // May be stored as a straight number in the future, but String - /// The student's birthdate. - #[serde(default)] - pub birth_date: DateTime, - /// The student's gender. - #[serde(default)] - pub gender: ValueType, // String - /// The student's athletic information. - #[serde(default)] - pub athletic_info: Vec, -} - -fn default_number() -> ValueType { - ValueType::Number(None) -} \ No newline at end of file diff --git a/src/common/value_type.rs b/src/common/value_type.rs index ffea234..982df27 100644 --- a/src/common/value_type.rs +++ b/src/common/value_type.rs @@ -1,11 +1,11 @@ +use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use serde::{Serialize, Deserialize}; use std::fmt::Display; /// Defines a set of possible input types. They allow for the type and value of every data member /// to be stored within a single `HashMap` or `Vec`. This means that even though all the data is /// created as a single type, the values are still able to be type safe. -/// +/// /// A note: other structs cannot be stored in this enum, however it was designed to work with /// a `HashMap` of more `ValueType` enums. There are numerous ways to convert a struct into a /// `HashMap`, specifically storing all the struct's fields as `String` keys in the map. @@ -24,7 +24,11 @@ pub enum ValueType { impl ValueType { /// Returns true if this is a `String`, otherwise false. pub fn is_string(&self) -> bool { - if let ValueType::String(_) = self { true } else { false } + if let ValueType::String(_) = self { + true + } else { + false + } } /// Attempts to get this value as a `String` type. This is different from the `to_string` method. @@ -38,7 +42,11 @@ impl ValueType { /// Returns true if this is a `Number`, otherwise false. pub fn is_number(&self) -> bool { - if let ValueType::Number(_) = self { true } else { false } + if let ValueType::Number(_) = self { + true + } else { + false + } } /// Attempts to get this value as an `i32`. @@ -52,7 +60,11 @@ impl ValueType { /// Returns true if this is a `List`, otherwise false. pub fn is_list(&self) -> bool { - if let ValueType::List(_) = self { true } else { false } + if let ValueType::List(_) = self { + true + } else { + false + } } /// Attempts to get this value as a `Vec`. Each of the values inside the list must @@ -64,12 +76,16 @@ impl ValueType { Err(self.clone()) } } - + /// Returns true if this is a `Map`, otherwise false. - pub fn is_map(&self) -> bool { - if let ValueType::Map(_) = self { true } else { false } + pub fn is_map(&self) -> bool { + if let ValueType::Map(_) = self { + true + } else { + false + } } - + /// Attempts to get this value as a `HashMap`. pub fn as_map(&self) -> Result>, Self> { if let ValueType::Map(v) = self { diff --git a/src/components/checkbox.rs b/src/components/checkbox.rs index 47e5791..a245834 100644 --- a/src/components/checkbox.rs +++ b/src/components/checkbox.rs @@ -1,6 +1,6 @@ -use std::collections::HashMap; +use crate::common::ValueType; use leptos::prelude::*; -use crate::components::ValueType; +use std::collections::HashMap; #[component] pub fn Checkbox( @@ -70,7 +70,7 @@ pub fn CheckboxList( .contains(&ValueType::String(Some(item_name.clone()))) // bool } }); - + let on_change = { let item_name = item.clone(); let data_member = data_member.clone(); @@ -102,7 +102,7 @@ pub fn CheckboxList( }); } }; - + view! { ) -> Option { /// Updates an Entry within the given `Vec`. fn update_entry_list(list: &mut Vec, data: &HashMap) { if let Some(uuid) = get_uuid_from_map(&data) { - if let Some(index) = list.iter() - .position(|x| get_uuid_from_map(&x.as_map().unwrap().unwrap_or_default()) == Some(uuid.clone())) - { + if let Some(index) = list.iter().position(|x| { + get_uuid_from_map(&x.as_map().unwrap().unwrap_or_default()) == Some(uuid.clone()) + }) { list[index] = ValueType::Map(Some(data.clone())); } } @@ -27,29 +27,32 @@ fn update_entry_list(list: &mut Vec, data: &HashMap ValueType { let mut entry_map = HashMap::new(); - entry_map.insert("uuid".into(), ValueType::String(Some(Uuid::new_v4().to_string()))); + entry_map.insert( + "uuid".into(), + ValueType::String(Some(Uuid::new_v4().to_string())), + ); ValueType::Map(Some(entry_map)) } // endregion /// # Multi-Entry Component -/// +/// /// A rewrite of the `MultiEntry` component. Uses a single source of truth in the form of the `data_map` /// prop. Creates and displays a series of `Entry` components that use their own `HashMap` signals. /// Each of these `Entry` components runs a callback that alters the original `data_map`. -/// +/// /// The `schema` prop determines what inputs are available to all `Entry` components created from /// this component. For example, using a `schema` that contains an `InputType::Text` and an /// `InputType::Select` means that all `Entry` components will render a text and dropdown input /// component. The users shall be able to use these components to alter the data. -/// +/// /// Example usage: /// ``` /// let data_signal = RwSignal::new(HashMap::new()); /// view! { /// ValueType { /// /> /// } /// ``` -/// +/// /// In this example, any time a new `Entry` is created, it will contain two text fields that edit the /// `first_name` and `last_name` keys within that `Entry`. #[component] -pub fn MultiEntryRewrite( +pub fn MultiEntry( /// The data member that contains the list of entries within the `data_map`. - #[prop(into)] data_member: String, + #[prop(into)] + data_member: String, /// The data map that contains all the information. Data is found within this map using the /// component's `data_member` input. - #[prop()] data_map: RwSignal>, + #[prop()] + data_map: RwSignal>, #[prop(into)] name_member: String, /// The type and order of the inputs for each `Entry` component within this `MultiEntry` component. - #[prop(optional)] schema: Vec, + #[prop(optional)] + schema: Vec, ) -> impl IntoView { // Make the values within the list reactive. Each of these values should be a ValueType::Map, // so we can pass these directly to the application. - let data_reactive = RwSignal::new(data_map.get_untracked() - .get(&data_member) - .unwrap_or(&ValueType::List(None)).as_list() - .unwrap_or(Some(Vec::::new())) - .unwrap_or(Vec::new())); + let data_reactive = RwSignal::new( + data_map + .get_untracked() + .get(&data_member) + .unwrap_or(&ValueType::List(None)) + .as_list() + .unwrap_or(Some(Vec::::new())) + .unwrap_or(Vec::new()), + ); // This effect updates the data_map with any new values from the data_reactive list, every time // the data_reactive list is changed. This allows changes from within the Entry component to @@ -89,9 +99,9 @@ pub fn MultiEntryRewrite( map.insert(data_member.clone(), ValueType::List(Some(entry_list))); }); }); - + let add_entry = move |_| data_reactive.update(|list| list.push(new_entry())); - + view! {
@@ -107,7 +117,7 @@ pub fn MultiEntryRewrite( .as_map() .unwrap_or_default() .unwrap_or_default()); - + // This effect updates the data_reactive list with the map_signal values, every // time the map_signal is changed. Effect::new(move |_| { @@ -116,7 +126,7 @@ pub fn MultiEntryRewrite( update_entry_list(&mut list, &child_map); }); }); - + view! { /// } /// ``` -/// +/// /// In this example, the `Entry` component that is rendered will have two text inputs. Each of these /// inputs will alter the `first_name` and `last_name` keys in the `data_map` respectively, and the /// `Entry` component will run a callback to its parent `MultiEntry` component to update the `data_map` @@ -166,18 +176,22 @@ pub fn MultiEntryRewrite( #[component] fn Entry( /// The data map that contains all information. Data is found within this map using the `member` - /// props. This map should be unique to this entry; in other words, it should be contained and + /// props. This map should be unique to this entry; in other words, it should be contained and /// passed down from within the entry list in the parent `MultiEntry` component. - #[prop()] data_map: RwSignal>, + #[prop()] + data_map: RwSignal>, /// The schema of this entry. Should contain all information about what inputs to use and what /// data members those inputs modify. - #[prop()] schema: Vec, + #[prop()] + schema: Vec, /// The display name member of this entry. Determines where the component should search for the /// name of the entry within the `data_map`. - #[prop(into)] name_member: String, + #[prop(into)] + name_member: String, /// The info member of this entry. Determines where the component should search for the extra /// information of this entry within the `data_map`. - #[prop(optional, into)] info_member: String, + #[prop(optional, into)] + info_member: String, ) -> impl IntoView { view! {
diff --git a/src/components/multi_entry/mod.rs b/src/components/multi_entry/mod.rs deleted file mode 100644 index 9e0fc57..0000000 --- a/src/components/multi_entry/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod multi_entry_data; -pub mod multi_entry; -mod multi_entry_member; -mod rewrite; - -pub use crate::common::value_type::*; -pub use self::{ - multi_entry::*, - multi_entry_data::*, - multi_entry_member::*, - rewrite::*, -}; diff --git a/src/components/multi_entry/multi_entry.rs b/src/components/multi_entry/multi_entry.rs deleted file mode 100644 index b65f158..0000000 --- a/src/components/multi_entry/multi_entry.rs +++ /dev/null @@ -1,144 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; -use leptos::either::either; -use crate::components::{ActionButton, OutlinedTextField}; -use leptos::prelude::*; -use crate::common::InputType; -use crate::components::ValueType; -use super::{multi_entry_data::*, multi_entry_member::*}; - -fn render_entry_component(entry_data: MultiEntryData, member_info: MultiEntryMember, label: String) -> impl IntoView { - // Parse the multientry members into the correct components - let data = entry_data.data.get(&member_info.member_name); - let reactive = RwSignal::new(data.unwrap().clone()); - let temp = either!(member_info.input_type, - InputType::Text(_, _, _) => view! { - - }, - _ => view! {} - ); - - temp.into_view() -} - -/// Represents a single entry in a MultiEntry component. -/// These are displayed in a list, and function a little bit like a dropdown. -/// When clicked, they expand to show all the inputs, which can be edited. -#[component] -pub fn Entry( - /// The data for this entry. - #[prop()] - entry_data: MultiEntryData, - /// The main display member for this entry. - #[prop()] - name_member: Signal, - /// The secondary display member for this entry. - #[prop()] - info_member: Signal, - /// The schema for all inputs that should be used, and what `MultiEntryMember` info is associated - /// with them. - #[prop()] - schema: Arc> -) -> impl IntoView { - let name_data = entry_data.data.get(&name_member.get().member_name).unwrap(); - let info_data = entry_data.data.get(&info_member.get().member_name).unwrap(); - - let schema = Arc::clone(&schema); - - view! { - // This div MUST have the same spacing CSS as the second-level header div -
- {name_data.to_string()} - {info_data.to_string()} -
- {schema - .iter() - .map(|input_type| { - let data = entry_data.clone(); - let input = input_type.clone(); - view! { - //
{render_entry_component(data, input, "test".to_owned())}
- } - }).collect_view() - } - } -} - -/// Allows users to enter more open-ended information, and generates a `Vec` filled -/// with the user's entered information. The schema for this data must be defined by this component -/// such that it is constructable using a series of `String` keys, where each key stores a single -/// `ValueType` enum. -#[component] -pub fn MultiEntry( - /// The list of entries to use. - #[prop()] - entries: RwSignal>, - /// The main display member for each entry object. - #[prop()] - name_member: Signal, - /// The secondary display member for each entry object. - #[prop()] - info_member: Signal, - /// The schema of the components. Determines what kind of data is stored and where. - /// The `name_member` and `info_member` should be included in this list. - #[prop(optional)] - schema: Vec -) -> impl IntoView { - // Creates an entry in the given entries list. - let create_entry = move |_| { - let mut new_entry = MultiEntryData::new(); - new_entry.data.insert( - name_member.get().member_name, - ValueType::String(Some(String::from("Testing"))), - ); - new_entry.data.insert( - info_member.get().member_name, - ValueType::String(Some(new_entry.id.to_string())), - ); - entries.update(|entry_vec| entry_vec.push(new_entry)); - }; - - let rc_schema = Arc::new(schema); - - view! { -
- // Header section -
- // This div MUST have the same spacing CSS as the main entry div -
- - {name_member.get().display_name} - - - {info_member.get().display_name} - -
-
- // Entry section -
- - } - } - /> -
-
- Add Entry - } -} diff --git a/src/components/multi_entry/multi_entry_data.rs b/src/components/multi_entry/multi_entry_data.rs deleted file mode 100644 index 5bc6ede..0000000 --- a/src/components/multi_entry/multi_entry_data.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::collections::HashMap; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; -use macros::Reactive; -use crate::components::ValueType; - -/// Stores all data for the `MultiEntry` component. -/// -/// All values are stored within the `HashMap`. These entries only contain the value and -/// the type of the value itself. Each piece of data also has a unique ID, which allows for simpler -/// dynamic rendering.`data` -#[derive(Clone, Serialize, Deserialize, Debug, Reactive, Default)] -pub struct MultiEntryData { - /// The data's unique ID. - pub id: Uuid, - /// The pertinent data for this struct. This may be variable - each `MultiEntry` component - /// may specify its own schema and way of storing this data. The only limitation will be the - /// types of data that can be stored - they must be easily parsable by a standard HTML input. - pub data: HashMap, -} - -impl MultiEntryData { - /// Creates a new MultiEntryData struct. - pub fn new() -> Self { - Self { - id: Uuid::new_v4(), - data: HashMap::new(), - } - } -} diff --git a/src/components/multi_entry/multi_entry_member.rs b/src/components/multi_entry/multi_entry_member.rs deleted file mode 100644 index c2f20bb..0000000 --- a/src/components/multi_entry/multi_entry_member.rs +++ /dev/null @@ -1,45 +0,0 @@ -use serde::{Deserialize, Serialize}; -use crate::common::InputType; - -/// Contains all information required to display an entry in the MultiEntry component. -/// -/// NOTE: This does not define a schema for the data stored by the component, only the data that the -/// component displays as "identifying information." -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct MultiEntryMember { - /// The name of the member, used for display only. - pub display_name: String, - /// The actual name of the member, used to get the data from an Entry struct. - pub member_name: String, - /// The type of input that should be used to display this member. - pub input_type: InputType -} - -impl MultiEntryMember { - /// Creates a new MultiEntryMember struct. - pub fn _new() -> Self { - Self { - display_name: String::new(), - member_name: String::new(), - input_type: InputType::Text("".to_string(), "".to_string(), "".to_string()), - } - } - - /// Creates a new MultiEntryMember struct with the specified information. - pub fn _new_with_data(display_name: String, member_name: String, input_type: InputType) -> Self { - Self { - display_name, - member_name, - input_type - } - } - - /// Creates a new MultiEntryMember struct with the specified information. Uses string slices. - pub fn from_str(display_name: &str, member_name: &str) -> Self { - Self { - display_name: display_name.into(), - member_name: member_name.into(), - input_type: InputType::Text("".to_string(), "".to_string(), "".to_string()) - } - } -} diff --git a/src/components/panel.rs b/src/components/panel.rs index 83c32e7..bfd17c9 100644 --- a/src/components/panel.rs +++ b/src/components/panel.rs @@ -25,7 +25,7 @@ use leptos::prelude::*; #[component] pub fn Panel( #[prop(optional, default = false)] hidden: bool, - #[prop(optional)] children: Option + #[prop(optional)] children: Option, ) -> impl IntoView { view! {