diff --git a/src/leptos_app/components/checkbox.rs b/src/leptos_app/components/checkbox.rs index 6f6e1d4..b3ae3a3 100644 --- a/src/leptos_app/components/checkbox.rs +++ b/src/leptos_app/components/checkbox.rs @@ -1,10 +1,12 @@ use leptos::prelude::*; +use crate::leptos_app::components::FieldTooltip; #[component] pub fn Checkbox( #[prop(into)] id: String, #[prop(into)] label: String, #[prop(into)] checked: Signal, + #[prop(optional, default = true)] show_help: bool, on_toggle: T, ) -> impl IntoView where @@ -19,9 +21,11 @@ where type="checkbox" on:input=move |_| on_toggle(!checked.get_untracked()) /> - + + + } diff --git a/src/leptos_app/components/field_status.rs b/src/leptos_app/components/field_status.rs new file mode 100644 index 0000000..e2213f4 --- /dev/null +++ b/src/leptos_app/components/field_status.rs @@ -0,0 +1,437 @@ +use leptos::prelude::*; +use leptos::context::Provider; +use leptos::{component, view, IntoView}; + +// Global context for research fields toggle +#[derive(Clone, Copy)] +pub struct ResearchFieldsContext { + pub show: RwSignal, +} + +impl ResearchFieldsContext { + pub fn new() -> Self { + Self { + show: RwSignal::new(false), + } + } +} + +#[component] +pub fn ResearchFieldProvider(children: Children) -> impl IntoView { + let context = ResearchFieldsContext::new(); + + view! { + + {children()} + + } +} + +#[component] +pub fn ResearchField( + children: Children, +) -> impl IntoView +{ + let context = expect_context::(); + + view! { +
+
+ + "! Research Field" +
+ + + +
+ "This field is under active development and may change in future versions" +
+
+
+
+ + "Under development - may change" + +
+
+ {children()} +
+
+ } +} + +#[component] +pub fn CoreField( + children: Children, +) -> impl IntoView +{ + view! { +
+ {children()} +
+ } +} + +#[component] +pub fn ResearchFieldToggle() -> impl IntoView { + let context = expect_context::(); + + view! { +
+
+ + +
+ + + +
+ "Toggle to show/hide experimental fields that are still under development" +
+
+
+
+

+ "Research fields may change or be removed in future versions. Use for experimentation only." +

+
+ } +} + +// Field help metadata system +#[derive(Clone, Debug)] +pub struct FieldHelp { + pub title: String, + pub description: String, + pub examples: Vec, + pub when_to_use: String, + pub field_type: FieldType, +} + +#[derive(Clone, Debug)] +pub enum FieldType { + Core, + Research, +} + +impl FieldHelp { + pub fn new_core(title: &str, description: &str, examples: Vec<&str>, when_to_use: &str) -> Self { + Self { + title: title.to_string(), + description: description.to_string(), + examples: examples.iter().map(|s| s.to_string()).collect(), + when_to_use: when_to_use.to_string(), + field_type: FieldType::Core, + } + } + + pub fn new_research(title: &str, description: &str, examples: Vec<&str>, when_to_use: &str) -> Self { + Self { + title: title.to_string(), + description: description.to_string(), + examples: examples.iter().map(|s| s.to_string()).collect(), + when_to_use: when_to_use.to_string(), + field_type: FieldType::Research, + } + } +} + +// Field help database +pub fn get_field_help(field_id: &str) -> Option { + match field_id { + // System Core Fields + "system-name" => Some(FieldHelp::new_core( + "System Name", + "The primary identifier for your system. Use clear, descriptive names that communicate the system's purpose.", + vec!["Payment Processing System", "User Authentication", "Data Analytics Pipeline"], + "Always required. Choose names that stakeholders will immediately understand." + )), + "system-description" => Some(FieldHelp::new_core( + "System Description", + "A detailed explanation of what the system does, its purpose, and key characteristics.", + vec!["Processes customer payments securely", "Manages user login and access control"], + "Essential for documentation and stakeholder communication. Be specific about system behavior." + )), + "complexity" => Some(FieldHelp::new_core( + "Complexity", + "Indicates the system's structural and behavioral complexity. Adaptable systems can change their structure, evolveable systems can develop new capabilities.", + vec!["Simple systems: Adaptable=false, Evolveable=false", "Organizations: Both true"], + "Helps understand system behavior and change potential. Critical for planning interventions." + )), + + // System Research Fields + "system-equivalence" => Some(FieldHelp::new_research( + "Equivalence", + "Mathematical representation of system behavior. Captures functional relationships and invariants.", + vec!["f(x) = mx + b for linear systems", "Conservation equations"], + "When you need formal analysis or want to model system mathematically. Advanced users." + )), + "system-time-unit" => Some(FieldHelp::new_research( + "Time Unit", + "The fundamental time scale at which the system operates or is measured.", + vec!["seconds", "days", "quarters", "milliseconds"], + "For temporal analysis and simulation. Specify when system behavior has time-dependent characteristics." + )), + "system-history" => Some(FieldHelp::new_research( + "History", + "Records of past states, behaviors, and changes. Enables path-dependent analysis.", + vec!["Previous configurations", "Change log", "Performance trends"], + "When system behavior depends on past states or you need to track evolution over time." + )), + "transformation" => Some(FieldHelp::new_research( + "Transformation", + "Rules or functions that describe how the system changes inputs to outputs or states.", + vec!["Data processing rules", "State transition functions"], + "For formal system modeling and when precise transformation logic is important." + )), + + // Boundary Research Fields + "boundary-name" => Some(FieldHelp::new_research( + "Boundary Name", + "Identifier for the system boundary. Useful when analyzing multiple boundary definitions.", + vec!["Legal Boundary", "Operational Boundary", "Physical Boundary"], + "When working with complex systems having multiple boundary interpretations." + )), + "boundary-description" => Some(FieldHelp::new_research( + "Boundary Description", + "Detailed explanation of what constitutes the system boundary and boundary conditions.", + vec!["Includes all employees", "Excludes external contractors"], + "For precise boundary definition in complex systems or formal analysis." + )), + + // Environment Research Fields + "environment-name" => Some(FieldHelp::new_research( + "Environment Name", + "Identifier for the system's operating environment.", + vec!["Market Environment", "Regulatory Environment", "Technical Environment"], + "When analyzing system-environment interactions or multiple environmental contexts." + )), + "environment-description" => Some(FieldHelp::new_research( + "Environment Description", + "Characteristics and properties of the environment in which the system operates.", + vec!["Highly regulated market", "Fast-changing technology landscape"], + "For environmental analysis and understanding external constraints and opportunities." + )), + + // Core Boundary Fields + "boundary-porosity" => Some(FieldHelp::new_core( + "Boundary Porosity", + "How permeable the system boundary is. 0 = completely closed, 1 = completely open.", + vec!["0.1 for secure systems", "0.8 for collaborative systems"], + "Indicates how easily things cross the boundary. Important for understanding system openness." + )), + "boundary-perceptive-fuzziness" => Some(FieldHelp::new_core( + "Perceptive Fuzziness", + "How clearly defined the boundary appears to observers. 0 = crystal clear, 1 = very fuzzy.", + vec!["0.1 for legal entities", "0.7 for cultural movements"], + "Measures boundary clarity. Higher values indicate disputed or unclear boundaries." + )), + + // Interaction Core Fields + "interaction-name" => Some(FieldHelp::new_core( + "Interaction Name", + "Descriptive name for this specific interaction or flow between system elements.", + vec!["Data Transfer", "Payment Flow", "User Request"], + "Always specify. Helps track and understand system dynamics and relationships." + )), + "interaction-description" => Some(FieldHelp::new_core( + "Interaction Description", + "Detailed explanation of what this interaction involves and how it works.", + vec!["User submits form data to server", "System sends payment to vendor"], + "Essential for understanding system behavior and documenting interactions." + )), + "interaction-type" => Some(FieldHelp::new_core( + "Interaction Type", + "Category of interaction: Inflow (into system), Outflow (from system), or Internal (within system).", + vec!["Inflow: Customer orders", "Outflow: Product delivery", "Internal: Data processing"], + "Critical for understanding system structure and mapping flows correctly." + )), + "substance-type" => Some(FieldHelp::new_core( + "Substance Type", + "What type of 'stuff' flows in this interaction: Information, Material, Energy, or People.", + vec!["Information: data, messages", "Material: products, resources", "Energy: power, fuel"], + "Fundamental classification. Essential for understanding what the system processes." + )), + "substance-unit" => Some(FieldHelp::new_core( + "Substance Unit", + "Unit of measurement for the substance in this interaction.", + vec!["GB (for data)", "USD (for money)", "people/hour", "kg/day"], + "Enables quantitative analysis and measurement of system flows." + )), + "substance-amount" => Some(FieldHelp::new_core( + "Substance Amount", + "Quantity of substance involved in this interaction, using the specified unit.", + vec!["100 (if unit is USD)", "50 (if unit is people/day)"], + "For capacity planning, performance analysis, and quantitative system understanding." + )), + "interaction-usability" => Some(FieldHelp::new_core( + "Interaction Usability", + "How the interaction serves the system: Resource (input needed), Product (valuable output), Waste (unwanted output), or Disruption (harmful input).", + vec!["Resource: Raw materials, data inputs", "Product: Services, deliverables", "Waste: Byproducts, emissions", "Disruption: Attacks, errors"], + "Critical for understanding value flows and system purpose. Helps identify what adds vs. subtracts value." + )), + + // Interaction Research Fields + "substance-sub-type" => Some(FieldHelp::new_research( + "Substance Sub Type", + "More specific categorization within the main substance type.", + vec!["Personal Data (under Information)", "Raw Materials (under Material)"], + "For detailed analysis when the main substance type is too broad." + )), + + // External Entity Core Fields + "external-entity-name" => Some(FieldHelp::new_core( + "External Entity Name", + "Name of the entity outside your system that interacts with it.", + vec!["Customer", "Supplier", "Regulatory Agency", "Partner System"], + "Always required. Clearly identify all external actors and systems." + )), + "external-entity-description" => Some(FieldHelp::new_core( + "External Entity Description", + "Description of the external entity, its role, and relationship to your system.", + vec!["Primary customer segment", "Key supplier for raw materials"], + "Important for stakeholder analysis and understanding system context." + )), + "model" => Some(FieldHelp::new_core( + "Model", + "Reference to external models or representations of this entity.", + vec!["Customer persona document", "Vendor profile ID", "API specification"], + "Links to detailed external documentation or models when available." + )), + + // External Entity Research Fields + "equivalence" => Some(FieldHelp::new_research( + "Equivalence", + "Mathematical or logical representation of the entity's behavior or characteristics.", + vec!["Statistical model", "Behavioral equations", "Rule sets"], + "For formal analysis and when precise modeling of external entities is needed." + )), + + // SubSystem Research Fields + "system-complexity" => Some(FieldHelp::new_research( + "Complexity Type", + "Detailed complexity classification for subsystems, including multiset membership.", + vec!["Complex with adaptation", "Multiset with autonomy", "Atomic"], + "Advanced modeling of subsystem internal structure and behavior." + )), + "system-membership" => Some(FieldHelp::new_research( + "Member Autonomy", + "For multiset subsystems, indicates how autonomous individual members are (0-1 scale).", + vec!["0.2 for tightly controlled teams", "0.8 for autonomous agents"], + "When modeling collections of semi-independent entities within the subsystem." + )), + + // Interface Core Fields + "interface-name" => Some(FieldHelp::new_core( + "Interface Name", + "Name of the connection point where system elements interact.", + vec!["API Endpoint", "Physical Port", "Communication Channel"], + "Always specify. Interfaces are critical connection points in system architecture." + )), + "interface-description" => Some(FieldHelp::new_core( + "Interface Description", + "Detailed description of how this interface works and what it enables.", + vec!["REST API for data exchange", "Physical connector for power"], + "Essential for technical documentation and system integration understanding." + )), + + _ => None, + } +} + +// Professional tooltip component +#[component] +pub fn FieldTooltip(field_id: String, #[prop(optional, default = true)] show_help: bool, children: Children) -> impl IntoView { + let (show_tooltip, set_show_tooltip) = signal(false); + + let field_help = get_field_help(&field_id); + + view! { +
+ {children()} + + {if show_help { + field_help.map(|help| view! { + + +
+
+
+

{help.title.clone()}

+ {match help.field_type { + FieldType::Core => view! { + + Core + + }.into_view(), + FieldType::Research => view! { + + Research + + }.into_view(), + }} +
+ +

{help.description.clone()}

+ + {(!help.examples.is_empty()).then(|| view! { +
+

Examples:

+
    + {help.examples.iter().map(|example| view! { +
  • + {"•"} + {example.clone()} +
  • + }).collect::>()} +
+
+ })} + +
+

When to use:

+

{help.when_to_use.clone()}

+
+
+ + // Tooltip arrow +
+
+
+
+ }) + } else { + None + }} +
+ } +} \ No newline at end of file diff --git a/src/leptos_app/components/input_group.rs b/src/leptos_app/components/input_group.rs index 5e78a1f..c7094cb 100644 --- a/src/leptos_app/components/input_group.rs +++ b/src/leptos_app/components/input_group.rs @@ -5,6 +5,7 @@ use leptos::web_sys::Event; use leptos::{component, slot, view, IntoView}; use std::fmt::Debug; use std::str::FromStr; +use crate::leptos_app::components::FieldTooltip; const BASE_LABEL_CLASS: &str = "block text-sm font-medium leading-6 text-gray-900"; const BASE_INPUT_CLASS: &str = "block w-full rounded-md border-0 py-1.5 text-gray-900 ring-1 \ @@ -32,6 +33,7 @@ pub fn InputGroup( #[prop(into, optional)] error: Signal, #[prop(optional)] input_group_icon: Option, #[prop(optional)] max_length: Option, + #[prop(optional, default = true)] show_help: bool, ) -> impl IntoView where F: Fn(I) + Clone + Send + 'static, @@ -56,9 +58,11 @@ where let label = label.map(|label| { view! { - + + + } }); diff --git a/src/leptos_app/components/mod.rs b/src/leptos_app/components/mod.rs index e91bcd9..4dea321 100644 --- a/src/leptos_app/components/mod.rs +++ b/src/leptos_app/components/mod.rs @@ -2,6 +2,7 @@ mod button; mod checkbox; mod controls_menu; mod divider; +mod field_status; mod input_group; mod select_group; mod slider; @@ -11,6 +12,7 @@ pub use button::*; pub use checkbox::*; pub use controls_menu::*; pub use divider::*; +pub use field_status::*; pub use input_group::*; pub use select_group::*; pub use slider::*; diff --git a/src/leptos_app/components/select_group.rs b/src/leptos_app/components/select_group.rs index 3a5e2ab..fc404b5 100644 --- a/src/leptos_app/components/select_group.rs +++ b/src/leptos_app/components/select_group.rs @@ -1,5 +1,6 @@ use leptos::prelude::*; use std::fmt::Display; +use crate::leptos_app::components::FieldTooltip; #[component] pub fn SelectGroup( @@ -9,6 +10,7 @@ pub fn SelectGroup( #[prop(into)] selected_option: Signal>, on_change: F, #[prop(into, optional)] disabled: Signal, + #[prop(optional, default = true)] show_help: bool, ) -> impl IntoView where Opt: Display + Sync + Send + Clone + PartialEq + Eq + 'static, @@ -25,11 +27,16 @@ where } }; + let id_clone = id.clone(); + let label_clone = label.clone(); + view! {
- + + +
( @@ -7,16 +8,22 @@ pub fn TextArea( #[prop(into, default = String::new())] placeholder: String, #[prop(into)] text: Signal, #[prop(into, optional)] disabled: Signal, + #[prop(optional, default = true)] show_help: bool, on_input: F, ) -> impl IntoView where F: Fn(String) + Clone + Send + 'static, { + let id_clone = id.clone(); + let label_clone = label.clone(); + view! {
- + + +