checkpoint
This commit is contained in:
		
							
								
								
									
										267
									
								
								platform/src/components/inbox.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								platform/src/components/inbox.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,267 @@ | ||||
| use yew::prelude::*; | ||||
|  | ||||
| #[derive(Clone, PartialEq)] | ||||
| pub struct NotificationItem { | ||||
|     pub id: String, | ||||
|     pub title: String, | ||||
|     pub message: String, | ||||
|     pub notification_type: NotificationType, | ||||
|     pub timestamp: String, | ||||
|     pub is_read: bool, | ||||
|     pub action_required: bool, | ||||
|     pub action_text: Option<String>, | ||||
|     pub action_url: Option<String>, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, PartialEq)] | ||||
| pub enum NotificationType { | ||||
|     Success, | ||||
|     Info, | ||||
|     Warning, | ||||
|     Action, | ||||
|     Vote, | ||||
| } | ||||
|  | ||||
| impl NotificationType { | ||||
|     pub fn get_icon(&self) -> &'static str { | ||||
|         match self { | ||||
|             NotificationType::Success => "bi-check-circle-fill", | ||||
|             NotificationType::Info => "bi-info-circle-fill", | ||||
|             NotificationType::Warning => "bi-exclamation-triangle-fill", | ||||
|             NotificationType::Action => "bi-bell-fill", | ||||
|             NotificationType::Vote => "bi-hand-thumbs-up-fill", | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn get_color(&self) -> &'static str { | ||||
|         match self { | ||||
|             NotificationType::Success => "text-success", | ||||
|             NotificationType::Info => "text-info", | ||||
|             NotificationType::Warning => "text-warning", | ||||
|             NotificationType::Action => "text-primary", | ||||
|             NotificationType::Vote => "text-purple", | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn get_bg_color(&self) -> &'static str { | ||||
|         match self { | ||||
|             NotificationType::Success => "bg-success-subtle", | ||||
|             NotificationType::Info => "bg-info-subtle", | ||||
|             NotificationType::Warning => "bg-warning-subtle", | ||||
|             NotificationType::Action => "bg-primary-subtle", | ||||
|             NotificationType::Vote => "bg-purple-subtle", | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Properties, PartialEq)] | ||||
| pub struct InboxProps { | ||||
|     #[prop_or_default] | ||||
|     pub notifications: Vec<NotificationItem>, | ||||
| } | ||||
|  | ||||
| #[function_component(Inbox)] | ||||
| pub fn inbox(props: &InboxProps) -> Html { | ||||
|     // Mock notifications for demo | ||||
|     let notifications = if props.notifications.is_empty() { | ||||
|         vec![ | ||||
|             NotificationItem { | ||||
|                 id: "1".to_string(), | ||||
|                 title: "Company Registration Successful".to_string(), | ||||
|                 message: "Your company 'TechCorp FZC' has been successfully registered.".to_string(), | ||||
|                 notification_type: NotificationType::Success, | ||||
|                 timestamp: "2 hours ago".to_string(), | ||||
|                 is_read: true, | ||||
|                 action_required: false, | ||||
|                 action_text: Some("View Company".to_string()), | ||||
|                 action_url: Some("/companies/1".to_string()), | ||||
|             }, | ||||
|             NotificationItem { | ||||
|                 id: "2".to_string(), | ||||
|                 title: "Vote Required".to_string(), | ||||
|                 message: "New governance proposal requires your vote: 'Budget Allocation Q1 2025'".to_string(), | ||||
|                 notification_type: NotificationType::Vote, | ||||
|                 timestamp: "1 day ago".to_string(), | ||||
|                 is_read: true, | ||||
|                 action_required: true, | ||||
|                 action_text: Some("Vote Now".to_string()), | ||||
|                 action_url: Some("/governance".to_string()), | ||||
|             }, | ||||
|             NotificationItem { | ||||
|                 id: "3".to_string(), | ||||
|                 title: "Payment Successful".to_string(), | ||||
|                 message: "Monthly subscription payment of $50.00 processed successfully.".to_string(), | ||||
|                 notification_type: NotificationType::Success, | ||||
|                 timestamp: "3 days ago".to_string(), | ||||
|                 is_read: true, | ||||
|                 action_required: false, | ||||
|                 action_text: None, | ||||
|                 action_url: None, | ||||
|             }, | ||||
|             NotificationItem { | ||||
|                 id: "4".to_string(), | ||||
|                 title: "Document Review Required".to_string(), | ||||
|                 message: "Please review and sign the updated Terms of Service.".to_string(), | ||||
|                 notification_type: NotificationType::Action, | ||||
|                 timestamp: "1 week ago".to_string(), | ||||
|                 is_read: true, | ||||
|                 action_required: true, | ||||
|                 action_text: Some("Review".to_string()), | ||||
|                 action_url: Some("/contracts".to_string()), | ||||
|             }, | ||||
|         ] | ||||
|     } else { | ||||
|         props.notifications.clone() | ||||
|     }; | ||||
|  | ||||
|     let unread_count = notifications.iter().filter(|n| !n.is_read).count(); | ||||
|  | ||||
|     html! { | ||||
|         <> | ||||
|             <style> | ||||
|                 {r#" | ||||
|                 .inbox-card { | ||||
|                     border: 1px solid #e9ecef; | ||||
|                     border-radius: 12px; | ||||
|                     transition: all 0.2s ease; | ||||
|                 } | ||||
|                 .inbox-card:hover { | ||||
|                     border-color: #dee2e6; | ||||
|                     box-shadow: 0 4px 12px rgba(0,0,0,0.08); | ||||
|                 } | ||||
|                 .notification-item { | ||||
|                     border-radius: 8px; | ||||
|                     transition: all 0.2s ease; | ||||
|                     cursor: pointer; | ||||
|                 } | ||||
|                 .notification-item:hover { | ||||
|                     background-color: #f8f9fa !important; | ||||
|                 } | ||||
|                 .notification-item.unread { | ||||
|                     border-left: 3px solid #0d6efd; | ||||
|                 } | ||||
|                 .notification-icon { | ||||
|                     width: 36px; | ||||
|                     height: 36px; | ||||
|                     border-radius: 8px; | ||||
|                     display: flex; | ||||
|                     align-items: center; | ||||
|                     justify-content: center; | ||||
|                     font-size: 1rem; | ||||
|                 } | ||||
|                 .action-btn { | ||||
|                     font-size: 0.8rem; | ||||
|                     padding: 4px 12px; | ||||
|                     border-radius: 6px; | ||||
|                     border: 1px solid #dee2e6; | ||||
|                     background: white; | ||||
|                     color: #495057; | ||||
|                     transition: all 0.2s ease; | ||||
|                 } | ||||
|                 .action-btn:hover { | ||||
|                     background: #f8f9fa; | ||||
|                     border-color: #adb5bd; | ||||
|                     color: #212529; | ||||
|                 } | ||||
|                 .action-btn.primary { | ||||
|                     background: #0d6efd; | ||||
|                     border-color: #0d6efd; | ||||
|                     color: white; | ||||
|                 } | ||||
|                 .action-btn.primary:hover { | ||||
|                     background: #0b5ed7; | ||||
|                     border-color: #0a58ca; | ||||
|                     color: white; | ||||
|                 } | ||||
|                 .bg-purple-subtle { | ||||
|                     background-color: rgba(102, 16, 242, 0.1); | ||||
|                 } | ||||
|                 .text-purple { | ||||
|                     color: #6610f2; | ||||
|                 } | ||||
|                 "#} | ||||
|             </style> | ||||
|              | ||||
|             <div class="inbox-card bg-white"> | ||||
|                 <div class="card-body p-4"> | ||||
|                     <div class="d-flex align-items-center justify-content-between mb-3"> | ||||
|                         <div class="d-flex align-items-center"> | ||||
|                             <i class="bi bi-inbox-fill text-primary me-2" style="font-size: 1.2rem;"></i> | ||||
|                             <h5 class="mb-0 fw-semibold">{"Inbox"}</h5> | ||||
|                         </div> | ||||
|                         if unread_count > 0 { | ||||
|                             <span class="badge bg-primary rounded-pill">{unread_count}</span> | ||||
|                         } | ||||
|                     </div> | ||||
|                      | ||||
|                     <div class="d-flex flex-column gap-3"> | ||||
|                         {for notifications.iter().take(4).map(|notification| { | ||||
|                             html! { | ||||
|                                 <div class={classes!( | ||||
|                                     "notification-item", | ||||
|                                     "p-3", | ||||
|                                     (!notification.is_read).then(|| "unread") | ||||
|                                 )}> | ||||
|                                     <div class="d-flex align-items-start"> | ||||
|                                         <div class={classes!( | ||||
|                                             "notification-icon", | ||||
|                                             "me-3", | ||||
|                                             "flex-shrink-0", | ||||
|                                             notification.notification_type.get_bg_color() | ||||
|                                         )}> | ||||
|                                             <i class={classes!( | ||||
|                                                 "bi", | ||||
|                                                 notification.notification_type.get_icon(), | ||||
|                                                 notification.notification_type.get_color() | ||||
|                                             )}></i> | ||||
|                                         </div> | ||||
|                                          | ||||
|                                         <div class="flex-grow-1 min-w-0"> | ||||
|                                             <div class="d-flex align-items-start justify-content-between mb-1"> | ||||
|                                                 <h6 class={classes!( | ||||
|                                                     "mb-0", | ||||
|                                                     "text-truncate", | ||||
|                                                     (!notification.is_read).then(|| "fw-semibold") | ||||
|                                                 )} style="font-size: 0.9rem;"> | ||||
|                                                     {¬ification.title} | ||||
|                                                 </h6> | ||||
|                                                 <small class="text-muted ms-2 flex-shrink-0" style="font-size: 0.75rem;"> | ||||
|                                                     {¬ification.timestamp} | ||||
|                                                 </small> | ||||
|                                             </div> | ||||
|                                              | ||||
|                                             <p class="text-muted mb-2 small" style="font-size: 0.8rem; line-height: 1.4;"> | ||||
|                                                 {¬ification.message} | ||||
|                                             </p> | ||||
|                                              | ||||
|                                             if let Some(action_text) = ¬ification.action_text { | ||||
|                                                 <button class={classes!( | ||||
|                                                     "action-btn", | ||||
|                                                     notification.action_required.then(|| "primary") | ||||
|                                                 )}> | ||||
|                                                     {action_text} | ||||
|                                                     if notification.action_required { | ||||
|                                                         <i class="bi bi-arrow-right ms-1" style="font-size: 0.7rem;"></i> | ||||
|                                                     } | ||||
|                                                 </button> | ||||
|                                             } | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             } | ||||
|                         })} | ||||
|                     </div> | ||||
|                      | ||||
|                     if notifications.len() > 4 { | ||||
|                         <div class="text-center mt-3 pt-3 border-top"> | ||||
|                             <button class="btn btn-outline-primary btn-sm"> | ||||
|                                 {"View All Notifications"} | ||||
|                                 <i class="bi bi-arrow-right ms-1"></i> | ||||
|                             </button> | ||||
|                         </div> | ||||
|                     } | ||||
|                 </div> | ||||
|             </div> | ||||
|         </> | ||||
|     } | ||||
| } | ||||
| @@ -26,7 +26,6 @@ pub fn sidebar(props: &SidebarProps) -> Html { | ||||
|         AppView::Home, | ||||
|         AppView::Administration, | ||||
|         AppView::PersonAdministration, | ||||
|         AppView::Residence, | ||||
|         AppView::Accounting, | ||||
|         AppView::Contracts, | ||||
|         AppView::Governance, | ||||
|   | ||||
| @@ -8,6 +8,8 @@ pub mod toast; | ||||
| pub mod common; | ||||
| pub mod accounting; | ||||
| pub mod resident_landing_overlay; | ||||
| pub mod inbox; | ||||
| pub mod residence_card; | ||||
|  | ||||
| pub use layout::*; | ||||
| pub use forms::*; | ||||
| @@ -18,4 +20,6 @@ pub use entities::*; | ||||
| pub use toast::*; | ||||
| pub use common::*; | ||||
| pub use accounting::*; | ||||
| pub use resident_landing_overlay::*; | ||||
| pub use resident_landing_overlay::*; | ||||
| pub use inbox::*; | ||||
| pub use residence_card::*; | ||||
							
								
								
									
										145
									
								
								platform/src/components/residence_card.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								platform/src/components/residence_card.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| use yew::prelude::*; | ||||
|  | ||||
| #[derive(Properties, PartialEq)] | ||||
| pub struct ResidenceCardProps { | ||||
|     pub user_name: String, | ||||
|     #[prop_or_default] | ||||
|     pub email: Option<String>, | ||||
|     #[prop_or_default] | ||||
|     pub public_key: Option<String>, | ||||
|     #[prop_or_default] | ||||
|     pub resident_id: Option<String>, | ||||
|     #[prop_or_default] | ||||
|     pub status: ResidenceStatus, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, PartialEq)] | ||||
| pub enum ResidenceStatus { | ||||
|     Active, | ||||
|     Pending, | ||||
|     Suspended, | ||||
| } | ||||
|  | ||||
| impl ResidenceStatus { | ||||
|     pub fn get_badge_class(&self) -> &'static str { | ||||
|         match self { | ||||
|             ResidenceStatus::Active => "bg-success", | ||||
|             ResidenceStatus::Pending => "bg-warning", | ||||
|             ResidenceStatus::Suspended => "bg-danger", | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn get_text(&self) -> &'static str { | ||||
|         match self { | ||||
|             ResidenceStatus::Active => "ACTIVE", | ||||
|             ResidenceStatus::Pending => "PENDING", | ||||
|             ResidenceStatus::Suspended => "SUSPENDED", | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for ResidenceStatus { | ||||
|     fn default() -> Self { | ||||
|         ResidenceStatus::Active | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[function_component(ResidenceCard)] | ||||
| pub fn residence_card(props: &ResidenceCardProps) -> Html { | ||||
|     html! { | ||||
|         <> | ||||
|             <style> | ||||
|                 {r#" | ||||
|                 .residence-card-container { | ||||
|                     perspective: 1000px; | ||||
|                 } | ||||
|                 .residence-card { | ||||
|                     transform-style: preserve-3d; | ||||
|                     transition: transform 0.3s ease; | ||||
|                 } | ||||
|                 .residence-card:hover { | ||||
|                     transform: rotateY(5deg) rotateX(5deg); | ||||
|                 } | ||||
|                 "#} | ||||
|             </style> | ||||
|              | ||||
|             <div class="residence-card-container d-flex align-items-center justify-content-center"> | ||||
|                 <div class="residence-card"> | ||||
|                     <div class="card border-0 shadow-lg" style="width: 350px; background: white; border-radius: 15px;"> | ||||
|                         // Header with Zanzibar flag gradient | ||||
|                         <div style="background: linear-gradient(135deg, #0099FF 0%, #00CC66 100%); height: 80px; border-radius: 15px 15px 0 0; position: relative;"> | ||||
|                             <div class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-between px-4"> | ||||
|                                 <div> | ||||
|                                     <h6 class="mb-0 text-white" style="font-size: 0.9rem; font-weight: 600;">{"DIGITAL RESIDENT"}</h6> | ||||
|                                     <small class="text-white" style="opacity: 0.9; font-size: 0.75rem;">{"Zanzibar Digital Freezone"}</small> | ||||
|                                 </div> | ||||
|                                 <i class="bi bi-shield-check-fill text-white" style="font-size: 1.5rem; opacity: 0.9;"></i> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                          | ||||
|                         // Card body with white background | ||||
|                         <div class="card-body p-4" style="background: white; border-radius: 0 0 15px 15px;"> | ||||
|                             <div class="mb-3"> | ||||
|                                 <div class="text-muted small" style="font-size: 0.7rem; font-weight: 500; letter-spacing: 0.5px;">{"FULL NAME"}</div> | ||||
|                                 <div class="h5 mb-0 text-dark" style="font-weight: 600;"> | ||||
|                                     {&props.user_name} | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                              | ||||
|                             <div class="mb-3"> | ||||
|                                 <div class="text-muted small" style="font-size: 0.7rem; font-weight: 500; letter-spacing: 0.5px;">{"EMAIL"}</div> | ||||
|                                 <div class="text-dark" style="font-size: 0.9rem;"> | ||||
|                                     {props.email.as_ref().unwrap_or(&"resident@zanzibar-freezone.com".to_string())} | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                              | ||||
|                             <div class="mb-3"> | ||||
|                                 <div class="text-muted small d-flex align-items-center" style="font-size: 0.7rem; font-weight: 500; letter-spacing: 0.5px;"> | ||||
|                                     <i class="bi bi-key me-1" style="font-size: 0.8rem;"></i> | ||||
|                                     {"PUBLIC KEY"} | ||||
|                                 </div> | ||||
|                                 <div class="text-dark" style="font-size: 0.7rem; font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; word-break: break-all; line-height: 1.3;"> | ||||
|                                     {if let Some(public_key) = &props.public_key { | ||||
|                                         format!("{}...", &public_key[..std::cmp::min(24, public_key.len())]) | ||||
|                                     } else { | ||||
|                                         "zdf1qxy2mlyjkjkpskpsw9fxtpugs450add72nyktmzqau...".to_string() | ||||
|                                     }} | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                              | ||||
|                             <div class="mb-3"> | ||||
|                                 <div class="text-muted small" style="font-size: 0.7rem; font-weight: 500; letter-spacing: 0.5px;">{"RESIDENT SINCE"}</div> | ||||
|                                 <div class="text-dark" style="font-size: 0.8rem;"> | ||||
|                                     {"2025"} | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                              | ||||
|                             <div class="d-flex justify-content-between align-items-end mb-3"> | ||||
|                                 <div> | ||||
|                                     <div class="text-muted small" style="font-size: 0.7rem; font-weight: 500; letter-spacing: 0.5px;">{"RESIDENT ID"}</div> | ||||
|                                     <div class="text-dark" style="font-weight: 600;"> | ||||
|                                         {props.resident_id.as_ref().unwrap_or(&"ZDF-2025-****".to_string())} | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div class="text-end"> | ||||
|                                     <div class="text-muted small" style="font-size: 0.7rem; font-weight: 500; letter-spacing: 0.5px;">{"STATUS"}</div> | ||||
|                                     <div class={classes!("badge", props.status.get_badge_class())} style="color: white; font-weight: 500;"> | ||||
|                                         {props.status.get_text()} | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                              | ||||
|                             // QR Code at bottom | ||||
|                             <div class="text-center border-top pt-3" style="border-color: #e9ecef !important;"> | ||||
|                                 <div class="d-inline-block p-2 rounded" style="background: #f8f9fa;"> | ||||
|                                     <div style="width: 60px; height: 60px; background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA2MCA2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjYwIiBoZWlnaHQ9IjYwIiBmaWxsPSJ3aGl0ZSIvPgo8cmVjdCB4PSI0IiB5PSI0IiB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjIwIiB5PSI0IiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJibGFjayIvPgo8cmVjdCB4PSIyOCIgeT0iNCIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iNDQiIHk9IjQiIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iOCIgeT0iOCIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iNDgiIHk9IjgiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjIwIiB5PSIxMiIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iMzYiIHk9IjEyIiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJibGFjayIvPgo8cmVjdCB4PSI0IiB5PSIyMCIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iMTIiIHk9IjIwIiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJibGFjayIvPgo8cmVjdCB4PSIyMCIgeT0iMjAiIHdpZHRoPSI4IiBoZWlnaHQ9IjgiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjM2IiB5PSIyMCIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iNDQiIHk9IjIwIiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJibGFjayIvPgo8cmVjdCB4PSI1MiIgeT0iMjAiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjI0IiB5PSIyNCIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iNCIgeT0iMjgiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjEyIiB5PSIyOCIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iMzYiIHk9IjI4IiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJibGFjayIvPgo8cmVjdCB4PSI0NCIgeT0iMjgiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjUyIiB5PSIyOCIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iNCIgeT0iMzYiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjEyIiB5PSIzNiIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iMjAiIHk9IjM2IiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJibGFjayIvPgo8cmVjdCB4PSIyOCIgeT0iMzYiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjM2IiB5PSIzNiIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iNDQiIHk9IjM2IiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJibGFjayIvPgo8cmVjdCB4PSI1MiIgeT0iMzYiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjQiIHk9IjQ0IiB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjIwIiB5PSI0NCIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iMjgiIHk9IjQ0IiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJibGFjayIvPgo8cmVjdCB4PSI0NCIgeT0iNDQiIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iOCIgeT0iNDgiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjIwIiB5PSI1MiIgd2lkdGg9IjQiIGhlaWdodD0iNCIgZmlsbD0iYmxhY2siLz4KPHJlY3QgeD0iNDgiIHk9IjQ4IiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K') no-repeat center; background-size: contain;"></div> | ||||
|                                 </div> | ||||
|                                 <div class="text-muted small mt-2" style="font-size: 0.7rem;">{"Scan to verify"}</div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </> | ||||
|     } | ||||
| } | ||||
| @@ -20,6 +20,8 @@ pub struct ViewComponentProps { | ||||
|     pub empty_state: Option<(String, String, String, Option<(String, String)>, Option<(String, String)>)>, // (icon, title, description, primary_action, secondary_action) | ||||
|     #[prop_or_default] | ||||
|     pub children: Children, // Main content when no tabs | ||||
|     #[prop_or_default] | ||||
|     pub use_modern_header: bool, // Use modern header style without card wrapper | ||||
| } | ||||
|  | ||||
| #[function_component(ViewComponent)] | ||||
| @@ -40,7 +42,8 @@ pub fn view_component(props: &ViewComponentProps) -> Html { | ||||
|     }; | ||||
|  | ||||
|     html! { | ||||
|         <div class="container-fluid"> | ||||
|         <div class="container-fluid" style="max-width: 1100px;"> | ||||
|             <div class="px-3 px-md-4 px-lg-5 px-xl-6"> | ||||
|             // Breadcrumbs (if provided) | ||||
|             if let Some(breadcrumbs) = &props.breadcrumbs { | ||||
|                 <ol class="breadcrumb mb-3"> | ||||
| @@ -59,72 +62,144 @@ pub fn view_component(props: &ViewComponentProps) -> Html { | ||||
|                 </ol> | ||||
|             } | ||||
|  | ||||
|             // Page Header in Card (with integrated tabs if provided) | ||||
|             if props.title.is_some() || props.description.is_some() || props.actions.is_some() || props.tabs.is_some() { | ||||
|                 <div class="row mb-4"> | ||||
|                     <div class="col-12"> | ||||
|                         <div class="card"> | ||||
|                             <div class="card-body"> | ||||
|                                 <div class="d-flex justify-content-between align-items-end"> | ||||
|                                     // Left side: Title and description | ||||
|                                     <div class="flex-grow-1"> | ||||
|                                         if let Some(title) = &props.title { | ||||
|                                             <h2 class="mb-1">{title}</h2> | ||||
|             if props.use_modern_header { | ||||
|                 // Modern header style without card wrapper | ||||
|                 if props.title.is_some() || props.description.is_some() || props.actions.is_some() { | ||||
|                     <div class="d-flex justify-content-between align-items-end mb-4"> | ||||
|                         // Left side: Title and description | ||||
|                         <div> | ||||
|                             if let Some(title) = &props.title { | ||||
|                                 <h2 class="mb-1 fw-bold">{title}</h2> | ||||
|                             } | ||||
|                             if let Some(description) = &props.description { | ||||
|                                 <p class="text-muted mb-0">{description}</p> | ||||
|                             } | ||||
|                         </div> | ||||
|                          | ||||
|                         // Right side: Actions | ||||
|                         if let Some(actions) = &props.actions { | ||||
|                             <div> | ||||
|                                 {actions.clone()} | ||||
|                             </div> | ||||
|                         } | ||||
|                     </div> | ||||
|                 } | ||||
|  | ||||
|                 // Modern tabs navigation (if provided) | ||||
|                 if let Some(tabs) = &props.tabs { | ||||
|                     <div class="mb-0"> | ||||
|                         <ul class="nav nav-tabs border-bottom-0" role="tablist"> | ||||
|                             {for tabs.keys().map(|tab_name| { | ||||
|                                 let is_active = *active_tab == *tab_name; | ||||
|                                 let tab_name_clone = tab_name.clone(); | ||||
|                                 let on_click = { | ||||
|                                     let on_tab_click = on_tab_click.clone(); | ||||
|                                     let tab_name = tab_name.clone(); | ||||
|                                     Callback::from(move |e: MouseEvent| { | ||||
|                                         e.prevent_default(); | ||||
|                                         on_tab_click.emit(tab_name.clone()); | ||||
|                                     }) | ||||
|                                 }; | ||||
|                                  | ||||
|                                 html! { | ||||
|                                     <li class="nav-item" role="presentation"> | ||||
|                                         <button | ||||
|                                             class={classes!( | ||||
|                                                 "nav-link", | ||||
|                                                 "px-3", | ||||
|                                                 "py-2", | ||||
|                                                 "small", | ||||
|                                                 "border", | ||||
|                                                 "border-bottom-0", | ||||
|                                                 "bg-light", | ||||
|                                                 "text-muted", | ||||
|                                                 if is_active { | ||||
|                                                     "active bg-white text-dark border-primary border-bottom-0" | ||||
|                                                 } else { | ||||
|                                                     "border-light" | ||||
|                                                 } | ||||
|                                             )} | ||||
|                                             type="button" | ||||
|                                             role="tab" | ||||
|                                             onclick={on_click} | ||||
|                                             style={if is_active { "margin-bottom: -1px; z-index: 1; position: relative;" } else { "" }} | ||||
|                                         > | ||||
|                                             {tab_name} | ||||
|                                         </button> | ||||
|                                     </li> | ||||
|                                 } | ||||
|                             })} | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 } | ||||
|             } else { | ||||
|                 // Original header style with card wrapper | ||||
|                 if props.title.is_some() || props.description.is_some() || props.actions.is_some() || props.tabs.is_some() { | ||||
|                     <div class="row mb-4"> | ||||
|                         <div class="col-12"> | ||||
|                             <div class="card"> | ||||
|                                 <div class="card-body"> | ||||
|                                     <div class="d-flex justify-content-between align-items-end"> | ||||
|                                         // Left side: Title and description | ||||
|                                         <div class="flex-grow-1"> | ||||
|                                             if let Some(title) = &props.title { | ||||
|                                                 <h2 class="mb-1">{title}</h2> | ||||
|                                             } | ||||
|                                             if let Some(description) = &props.description { | ||||
|                                                 <p class="text-muted mb-0">{description}</p> | ||||
|                                             } | ||||
|                                         </div> | ||||
|                                          | ||||
|                                         // Center: Tabs navigation (if provided) | ||||
|                                         if let Some(tabs) = &props.tabs { | ||||
|                                             <div class="flex-grow-1 d-flex justify-content-right"> | ||||
|                                                 <ul class="nav nav-tabs border-0" role="tablist"> | ||||
|                                                     {for tabs.keys().map(|tab_name| { | ||||
|                                                         let is_active = *active_tab == *tab_name; | ||||
|                                                         let tab_name_clone = tab_name.clone(); | ||||
|                                                         let on_click = { | ||||
|                                                             let on_tab_click = on_tab_click.clone(); | ||||
|                                                             let tab_name = tab_name.clone(); | ||||
|                                                             Callback::from(move |e: MouseEvent| { | ||||
|                                                                 e.prevent_default(); | ||||
|                                                                 on_tab_click.emit(tab_name.clone()); | ||||
|                                                             }) | ||||
|                                                         }; | ||||
|                                                          | ||||
|                                                         html! { | ||||
|                                                             <li class="nav-item" role="presentation"> | ||||
|                                                                 <button | ||||
|                                                                     class={classes!("nav-link", "px-4", "py-2", is_active.then(|| "active"))} | ||||
|                                                                     type="button" | ||||
|                                                                     role="tab" | ||||
|                                                                     onclick={on_click} | ||||
|                                                                 > | ||||
|                                                                     {tab_name} | ||||
|                                                                 </button> | ||||
|                                                             </li> | ||||
|                                                         } | ||||
|                                                     })} | ||||
|                                                 </ul> | ||||
|                                             </div> | ||||
|                                         } | ||||
|                                         if let Some(description) = &props.description { | ||||
|                                             <p class="text-muted mb-0">{description}</p> | ||||
|                                          | ||||
|                                         // Right side: Actions | ||||
|                                         if let Some(actions) = &props.actions { | ||||
|                                             <div> | ||||
|                                                 {actions.clone()} | ||||
|                                             </div> | ||||
|                                         } | ||||
|                                     </div> | ||||
|                                      | ||||
|                                     // Center: Tabs navigation (if provided) | ||||
|                                     if let Some(tabs) = &props.tabs { | ||||
|                                         <div class="flex-grow-1 d-flex justify-content-right"> | ||||
|                                             <ul class="nav nav-tabs border-0" role="tablist"> | ||||
|                                                 {for tabs.keys().map(|tab_name| { | ||||
|                                                     let is_active = *active_tab == *tab_name; | ||||
|                                                     let tab_name_clone = tab_name.clone(); | ||||
|                                                     let on_click = { | ||||
|                                                         let on_tab_click = on_tab_click.clone(); | ||||
|                                                         let tab_name = tab_name.clone(); | ||||
|                                                         Callback::from(move |e: MouseEvent| { | ||||
|                                                             e.prevent_default(); | ||||
|                                                             on_tab_click.emit(tab_name.clone()); | ||||
|                                                         }) | ||||
|                                                     }; | ||||
|                                                      | ||||
|                                                     html! { | ||||
|                                                         <li class="nav-item" role="presentation"> | ||||
|                                                             <button | ||||
|                                                                 class={classes!("nav-link", "px-4", "py-2", is_active.then(|| "active"))} | ||||
|                                                                 type="button" | ||||
|                                                                 role="tab" | ||||
|                                                                 onclick={on_click} | ||||
|                                                             > | ||||
|                                                                 {tab_name} | ||||
|                                                             </button> | ||||
|                                                         </li> | ||||
|                                                     } | ||||
|                                                 })} | ||||
|                                             </ul> | ||||
|                                         </div> | ||||
|                                     } | ||||
|                                      | ||||
|                                     // Right side: Actions | ||||
|                                     if let Some(actions) = &props.actions { | ||||
|                                         <div> | ||||
|                                             {actions.clone()} | ||||
|                                         </div> | ||||
|                                     } | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Tab Content (if tabs are provided) | ||||
|             if let Some(tabs) = &props.tabs { | ||||
|                 <div class="tab-content"> | ||||
|                 <div class="tab-content border border-top-0 rounded-bottom bg-white p-4"> | ||||
|                     {for tabs.iter().map(|(tab_name, content)| { | ||||
|                         let is_active = *active_tab == *tab_name; | ||||
|                         html! { | ||||
| @@ -147,6 +222,7 @@ pub fn view_component(props: &ViewComponentProps) -> Html { | ||||
|                 // No tabs, render children directly | ||||
|                 {for props.children.iter()} | ||||
|             } | ||||
|             </div> | ||||
|         </div> | ||||
|     } | ||||
| } | ||||
| @@ -254,12 +254,30 @@ pub fn accounting_view(props: &AccountingViewProps) -> Html { | ||||
|             }); | ||||
|         }, | ||||
|         ViewContext::Person => { | ||||
|             // For personal context, show simplified version | ||||
|             tabs.insert("Income Tracking".to_string(), html! { | ||||
|                 <div class="alert alert-info"> | ||||
|                     <i class="bi bi-info-circle me-2"></i> | ||||
|                     {"Personal accounting features coming soon. Switch to Business context for full accounting functionality."} | ||||
|                 </div> | ||||
|             // Show same functionality as business context | ||||
|             // Overview Tab | ||||
|             tabs.insert("Overview".to_string(), html! { | ||||
|                 <OverviewTab state={state.clone()} /> | ||||
|             }); | ||||
|  | ||||
|             // Revenue Tab | ||||
|             tabs.insert("Revenue".to_string(), html! { | ||||
|                 <RevenueTab state={state.clone()} /> | ||||
|             }); | ||||
|  | ||||
|             // Expenses Tab | ||||
|             tabs.insert("Expenses".to_string(), html! { | ||||
|                 <ExpensesTab state={state.clone()} /> | ||||
|             }); | ||||
|  | ||||
|             // Tax Tab | ||||
|             tabs.insert("Tax".to_string(), html! { | ||||
|                 <TaxTab state={state.clone()} /> | ||||
|             }); | ||||
|  | ||||
|             // Financial Reports Tab | ||||
|             tabs.insert("Financial Reports".to_string(), html! { | ||||
|                 <FinancialReportsTab state={state.clone()} /> | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| @@ -274,10 +292,8 @@ pub fn accounting_view(props: &AccountingViewProps) -> Html { | ||||
|             title={Some(title.to_string())} | ||||
|             description={Some(description.to_string())} | ||||
|             tabs={Some(tabs)} | ||||
|             default_tab={match context { | ||||
|                 ViewContext::Business => Some("Overview".to_string()), | ||||
|                 ViewContext::Person => Some("Income Tracking".to_string()), | ||||
|             }} | ||||
|             default_tab={Some("Overview".to_string())} | ||||
|             use_modern_header={true} | ||||
|         /> | ||||
|     } | ||||
| } | ||||
| @@ -164,6 +164,7 @@ impl Component for CompaniesView { | ||||
|                 <ViewComponent | ||||
|                     title={Some("Registration Successful".to_string())} | ||||
|                     description={Some("Your company registration has been completed successfully".to_string())} | ||||
|                     use_modern_header={true} | ||||
|                 > | ||||
|                     <RegistrationWizard | ||||
|                         on_registration_complete={link.callback(CompaniesViewMsg::RegistrationComplete)} | ||||
| @@ -182,6 +183,7 @@ impl Component for CompaniesView { | ||||
|                 <ViewComponent | ||||
|                     title={Some("Register New Company".to_string())} | ||||
|                     description={Some("Complete the registration process to create your new company".to_string())} | ||||
|                     use_modern_header={true} | ||||
|                 > | ||||
|                     <RegistrationWizard | ||||
|                         on_registration_complete={link.callback(CompaniesViewMsg::RegistrationComplete)} | ||||
| @@ -200,6 +202,7 @@ impl Component for CompaniesView { | ||||
|                 <ViewComponent | ||||
|                     title={Some("Companies".to_string())} | ||||
|                     description={Some("Manage your companies and registrations".to_string())} | ||||
|                     use_modern_header={true} | ||||
|                 > | ||||
|                     {self.render_companies_content(ctx)} | ||||
|                 </ViewComponent> | ||||
| @@ -258,24 +261,27 @@ impl CompaniesView { | ||||
|         let link = ctx.link(); | ||||
|          | ||||
|         html! { | ||||
|             <div class="card"> | ||||
|                 <div class="card-header d-flex justify-content-between align-items-center"> | ||||
|                     <div> | ||||
|                         <h5 class="mb-0"> | ||||
|                             <i class="bi bi-building me-2"></i>{"Companies & Registrations"} | ||||
|                         </h5> | ||||
|                         <small class="text-muted"> | ||||
|                             {format!("{} companies, {} pending registrations", self.companies.len(), self.registrations.len())} | ||||
|                         </small> | ||||
|             <div class="card border-0 shadow-sm"> | ||||
|                 <div class="card-body p-4"> | ||||
|                     <div class="d-flex justify-content-between align-items-center mb-3"> | ||||
|                         <div class="d-flex align-items-center"> | ||||
|                             <div class="bg-primary bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                 <i class="bi bi-building text-primary fs-5"></i> | ||||
|                             </div> | ||||
|                             <div> | ||||
|                                 <h5 class="mb-0">{"Companies & Registrations"}</h5> | ||||
|                                 <small class="text-muted"> | ||||
|                                     {format!("{} companies, {} pending registrations", self.companies.len(), self.registrations.len())} | ||||
|                                 </small> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <button | ||||
|                             class="btn btn-success" | ||||
|                             onclick={link.callback(|_| CompaniesViewMsg::StartNewRegistration)} | ||||
|                         > | ||||
|                             <i class="bi bi-plus-circle me-2"></i>{"New Registration"} | ||||
|                         </button> | ||||
|                     </div> | ||||
|                     <button | ||||
|                         class="btn btn-success" | ||||
|                         onclick={link.callback(|_| CompaniesViewMsg::StartNewRegistration)} | ||||
|                     > | ||||
|                         <i class="bi bi-plus-circle me-2"></i>{"New Registration"} | ||||
|                     </button> | ||||
|                 </div> | ||||
|                 <div class="card-body p-0"> | ||||
|                     <div class="table-responsive"> | ||||
|                         <table class="table table-hover align-middle mb-0"> | ||||
|                             <thead class="table-light"> | ||||
|   | ||||
| @@ -170,10 +170,11 @@ impl Component for ContractsViewComponent { | ||||
|  | ||||
|         html! { | ||||
|             <ViewComponent | ||||
|                 title={title.to_string()} | ||||
|                 description={description.to_string()} | ||||
|                 tabs={tabs} | ||||
|                 default_tab={"Contracts".to_string()} | ||||
|                 title={Some(title.to_string())} | ||||
|                 description={Some(description.to_string())} | ||||
|                 tabs={Some(tabs)} | ||||
|                 default_tab={Some("Contracts".to_string())} | ||||
|                 use_modern_header={true} | ||||
|             /> | ||||
|         } | ||||
|     } | ||||
| @@ -296,11 +297,14 @@ impl ContractsViewComponent { | ||||
|                 // Filters Section | ||||
|                 <div class="row mb-4"> | ||||
|                     <div class="col-12"> | ||||
|                         <div class="card"> | ||||
|                             <div class="card-header"> | ||||
|                                 <h5 class="mb-0">{"Filters"}</h5> | ||||
|                             </div> | ||||
|                             <div class="card-body"> | ||||
|                         <div class="card border-0 shadow-sm"> | ||||
|                             <div class="card-body p-4"> | ||||
|                                 <div class="d-flex align-items-center mb-3"> | ||||
|                                     <div class="bg-primary bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                         <i class="bi bi-funnel text-primary fs-5"></i> | ||||
|                                     </div> | ||||
|                                     <h5 class="mb-0">{"Filters"}</h5> | ||||
|                                 </div> | ||||
|                                 <div class="row g-3"> | ||||
|                                     <div class="col-md-3"> | ||||
|                                         <label for="status" class="form-label">{"Status"}</label> | ||||
| @@ -344,11 +348,14 @@ impl ContractsViewComponent { | ||||
|                 // Contracts Table | ||||
|                 <div class="row"> | ||||
|                     <div class="col-12"> | ||||
|                         <div class="card"> | ||||
|                             <div class="card-header"> | ||||
|                                 <h5 class="mb-0">{"Contracts"}</h5> | ||||
|                             </div> | ||||
|                             <div class="card-body"> | ||||
|                         <div class="card border-0 shadow-sm"> | ||||
|                             <div class="card-body p-4"> | ||||
|                                 <div class="d-flex align-items-center mb-3"> | ||||
|                                     <div class="bg-success bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                         <i class="bi bi-file-earmark-text text-success fs-5"></i> | ||||
|                                     </div> | ||||
|                                     <h5 class="mb-0">{"Contracts"}</h5> | ||||
|                                 </div> | ||||
|                                 {self.render_contracts_table(_ctx)} | ||||
|                             </div> | ||||
|                         </div> | ||||
| @@ -442,11 +449,14 @@ impl ContractsViewComponent { | ||||
|         html! { | ||||
|             <div class="row"> | ||||
|                 <div class="col-lg-8"> | ||||
|                     <div class="card"> | ||||
|                         <div class="card-header"> | ||||
|                             <h5 class="mb-0">{"Contract Details"}</h5> | ||||
|                         </div> | ||||
|                         <div class="card-body"> | ||||
|                     <div class="card border-0 shadow-sm"> | ||||
|                         <div class="card-body p-4"> | ||||
|                             <div class="d-flex align-items-center mb-4"> | ||||
|                                 <div class="bg-primary bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                     <i class="bi bi-file-earmark-plus text-primary fs-5"></i> | ||||
|                                 </div> | ||||
|                                 <h5 class="mb-0">{"Contract Details"}</h5> | ||||
|                             </div> | ||||
|                             <form> | ||||
|                                 <div class="mb-3"> | ||||
|                                     <label for="title" class="form-label"> | ||||
| @@ -531,11 +541,14 @@ Payment will be made according to the following schedule: | ||||
|                 </div> | ||||
|  | ||||
|                 <div class="col-lg-4"> | ||||
|                     <div class="card mb-4"> | ||||
|                         <div class="card-header"> | ||||
|                             <h5 class="mb-0">{"Tips"}</h5> | ||||
|                         </div> | ||||
|                         <div class="card-body"> | ||||
|                     <div class="card border-0 shadow-sm mb-4"> | ||||
|                         <div class="card-body p-4"> | ||||
|                             <div class="d-flex align-items-center mb-3"> | ||||
|                                 <div class="bg-info bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                     <i class="bi bi-lightbulb text-info fs-5"></i> | ||||
|                                 </div> | ||||
|                                 <h5 class="mb-0">{"Tips"}</h5> | ||||
|                             </div> | ||||
|                             <p>{"Creating a new contract is just the first step. After creating the contract, you'll be able to:"}</p> | ||||
|                             <ul> | ||||
|                                 <li>{"Add signers who need to approve the contract"}</li> | ||||
| @@ -547,11 +560,14 @@ Payment will be made according to the following schedule: | ||||
|                         </div> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="card"> | ||||
|                         <div class="card-header"> | ||||
|                             <h5 class="mb-0">{"Contract Templates"}</h5> | ||||
|                         </div> | ||||
|                         <div class="card-body"> | ||||
|                     <div class="card border-0 shadow-sm"> | ||||
|                         <div class="card-body p-4"> | ||||
|                             <div class="d-flex align-items-center mb-3"> | ||||
|                                 <div class="bg-warning bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                     <i class="bi bi-file-earmark-code text-warning fs-5"></i> | ||||
|                                 </div> | ||||
|                                 <h5 class="mb-0">{"Contract Templates"}</h5> | ||||
|                             </div> | ||||
|                             <p>{"You can use one of our pre-defined templates to get started quickly:"}</p> | ||||
|                             <div class="list-group"> | ||||
|                                 <button type="button" class="list-group-item list-group-item-action"> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use yew::prelude::*; | ||||
| use crate::components::FeatureCard; | ||||
| use crate::components::{Inbox, ResidenceCard, ResidenceStatus}; | ||||
| use crate::routing::ViewContext; | ||||
|  | ||||
| #[derive(Properties, PartialEq)] | ||||
| @@ -9,74 +9,125 @@ pub struct HomeViewProps { | ||||
|  | ||||
| #[function_component(HomeView)] | ||||
| pub fn home_view(props: &HomeViewProps) -> Html { | ||||
|     // Mock user data - in a real app this would come from authentication/user context | ||||
|     let user_name = "Timur Gordon".to_string(); | ||||
|     let user_email = Some("timur@example.com".to_string()); | ||||
|      | ||||
|     html! { | ||||
|         <div class="row"> | ||||
|             <div class="col-md-12"> | ||||
|                 <div class="card"> | ||||
|                     <div class="card-body"> | ||||
|                         <h1 class="card-title text-center mb-4">{"Zanzibar Digital Freezone"}</h1> | ||||
|                         <p class="card-text text-center lead mb-5">{"Convenience, Safety and Privacy"}</p> | ||||
|                          | ||||
|                         <div class="row g-3 mb-4"> | ||||
|                             // Left Column (3 items) | ||||
|                             <div class="col-md-6"> | ||||
|                                 // Card 1: Frictionless Collaboration | ||||
|                                 <FeatureCard | ||||
|                                     title="Frictionless Collaboration" | ||||
|                                     description="Direct communication and transactions between individuals and organizations, making processes efficient and cost-effective." | ||||
|                                     icon="bi-people-fill" | ||||
|                                     color_variant="primary" | ||||
|                                 /> | ||||
|                                  | ||||
|                                 // Card 2: Frictionless Banking | ||||
|                                 <FeatureCard | ||||
|                                     title="Frictionless Banking" | ||||
|                                     description="Simplified financial transactions without the complications and fees of traditional banking systems." | ||||
|                                     icon="bi-currency-exchange" | ||||
|                                     color_variant="success" | ||||
|                                 /> | ||||
|                                  | ||||
|                                 // Card 3: Tax Efficiency | ||||
|                                 <FeatureCard | ||||
|                                     title="Tax Efficiency" | ||||
|                                     description="Lower taxes making business operations more profitable and competitive in the global market." | ||||
|                                     icon="bi-graph-up-arrow" | ||||
|                                     color_variant="info" | ||||
|                                 /> | ||||
|         <> | ||||
|             <style> | ||||
|                 {r#" | ||||
|                 .welcome-section { | ||||
|                     background: linear-gradient(135deg, rgba(0,153,255,0.05) 0%, rgba(0,204,102,0.05) 100%); | ||||
|                     border-radius: 16px; | ||||
|                     border: 1px solid rgba(0,153,255,0.1); | ||||
|                 } | ||||
|                 .greeting-card { | ||||
|                     border: 1px solid #e9ecef; | ||||
|                     border-radius: 12px; | ||||
|                     transition: all 0.2s ease; | ||||
|                 } | ||||
|                 .greeting-card:hover { | ||||
|                     border-color: #dee2e6; | ||||
|                     box-shadow: 0 4px 12px rgba(0,0,0,0.08); | ||||
|                 } | ||||
|                 .time-badge { | ||||
|                     background: linear-gradient(135deg, #0099FF 0%, #00CC66 100%); | ||||
|                     color: white; | ||||
|                     padding: 4px 12px; | ||||
|                     border-radius: 20px; | ||||
|                     font-size: 0.8rem; | ||||
|                     font-weight: 500; | ||||
|                 } | ||||
|                 .stats-item { | ||||
|                     text-align: center; | ||||
|                     padding: 1rem; | ||||
|                     border-radius: 8px; | ||||
|                     background: #f8f9fa; | ||||
|                     transition: all 0.2s ease; | ||||
|                 } | ||||
|                 .stats-item:hover { | ||||
|                     background: #e9ecef; | ||||
|                     transform: translateY(-2px); | ||||
|                 } | ||||
|                 .stats-number { | ||||
|                     font-size: 1.5rem; | ||||
|                     font-weight: 700; | ||||
|                     color: #0d6efd; | ||||
|                 } | ||||
|                 .stats-label { | ||||
|                     font-size: 0.8rem; | ||||
|                     color: #6c757d; | ||||
|                     font-weight: 500; | ||||
|                 } | ||||
|                 "#} | ||||
|             </style> | ||||
|              | ||||
|             <div class="container-fluid py-4 px-3 px-md-4 px-lg-5 px-xl-6"> | ||||
|                 <div class="row g-4"> | ||||
|                     // Left Column: Greeting and Inbox | ||||
|                     <div class="col-lg-6"> | ||||
|                         // Welcome Section | ||||
|                         <div class="welcome-section p-4 mb-4"> | ||||
|                             <div class="d-flex align-items-center justify-content-between mb-3"> | ||||
|                                 <div> | ||||
|                                     <h1 class="h3 mb-1 fw-bold text-dark"> | ||||
|                                         {"Hello, "}{&user_name}{"! 👋"} | ||||
|                                     </h1> | ||||
|                                     <p class="text-muted mb-0"> | ||||
|                                         {"Welcome back to your Digital Freezone dashboard"} | ||||
|                                     </p> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                              | ||||
|                             // Right Column (2 items) | ||||
|                             <div class="col-md-6"> | ||||
|                                 // Card 4: Global Ecommerce | ||||
|                                 <FeatureCard | ||||
|                                     title="Global Ecommerce" | ||||
|                                     description="Easily expand your business globally with streamlined operations and tools to reach customers worldwide." | ||||
|                                     icon="bi-globe" | ||||
|                                     color_variant="warning" | ||||
|                                 /> | ||||
|                                  | ||||
|                                 // Card 5: Clear Regulations | ||||
|                                 <FeatureCard | ||||
|                                     title="Clear Regulations" | ||||
|                                     description="Clear regulations and efficient dispute resolution mechanisms providing a stable business environment." | ||||
|                                     icon="bi-shield-check" | ||||
|                                     color_variant="danger" | ||||
|                                 /> | ||||
|                             </div> | ||||
|                             // Quick Actions | ||||
|                             <div class="row g-3 mb-3"> | ||||
|                                 <div class="col-4"> | ||||
|                                     <a href="/companies/register" class="text-decoration-none"> | ||||
|                                         <div class="stats-item"> | ||||
|                                             <i class="bi bi-building-add text-primary mb-2" style="font-size: 1.5rem;"></i> | ||||
|                                             <div class="stats-label">{"Register Company"}</div> | ||||
|                                         </div> | ||||
|                                     </a> | ||||
|                                 </div> | ||||
|                                 <div class="col-4"> | ||||
|                                     <a href="/governance" class="text-decoration-none"> | ||||
|                                         <div class="stats-item"> | ||||
|                                             <i class="bi bi-hand-thumbs-up text-success mb-2" style="font-size: 1.5rem;"></i> | ||||
|                                             <div class="stats-label">{"Vote on Proposals"}</div> | ||||
|                                         </div> | ||||
|                                     </a> | ||||
|                                 </div> | ||||
|                                 <div class="col-4"> | ||||
|                                     <a href="/treasury" class="text-decoration-none"> | ||||
|                                         <div class="stats-item"> | ||||
|                                             <i class="bi bi-wallet2 text-info mb-2" style="font-size: 1.5rem;"></i> | ||||
|                                             <div class="stats-label">{"Manage Wallet"}</div> | ||||
|                                         </div> | ||||
|                                     </a> | ||||
|                                 </div> | ||||
|                             </div>     | ||||
|                         </div> | ||||
|                          | ||||
|                         <div class="text-center"> | ||||
|                             <a  | ||||
|                                 href="https://info.ourworld.tf/zdfz"  | ||||
|                                 target="_blank" | ||||
|                                 class="btn btn-primary btn-lg" | ||||
|                             > | ||||
|                                 {"Learn More"} | ||||
|                             </a> | ||||
|                         // Inbox Component | ||||
|                         <Inbox /> | ||||
|                     </div> | ||||
|                      | ||||
|                     // Right Column: Residence Card | ||||
|                     <div class="col-lg-6"> | ||||
|                         <div class="d-flex align-items-center justify-content-center h-100"> | ||||
|                             <ResidenceCard | ||||
|                                 user_name={user_name} | ||||
|                                 email={user_email} | ||||
|                                 public_key={Some("zdf1qxy2mlyjkjkpskpsw9fxtpugs450add72nyktmzqau...".to_string())} | ||||
|                                 resident_id={Some("ZDF-2025-0001".to_string())} | ||||
|                                 status={ResidenceStatus::Active} | ||||
|                             /> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                  | ||||
|             </div> | ||||
|         </div> | ||||
|         </> | ||||
|     } | ||||
| } | ||||
| @@ -273,29 +273,33 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html | ||||
|      | ||||
|     // Account Settings Tab (Person-specific) | ||||
|     tabs.insert("Account Settings".to_string(), html! { | ||||
|         <div class="card"> | ||||
|             <div class="card-header"> | ||||
|                 <h5 class="mb-0"> | ||||
|                     <i class="bi bi-person-gear me-2"></i> | ||||
|                     {"Personal Account Settings"} | ||||
|                 </h5> | ||||
|             </div> | ||||
|             <div class="card-body"> | ||||
|         <div class="card border-0 shadow-sm"> | ||||
|             <div class="card-body p-4"> | ||||
|                 <div class="d-flex align-items-center mb-4"> | ||||
|                     <div class="bg-primary bg-opacity-10 rounded-3 p-3 me-3"> | ||||
|                         <i class="bi bi-person-gear text-primary fs-4"></i> | ||||
|                     </div> | ||||
|                     <div> | ||||
|                         <h5 class="mb-1">{"Personal Account Settings"}</h5> | ||||
|                         <p class="text-muted mb-0">{"Manage your personal information and preferences"}</p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                  | ||||
|                 <div class="row"> | ||||
|                     <div class="col-md-6 mb-3"> | ||||
|                         <label class="form-label">{"Full Name"}</label> | ||||
|                         <label class="form-label fw-medium">{"Full Name"}</label> | ||||
|                         <input type="text" class="form-control" value="Timur Gordon" /> | ||||
|                     </div> | ||||
|                     <div class="col-md-6 mb-3"> | ||||
|                         <label class="form-label">{"Email Address"}</label> | ||||
|                         <label class="form-label fw-medium">{"Email Address"}</label> | ||||
|                         <input type="email" class="form-control" value="john.doe@example.com" /> | ||||
|                     </div> | ||||
|                     <div class="col-md-6 mb-3"> | ||||
|                         <label class="form-label">{"Phone Number"}</label> | ||||
|                         <label class="form-label fw-medium">{"Phone Number"}</label> | ||||
|                         <input type="tel" class="form-control" value="+1 (555) 123-4567" /> | ||||
|                     </div> | ||||
|                     <div class="col-md-6 mb-3"> | ||||
|                         <label class="form-label">{"Preferred Language"}</label> | ||||
|                         <label class="form-label fw-medium">{"Preferred Language"}</label> | ||||
|                         <select class="form-select"> | ||||
|                             <option selected=true>{"English"}</option> | ||||
|                             <option>{"French"}</option> | ||||
| @@ -304,7 +308,7 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html | ||||
|                         </select> | ||||
|                     </div> | ||||
|                     <div class="col-12 mb-3"> | ||||
|                         <label class="form-label">{"Time Zone"}</label> | ||||
|                         <label class="form-label fw-medium">{"Time Zone"}</label> | ||||
|                         <select class="form-select"> | ||||
|                             <option selected=true>{"UTC+00:00 (GMT)"}</option> | ||||
|                             <option>{"UTC-05:00 (EST)"}</option> | ||||
| @@ -313,7 +317,7 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html | ||||
|                         </select> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="mt-4"> | ||||
|                 <div class="mt-4 pt-3 border-top"> | ||||
|                     <button class="btn btn-primary me-2">{"Save Changes"}</button> | ||||
|                     <button class="btn btn-outline-secondary">{"Reset"}</button> | ||||
|                 </div> | ||||
| @@ -323,52 +327,56 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html | ||||
|  | ||||
|     // Privacy & Security Tab | ||||
|     tabs.insert("Privacy & Security".to_string(), html! { | ||||
|         <div class="card"> | ||||
|             <div class="card-header"> | ||||
|                 <h5 class="mb-0"> | ||||
|                     <i class="bi bi-shield-lock me-2"></i> | ||||
|                     {"Privacy & Security Settings"} | ||||
|                 </h5> | ||||
|             </div> | ||||
|             <div class="card-body"> | ||||
|                 <div class="mb-4"> | ||||
|                     <h6>{"Two-Factor Authentication"}</h6> | ||||
|         <div class="card border-0 shadow-sm"> | ||||
|             <div class="card-body p-4"> | ||||
|                 <div class="d-flex align-items-center mb-4"> | ||||
|                     <div class="bg-success bg-opacity-10 rounded-3 p-3 me-3"> | ||||
|                         <i class="bi bi-shield-lock text-success fs-4"></i> | ||||
|                     </div> | ||||
|                     <div> | ||||
|                         <h5 class="mb-1">{"Privacy & Security Settings"}</h5> | ||||
|                         <p class="text-muted mb-0">{"Manage your security preferences and privacy controls"}</p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                  | ||||
|                 <div class="mb-4 p-3 bg-light rounded-3"> | ||||
|                     <h6 class="fw-medium mb-3">{"Two-Factor Authentication"}</h6> | ||||
|                     <div class="form-check form-switch"> | ||||
|                         <input class="form-check-input" type="checkbox" id="twoFactorAuth" checked=true /> | ||||
|                         <label class="form-check-label" for="twoFactorAuth"> | ||||
|                         <label class="form-check-label fw-medium" for="twoFactorAuth"> | ||||
|                             {"Enable two-factor authentication"} | ||||
|                         </label> | ||||
|                     </div> | ||||
|                     <small class="text-muted">{"Adds an extra layer of security to your account"}</small> | ||||
|                 </div> | ||||
|                  | ||||
|                 <div class="mb-4"> | ||||
|                     <h6>{"Login Notifications"}</h6> | ||||
|                 <div class="mb-4 p-3 bg-light rounded-3"> | ||||
|                     <h6 class="fw-medium mb-3">{"Login Notifications"}</h6> | ||||
|                     <div class="form-check form-switch"> | ||||
|                         <input class="form-check-input" type="checkbox" id="loginNotifications" checked=true /> | ||||
|                         <label class="form-check-label" for="loginNotifications"> | ||||
|                         <label class="form-check-label fw-medium" for="loginNotifications"> | ||||
|                             {"Email me when someone logs into my account"} | ||||
|                         </label> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                  | ||||
|                 <div class="mb-4"> | ||||
|                     <h6>{"Data Privacy"}</h6> | ||||
|                 <div class="mb-4 p-3 bg-light rounded-3"> | ||||
|                     <h6 class="fw-medium mb-3">{"Data Privacy"}</h6> | ||||
|                     <div class="form-check form-switch mb-2"> | ||||
|                         <input class="form-check-input" type="checkbox" id="dataSharing" /> | ||||
|                         <label class="form-check-label" for="dataSharing"> | ||||
|                         <label class="form-check-label fw-medium" for="dataSharing"> | ||||
|                             {"Allow anonymous usage analytics"} | ||||
|                         </label> | ||||
|                     </div> | ||||
|                     <div class="form-check form-switch"> | ||||
|                         <input class="form-check-input" type="checkbox" id="marketingEmails" /> | ||||
|                         <label class="form-check-label" for="marketingEmails"> | ||||
|                         <label class="form-check-label fw-medium" for="marketingEmails"> | ||||
|                             {"Receive marketing communications"} | ||||
|                         </label> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                  | ||||
|                 <div class="mt-4"> | ||||
|                 <div class="mt-4 pt-3 border-top"> | ||||
|                     <button class="btn btn-primary me-2">{"Update Security Settings"}</button> | ||||
|                     <button class="btn btn-outline-danger">{"Download My Data"}</button> | ||||
|                 </div> | ||||
| @@ -385,14 +393,14 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html | ||||
|             <div class="row"> | ||||
|                 // Subscription Tier Pane | ||||
|                 <div class="col-lg-4 mb-4"> | ||||
|                     <div class="card h-100"> | ||||
|                         <div class="card-header"> | ||||
|                             <h5 class="mb-0"> | ||||
|                                 <i class="bi bi-star me-2"></i> | ||||
|                                 {"Current Plan"} | ||||
|                             </h5> | ||||
|                         </div> | ||||
|                         <div class="card-body"> | ||||
|                     <div class="card border-0 shadow-sm h-100"> | ||||
|                         <div class="card-body p-4"> | ||||
|                             <div class="d-flex align-items-center mb-3"> | ||||
|                                 <div class="bg-warning bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                     <i class="bi bi-star text-warning fs-5"></i> | ||||
|                                 </div> | ||||
|                                 <h5 class="mb-0">{"Current Plan"}</h5> | ||||
|                             </div> | ||||
|                             <div class="text-center mb-3"> | ||||
|                                 <div class="badge bg-primary fs-6 px-3 py-2 mb-2">{¤t_plan.name}</div> | ||||
|                                 <h3 class="text-primary mb-0">{format!("${:.0}", current_plan.price)}<small class="text-muted">{"/month"}</small></h3> | ||||
| @@ -438,14 +446,14 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html | ||||
|  | ||||
|                 <div class="col-lg-8"> | ||||
|                     // Payments Table Pane | ||||
|                     <div class="card mb-4"> | ||||
|                         <div class="card-header"> | ||||
|                             <h5 class="mb-0"> | ||||
|                                 <i class="bi bi-receipt me-2"></i> | ||||
|                                 {"Payment History"} | ||||
|                             </h5> | ||||
|                         </div> | ||||
|                         <div class="card-body"> | ||||
|                     <div class="card border-0 shadow-sm mb-4"> | ||||
|                         <div class="card-body p-4"> | ||||
|                             <div class="d-flex align-items-center mb-3"> | ||||
|                                 <div class="bg-info bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                     <i class="bi bi-receipt text-info fs-5"></i> | ||||
|                                 </div> | ||||
|                                 <h5 class="mb-0">{"Payment History"}</h5> | ||||
|                             </div> | ||||
|                             <div class="table-responsive"> | ||||
|                                 <table class="table table-hover"> | ||||
|                                     <thead> | ||||
| @@ -483,26 +491,28 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html | ||||
|                     </div> | ||||
|  | ||||
|                     // Payment Methods Pane | ||||
|                     <div class="card"> | ||||
|                         <div class="card-header d-flex justify-content-between align-items-center"> | ||||
|                             <h5 class="mb-0"> | ||||
|                                 <i class="bi bi-credit-card me-2"></i> | ||||
|                                 {"Payment Methods"} | ||||
|                             </h5> | ||||
|                             <button | ||||
|                                 class="btn btn-primary btn-sm" | ||||
|                                 onclick={on_add_payment_method.clone()} | ||||
|                                 disabled={loading_action.as_ref().map_or(false, |action| action == "adding_payment")} | ||||
|                             > | ||||
|                                 <i class="bi bi-plus me-1"></i> | ||||
|                                 {if loading_action.as_ref().map_or(false, |action| action == "adding_payment") { | ||||
|                                     "Adding..." | ||||
|                                 } else { | ||||
|                                     "Add Method" | ||||
|                                 }} | ||||
|                             </button> | ||||
|                         </div> | ||||
|                         <div class="card-body"> | ||||
|                     <div class="card border-0 shadow-sm"> | ||||
|                         <div class="card-body p-4"> | ||||
|                             <div class="d-flex justify-content-between align-items-center mb-3"> | ||||
|                                 <div class="d-flex align-items-center"> | ||||
|                                     <div class="bg-primary bg-opacity-10 rounded-3 p-2 me-3"> | ||||
|                                         <i class="bi bi-credit-card text-primary fs-5"></i> | ||||
|                                     </div> | ||||
|                                     <h5 class="mb-0">{"Payment Methods"}</h5> | ||||
|                                 </div> | ||||
|                                 <button | ||||
|                                     class="btn btn-primary btn-sm" | ||||
|                                     onclick={on_add_payment_method.clone()} | ||||
|                                     disabled={loading_action.as_ref().map_or(false, |action| action == "adding_payment")} | ||||
|                                 > | ||||
|                                     <i class="bi bi-plus me-1"></i> | ||||
|                                     {if loading_action.as_ref().map_or(false, |action| action == "adding_payment") { | ||||
|                                         "Adding..." | ||||
|                                     } else { | ||||
|                                         "Add Method" | ||||
|                                     }} | ||||
|                                 </button> | ||||
|                             </div> | ||||
|                             <div class="row"> | ||||
|                                 {for billing_api.payment_methods.iter().map(|method| html! { | ||||
|                                     <div class="col-md-6 mb-3"> | ||||
| @@ -566,25 +576,28 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     // Integrations Tab | ||||
|     tabs.insert("Integrations".to_string(), html! { | ||||
|         <EmptyState | ||||
|             icon={"diagram-3".to_string()} | ||||
|             title={"No integrations configured".to_string()} | ||||
|             description={"Connect with external services and configure API integrations for your personal account.".to_string()} | ||||
|             primary_action={Some(("Browse Integrations".to_string(), "#".to_string()))} | ||||
|             secondary_action={Some(("API Documentation".to_string(), "#".to_string()))} | ||||
|         /> | ||||
|     }); | ||||
|  | ||||
|     html! { | ||||
|         <> | ||||
|             <ViewComponent | ||||
|                 title={Some("Administration".to_string())} | ||||
|                 description={Some("Account settings, billing, integrations".to_string())} | ||||
|                 tabs={Some(tabs)} | ||||
|                 default_tab={Some("Account Settings".to_string())} | ||||
|             /> | ||||
|             <div class="container-fluid px-3 px-md-4 px-lg-5 px-xl-6"> | ||||
|                 <div class="row"> | ||||
|                     <div class="col-12"> | ||||
|                         <div class="d-flex justify-content-between align-items-center mb-4"> | ||||
|                             <div> | ||||
|                                 <h1 class="h3 mb-1">{"Settings"}</h1> | ||||
|                                 <p class="text-muted mb-0">{"Manage your account settings and preferences"}</p> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                          | ||||
|                         <ViewComponent | ||||
|                             title={None::<String>} | ||||
|                             description={None::<String>} | ||||
|                             tabs={Some(tabs)} | ||||
|                             default_tab={Some("Account Settings".to_string())} | ||||
|                             use_modern_header={true} | ||||
|                         /> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|              | ||||
|             // Plan Selection Modal | ||||
|             if *show_plan_modal { | ||||
|   | ||||
| @@ -74,7 +74,7 @@ pub fn residence_view(props: &ResidenceViewProps) -> Html { | ||||
|                                             </tr> | ||||
|                                             <tr> | ||||
|                                                 <td class="fw-bold">{"Email:"}</td> | ||||
|                                                 <td>{"john.doe@resident.zdf"}</td> | ||||
|                                                 <td>{"timur@resident.zdf"}</td> | ||||
|                                             </tr> | ||||
|                                         </tbody> | ||||
|                                     </table> | ||||
|   | ||||
| @@ -1005,6 +1005,7 @@ pub fn treasury_view(_props: &TreasuryViewProps) -> Html { | ||||
|                 description={Some("Manage wallets, digital assets, and transactions".to_string())} | ||||
|                 tabs={Some(tabs)} | ||||
|                 default_tab={Some("Overview".to_string())} | ||||
|                 use_modern_header={true} | ||||
|             /> | ||||
|              | ||||
|             // Import Wallet Modal | ||||
|   | ||||
		Reference in New Issue
	
	Block a user