From 60198dc2d4633b59840c8467ec60b6325fbce187 Mon Sep 17 00:00:00 2001 From: Mahmoud Emad Date: Sun, 18 May 2025 09:48:28 +0300 Subject: [PATCH] fix: Remove warnings --- actix_mvc_app/src/config/mod.rs | 8 +- actix_mvc_app/src/controllers/asset.rs | 639 +++++++++++++------ actix_mvc_app/src/controllers/auth.rs | 1 + actix_mvc_app/src/controllers/company.rs | 148 +++-- actix_mvc_app/src/controllers/contract.rs | 633 +++++++++++------- actix_mvc_app/src/controllers/defi.rs | 306 ++++++--- actix_mvc_app/src/controllers/flow.rs | 3 + actix_mvc_app/src/controllers/governance.rs | 2 + actix_mvc_app/src/controllers/home.rs | 1 + actix_mvc_app/src/controllers/marketplace.rs | 309 +++++---- actix_mvc_app/src/models/asset.rs | 1 + actix_mvc_app/src/models/contract.rs | 3 + actix_mvc_app/src/models/defi.rs | 3 + actix_mvc_app/src/models/flow.rs | 3 + actix_mvc_app/src/models/governance.rs | 3 + actix_mvc_app/src/models/marketplace.rs | 34 +- actix_mvc_app/src/models/mod.rs | 21 +- actix_mvc_app/src/models/ticket.rs | 1 + actix_mvc_app/src/models/user.rs | 4 + actix_mvc_app/src/utils/mod.rs | 52 +- 20 files changed, 1411 insertions(+), 764 deletions(-) diff --git a/actix_mvc_app/src/config/mod.rs b/actix_mvc_app/src/config/mod.rs index 7c1cb06..87ab06d 100644 --- a/actix_mvc_app/src/config/mod.rs +++ b/actix_mvc_app/src/config/mod.rs @@ -1,6 +1,6 @@ -use std::env; use config::{Config, ConfigError, File}; use serde::Deserialize; +use std::env; /// Application configuration #[derive(Debug, Deserialize, Clone)] @@ -13,6 +13,7 @@ pub struct AppConfig { /// Server configuration #[derive(Debug, Deserialize, Clone)] +#[allow(dead_code)] pub struct ServerConfig { /// Host address to bind to pub host: String, @@ -50,7 +51,8 @@ impl AppConfig { } // Override with environment variables (e.g., SERVER__HOST, SERVER__PORT) - config_builder = config_builder.add_source(config::Environment::with_prefix("APP").separator("__")); + config_builder = + config_builder.add_source(config::Environment::with_prefix("APP").separator("__")); // Build and deserialize the config let config = config_builder.build()?; @@ -61,4 +63,4 @@ impl AppConfig { /// Returns the application configuration pub fn get_config() -> AppConfig { AppConfig::new().expect("Failed to load configuration") -} \ No newline at end of file +} diff --git a/actix_mvc_app/src/controllers/asset.rs b/actix_mvc_app/src/controllers/asset.rs index 6e937c6..741294f 100644 --- a/actix_mvc_app/src/controllers/asset.rs +++ b/actix_mvc_app/src/controllers/asset.rs @@ -1,12 +1,13 @@ -use actix_web::{web, HttpResponse, Result}; -use tera::{Context, Tera}; -use chrono::{Utc, Duration}; +use actix_web::{HttpResponse, Result, web}; +use chrono::{Duration, Utc}; use serde::Deserialize; +use tera::{Context, Tera}; -use crate::models::asset::{Asset, AssetType, AssetStatus, BlockchainInfo, ValuationPoint, AssetTransaction, AssetStatistics}; +use crate::models::asset::{Asset, AssetStatistics, AssetStatus, AssetType, BlockchainInfo}; use crate::utils::render_template; #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct AssetForm { pub name: String, pub description: String, @@ -14,6 +15,7 @@ pub struct AssetForm { } #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct ValuationForm { pub value: f64, pub currency: String, @@ -22,6 +24,7 @@ pub struct ValuationForm { } #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct TransactionForm { pub transaction_type: String, pub from_address: Option, @@ -38,31 +41,31 @@ impl AssetController { // Display the assets dashboard pub async fn index(tmpl: web::Data) -> Result { let mut context = Context::new(); - + println!("DEBUG: Starting assets dashboard rendering"); - + let assets = Self::get_mock_assets(); println!("DEBUG: Generated {} mock assets", assets.len()); - + let stats = AssetStatistics::new(&assets); println!("DEBUG: Generated asset statistics: {:?}", stats); - + // Add active_page for navigation highlighting context.insert("active_page", &"assets"); - + // Add stats context.insert("stats", &serde_json::to_value(stats).unwrap()); println!("DEBUG: Added stats to context"); - + // Add recent assets let recent_assets: Vec> = assets .iter() .take(5) .map(|a| Self::asset_to_json(a)) .collect(); - + context.insert("recent_assets", &recent_assets); - + // Add assets by type let asset_types = vec![ AssetType::Artwork, @@ -74,139 +77,155 @@ impl AssetController { AssetType::IntellectualProperty, AssetType::Other, ]; - + let assets_by_type: Vec> = asset_types .iter() .map(|asset_type| { let mut map = serde_json::Map::new(); let type_str = asset_type.as_str(); - let count = assets.iter().filter(|a| a.asset_type == *asset_type).count(); - - map.insert("type".to_string(), serde_json::Value::String(type_str.to_string())); - map.insert("count".to_string(), serde_json::Value::Number(serde_json::Number::from(count))); - + let count = assets + .iter() + .filter(|a| a.asset_type == *asset_type) + .count(); + + map.insert( + "type".to_string(), + serde_json::Value::String(type_str.to_string()), + ); + map.insert( + "count".to_string(), + serde_json::Value::Number(serde_json::Number::from(count)), + ); + map }) .collect(); - + context.insert("assets_by_type", &assets_by_type); - + println!("DEBUG: Rendering assets dashboard template"); let response = render_template(&tmpl, "assets/index.html", &context); println!("DEBUG: Finished rendering assets dashboard template"); response } - + // Display the list of all assets pub async fn list(tmpl: web::Data) -> Result { let mut context = Context::new(); - + println!("DEBUG: Starting assets list rendering"); - + let assets = Self::get_mock_assets(); println!("DEBUG: Generated {} mock assets", assets.len()); - - let assets_data: Vec> = assets - .iter() - .map(|a| Self::asset_to_json(a)) - .collect(); - + + let assets_data: Vec> = + assets.iter().map(|a| Self::asset_to_json(a)).collect(); + // Add active_page for navigation highlighting context.insert("active_page", &"assets"); - + context.insert("assets", &assets_data); context.insert("filter", &"all"); - + println!("DEBUG: Rendering assets list template"); let response = render_template(&tmpl, "assets/list.html", &context); println!("DEBUG: Finished rendering assets list template"); response } - + // Display the list of user's assets pub async fn my_assets(tmpl: web::Data) -> Result { let mut context = Context::new(); - + println!("DEBUG: Starting my assets rendering"); - + let assets = Self::get_mock_assets(); println!("DEBUG: Generated {} mock assets", assets.len()); - - let assets_data: Vec> = assets - .iter() - .map(|a| Self::asset_to_json(a)) - .collect(); - + + let assets_data: Vec> = + assets.iter().map(|a| Self::asset_to_json(a)).collect(); + // Add active_page for navigation highlighting context.insert("active_page", &"assets"); - + context.insert("assets", &assets_data); - + println!("DEBUG: Rendering my assets template"); let response = render_template(&tmpl, "assets/my_assets.html", &context); println!("DEBUG: Finished rendering my assets template"); response } - + // Display a specific asset pub async fn detail(tmpl: web::Data, path: web::Path) -> Result { let asset_id = path.into_inner(); let mut context = Context::new(); - + println!("DEBUG: Starting asset detail rendering"); - + // Add active_page for navigation highlighting context.insert("active_page", &"assets"); - + // Find the asset by ID let assets = Self::get_mock_assets(); let asset = assets.iter().find(|a| a.id == asset_id); - + match asset { Some(asset) => { println!("DEBUG: Found asset with ID {}", asset_id); - + // Convert asset to JSON let asset_json = Self::asset_to_json(asset); - + context.insert("asset", &asset_json); - + // Add valuation history for chart let valuation_history: Vec> = asset .sorted_valuation_history() .iter() .map(|v| { let mut map = serde_json::Map::new(); - map.insert("date".to_string(), serde_json::Value::String(v.date.format("%Y-%m-%d").to_string())); - map.insert("value".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(v.value).unwrap())); - map.insert("currency".to_string(), serde_json::Value::String(v.currency.clone())); + map.insert( + "date".to_string(), + serde_json::Value::String(v.date.format("%Y-%m-%d").to_string()), + ); + map.insert( + "value".to_string(), + serde_json::Value::Number( + serde_json::Number::from_f64(v.value).unwrap(), + ), + ); + map.insert( + "currency".to_string(), + serde_json::Value::String(v.currency.clone()), + ); map }) .collect(); - + context.insert("valuation_history", &valuation_history); - + println!("DEBUG: Rendering asset detail template"); let response = render_template(&tmpl, "assets/detail.html", &context); println!("DEBUG: Finished rendering asset detail template"); response - }, + } None => { println!("DEBUG: Asset not found with ID {}", asset_id); Ok(HttpResponse::NotFound().finish()) } } } - + // Display the create asset form pub async fn create_form(tmpl: web::Data) -> Result { let mut context = Context::new(); - + println!("DEBUG: Starting create asset form rendering"); - + // Add active_page for navigation highlighting context.insert("active_page", &"assets"); - + // Add asset types for dropdown let asset_types = vec![ ("Artwork", "Artwork"), @@ -216,30 +235,32 @@ impl AssetController { ("Share", "Share"), ("Bond", "Bond"), ("IntellectualProperty", "Intellectual Property"), - ("Other", "Other") + ("Other", "Other"), ]; - + context.insert("asset_types", &asset_types); - + println!("DEBUG: Rendering create asset form template"); let response = render_template(&tmpl, "assets/create.html", &context); println!("DEBUG: Finished rendering create asset form template"); response } - + // Process the create asset form pub async fn create( _tmpl: web::Data, _form: web::Form, ) -> Result { println!("DEBUG: Processing create asset form"); - + // In a real application, we would save the asset to the database // For now, we'll just redirect to the assets list - - Ok(HttpResponse::Found().append_header(("Location", "/assets")).finish()) + + Ok(HttpResponse::Found() + .append_header(("Location", "/assets")) + .finish()) } - + // Add a valuation to an asset pub async fn add_valuation( _tmpl: web::Data, @@ -247,15 +268,17 @@ impl AssetController { _form: web::Form, ) -> Result { let asset_id = path.into_inner(); - + println!("DEBUG: Adding valuation to asset with ID {}", asset_id); - + // In a real application, we would update the asset in the database // For now, we'll just redirect to the asset detail page - - Ok(HttpResponse::Found().append_header(("Location", format!("/assets/{}", asset_id))).finish()) + + Ok(HttpResponse::Found() + .append_header(("Location", format!("/assets/{}", asset_id))) + .finish()) } - + // Add a transaction to an asset pub async fn add_transaction( _tmpl: web::Data, @@ -263,190 +286,309 @@ impl AssetController { _form: web::Form, ) -> Result { let asset_id = path.into_inner(); - + println!("DEBUG: Adding transaction to asset with ID {}", asset_id); - + // In a real application, we would update the asset in the database // For now, we'll just redirect to the asset detail page - - Ok(HttpResponse::Found().append_header(("Location", format!("/assets/{}", asset_id))).finish()) + + Ok(HttpResponse::Found() + .append_header(("Location", format!("/assets/{}", asset_id))) + .finish()) } - + // Update the status of an asset pub async fn update_status( _tmpl: web::Data, path: web::Path<(String, String)>, ) -> Result { let (asset_id, _status) = path.into_inner(); - + println!("DEBUG: Updating status of asset with ID {}", asset_id); - + // In a real application, we would update the asset in the database // For now, we'll just redirect to the asset detail page - - Ok(HttpResponse::Found().append_header(("Location", format!("/assets/{}", asset_id))).finish()) + + Ok(HttpResponse::Found() + .append_header(("Location", format!("/assets/{}", asset_id))) + .finish()) } - + // Test method to render a simple test page pub async fn test(tmpl: web::Data) -> Result { println!("DEBUG: Starting test page rendering"); - + let mut context = Context::new(); - + let assets = Self::get_mock_assets(); println!("DEBUG: Generated {} mock assets for test", assets.len()); - + let stats = AssetStatistics::new(&assets); println!("DEBUG: Generated asset statistics for test: {:?}", stats); - + // Add active_page for navigation highlighting context.insert("active_page", &"assets"); - + // Add stats context.insert("stats", &serde_json::to_value(stats).unwrap()); println!("DEBUG: Added stats to context for test"); - + // Add recent assets let recent_assets: Vec> = assets .iter() .take(5) .map(|a| Self::asset_to_json(a)) .collect(); - + context.insert("recent_assets", &recent_assets); - + println!("DEBUG: Rendering test_base.html with full context"); let response = render_template(&tmpl, "test_base.html", &context); println!("DEBUG: Finished rendering test_base.html"); response } - + // Helper method to convert Asset to a JSON object for templates fn asset_to_json(asset: &Asset) -> serde_json::Map { let mut map = serde_json::Map::new(); - - map.insert("id".to_string(), serde_json::Value::String(asset.id.clone())); - map.insert("name".to_string(), serde_json::Value::String(asset.name.clone())); - map.insert("description".to_string(), serde_json::Value::String(asset.description.clone())); - map.insert("asset_type".to_string(), serde_json::Value::String(asset.asset_type.as_str().to_string())); - map.insert("status".to_string(), serde_json::Value::String(asset.status.as_str().to_string())); - map.insert("owner_id".to_string(), serde_json::Value::String(asset.owner_id.clone())); - map.insert("owner_name".to_string(), serde_json::Value::String(asset.owner_name.clone())); - map.insert("created_at".to_string(), serde_json::Value::String(asset.created_at.format("%Y-%m-%d").to_string())); - map.insert("updated_at".to_string(), serde_json::Value::String(asset.updated_at.format("%Y-%m-%d").to_string())); - + + map.insert( + "id".to_string(), + serde_json::Value::String(asset.id.clone()), + ); + map.insert( + "name".to_string(), + serde_json::Value::String(asset.name.clone()), + ); + map.insert( + "description".to_string(), + serde_json::Value::String(asset.description.clone()), + ); + map.insert( + "asset_type".to_string(), + serde_json::Value::String(asset.asset_type.as_str().to_string()), + ); + map.insert( + "status".to_string(), + serde_json::Value::String(asset.status.as_str().to_string()), + ); + map.insert( + "owner_id".to_string(), + serde_json::Value::String(asset.owner_id.clone()), + ); + map.insert( + "owner_name".to_string(), + serde_json::Value::String(asset.owner_name.clone()), + ); + map.insert( + "created_at".to_string(), + serde_json::Value::String(asset.created_at.format("%Y-%m-%d").to_string()), + ); + map.insert( + "updated_at".to_string(), + serde_json::Value::String(asset.updated_at.format("%Y-%m-%d").to_string()), + ); + // Add current valuation if available if let Some(current_valuation) = asset.current_valuation { - map.insert("current_valuation".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(current_valuation).unwrap())); - + map.insert( + "current_valuation".to_string(), + serde_json::Value::Number(serde_json::Number::from_f64(current_valuation).unwrap()), + ); + if let Some(valuation_currency) = &asset.valuation_currency { - map.insert("valuation_currency".to_string(), serde_json::Value::String(valuation_currency.clone())); + map.insert( + "valuation_currency".to_string(), + serde_json::Value::String(valuation_currency.clone()), + ); } - + if let Some(valuation_date) = asset.valuation_date { - map.insert("valuation_date".to_string(), serde_json::Value::String(valuation_date.format("%Y-%m-%d").to_string())); + map.insert( + "valuation_date".to_string(), + serde_json::Value::String(valuation_date.format("%Y-%m-%d").to_string()), + ); } } - + // Add blockchain info if available if let Some(blockchain_info) = &asset.blockchain_info { let mut blockchain_map = serde_json::Map::new(); - blockchain_map.insert("blockchain".to_string(), serde_json::Value::String(blockchain_info.blockchain.clone())); - blockchain_map.insert("token_id".to_string(), serde_json::Value::String(blockchain_info.token_id.clone())); - blockchain_map.insert("contract_address".to_string(), serde_json::Value::String(blockchain_info.contract_address.clone())); - blockchain_map.insert("owner_address".to_string(), serde_json::Value::String(blockchain_info.owner_address.clone())); - + blockchain_map.insert( + "blockchain".to_string(), + serde_json::Value::String(blockchain_info.blockchain.clone()), + ); + blockchain_map.insert( + "token_id".to_string(), + serde_json::Value::String(blockchain_info.token_id.clone()), + ); + blockchain_map.insert( + "contract_address".to_string(), + serde_json::Value::String(blockchain_info.contract_address.clone()), + ); + blockchain_map.insert( + "owner_address".to_string(), + serde_json::Value::String(blockchain_info.owner_address.clone()), + ); + if let Some(transaction_hash) = &blockchain_info.transaction_hash { - blockchain_map.insert("transaction_hash".to_string(), serde_json::Value::String(transaction_hash.clone())); + blockchain_map.insert( + "transaction_hash".to_string(), + serde_json::Value::String(transaction_hash.clone()), + ); } - + if let Some(block_number) = blockchain_info.block_number { - blockchain_map.insert("block_number".to_string(), serde_json::Value::Number(serde_json::Number::from(block_number))); + blockchain_map.insert( + "block_number".to_string(), + serde_json::Value::Number(serde_json::Number::from(block_number)), + ); } - + if let Some(timestamp) = blockchain_info.timestamp { - blockchain_map.insert("timestamp".to_string(), serde_json::Value::String(timestamp.format("%Y-%m-%d").to_string())); + blockchain_map.insert( + "timestamp".to_string(), + serde_json::Value::String(timestamp.format("%Y-%m-%d").to_string()), + ); } - - map.insert("blockchain_info".to_string(), serde_json::Value::Object(blockchain_map)); + + map.insert( + "blockchain_info".to_string(), + serde_json::Value::Object(blockchain_map), + ); } - + // Add valuation history - let valuation_history: Vec = asset.valuation_history.iter() + let valuation_history: Vec = asset + .valuation_history + .iter() .map(|v| { let mut valuation_map = serde_json::Map::new(); valuation_map.insert("id".to_string(), serde_json::Value::String(v.id.clone())); - valuation_map.insert("date".to_string(), serde_json::Value::String(v.date.format("%Y-%m-%d").to_string())); - valuation_map.insert("value".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(v.value).unwrap())); - valuation_map.insert("currency".to_string(), serde_json::Value::String(v.currency.clone())); - valuation_map.insert("source".to_string(), serde_json::Value::String(v.source.clone())); - + valuation_map.insert( + "date".to_string(), + serde_json::Value::String(v.date.format("%Y-%m-%d").to_string()), + ); + valuation_map.insert( + "value".to_string(), + serde_json::Value::Number(serde_json::Number::from_f64(v.value).unwrap()), + ); + valuation_map.insert( + "currency".to_string(), + serde_json::Value::String(v.currency.clone()), + ); + valuation_map.insert( + "source".to_string(), + serde_json::Value::String(v.source.clone()), + ); + if let Some(notes) = &v.notes { - valuation_map.insert("notes".to_string(), serde_json::Value::String(notes.clone())); + valuation_map.insert( + "notes".to_string(), + serde_json::Value::String(notes.clone()), + ); } - + serde_json::Value::Object(valuation_map) }) .collect(); - - map.insert("valuation_history".to_string(), serde_json::Value::Array(valuation_history)); - + + map.insert( + "valuation_history".to_string(), + serde_json::Value::Array(valuation_history), + ); + // Add transaction history - let transaction_history: Vec = asset.transaction_history.iter() + let transaction_history: Vec = asset + .transaction_history + .iter() .map(|t| { let mut transaction_map = serde_json::Map::new(); transaction_map.insert("id".to_string(), serde_json::Value::String(t.id.clone())); - transaction_map.insert("transaction_type".to_string(), serde_json::Value::String(t.transaction_type.clone())); - transaction_map.insert("date".to_string(), serde_json::Value::String(t.date.format("%Y-%m-%d").to_string())); - + transaction_map.insert( + "transaction_type".to_string(), + serde_json::Value::String(t.transaction_type.clone()), + ); + transaction_map.insert( + "date".to_string(), + serde_json::Value::String(t.date.format("%Y-%m-%d").to_string()), + ); + if let Some(from_address) = &t.from_address { - transaction_map.insert("from_address".to_string(), serde_json::Value::String(from_address.clone())); + transaction_map.insert( + "from_address".to_string(), + serde_json::Value::String(from_address.clone()), + ); } - + if let Some(to_address) = &t.to_address { - transaction_map.insert("to_address".to_string(), serde_json::Value::String(to_address.clone())); + transaction_map.insert( + "to_address".to_string(), + serde_json::Value::String(to_address.clone()), + ); } - + if let Some(amount) = t.amount { - transaction_map.insert("amount".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(amount).unwrap())); + transaction_map.insert( + "amount".to_string(), + serde_json::Value::Number(serde_json::Number::from_f64(amount).unwrap()), + ); } - + if let Some(currency) = &t.currency { - transaction_map.insert("currency".to_string(), serde_json::Value::String(currency.clone())); + transaction_map.insert( + "currency".to_string(), + serde_json::Value::String(currency.clone()), + ); } - + if let Some(transaction_hash) = &t.transaction_hash { - transaction_map.insert("transaction_hash".to_string(), serde_json::Value::String(transaction_hash.clone())); + transaction_map.insert( + "transaction_hash".to_string(), + serde_json::Value::String(transaction_hash.clone()), + ); } - + if let Some(notes) = &t.notes { - transaction_map.insert("notes".to_string(), serde_json::Value::String(notes.clone())); + transaction_map.insert( + "notes".to_string(), + serde_json::Value::String(notes.clone()), + ); } - + serde_json::Value::Object(transaction_map) }) .collect(); - - map.insert("transaction_history".to_string(), serde_json::Value::Array(transaction_history)); - + + map.insert( + "transaction_history".to_string(), + serde_json::Value::Array(transaction_history), + ); + // Add image URL if available if let Some(image_url) = &asset.image_url { - map.insert("image_url".to_string(), serde_json::Value::String(image_url.clone())); + map.insert( + "image_url".to_string(), + serde_json::Value::String(image_url.clone()), + ); } - + // Add external URL if available if let Some(external_url) = &asset.external_url { - map.insert("external_url".to_string(), serde_json::Value::String(external_url.clone())); + map.insert( + "external_url".to_string(), + serde_json::Value::String(external_url.clone()), + ); } - + map } - + // Generate mock assets for testing pub fn get_mock_assets() -> Vec { let now = Utc::now(); let mut assets = Vec::new(); - + // Create Tokenized Real Estate asset let mut zanzibar_resort = Asset { id: "asset-zanzibar-resort".to_string(), @@ -475,21 +617,38 @@ impl AssetController { image_url: Some("https://images.unsplash.com/photo-1506744038136-46273834b3fb?auto=format&fit=crop&w=600&q=80".to_string()), external_url: Some("https://oceanviewholdings.zdfz/resort".to_string()), }; - + zanzibar_resort.add_blockchain_info(BlockchainInfo { blockchain: "Ethereum".to_string(), token_id: "ZRESORT".to_string(), contract_address: "0x123456789abcdef123456789abcdef12345678ab".to_string(), owner_address: "0xc3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4".to_string(), - transaction_hash: Some("0xabcdef123456789abcdef123456789abcdef123456789abcdef123456789abcd".to_string()), + transaction_hash: Some( + "0xabcdef123456789abcdef123456789abcdef123456789abcdef123456789abcd".to_string(), + ), block_number: Some(9876543), timestamp: Some(now - Duration::days(120)), }); - - zanzibar_resort.add_valuation(650000.0, "USD", "ZDFZ Property Registry", Some("Initial tokenization valuation".to_string())); - zanzibar_resort.add_valuation(700000.0, "USD", "International Property Appraisers", Some("Independent third-party valuation".to_string())); - zanzibar_resort.add_valuation(750000.0, "USD", "ZDFZ Property Registry", Some("Updated valuation after infrastructure improvements".to_string())); - + + zanzibar_resort.add_valuation( + 650000.0, + "USD", + "ZDFZ Property Registry", + Some("Initial tokenization valuation".to_string()), + ); + zanzibar_resort.add_valuation( + 700000.0, + "USD", + "International Property Appraisers", + Some("Independent third-party valuation".to_string()), + ); + zanzibar_resort.add_valuation( + 750000.0, + "USD", + "ZDFZ Property Registry", + Some("Updated valuation after infrastructure improvements".to_string()), + ); + zanzibar_resort.add_transaction( "Tokenization", None, @@ -499,7 +658,7 @@ impl AssetController { Some("0xabcdef123456789abcdef123456789abcdef123456789abcdef123456789abcd".to_string()), Some("Initial property tokenization under ZDFZ Property Registry".to_string()), ); - + zanzibar_resort.add_transaction( "Token Sale", Some("0xc3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4".to_string()), @@ -509,9 +668,9 @@ impl AssetController { Some("0xdef123456789abcdef123456789abcdef123456789abcdef123456789abcdef".to_string()), Some("Sale of 10% ownership tokens to Zanzibar Investment Collective".to_string()), ); - + assets.push(zanzibar_resort); - + // Create ZDFZ Governance Token let mut zaz_token = Asset { id: "asset-zdfz-governance".to_string(), @@ -539,7 +698,7 @@ impl AssetController { image_url: Some("https://images.unsplash.com/photo-1431540015161-0bf868a2d407?q=80&w=3540&?auto=format&fit=crop&w=600&q=80".to_string()), external_url: Some("https://governance.zdfz/token".to_string()), }; - + zaz_token.add_blockchain_info(BlockchainInfo { blockchain: "ThreeFold".to_string(), token_id: "ZAZT".to_string(), @@ -549,11 +708,26 @@ impl AssetController { block_number: None, timestamp: Some(now - Duration::days(365)), }); - - zaz_token.add_valuation(300000.0, "USD", "ZDFZ Token Exchange", Some("Initial valuation at launch".to_string())); - zaz_token.add_valuation(320000.0, "USD", "ZDFZ Token Exchange", Some("Valuation after successful governance implementation".to_string())); - zaz_token.add_valuation(350000.0, "USD", "ZDFZ Token Exchange", Some("Current market valuation".to_string())); - + + zaz_token.add_valuation( + 300000.0, + "USD", + "ZDFZ Token Exchange", + Some("Initial valuation at launch".to_string()), + ); + zaz_token.add_valuation( + 320000.0, + "USD", + "ZDFZ Token Exchange", + Some("Valuation after successful governance implementation".to_string()), + ); + zaz_token.add_valuation( + 350000.0, + "USD", + "ZDFZ Token Exchange", + Some("Current market valuation".to_string()), + ); + zaz_token.add_transaction( "Distribution", Some("0xe5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6".to_string()), @@ -563,7 +737,7 @@ impl AssetController { Some("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string()), Some("Initial token distribution to founding members".to_string()), ); - + zaz_token.add_transaction( "Distribution", Some("0xe5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6".to_string()), @@ -573,9 +747,9 @@ impl AssetController { Some("0x234567890abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string()), Some("Public token sale for zone participants".to_string()), ); - + assets.push(zaz_token); - + // Create Spice Trade Venture Shares let mut spice_trade_shares = Asset { id: "asset-spice-trade-shares".to_string(), @@ -604,21 +778,38 @@ impl AssetController { image_url: Some("https://images.unsplash.com/photo-1464983953574-0892a716854b?auto=format&fit=crop&w=600&q=80".to_string()), external_url: Some("https://spicetrade.zdfz".to_string()), }; - + spice_trade_shares.add_blockchain_info(BlockchainInfo { blockchain: "Ethereum".to_string(), token_id: "SPICE".to_string(), contract_address: "0x3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4".to_string(), owner_address: "0x6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b".to_string(), - transaction_hash: Some("0x789abcdef123456789abcdef123456789abcdef123456789abcdef123456789a".to_string()), + transaction_hash: Some( + "0x789abcdef123456789abcdef123456789abcdef123456789abcdef123456789a".to_string(), + ), block_number: Some(7654321), timestamp: Some(now - Duration::days(180)), }); - - spice_trade_shares.add_valuation(150000.0, "USD", "ZDFZ Business Registry", Some("Initial company valuation at incorporation".to_string())); - spice_trade_shares.add_valuation(175000.0, "USD", "ZDFZ Business Registry", Some("Valuation after first export contracts".to_string())); - spice_trade_shares.add_valuation(200000.0, "USD", "ZDFZ Business Registry", Some("Current valuation after expansion to European markets".to_string())); - + + spice_trade_shares.add_valuation( + 150000.0, + "USD", + "ZDFZ Business Registry", + Some("Initial company valuation at incorporation".to_string()), + ); + spice_trade_shares.add_valuation( + 175000.0, + "USD", + "ZDFZ Business Registry", + Some("Valuation after first export contracts".to_string()), + ); + spice_trade_shares.add_valuation( + 200000.0, + "USD", + "ZDFZ Business Registry", + Some("Current valuation after expansion to European markets".to_string()), + ); + spice_trade_shares.add_transaction( "Share Issuance", None, @@ -628,7 +819,7 @@ impl AssetController { Some("0x789abcdef123456789abcdef123456789abcdef123456789abcdef123456789a".to_string()), Some("Initial share issuance at company formation".to_string()), ); - + spice_trade_shares.add_transaction( "Share Transfer", Some("0x6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b".to_string()), @@ -638,9 +829,9 @@ impl AssetController { Some("0x89abcdef123456789abcdef123456789abcdef123456789abcdef123456789ab".to_string()), Some("Sale of 25% equity to East African Growth Partners".to_string()), ); - + assets.push(spice_trade_shares); - + // Create Sustainable Energy Patent let mut tidal_energy_patent = Asset { id: "asset-tidal-energy-patent".to_string(), @@ -669,21 +860,38 @@ impl AssetController { image_url: Some("https://images.unsplash.com/photo-1708851148146-783a5b7da55d?q=80&w=3474&?auto=format&fit=crop&w=600&q=80".to_string()), external_url: Some("https://patents.zdfz/ZDFZ-PAT-2024-0142".to_string()), }; - + tidal_energy_patent.add_blockchain_info(BlockchainInfo { blockchain: "Polygon".to_string(), token_id: "TIDALIP".to_string(), contract_address: "0x2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3".to_string(), owner_address: "0x4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f".to_string(), - transaction_hash: Some("0x56789abcdef123456789abcdef123456789abcdef123456789abcdef12345678".to_string()), + transaction_hash: Some( + "0x56789abcdef123456789abcdef123456789abcdef123456789abcdef12345678".to_string(), + ), block_number: Some(5432109), timestamp: Some(now - Duration::days(120)), }); - - tidal_energy_patent.add_valuation(80000.0, "USD", "ZDFZ IP Registry", Some("Initial patent valuation upon filing".to_string())); - tidal_energy_patent.add_valuation(100000.0, "USD", "ZDFZ IP Registry", Some("Valuation after successful prototype testing".to_string())); - tidal_energy_patent.add_valuation(120000.0, "USD", "ZDFZ IP Registry", Some("Current valuation after pilot implementation".to_string())); - + + tidal_energy_patent.add_valuation( + 80000.0, + "USD", + "ZDFZ IP Registry", + Some("Initial patent valuation upon filing".to_string()), + ); + tidal_energy_patent.add_valuation( + 100000.0, + "USD", + "ZDFZ IP Registry", + Some("Valuation after successful prototype testing".to_string()), + ); + tidal_energy_patent.add_valuation( + 120000.0, + "USD", + "ZDFZ IP Registry", + Some("Current valuation after pilot implementation".to_string()), + ); + tidal_energy_patent.add_transaction( "Registration", None, @@ -693,7 +901,7 @@ impl AssetController { Some("0x56789abcdef123456789abcdef123456789abcdef123456789abcdef12345678".to_string()), Some("Initial patent registration and tokenization".to_string()), ); - + tidal_energy_patent.add_transaction( "Licensing", Some("0x4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f".to_string()), @@ -703,9 +911,9 @@ impl AssetController { Some("0x6789abcdef123456789abcdef123456789abcdef123456789abcdef123456789".to_string()), Some("Licensing agreement with Coastal Energy Solutions".to_string()), ); - + assets.push(tidal_energy_patent); - + // Create Digital Art Artwork let mut zanzibar_heritage_nft = Asset { id: "asset-heritage-Artwork".to_string(), @@ -734,21 +942,38 @@ impl AssetController { image_url: Some("https://images.unsplash.com/photo-1519125323398-675f0ddb6308?auto=format&fit=crop&w=600&q=80".to_string()), external_url: Some("https://digitalarts.zdfz/collections/heritage/1".to_string()), }; - + zanzibar_heritage_nft.add_blockchain_info(BlockchainInfo { blockchain: "Ethereum".to_string(), token_id: "HERITAGE1".to_string(), contract_address: "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d".to_string(), owner_address: "0xb794f5ea0ba39494ce839613fffba74279579268".to_string(), - transaction_hash: Some("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string()), + transaction_hash: Some( + "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(), + ), block_number: Some(12345678), timestamp: Some(now - Duration::days(90)), }); - - zanzibar_heritage_nft.add_valuation(5000.0, "USD", "ZDFZ Artwork Marketplace", Some("Initial offering price".to_string())); - zanzibar_heritage_nft.add_valuation(5500.0, "USD", "ZDFZ Artwork Marketplace", Some("Valuation after artist exhibition".to_string())); - zanzibar_heritage_nft.add_valuation(6000.0, "USD", "ZDFZ Artwork Marketplace", Some("Current market valuation".to_string())); - + + zanzibar_heritage_nft.add_valuation( + 5000.0, + "USD", + "ZDFZ Artwork Marketplace", + Some("Initial offering price".to_string()), + ); + zanzibar_heritage_nft.add_valuation( + 5500.0, + "USD", + "ZDFZ Artwork Marketplace", + Some("Valuation after artist exhibition".to_string()), + ); + zanzibar_heritage_nft.add_valuation( + 6000.0, + "USD", + "ZDFZ Artwork Marketplace", + Some("Current market valuation".to_string()), + ); + zanzibar_heritage_nft.add_transaction( "Minting", None, @@ -758,7 +983,7 @@ impl AssetController { Some("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string()), Some("Initial Artwork minting by artist".to_string()), ); - + zanzibar_heritage_nft.add_transaction( "Sale", Some("0xb794f5ea0ba39494ce839613fffba74279579268".to_string()), @@ -768,9 +993,9 @@ impl AssetController { Some("0x234567890abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string()), Some("Primary sale to ZDFZ Digital Arts Collective".to_string()), ); - + assets.push(zanzibar_heritage_nft); - + assets } } diff --git a/actix_mvc_app/src/controllers/auth.rs b/actix_mvc_app/src/controllers/auth.rs index 681a1c8..08934ba 100644 --- a/actix_mvc_app/src/controllers/auth.rs +++ b/actix_mvc_app/src/controllers/auth.rs @@ -25,6 +25,7 @@ lazy_static! { /// Controller for handling authentication-related routes pub struct AuthController; +#[allow(dead_code)] impl AuthController { /// Generate a JWT token for a user fn generate_token(email: &str, role: &UserRole) -> Result { diff --git a/actix_mvc_app/src/controllers/company.rs b/actix_mvc_app/src/controllers/company.rs index 554d1e4..cf25c3f 100644 --- a/actix_mvc_app/src/controllers/company.rs +++ b/actix_mvc_app/src/controllers/company.rs @@ -1,12 +1,12 @@ -use actix_web::{web, HttpResponse, Responder, Result}; -use actix_web::HttpRequest; -use tera::{Context, Tera}; -use serde::Deserialize; -use chrono::Utc; use crate::utils::render_template; +use actix_web::HttpRequest; +use actix_web::{HttpResponse, Result, web}; +use serde::Deserialize; +use tera::{Context, Tera}; // Form structs for company operations #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct CompanyRegistrationForm { pub company_name: String, pub company_type: String, @@ -20,59 +20,69 @@ impl CompanyController { // Display the company management dashboard pub async fn index(tmpl: web::Data, req: HttpRequest) -> Result { let mut context = Context::new(); - + println!("DEBUG: Starting Company dashboard rendering"); - + // Add active_page for navigation highlighting context.insert("active_page", &"company"); - + // Parse query parameters let query_string = req.query_string(); - + // Check for success message if let Some(pos) = query_string.find("success=") { let start = pos + 8; // length of "success=" - let end = query_string[start..].find('&').map_or(query_string.len(), |e| e + start); + let end = query_string[start..] + .find('&') + .map_or(query_string.len(), |e| e + start); let success = &query_string[start..end]; let decoded = urlencoding::decode(success).unwrap_or_else(|_| success.into()); context.insert("success", &decoded); } - + // Check for entity context if let Some(pos) = query_string.find("entity=") { let start = pos + 7; // length of "entity=" - let end = query_string[start..].find('&').map_or(query_string.len(), |e| e + start); + let end = query_string[start..] + .find('&') + .map_or(query_string.len(), |e| e + start); let entity = &query_string[start..end]; context.insert("entity", &entity); - + // Also get entity name if present if let Some(pos) = query_string.find("entity_name=") { let start = pos + 12; // length of "entity_name=" - let end = query_string[start..].find('&').map_or(query_string.len(), |e| e + start); + let end = query_string[start..] + .find('&') + .map_or(query_string.len(), |e| e + start); let entity_name = &query_string[start..end]; - let decoded_name = urlencoding::decode(entity_name).unwrap_or_else(|_| entity_name.into()); + let decoded_name = + urlencoding::decode(entity_name).unwrap_or_else(|_| entity_name.into()); context.insert("entity_name", &decoded_name); println!("DEBUG: Entity context set to {} ({})", entity, decoded_name); } } - + println!("DEBUG: Rendering Company dashboard template"); let response = render_template(&tmpl, "company/index.html", &context); println!("DEBUG: Finished rendering Company dashboard template"); response } - + // View company details - pub async fn view_company(tmpl: web::Data, path: web::Path) -> Result { + pub async fn view_company( + tmpl: web::Data, + path: web::Path, + ) -> Result { let company_id = path.into_inner(); let mut context = Context::new(); - + println!("DEBUG: Viewing company details for {}", company_id); - + // Add active_page for navigation highlighting context.insert("active_page", &"company"); context.insert("company_id", &company_id); - + // In a real application, we would fetch company data from a database // For now, we'll use mock data based on the company_id match company_id.as_str() { @@ -85,14 +95,11 @@ impl CompanyController { context.insert("plan", &"Startup FZC - $50/month"); context.insert("next_billing", &"2025-06-01"); context.insert("payment_method", &"Credit Card (****4582)"); - + // Shareholders data - let shareholders = vec![ - ("John Smith", "60%"), - ("Sarah Johnson", "40%"), - ]; + let shareholders = vec![("John Smith", "60%"), ("Sarah Johnson", "40%")]; context.insert("shareholders", &shareholders); - + // Contracts data let contracts = vec![ ("Articles of Incorporation", "Signed"), @@ -100,7 +107,7 @@ impl CompanyController { ("Digital Asset Issuance", "Signed"), ]; context.insert("contracts", &contracts); - }, + } "company2" => { context.insert("company_name", &"Blockchain Innovations Ltd"); context.insert("company_type", &"Growth FZC"); @@ -110,7 +117,7 @@ impl CompanyController { context.insert("plan", &"Growth FZC - $100/month"); context.insert("next_billing", &"2025-06-15"); context.insert("payment_method", &"Bank Transfer"); - + // Shareholders data let shareholders = vec![ ("Michael Chen", "35%"), @@ -118,7 +125,7 @@ impl CompanyController { ("David Okonkwo", "30%"), ]; context.insert("shareholders", &shareholders); - + // Contracts data let contracts = vec![ ("Articles of Incorporation", "Signed"), @@ -127,7 +134,7 @@ impl CompanyController { ("Physical Asset Holding", "Signed"), ]; context.insert("contracts", &contracts); - }, + } "company3" => { context.insert("company_name", &"Sustainable Energy Cooperative"); context.insert("company_type", &"Cooperative FZC"); @@ -137,7 +144,7 @@ impl CompanyController { context.insert("plan", &"Cooperative FZC - $200/month"); context.insert("next_billing", &"Pending Activation"); context.insert("payment_method", &"Pending"); - + // Shareholders data let shareholders = vec![ ("Community Energy Group", "40%"), @@ -145,7 +152,7 @@ impl CompanyController { ("Sustainable Living Collective", "30%"), ]; context.insert("shareholders", &shareholders); - + // Contracts data let contracts = vec![ ("Articles of Incorporation", "Signed"), @@ -153,7 +160,7 @@ impl CompanyController { ("Cooperative Governance", "Pending"), ]; context.insert("contracts", &contracts); - }, + } _ => { // If company_id is not recognized, redirect to company index return Ok(HttpResponse::Found() @@ -161,51 +168,56 @@ impl CompanyController { .finish()); } } - + println!("DEBUG: Rendering company view template"); let response = render_template(&tmpl, "company/view.html", &context); println!("DEBUG: Finished rendering company view template"); response } - + // Switch to entity context pub async fn switch_entity(path: web::Path) -> Result { let company_id = path.into_inner(); - + println!("DEBUG: Switching to entity context for {}", company_id); - + // Get company name based on ID (in a real app, this would come from a database) let company_name = match company_id.as_str() { "company1" => "Zanzibar Digital Solutions", "company2" => "Blockchain Innovations Ltd", "company3" => "Sustainable Energy Cooperative", - _ => "Unknown Company" + _ => "Unknown Company", }; - + // In a real application, we would set a session/cookie for the current entity // Here we'll redirect back to the company page with a success message and entity parameter let success_message = format!("Switched to {} entity context", company_name); let encoded_message = urlencoding::encode(&success_message); - + Ok(HttpResponse::Found() - .append_header(("Location", format!("/company?success={}&entity={}&entity_name={}", - encoded_message, company_id, urlencoding::encode(company_name)))) + .append_header(( + "Location", + format!( + "/company?success={}&entity={}&entity_name={}", + encoded_message, + company_id, + urlencoding::encode(company_name) + ), + )) .finish()) } - + // Process company registration - pub async fn register( - mut form: actix_multipart::Multipart, - ) -> Result { - use actix_web::{http::header}; + pub async fn register(mut form: actix_multipart::Multipart) -> Result { + use actix_web::http::header; use futures_util::stream::StreamExt as _; use std::collections::HashMap; - + println!("DEBUG: Processing company registration request"); - + let mut fields: HashMap = HashMap::new(); let mut files = Vec::new(); - + // Parse multipart form while let Some(Ok(mut field)) = form.next().await { let mut value = Vec::new(); @@ -213,33 +225,47 @@ impl CompanyController { let data = chunk.unwrap(); value.extend_from_slice(&data); } - + // Get field name from content disposition let cd = field.content_disposition(); if let Some(name) = cd.get_name() { if name == "company_docs" { files.push(value); // Just collect files in memory for now } else { - fields.insert(name.to_string(), String::from_utf8_lossy(&value).to_string()); + fields.insert( + name.to_string(), + String::from_utf8_lossy(&value).to_string(), + ); } } } - + // Extract company details let company_name = fields.get("company_name").cloned().unwrap_or_default(); let company_type = fields.get("company_type").cloned().unwrap_or_default(); let shareholders = fields.get("shareholders").cloned().unwrap_or_default(); - + // Log received fields (mock DB insert) - println!("[Company Registration] Name: {}, Type: {}, Shareholders: {}, Files: {}", - company_name, company_type, shareholders, files.len()); - + println!( + "[Company Registration] Name: {}, Type: {}, Shareholders: {}, Files: {}", + company_name, + company_type, + shareholders, + files.len() + ); + // Create success message - let success_message = format!("Successfully registered {} as a {}", company_name, company_type); - + let success_message = format!( + "Successfully registered {} as a {}", + company_name, company_type + ); + // Redirect back to /company with success message Ok(HttpResponse::SeeOther() - .append_header((header::LOCATION, format!("/company?success={}", urlencoding::encode(&success_message)))) + .append_header(( + header::LOCATION, + format!("/company?success={}", urlencoding::encode(&success_message)), + )) .finish()) } } diff --git a/actix_mvc_app/src/controllers/contract.rs b/actix_mvc_app/src/controllers/contract.rs index 476d76e..9943aff 100644 --- a/actix_mvc_app/src/controllers/contract.rs +++ b/actix_mvc_app/src/controllers/contract.rs @@ -1,15 +1,18 @@ -use actix_web::{web, HttpResponse, Result, Error}; -use tera::{Context, Tera}; -use chrono::{Utc, Duration}; -use serde::Deserialize; -use serde_json::json; use actix_web::web::Query; +use actix_web::{Error, HttpResponse, Result, web}; +use chrono::{Duration, Utc}; +use serde::Deserialize; use std::collections::HashMap; +use tera::{Context, Tera}; -use crate::models::contract::{Contract, ContractStatus, ContractType, ContractStatistics, ContractSigner, ContractRevision, SignerStatus, TocItem}; +use crate::models::contract::{ + Contract, ContractRevision, ContractSigner, ContractStatistics, ContractStatus, ContractType, + SignerStatus, TocItem, +}; use crate::utils::render_template; #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct ContractForm { pub title: String, pub description: String, @@ -18,6 +21,7 @@ pub struct ContractForm { } #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct SignerForm { pub name: String, pub email: String, @@ -29,98 +33,99 @@ impl ContractController { // Display the contracts dashboard pub async fn index(tmpl: web::Data) -> Result { let mut context = Context::new(); - + let contracts = Self::get_mock_contracts(); let stats = ContractStatistics::new(&contracts); - + // Add active_page for navigation highlighting context.insert("active_page", &"contracts"); - + // Add stats context.insert("stats", &serde_json::to_value(stats).unwrap()); - + // Add recent contracts let recent_contracts: Vec> = contracts .iter() .take(5) .map(|c| Self::contract_to_json(c)) .collect(); - + context.insert("recent_contracts", &recent_contracts); - + // Add pending signature contracts - let pending_signature_contracts: Vec> = contracts - .iter() - .filter(|c| c.status == ContractStatus::PendingSignatures) - .map(|c| Self::contract_to_json(c)) - .collect(); - + let pending_signature_contracts: Vec> = + contracts + .iter() + .filter(|c| c.status == ContractStatus::PendingSignatures) + .map(|c| Self::contract_to_json(c)) + .collect(); + context.insert("pending_signature_contracts", &pending_signature_contracts); - + // Add draft contracts let draft_contracts: Vec> = contracts .iter() .filter(|c| c.status == ContractStatus::Draft) .map(|c| Self::contract_to_json(c)) .collect(); - + context.insert("draft_contracts", &draft_contracts); - + render_template(&tmpl, "contracts/index.html", &context) } - + // Display the list of all contracts pub async fn list(tmpl: web::Data) -> Result { let mut context = Context::new(); - + let contracts = Self::get_mock_contracts(); let contracts_data: Vec> = contracts .iter() .map(|c| Self::contract_to_json(c)) .collect(); - + // Add active_page for navigation highlighting context.insert("active_page", &"contracts"); - + context.insert("contracts", &contracts_data); context.insert("filter", &"all"); - + render_template(&tmpl, "contracts/contracts.html", &context) } - + // Display the list of user's contracts pub async fn my_contracts(tmpl: web::Data) -> Result { let mut context = Context::new(); - + let contracts = Self::get_mock_contracts(); let contracts_data: Vec> = contracts .iter() .map(|c| Self::contract_to_json(c)) .collect(); - + // Add active_page for navigation highlighting context.insert("active_page", &"contracts"); - + context.insert("contracts", &contracts_data); - + render_template(&tmpl, "contracts/my_contracts.html", &context) } - + // Display a specific contract pub async fn detail( tmpl: web::Data, path: web::Path, - query: Query> + query: Query>, ) -> Result { let contract_id = path.into_inner(); let mut context = Context::new(); - + // Add active_page for navigation highlighting context.insert("active_page", &"contracts"); - + // Find the contract by ID let contracts = Self::get_mock_contracts(); - + // For demo purposes, if the ID doesn't match exactly, just show the first contract // In a real app, we would return a 404 if the contract is not found let contract = if let Some(found) = contracts.iter().find(|c| c.id == contract_id) { @@ -129,7 +134,7 @@ impl ContractController { // For demo, just use the first contract contracts.first().unwrap() }; - + // Convert contract to JSON let contract_json = Self::contract_to_json(contract); @@ -137,10 +142,13 @@ impl ContractController { context.insert("contract", &contract_json); // If this contract uses multi-page markdown, load the selected section - println!("DEBUG: content_dir = {:?}, toc = {:?}", contract.content_dir, contract.toc); + println!( + "DEBUG: content_dir = {:?}, toc = {:?}", + contract.content_dir, contract.toc + ); if let (Some(content_dir), Some(toc)) = (&contract.content_dir, &contract.toc) { + use pulldown_cmark::{Options, Parser, html}; use std::fs; - use pulldown_cmark::{Parser, Options, html}; // Helper to flatten toc recursively fn flatten_toc<'a>(items: &'a Vec, out: &mut Vec<&'a TocItem>) { for item in items { @@ -154,15 +162,28 @@ impl ContractController { flatten_toc(&toc, &mut flat_toc); let section_param = query.get("section"); let selected_file = section_param - .and_then(|f| flat_toc.iter().find(|item| item.file == *f).map(|item| item.file.clone())) - .unwrap_or_else(|| flat_toc.get(0).map(|item| item.file.clone()).unwrap_or_default()); + .and_then(|f| { + flat_toc + .iter() + .find(|item| item.file == *f) + .map(|item| item.file.clone()) + }) + .unwrap_or_else(|| { + flat_toc + .get(0) + .map(|item| item.file.clone()) + .unwrap_or_default() + }); context.insert("section", &selected_file); let rel_path = format!("{}/{}", content_dir, selected_file); let abs_path = match std::env::current_dir() { Ok(dir) => dir.join(&rel_path), Err(_) => std::path::PathBuf::from(&rel_path), }; - println!("DEBUG: Attempting to read markdown file at absolute path: {:?}", abs_path); + println!( + "DEBUG: Attempting to read markdown file at absolute path: {:?}", + abs_path + ); match fs::read_to_string(&abs_path) { Ok(md) => { println!("DEBUG: Successfully read markdown file"); @@ -170,52 +191,63 @@ impl ContractController { let mut html_output = String::new(); html::push_html(&mut html_output, parser); context.insert("contract_section_content", &html_output); - }, + } Err(e) => { - let error_msg = format!("Error: Could not read contract section markdown at '{:?}': {}", abs_path, e); + let error_msg = format!( + "Error: Could not read contract section markdown at '{:?}': {}", + abs_path, e + ); println!("{}", error_msg); context.insert("contract_section_content_error", &error_msg); } } context.insert("toc", &toc); } - + // Count signed signers for the template - let signed_signers = contract.signers.iter().filter(|s| s.status == SignerStatus::Signed).count(); + let signed_signers = contract + .signers + .iter() + .filter(|s| s.status == SignerStatus::Signed) + .count(); context.insert("signed_signers", &signed_signers); - + // Count pending signers for the template - let pending_signers = contract.signers.iter().filter(|s| s.status == SignerStatus::Pending).count(); + let pending_signers = contract + .signers + .iter() + .filter(|s| s.status == SignerStatus::Pending) + .count(); context.insert("pending_signers", &pending_signers); - + // For demo purposes, set user_has_signed to false // In a real app, we would check if the current user has already signed context.insert("user_has_signed", &false); - + render_template(&tmpl, "contracts/contract_detail.html", &context) } - + // Display the create contract form pub async fn create_form(tmpl: web::Data) -> Result { let mut context = Context::new(); - + // Add active_page for navigation highlighting context.insert("active_page", &"contracts"); - + // Add contract types for dropdown let contract_types = vec![ ("Service", "Service Agreement"), ("Employment", "Employment Contract"), ("NDA", "Non-Disclosure Agreement"), ("SLA", "Service Level Agreement"), - ("Other", "Other") + ("Other", "Other"), ]; - + context.insert("contract_types", &contract_types); - + render_template(&tmpl, "contracts/create_contract.html", &context) } - + // Process the create contract form pub async fn create( _tmpl: web::Data, @@ -223,158 +255,334 @@ impl ContractController { ) -> Result { // In a real application, we would save the contract to the database // For now, we'll just redirect to the contracts list - - Ok(HttpResponse::Found().append_header(("Location", "/contracts")).finish()) + + Ok(HttpResponse::Found() + .append_header(("Location", "/contracts")) + .finish()) } - + // Helper method to convert Contract to a JSON object for templates fn contract_to_json(contract: &Contract) -> serde_json::Map { let mut map = serde_json::Map::new(); - + // Basic contract info - map.insert("id".to_string(), serde_json::Value::String(contract.id.clone())); - map.insert("title".to_string(), serde_json::Value::String(contract.title.clone())); - map.insert("description".to_string(), serde_json::Value::String(contract.description.clone())); - map.insert("status".to_string(), serde_json::Value::String(contract.status.as_str().to_string())); - map.insert("contract_type".to_string(), serde_json::Value::String(contract.contract_type.as_str().to_string())); - map.insert("created_by".to_string(), serde_json::Value::String(contract.created_by.clone())); - map.insert("created_at".to_string(), serde_json::Value::String(contract.created_at.format("%Y-%m-%d").to_string())); - map.insert("updated_at".to_string(), serde_json::Value::String(contract.updated_at.format("%Y-%m-%d").to_string())); - + map.insert( + "id".to_string(), + serde_json::Value::String(contract.id.clone()), + ); + map.insert( + "title".to_string(), + serde_json::Value::String(contract.title.clone()), + ); + map.insert( + "description".to_string(), + serde_json::Value::String(contract.description.clone()), + ); + map.insert( + "status".to_string(), + serde_json::Value::String(contract.status.as_str().to_string()), + ); + map.insert( + "contract_type".to_string(), + serde_json::Value::String(contract.contract_type.as_str().to_string()), + ); + map.insert( + "created_by".to_string(), + serde_json::Value::String(contract.created_by.clone()), + ); + map.insert( + "created_at".to_string(), + serde_json::Value::String(contract.created_at.format("%Y-%m-%d").to_string()), + ); + map.insert( + "updated_at".to_string(), + serde_json::Value::String(contract.updated_at.format("%Y-%m-%d").to_string()), + ); + // Organization info if let Some(org) = &contract.organization_id { - map.insert("organization".to_string(), serde_json::Value::String(org.clone())); + map.insert( + "organization".to_string(), + serde_json::Value::String(org.clone()), + ); } else { map.insert("organization".to_string(), serde_json::Value::Null); } - + // Add signers - let signers: Vec = contract.signers.iter() + let signers: Vec = contract + .signers + .iter() .map(|s| { let mut signer_map = serde_json::Map::new(); signer_map.insert("id".to_string(), serde_json::Value::String(s.id.clone())); - signer_map.insert("name".to_string(), serde_json::Value::String(s.name.clone())); - signer_map.insert("email".to_string(), serde_json::Value::String(s.email.clone())); - signer_map.insert("status".to_string(), serde_json::Value::String(s.status.as_str().to_string())); - + signer_map.insert( + "name".to_string(), + serde_json::Value::String(s.name.clone()), + ); + signer_map.insert( + "email".to_string(), + serde_json::Value::String(s.email.clone()), + ); + signer_map.insert( + "status".to_string(), + serde_json::Value::String(s.status.as_str().to_string()), + ); + if let Some(signed_at) = s.signed_at { - signer_map.insert("signed_at".to_string(), serde_json::Value::String(signed_at.format("%Y-%m-%d").to_string())); + signer_map.insert( + "signed_at".to_string(), + serde_json::Value::String(signed_at.format("%Y-%m-%d").to_string()), + ); } else { // For display purposes, add a placeholder date for pending signers if s.status == SignerStatus::Pending { - signer_map.insert("signed_at".to_string(), serde_json::Value::String("Pending".to_string())); + signer_map.insert( + "signed_at".to_string(), + serde_json::Value::String("Pending".to_string()), + ); } else if s.status == SignerStatus::Rejected { - signer_map.insert("signed_at".to_string(), serde_json::Value::String("Rejected".to_string())); + signer_map.insert( + "signed_at".to_string(), + serde_json::Value::String("Rejected".to_string()), + ); } } - + if let Some(comments) = &s.comments { - signer_map.insert("comments".to_string(), serde_json::Value::String(comments.clone())); + signer_map.insert( + "comments".to_string(), + serde_json::Value::String(comments.clone()), + ); } else { - signer_map.insert("comments".to_string(), serde_json::Value::String("".to_string())); + signer_map.insert( + "comments".to_string(), + serde_json::Value::String("".to_string()), + ); } - + serde_json::Value::Object(signer_map) }) .collect(); - + map.insert("signers".to_string(), serde_json::Value::Array(signers)); - + // Add pending_signers count for templates - let pending_signers = contract.signers.iter().filter(|s| s.status == SignerStatus::Pending).count(); - map.insert("pending_signers".to_string(), serde_json::Value::Number(serde_json::Number::from(pending_signers))); - + let pending_signers = contract + .signers + .iter() + .filter(|s| s.status == SignerStatus::Pending) + .count(); + map.insert( + "pending_signers".to_string(), + serde_json::Value::Number(serde_json::Number::from(pending_signers)), + ); + // Add signed_signers count for templates - let signed_signers = contract.signers.iter().filter(|s| s.status == SignerStatus::Signed).count(); - map.insert("signed_signers".to_string(), serde_json::Value::Number(serde_json::Number::from(signed_signers))); - + let signed_signers = contract + .signers + .iter() + .filter(|s| s.status == SignerStatus::Signed) + .count(); + map.insert( + "signed_signers".to_string(), + serde_json::Value::Number(serde_json::Number::from(signed_signers)), + ); + // Add revisions - let revisions: Vec = contract.revisions.iter() + let revisions: Vec = contract + .revisions + .iter() .map(|r| { let mut revision_map = serde_json::Map::new(); - revision_map.insert("version".to_string(), serde_json::Value::Number(serde_json::Number::from(r.version))); - revision_map.insert("content".to_string(), serde_json::Value::String(r.content.clone())); - revision_map.insert("created_at".to_string(), serde_json::Value::String(r.created_at.format("%Y-%m-%d").to_string())); - revision_map.insert("created_by".to_string(), serde_json::Value::String(r.created_by.clone())); - + revision_map.insert( + "version".to_string(), + serde_json::Value::Number(serde_json::Number::from(r.version)), + ); + revision_map.insert( + "content".to_string(), + serde_json::Value::String(r.content.clone()), + ); + revision_map.insert( + "created_at".to_string(), + serde_json::Value::String(r.created_at.format("%Y-%m-%d").to_string()), + ); + revision_map.insert( + "created_by".to_string(), + serde_json::Value::String(r.created_by.clone()), + ); + if let Some(comments) = &r.comments { - revision_map.insert("comments".to_string(), serde_json::Value::String(comments.clone())); + revision_map.insert( + "comments".to_string(), + serde_json::Value::String(comments.clone()), + ); // Add notes field using comments since ContractRevision doesn't have a notes field - revision_map.insert("notes".to_string(), serde_json::Value::String(comments.clone())); + revision_map.insert( + "notes".to_string(), + serde_json::Value::String(comments.clone()), + ); } else { - revision_map.insert("comments".to_string(), serde_json::Value::String("".to_string())); - revision_map.insert("notes".to_string(), serde_json::Value::String("".to_string())); + revision_map.insert( + "comments".to_string(), + serde_json::Value::String("".to_string()), + ); + revision_map.insert( + "notes".to_string(), + serde_json::Value::String("".to_string()), + ); } - + serde_json::Value::Object(revision_map) }) .collect(); - - map.insert("revisions".to_string(), serde_json::Value::Array(revisions.clone())); - + + map.insert( + "revisions".to_string(), + serde_json::Value::Array(revisions.clone()), + ); + // Add current_version - map.insert("current_version".to_string(), serde_json::Value::Number(serde_json::Number::from(contract.current_version))); - + map.insert( + "current_version".to_string(), + serde_json::Value::Number(serde_json::Number::from(contract.current_version)), + ); + // Add latest_revision as an object if !contract.revisions.is_empty() { // Find the latest revision based on version number if let Some(latest) = contract.revisions.iter().max_by_key(|r| r.version) { let mut latest_revision_map = serde_json::Map::new(); - latest_revision_map.insert("version".to_string(), serde_json::Value::Number(serde_json::Number::from(latest.version))); - latest_revision_map.insert("content".to_string(), serde_json::Value::String(latest.content.clone())); - latest_revision_map.insert("created_at".to_string(), serde_json::Value::String(latest.created_at.format("%Y-%m-%d").to_string())); - latest_revision_map.insert("created_by".to_string(), serde_json::Value::String(latest.created_by.clone())); - + latest_revision_map.insert( + "version".to_string(), + serde_json::Value::Number(serde_json::Number::from(latest.version)), + ); + latest_revision_map.insert( + "content".to_string(), + serde_json::Value::String(latest.content.clone()), + ); + latest_revision_map.insert( + "created_at".to_string(), + serde_json::Value::String(latest.created_at.format("%Y-%m-%d").to_string()), + ); + latest_revision_map.insert( + "created_by".to_string(), + serde_json::Value::String(latest.created_by.clone()), + ); + if let Some(comments) = &latest.comments { - latest_revision_map.insert("comments".to_string(), serde_json::Value::String(comments.clone())); - latest_revision_map.insert("notes".to_string(), serde_json::Value::String(comments.clone())); + latest_revision_map.insert( + "comments".to_string(), + serde_json::Value::String(comments.clone()), + ); + latest_revision_map.insert( + "notes".to_string(), + serde_json::Value::String(comments.clone()), + ); } else { - latest_revision_map.insert("comments".to_string(), serde_json::Value::String("".to_string())); - latest_revision_map.insert("notes".to_string(), serde_json::Value::String("".to_string())); + latest_revision_map.insert( + "comments".to_string(), + serde_json::Value::String("".to_string()), + ); + latest_revision_map.insert( + "notes".to_string(), + serde_json::Value::String("".to_string()), + ); } - - map.insert("latest_revision".to_string(), serde_json::Value::Object(latest_revision_map)); + + map.insert( + "latest_revision".to_string(), + serde_json::Value::Object(latest_revision_map), + ); } else { // Create an empty latest_revision object to avoid template errors let mut empty_revision = serde_json::Map::new(); - empty_revision.insert("version".to_string(), serde_json::Value::Number(serde_json::Number::from(0))); - empty_revision.insert("content".to_string(), serde_json::Value::String("No content available".to_string())); - empty_revision.insert("created_at".to_string(), serde_json::Value::String("N/A".to_string())); - empty_revision.insert("created_by".to_string(), serde_json::Value::String("N/A".to_string())); - empty_revision.insert("comments".to_string(), serde_json::Value::String("".to_string())); - empty_revision.insert("notes".to_string(), serde_json::Value::String("".to_string())); - - map.insert("latest_revision".to_string(), serde_json::Value::Object(empty_revision)); + empty_revision.insert( + "version".to_string(), + serde_json::Value::Number(serde_json::Number::from(0)), + ); + empty_revision.insert( + "content".to_string(), + serde_json::Value::String("No content available".to_string()), + ); + empty_revision.insert( + "created_at".to_string(), + serde_json::Value::String("N/A".to_string()), + ); + empty_revision.insert( + "created_by".to_string(), + serde_json::Value::String("N/A".to_string()), + ); + empty_revision.insert( + "comments".to_string(), + serde_json::Value::String("".to_string()), + ); + empty_revision.insert( + "notes".to_string(), + serde_json::Value::String("".to_string()), + ); + + map.insert( + "latest_revision".to_string(), + serde_json::Value::Object(empty_revision), + ); } } else { // Create an empty latest_revision object to avoid template errors let mut empty_revision = serde_json::Map::new(); - empty_revision.insert("version".to_string(), serde_json::Value::Number(serde_json::Number::from(0))); - empty_revision.insert("content".to_string(), serde_json::Value::String("No content available".to_string())); - empty_revision.insert("created_at".to_string(), serde_json::Value::String("N/A".to_string())); - empty_revision.insert("created_by".to_string(), serde_json::Value::String("N/A".to_string())); - empty_revision.insert("comments".to_string(), serde_json::Value::String("".to_string())); - empty_revision.insert("notes".to_string(), serde_json::Value::String("".to_string())); - - map.insert("latest_revision".to_string(), serde_json::Value::Object(empty_revision)); + empty_revision.insert( + "version".to_string(), + serde_json::Value::Number(serde_json::Number::from(0)), + ); + empty_revision.insert( + "content".to_string(), + serde_json::Value::String("No content available".to_string()), + ); + empty_revision.insert( + "created_at".to_string(), + serde_json::Value::String("N/A".to_string()), + ); + empty_revision.insert( + "created_by".to_string(), + serde_json::Value::String("N/A".to_string()), + ); + empty_revision.insert( + "comments".to_string(), + serde_json::Value::String("".to_string()), + ); + empty_revision.insert( + "notes".to_string(), + serde_json::Value::String("".to_string()), + ); + + map.insert( + "latest_revision".to_string(), + serde_json::Value::Object(empty_revision), + ); } - + // Add effective and expiration dates if present if let Some(effective_date) = &contract.effective_date { - map.insert("effective_date".to_string(), serde_json::Value::String(effective_date.format("%Y-%m-%d").to_string())); + map.insert( + "effective_date".to_string(), + serde_json::Value::String(effective_date.format("%Y-%m-%d").to_string()), + ); } - + if let Some(expiration_date) = &contract.expiration_date { - map.insert("expiration_date".to_string(), serde_json::Value::String(expiration_date.format("%Y-%m-%d").to_string())); + map.insert( + "expiration_date".to_string(), + serde_json::Value::String(expiration_date.format("%Y-%m-%d").to_string()), + ); } - + map } - + // Generate mock contracts for testing fn get_mock_contracts() -> Vec { let mut contracts = Vec::new(); - + // Mock contract 1 - Signed Service Agreement let mut contract1 = Contract { content_dir: None, @@ -394,7 +602,7 @@ impl ContractController { revisions: Vec::new(), current_version: 2, }; - + // Add signers to contract 1 contract1.signers.push(ContractSigner { id: "signer-001".to_string(), @@ -404,7 +612,7 @@ impl ContractController { signed_at: Some(Utc::now() - Duration::days(5)), comments: Some("Approved as per our discussion.".to_string()), }); - + contract1.signers.push(ContractSigner { id: "signer-002".to_string(), name: "Nala Okafor".to_string(), @@ -413,7 +621,7 @@ impl ContractController { signed_at: Some(Utc::now() - Duration::days(6)), comments: Some("Terms look good. Happy to proceed.".to_string()), }); - + // Add revisions to contract 1 contract1.revisions.push(ContractRevision { version: 1, @@ -422,7 +630,7 @@ impl ContractController { created_by: "Wei Chen".to_string(), comments: Some("Initial draft of the service agreement.".to_string()), }); - + contract1.revisions.push(ContractRevision { version: 2, content: "

Digital Hub Service Agreement

This Service Agreement (the \"Agreement\") is entered into between Zanzibar Digital Hub (\"Provider\") and the undersigned client (\"Client\").

1. Services

Provider agrees to provide Client with cloud hosting and digital infrastructure services as specified in Appendix A.

2. Term

This Agreement shall commence on the Effective Date and continue for a period of one (1) year unless terminated earlier in accordance with the terms herein.

3. Fees

Client agrees to pay Provider the fees set forth in Appendix B. All fees are due within thirty (30) days of invoice date.

4. Confidentiality

Each party agrees to maintain the confidentiality of any proprietary information received from the other party during the term of this Agreement.

5. Data Protection

Provider shall implement appropriate technical and organizational measures to ensure a level of security appropriate to the risk, including encryption of personal data, and shall comply with all applicable data protection laws.

".to_string(), @@ -430,7 +638,7 @@ impl ContractController { created_by: "Wei Chen".to_string(), comments: Some("Added data protection clause as requested by legal.".to_string()), }); - + // Mock contract 2 - Pending Signatures let mut contract2 = Contract { content_dir: None, @@ -450,7 +658,7 @@ impl ContractController { revisions: Vec::new(), current_version: 1, }; - + // Add signers to contract 2 contract2.signers.push(ContractSigner { id: "signer-003".to_string(), @@ -460,7 +668,7 @@ impl ContractController { signed_at: Some(Utc::now() - Duration::days(2)), comments: None, }); - + contract2.signers.push(ContractSigner { id: "signer-004".to_string(), name: "Maya Rodriguez".to_string(), @@ -469,7 +677,7 @@ impl ContractController { signed_at: None, comments: None, }); - + contract2.signers.push(ContractSigner { id: "signer-005".to_string(), name: "Jamal Washington".to_string(), @@ -478,7 +686,7 @@ impl ContractController { signed_at: None, comments: None, }); - + // Add revisions to contract 2 contract2.revisions.push(ContractRevision { version: 1, @@ -487,7 +695,7 @@ impl ContractController { created_by: "Dr. Raj Patel".to_string(), comments: Some("Initial draft of the development agreement.".to_string()), }); - + // Mock contract 3 - Draft let mut contract3 = Contract { id: "contract-003".to_string(), @@ -554,7 +762,6 @@ impl ContractController { ]), }; - // Add potential signers to contract 3 (still in draft) contract3.signers.push(ContractSigner { id: "signer-006".to_string(), @@ -564,7 +771,7 @@ impl ContractController { signed_at: None, comments: None, }); - + contract3.signers.push(ContractSigner { id: "signer-007".to_string(), name: "Ibrahim Al-Farsi".to_string(), @@ -573,59 +780,57 @@ impl ContractController { signed_at: None, comments: None, }); - + // Add ToC and content directory to contract 3 contract3.content_dir = Some("src/content/contract-003".to_string()); - contract3.toc = Some(vec![ - TocItem { - title: "Digital Asset Tokenization Agreement".to_string(), - file: "cover.md".to_string(), - children: vec![ - TocItem { - title: "1. Purpose".to_string(), - file: "1-purpose.md".to_string(), - children: vec![], - }, - TocItem { - title: "2. Tokenization Process".to_string(), - file: "2-tokenization-process.md".to_string(), - children: vec![], - }, - TocItem { - title: "3. Revenue Sharing".to_string(), - file: "3-revenue-sharing.md".to_string(), - children: vec![], - }, - TocItem { - title: "4. Governance".to_string(), - file: "4-governance.md".to_string(), - children: vec![], - }, - TocItem { - title: "Appendix A: Properties".to_string(), - file: "appendix-a.md".to_string(), - children: vec![], - }, - TocItem { - title: "Appendix B: Specifications".to_string(), - file: "appendix-b.md".to_string(), - children: vec![], - }, - TocItem { - title: "Appendix C: Revenue Formula".to_string(), - file: "appendix-c.md".to_string(), - children: vec![], - }, - TocItem { - title: "Appendix D: Governance Framework".to_string(), - file: "appendix-d.md".to_string(), - children: vec![], - }, - ], - } - ]); + contract3.toc = Some(vec![TocItem { + title: "Digital Asset Tokenization Agreement".to_string(), + file: "cover.md".to_string(), + children: vec![ + TocItem { + title: "1. Purpose".to_string(), + file: "1-purpose.md".to_string(), + children: vec![], + }, + TocItem { + title: "2. Tokenization Process".to_string(), + file: "2-tokenization-process.md".to_string(), + children: vec![], + }, + TocItem { + title: "3. Revenue Sharing".to_string(), + file: "3-revenue-sharing.md".to_string(), + children: vec![], + }, + TocItem { + title: "4. Governance".to_string(), + file: "4-governance.md".to_string(), + children: vec![], + }, + TocItem { + title: "Appendix A: Properties".to_string(), + file: "appendix-a.md".to_string(), + children: vec![], + }, + TocItem { + title: "Appendix B: Specifications".to_string(), + file: "appendix-b.md".to_string(), + children: vec![], + }, + TocItem { + title: "Appendix C: Revenue Formula".to_string(), + file: "appendix-c.md".to_string(), + children: vec![], + }, + TocItem { + title: "Appendix D: Governance Framework".to_string(), + file: "appendix-d.md".to_string(), + children: vec![], + }, + ], + }]); // No revision content for contract 3, content is in markdown files. - + // Mock contract 4 - Rejected let mut contract4 = Contract { content_dir: None, @@ -645,7 +850,7 @@ impl ContractController { revisions: Vec::new(), current_version: 1, }; - + // Add signers to contract 4 with a rejection contract4.signers.push(ContractSigner { id: "signer-008".to_string(), @@ -655,7 +860,7 @@ impl ContractController { signed_at: Some(Utc::now() - Duration::days(10)), comments: None, }); - + contract4.signers.push(ContractSigner { id: "signer-009".to_string(), name: "Dr. Amina Diallo".to_string(), @@ -664,7 +869,7 @@ impl ContractController { signed_at: Some(Utc::now() - Duration::days(8)), comments: Some("Cannot agree to these terms due to privacy concerns. Please revise section 3.2 regarding data retention.".to_string()), }); - + // Add revisions to contract 4 contract4.revisions.push(ContractRevision { version: 1, @@ -673,7 +878,7 @@ impl ContractController { created_by: "Wei Chen".to_string(), comments: Some("Initial draft of the data sharing agreement.".to_string()), }); - + // Mock contract 5 - Active let mut contract5 = Contract { content_dir: None, @@ -693,7 +898,7 @@ impl ContractController { revisions: Vec::new(), current_version: 2, }; - + // Add signers to contract 5 contract5.signers.push(ContractSigner { id: "signer-010".to_string(), @@ -703,7 +908,7 @@ impl ContractController { signed_at: Some(Utc::now() - Duration::days(47)), comments: None, }); - + contract5.signers.push(ContractSigner { id: "signer-011".to_string(), name: "Li Wei".to_string(), @@ -712,7 +917,7 @@ impl ContractController { signed_at: Some(Utc::now() - Duration::days(45)), comments: Some("Approved after legal review.".to_string()), }); - + // Add revisions to contract 5 contract5.revisions.push(ContractRevision { version: 1, @@ -721,7 +926,7 @@ impl ContractController { created_by: "Maya Rodriguez".to_string(), comments: Some("Initial draft of the identity verification service agreement.".to_string()), }); - + contract5.revisions.push(ContractRevision { version: 2, content: "

Digital Identity Verification Service Agreement

This Service Agreement (the \"Agreement\") is entered into between Zanzibar Digital Hub (\"Provider\") and the businesses listed in Appendix A (\"Clients\").

1. Services

Provider agrees to provide Clients with digital identity verification services as specified in Appendix B.

2. Term

This Agreement shall commence on the Effective Date and continue for a period of one (1) year unless terminated earlier in accordance with the terms herein.

3. Fees

Clients agree to pay Provider the fees set forth in Appendix C. All fees are due within thirty (30) days of invoice date.

4. Service Level Agreement

Provider shall maintain a service uptime of at least 99.9% as measured on a monthly basis.

5. Compliance

Provider shall comply with all applicable laws and regulations regarding identity verification and data protection, including but not limited to the Zanzibar Digital Economy Act.

".to_string(), @@ -729,14 +934,14 @@ impl ContractController { created_by: "Maya Rodriguez".to_string(), comments: Some("Added compliance clause as requested by legal.".to_string()), }); - + // Add all contracts to the vector contracts.push(contract1); contracts.push(contract2); contracts.push(contract3); contracts.push(contract4); contracts.push(contract5); - + contracts } } diff --git a/actix_mvc_app/src/controllers/defi.rs b/actix_mvc_app/src/controllers/defi.rs index ec74d0b..7a275ad 100644 --- a/actix_mvc_app/src/controllers/defi.rs +++ b/actix_mvc_app/src/controllers/defi.rs @@ -1,12 +1,15 @@ -use actix_web::{web, HttpResponse, Result}; use actix_web::HttpRequest; -use tera::{Context, Tera}; -use chrono::{Utc, Duration}; +use actix_web::{HttpResponse, Result, web}; +use chrono::{Duration, Utc}; use serde::Deserialize; +use tera::{Context, Tera}; use uuid::Uuid; -use crate::models::asset::{Asset, AssetType, AssetStatus}; -use crate::models::defi::{DefiPosition, DefiPositionType, DefiPositionStatus, ProvidingPosition, ReceivingPosition, DEFI_DB}; +use crate::models::asset::Asset; +use crate::models::defi::{ + DEFI_DB, DefiPosition, DefiPositionStatus, DefiPositionType, ProvidingPosition, + ReceivingPosition, +}; use crate::utils::render_template; // Form structs for DeFi operations @@ -26,6 +29,7 @@ pub struct ReceivingForm { } #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct LiquidityForm { pub first_token: String, pub first_amount: f64, @@ -35,6 +39,7 @@ pub struct LiquidityForm { } #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct StakingForm { pub asset_id: String, pub amount: f64, @@ -49,6 +54,7 @@ pub struct SwapForm { } #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct CollateralForm { pub asset_id: String, pub amount: f64, @@ -63,29 +69,29 @@ impl DefiController { // Display the DeFi dashboard pub async fn index(tmpl: web::Data, req: HttpRequest) -> Result { let mut context = Context::new(); - + println!("DEBUG: Starting DeFi dashboard rendering"); - + // Get mock assets for the dropdown selectors let assets = Self::get_mock_assets(); println!("DEBUG: Generated {} mock assets", assets.len()); - + // Add active_page for navigation highlighting context.insert("active_page", &"defi"); - + // Add DeFi stats let defi_stats = Self::get_defi_stats(); context.insert("defi_stats", &serde_json::to_value(defi_stats).unwrap()); - + // Add recent assets for selection in forms let recent_assets: Vec> = assets .iter() .take(5) .map(|a| Self::asset_to_json(a)) .collect(); - + context.insert("recent_assets", &recent_assets); - + // Get user's providing positions let db = DEFI_DB.lock().unwrap(); let providing_positions = db.get_user_providing_positions("user123"); @@ -94,7 +100,7 @@ impl DefiController { .map(|p| serde_json::to_value(p).unwrap()) .collect(); context.insert("providing_positions", &providing_positions_json); - + // Get user's receiving positions let receiving_positions = db.get_user_receiving_positions("user123"); let receiving_positions_json: Vec = receiving_positions @@ -102,27 +108,30 @@ impl DefiController { .map(|p| serde_json::to_value(p).unwrap()) .collect(); context.insert("receiving_positions", &receiving_positions_json); - + // Add success message if present in query params if let Some(success) = req.query_string().strip_prefix("success=") { let decoded = urlencoding::decode(success).unwrap_or_else(|_| success.into()); context.insert("success_message", &decoded); } - + println!("DEBUG: Rendering DeFi dashboard template"); let response = render_template(&tmpl, "defi/index.html", &context); println!("DEBUG: Finished rendering DeFi dashboard template"); response } - + // Process providing request - pub async fn create_providing(_tmpl: web::Data, form: web::Form) -> Result { + pub async fn create_providing( + _tmpl: web::Data, + form: web::Form, + ) -> Result { println!("DEBUG: Processing providing request: {:?}", form); - + // Get the asset obligationails (in a real app, this would come from a database) let assets = Self::get_mock_assets(); let asset = assets.iter().find(|a| a.id == form.asset_id); - + if let Some(asset) = asset { // Calculate profit share and return amount let profit_share = match form.duration { @@ -133,9 +142,10 @@ impl DefiController { 365 => 12.0, _ => 4.2, // Default to 30 days rate }; - - let return_amount = form.amount + (form.amount * (profit_share / 100.0) * (form.duration as f64 / 365.0)); - + + let return_amount = form.amount + + (form.amount * (profit_share / 100.0) * (form.duration as f64 / 365.0)); + // Create a new providing position let providing_position = ProvidingPosition { base: DefiPosition { @@ -156,17 +166,23 @@ impl DefiController { profit_share_earned: profit_share, return_amount, }; - + // Add the position to the database { let mut db = DEFI_DB.lock().unwrap(); db.add_providing_position(providing_position); } - + // Redirect with success message - let success_message = format!("Successfully provided {} {} for {} days", form.amount, asset.name, form.duration); + let success_message = format!( + "Successfully provided {} {} for {} days", + form.amount, asset.name, form.duration + ); Ok(HttpResponse::SeeOther() - .append_header(("Location", format!("/defi?success={}", urlencoding::encode(&success_message)))) + .append_header(( + "Location", + format!("/defi?success={}", urlencoding::encode(&success_message)), + )) .finish()) } else { // Asset not found, redirect with error @@ -175,15 +191,18 @@ impl DefiController { .finish()) } } - + // Process receiving request - pub async fn create_receiving(_tmpl: web::Data, form: web::Form) -> Result { + pub async fn create_receiving( + _tmpl: web::Data, + form: web::Form, + ) -> Result { println!("DEBUG: Processing receiving request: {:?}", form); - + // Get the asset obligationails (in a real app, this would come from a database) let assets = Self::get_mock_assets(); let collateral_asset = assets.iter().find(|a| a.id == form.collateral_asset_id); - + if let Some(collateral_asset) = collateral_asset { // Calculate profit share rate based on duration let profit_share_rate = match form.duration { @@ -194,15 +213,17 @@ impl DefiController { 365 => 10.0, _ => 5.0, // Default to 30 days rate }; - + // Calculate profit share and total to repay - let profit_share = form.amount * (profit_share_rate / 100.0) * (form.duration as f64 / 365.0); + let profit_share = + form.amount * (profit_share_rate / 100.0) * (form.duration as f64 / 365.0); let total_to_repay = form.amount + profit_share; - + // Calculate collateral value and ratio - let collateral_value = form.collateral_amount * collateral_asset.latest_valuation().map_or(0.5, |v| v.value); + let collateral_value = form.collateral_amount + * collateral_asset.latest_valuation().map_or(0.5, |v| v.value); let collateral_ratio = (collateral_value / form.amount) * 100.0; - + // Create a new receiving position let receiving_position = ReceivingPosition { base: DefiPosition { @@ -230,18 +251,23 @@ impl DefiController { total_to_repay, collateral_ratio, }; - + // Add the position to the database { let mut db = DEFI_DB.lock().unwrap(); db.add_receiving_position(receiving_position); } - + // Redirect with success message - let success_message = format!("Successfully borrowed {} ZDFZ using {} {} as collateral", - form.amount, form.collateral_amount, collateral_asset.name); + let success_message = format!( + "Successfully borrowed {} ZDFZ using {} {} as collateral", + form.amount, form.collateral_amount, collateral_asset.name + ); Ok(HttpResponse::SeeOther() - .append_header(("Location", format!("/defi?success={}", urlencoding::encode(&success_message)))) + .append_header(( + "Location", + format!("/defi?success={}", urlencoding::encode(&success_message)), + )) .finish()) } else { // Asset not found, redirect with error @@ -250,116 +276,202 @@ impl DefiController { .finish()) } } - + // Process liquidity provision - pub async fn add_liquidity(_tmpl: web::Data, form: web::Form) -> Result { + pub async fn add_liquidity( + _tmpl: web::Data, + form: web::Form, + ) -> Result { println!("DEBUG: Processing liquidity provision: {:?}", form); - + // In a real application, this would add liquidity to a pool in the database // For now, we'll just redirect back to the DeFi dashboard with a success message - - let success_message = format!("Successfully added liquidity: {} {} and {} {}", - form.first_amount, form.first_token, form.second_amount, form.second_token); - + + let success_message = format!( + "Successfully added liquidity: {} {} and {} {}", + form.first_amount, form.first_token, form.second_amount, form.second_token + ); + Ok(HttpResponse::SeeOther() - .append_header(("Location", format!("/defi?success={}", urlencoding::encode(&success_message)))) + .append_header(( + "Location", + format!("/defi?success={}", urlencoding::encode(&success_message)), + )) .finish()) } - + // Process staking request - pub async fn create_staking(_tmpl: web::Data, form: web::Form) -> Result { + pub async fn create_staking( + _tmpl: web::Data, + form: web::Form, + ) -> Result { println!("DEBUG: Processing staking request: {:?}", form); - + // In a real application, this would create a staking position in the database // For now, we'll just redirect back to the DeFi dashboard with a success message - + let success_message = format!("Successfully staked {} {}", form.amount, form.asset_id); - + Ok(HttpResponse::SeeOther() - .append_header(("Location", format!("/defi?success={}", urlencoding::encode(&success_message)))) + .append_header(( + "Location", + format!("/defi?success={}", urlencoding::encode(&success_message)), + )) .finish()) } - + // Process token swap - pub async fn swap_tokens(_tmpl: web::Data, form: web::Form) -> Result { + pub async fn swap_tokens( + _tmpl: web::Data, + form: web::Form, + ) -> Result { println!("DEBUG: Processing token swap: {:?}", form); - + // In a real application, this would perform a token swap in the database // For now, we'll just redirect back to the DeFi dashboard with a success message - - let success_message = format!("Successfully swapped {} {} to {}", - form.from_amount, form.from_token, form.to_token); - + + let success_message = format!( + "Successfully swapped {} {} to {}", + form.from_amount, form.from_token, form.to_token + ); + Ok(HttpResponse::SeeOther() - .append_header(("Location", format!("/defi?success={}", urlencoding::encode(&success_message)))) + .append_header(( + "Location", + format!("/defi?success={}", urlencoding::encode(&success_message)), + )) .finish()) } - + // Process collateral position creation - pub async fn create_collateral(_tmpl: web::Data, form: web::Form) -> Result { + pub async fn create_collateral( + _tmpl: web::Data, + form: web::Form, + ) -> Result { println!("DEBUG: Processing collateral creation: {:?}", form); - + // In a real application, this would create a collateral position in the database // For now, we'll just redirect back to the DeFi dashboard with a success message - + let purpose_str = match form.purpose.as_str() { "funds" => "secure a funds", "synthetic" => "generate synthetic assets", "leverage" => "leverage trading", _ => "collateralization", }; - - let success_message = format!("Successfully collateralized {} {} for {}", - form.amount, form.asset_id, purpose_str); - + + let success_message = format!( + "Successfully collateralized {} {} for {}", + form.amount, form.asset_id, purpose_str + ); + Ok(HttpResponse::SeeOther() - .append_header(("Location", format!("/defi?success={}", urlencoding::encode(&success_message)))) + .append_header(( + "Location", + format!("/defi?success={}", urlencoding::encode(&success_message)), + )) .finish()) } - + // Helper method to get DeFi statistics fn get_defi_stats() -> serde_json::Map { let mut stats = serde_json::Map::new(); - + // Handle Option by unwrapping with expect - stats.insert("total_value_locked".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(1250000.0).expect("Valid float"))); - stats.insert("providing_volume".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(450000.0).expect("Valid float"))); - stats.insert("receiving_volume".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(320000.0).expect("Valid float"))); - stats.insert("liquidity_pools_count".to_string(), serde_json::Value::Number(serde_json::Number::from(12))); - stats.insert("active_stakers".to_string(), serde_json::Value::Number(serde_json::Number::from(156))); - stats.insert("total_swap_volume".to_string(), serde_json::Value::Number(serde_json::Number::from_f64(780000.0).expect("Valid float"))); - + stats.insert( + "total_value_locked".to_string(), + serde_json::Value::Number( + serde_json::Number::from_f64(1250000.0).expect("Valid float"), + ), + ); + stats.insert( + "providing_volume".to_string(), + serde_json::Value::Number(serde_json::Number::from_f64(450000.0).expect("Valid float")), + ); + stats.insert( + "receiving_volume".to_string(), + serde_json::Value::Number(serde_json::Number::from_f64(320000.0).expect("Valid float")), + ); + stats.insert( + "liquidity_pools_count".to_string(), + serde_json::Value::Number(serde_json::Number::from(12)), + ); + stats.insert( + "active_stakers".to_string(), + serde_json::Value::Number(serde_json::Number::from(156)), + ); + stats.insert( + "total_swap_volume".to_string(), + serde_json::Value::Number(serde_json::Number::from_f64(780000.0).expect("Valid float")), + ); + stats } - + // Helper method to convert Asset to a JSON object for templates fn asset_to_json(asset: &Asset) -> serde_json::Map { let mut map = serde_json::Map::new(); - - map.insert("id".to_string(), serde_json::Value::String(asset.id.clone())); - map.insert("name".to_string(), serde_json::Value::String(asset.name.clone())); - map.insert("description".to_string(), serde_json::Value::String(asset.description.clone())); - map.insert("asset_type".to_string(), serde_json::Value::String(asset.asset_type.as_str().to_string())); - map.insert("status".to_string(), serde_json::Value::String(asset.status.as_str().to_string())); - + + map.insert( + "id".to_string(), + serde_json::Value::String(asset.id.clone()), + ); + map.insert( + "name".to_string(), + serde_json::Value::String(asset.name.clone()), + ); + map.insert( + "description".to_string(), + serde_json::Value::String(asset.description.clone()), + ); + map.insert( + "asset_type".to_string(), + serde_json::Value::String(asset.asset_type.as_str().to_string()), + ); + map.insert( + "status".to_string(), + serde_json::Value::String(asset.status.as_str().to_string()), + ); + // Add current valuation if let Some(latest) = asset.latest_valuation() { if let Some(num) = serde_json::Number::from_f64(latest.value) { - map.insert("current_valuation".to_string(), serde_json::Value::Number(num)); + map.insert( + "current_valuation".to_string(), + serde_json::Value::Number(num), + ); } else { - map.insert("current_valuation".to_string(), serde_json::Value::Number(serde_json::Number::from(0))); + map.insert( + "current_valuation".to_string(), + serde_json::Value::Number(serde_json::Number::from(0)), + ); } - map.insert("valuation_currency".to_string(), serde_json::Value::String(latest.currency.clone())); - map.insert("valuation_date".to_string(), serde_json::Value::String(latest.date.format("%Y-%m-%d").to_string())); + map.insert( + "valuation_currency".to_string(), + serde_json::Value::String(latest.currency.clone()), + ); + map.insert( + "valuation_date".to_string(), + serde_json::Value::String(latest.date.format("%Y-%m-%d").to_string()), + ); } else { - map.insert("current_valuation".to_string(), serde_json::Value::Number(serde_json::Number::from(0))); - map.insert("valuation_currency".to_string(), serde_json::Value::String("USD".to_string())); - map.insert("valuation_date".to_string(), serde_json::Value::String("N/A".to_string())); + map.insert( + "current_valuation".to_string(), + serde_json::Value::Number(serde_json::Number::from(0)), + ); + map.insert( + "valuation_currency".to_string(), + serde_json::Value::String("USD".to_string()), + ); + map.insert( + "valuation_date".to_string(), + serde_json::Value::String("N/A".to_string()), + ); } - + map } - + // Generate mock assets for testing fn get_mock_assets() -> Vec { // Reuse the asset controller's mock data function diff --git a/actix_mvc_app/src/controllers/flow.rs b/actix_mvc_app/src/controllers/flow.rs index 0757448..4bf4c0d 100644 --- a/actix_mvc_app/src/controllers/flow.rs +++ b/actix_mvc_app/src/controllers/flow.rs @@ -609,6 +609,7 @@ impl FlowController { /// Form for creating a new flow #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct FlowForm { /// Flow name pub name: String, @@ -620,6 +621,7 @@ pub struct FlowForm { /// Form for marking a step as stuck #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct StuckForm { /// Reason for being stuck pub reason: String, @@ -627,6 +629,7 @@ pub struct StuckForm { /// Form for adding a log to a step #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct LogForm { /// Log message pub message: String, diff --git a/actix_mvc_app/src/controllers/governance.rs b/actix_mvc_app/src/controllers/governance.rs index be2b9f4..8526ee6 100644 --- a/actix_mvc_app/src/controllers/governance.rs +++ b/actix_mvc_app/src/controllers/governance.rs @@ -13,6 +13,7 @@ use chrono::prelude::*; /// Controller for handling governance-related routes pub struct GovernanceController; +#[allow(dead_code)] impl GovernanceController { /// Helper function to get user from session /// For testing purposes, this will always return a mock user @@ -607,6 +608,7 @@ pub struct ProposalForm { /// Represents the data submitted in the vote form #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct VoteForm { /// Type of vote (yes, no, abstain) pub vote_type: String, diff --git a/actix_mvc_app/src/controllers/home.rs b/actix_mvc_app/src/controllers/home.rs index 166f2e5..bf8bf56 100644 --- a/actix_mvc_app/src/controllers/home.rs +++ b/actix_mvc_app/src/controllers/home.rs @@ -96,6 +96,7 @@ impl HomeController { /// Represents the data submitted in the contact form #[derive(Debug, serde::Deserialize)] +#[allow(dead_code)] pub struct ContactForm { pub name: String, pub email: String, diff --git a/actix_mvc_app/src/controllers/marketplace.rs b/actix_mvc_app/src/controllers/marketplace.rs index f7e3f83..72df6a2 100644 --- a/actix_mvc_app/src/controllers/marketplace.rs +++ b/actix_mvc_app/src/controllers/marketplace.rs @@ -1,12 +1,11 @@ -use actix_web::{web, HttpResponse, Result, http}; -use tera::{Context, Tera}; -use chrono::{Utc, Duration}; +use actix_web::{HttpResponse, Result, http, web}; +use chrono::{Duration, Utc}; use serde::Deserialize; -use uuid::Uuid; +use tera::{Context, Tera}; -use crate::models::asset::{Asset, AssetType, AssetStatus}; -use crate::models::marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus, MarketplaceStatistics}; use crate::controllers::asset::AssetController; +use crate::models::asset::{Asset, AssetStatus, AssetType}; +use crate::models::marketplace::{Listing, ListingStatus, ListingType, MarketplaceStatistics}; use crate::utils::render_template; #[derive(Debug, Deserialize)] @@ -22,6 +21,7 @@ pub struct ListingForm { } #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct BidForm { pub amount: f64, pub currency: String, @@ -38,30 +38,33 @@ impl MarketplaceController { // Display the marketplace dashboard pub async fn index(tmpl: web::Data) -> Result { let mut context = Context::new(); - + let listings = Self::get_mock_listings(); let stats = MarketplaceStatistics::new(&listings); - + // Get featured listings (up to 4) - let featured_listings: Vec<&Listing> = listings.iter() + let featured_listings: Vec<&Listing> = listings + .iter() .filter(|l| l.featured && l.status == ListingStatus::Active) .take(4) .collect(); - + // Get recent listings (up to 8) - let mut recent_listings: Vec<&Listing> = listings.iter() + let mut recent_listings: Vec<&Listing> = listings + .iter() .filter(|l| l.status == ListingStatus::Active) .collect(); - + // Sort by created_at (newest first) recent_listings.sort_by(|a, b| b.created_at.cmp(&a.created_at)); let recent_listings = recent_listings.into_iter().take(8).collect::>(); - + // Get recent sales (up to 5) - let mut recent_sales: Vec<&Listing> = listings.iter() + let mut recent_sales: Vec<&Listing> = listings + .iter() .filter(|l| l.status == ListingStatus::Sold) .collect(); - + // Sort by sold_at (newest first) recent_sales.sort_by(|a, b| { let a_sold = a.sold_at.unwrap_or(a.created_at); @@ -69,88 +72,101 @@ impl MarketplaceController { b_sold.cmp(&a_sold) }); let recent_sales = recent_sales.into_iter().take(5).collect::>(); - + // Add data to context context.insert("active_page", &"marketplace"); context.insert("stats", &stats); context.insert("featured_listings", &featured_listings); context.insert("recent_listings", &recent_listings); context.insert("recent_sales", &recent_sales); - + render_template(&tmpl, "marketplace/index.html", &context) } - + // Display all marketplace listings pub async fn list_listings(tmpl: web::Data) -> Result { let mut context = Context::new(); - + let listings = Self::get_mock_listings(); - + // Filter active listings - let active_listings: Vec<&Listing> = listings.iter() + let active_listings: Vec<&Listing> = listings + .iter() .filter(|l| l.status == ListingStatus::Active) .collect(); - + context.insert("active_page", &"marketplace"); context.insert("listings", &active_listings); - context.insert("listing_types", &[ - ListingType::FixedPrice.as_str(), - ListingType::Auction.as_str(), - ListingType::Exchange.as_str(), - ]); - context.insert("asset_types", &[ - AssetType::Token.as_str(), - AssetType::Artwork.as_str(), - AssetType::RealEstate.as_str(), - AssetType::IntellectualProperty.as_str(), - AssetType::Commodity.as_str(), - AssetType::Share.as_str(), - AssetType::Bond.as_str(), - AssetType::Other.as_str(), - ]); - + context.insert( + "listing_types", + &[ + ListingType::FixedPrice.as_str(), + ListingType::Auction.as_str(), + ListingType::Exchange.as_str(), + ], + ); + context.insert( + "asset_types", + &[ + AssetType::Token.as_str(), + AssetType::Artwork.as_str(), + AssetType::RealEstate.as_str(), + AssetType::IntellectualProperty.as_str(), + AssetType::Commodity.as_str(), + AssetType::Share.as_str(), + AssetType::Bond.as_str(), + AssetType::Other.as_str(), + ], + ); + render_template(&tmpl, "marketplace/listings.html", &context) } - + // Display my listings pub async fn my_listings(tmpl: web::Data) -> Result { let mut context = Context::new(); - + let listings = Self::get_mock_listings(); - + // Filter by current user (mock user ID) let user_id = "user-123"; - let my_listings: Vec<&Listing> = listings.iter() - .filter(|l| l.seller_id == user_id) - .collect(); - + let my_listings: Vec<&Listing> = + listings.iter().filter(|l| l.seller_id == user_id).collect(); + context.insert("active_page", &"marketplace"); context.insert("listings", &my_listings); - + render_template(&tmpl, "marketplace/my_listings.html", &context) } - + // Display listing details - pub async fn listing_detail(tmpl: web::Data, path: web::Path) -> Result { + pub async fn listing_detail( + tmpl: web::Data, + path: web::Path, + ) -> Result { let listing_id = path.into_inner(); let mut context = Context::new(); - + let listings = Self::get_mock_listings(); - + // Find the listing let listing = listings.iter().find(|l| l.id == listing_id); - + if let Some(listing) = listing { // Get similar listings (same asset type, active) - let similar_listings: Vec<&Listing> = listings.iter() - .filter(|l| l.asset_type == listing.asset_type && - l.status == ListingStatus::Active && - l.id != listing.id) + let similar_listings: Vec<&Listing> = listings + .iter() + .filter(|l| { + l.asset_type == listing.asset_type + && l.status == ListingStatus::Active + && l.id != listing.id + }) .take(4) .collect(); - + // Get highest bid amount and minimum bid for auction listings - let (highest_bid_amount, minimum_bid) = if listing.listing_type == ListingType::Auction { + let (highest_bid_amount, minimum_bid) = if listing.listing_type == ListingType::Auction + { if let Some(bid) = listing.highest_bid() { (Some(bid.amount), bid.amount + 1.0) } else { @@ -159,74 +175,79 @@ impl MarketplaceController { } else { (None, 0.0) }; - + context.insert("active_page", &"marketplace"); context.insert("listing", listing); context.insert("similar_listings", &similar_listings); context.insert("highest_bid_amount", &highest_bid_amount); context.insert("minimum_bid", &minimum_bid); - + // Add current user info for bid/purchase forms let user_id = "user-123"; let user_name = "Alice Hostly"; context.insert("user_id", &user_id); context.insert("user_name", &user_name); - + render_template(&tmpl, "marketplace/listing_detail.html", &context) } else { Ok(HttpResponse::NotFound().finish()) } } - + // Display create listing form pub async fn create_listing_form(tmpl: web::Data) -> Result { let mut context = Context::new(); - + // Get user's assets for selection let assets = AssetController::get_mock_assets(); let user_id = "user-123"; // Mock user ID - - let user_assets: Vec<&Asset> = assets.iter() + + let user_assets: Vec<&Asset> = assets + .iter() .filter(|a| a.owner_id == user_id && a.status == AssetStatus::Active) .collect(); - + context.insert("active_page", &"marketplace"); context.insert("assets", &user_assets); - context.insert("listing_types", &[ - ListingType::FixedPrice.as_str(), - ListingType::Auction.as_str(), - ListingType::Exchange.as_str(), - ]); - + context.insert( + "listing_types", + &[ + ListingType::FixedPrice.as_str(), + ListingType::Auction.as_str(), + ListingType::Exchange.as_str(), + ], + ); + render_template(&tmpl, "marketplace/create_listing.html", &context) } - + // Create a new listing pub async fn create_listing( tmpl: web::Data, form: web::Form, ) -> Result { let form = form.into_inner(); - + // Get the asset details let assets = AssetController::get_mock_assets(); let asset = assets.iter().find(|a| a.id == form.asset_id); - + if let Some(asset) = asset { // Process tags let tags = match form.tags { - Some(tags_str) => tags_str.split(',') + Some(tags_str) => tags_str + .split(',') .map(|s| s.trim().to_string()) .filter(|s| !s.is_empty()) .collect(), None => Vec::new(), }; - + // Calculate expiration date if provided - let expires_at = form.duration_days.map(|days| { - Utc::now() + Duration::days(days as i64) - }); - + let expires_at = form + .duration_days + .map(|days| Utc::now() + Duration::days(days as i64)); + // Parse listing type let listing_type = match form.listing_type.as_str() { "Fixed Price" => ListingType::FixedPrice, @@ -234,11 +255,11 @@ impl MarketplaceController { "Exchange" => ListingType::Exchange, _ => ListingType::FixedPrice, }; - + // Mock user data let user_id = "user-123"; let user_name = "Alice Hostly"; - + // Create the listing let _listing = Listing::new( form.title, @@ -255,9 +276,9 @@ impl MarketplaceController { tags, asset.image_url.clone(), ); - + // In a real application, we would save the listing to a database here - + // Redirect to the marketplace Ok(HttpResponse::SeeOther() .insert_header((http::header::LOCATION, "/marketplace")) @@ -267,94 +288,101 @@ impl MarketplaceController { let mut context = Context::new(); context.insert("active_page", &"marketplace"); context.insert("error", &"Asset not found"); - + render_template(&tmpl, "marketplace/create_listing.html", &context) } } - + // Submit a bid on an auction listing + #[allow(dead_code)] pub async fn submit_bid( - tmpl: web::Data, + _tmpl: web::Data, path: web::Path, - form: web::Form, + _form: web::Form, ) -> Result { let listing_id = path.into_inner(); - let form = form.into_inner(); - + let _form = _form.into_inner(); + // In a real application, we would: // 1. Find the listing in the database // 2. Validate the bid // 3. Create the bid // 4. Save it to the database - + // For now, we'll just redirect back to the listing Ok(HttpResponse::SeeOther() - .insert_header((http::header::LOCATION, format!("/marketplace/{}", listing_id))) + .insert_header(( + http::header::LOCATION, + format!("/marketplace/{}", listing_id), + )) .finish()) } - + // Purchase a fixed-price listing pub async fn purchase_listing( - tmpl: web::Data, + _tmpl: web::Data, path: web::Path, form: web::Form, ) -> Result { let listing_id = path.into_inner(); let form = form.into_inner(); - + if !form.agree_to_terms { // User must agree to terms return Ok(HttpResponse::SeeOther() - .insert_header((http::header::LOCATION, format!("/marketplace/{}", listing_id))) + .insert_header(( + http::header::LOCATION, + format!("/marketplace/{}", listing_id), + )) .finish()); } - + // In a real application, we would: // 1. Find the listing in the database // 2. Validate the purchase // 3. Process the transaction // 4. Update the listing status // 5. Transfer the asset - + // For now, we'll just redirect to the marketplace Ok(HttpResponse::SeeOther() .insert_header((http::header::LOCATION, "/marketplace")) .finish()) } - + // Cancel a listing pub async fn cancel_listing( - tmpl: web::Data, + _tmpl: web::Data, path: web::Path, ) -> Result { let _listing_id = path.into_inner(); - + // In a real application, we would: // 1. Find the listing in the database // 2. Validate that the current user is the seller // 3. Update the listing status - + // For now, we'll just redirect to my listings Ok(HttpResponse::SeeOther() .insert_header((http::header::LOCATION, "/marketplace/my")) .finish()) } - + // Generate mock listings for development pub fn get_mock_listings() -> Vec { let assets = AssetController::get_mock_assets(); let mut listings = Vec::new(); - + // Mock user data let user_ids = vec!["user-123", "user-456", "user-789"]; let user_names = vec!["Alice Hostly", "Ethan Cloudman", "Priya Servera"]; - + // Create some fixed price listings for i in 0..6 { let asset_index = i % assets.len(); let asset = &assets[asset_index]; let user_index = i % user_ids.len(); - + let price = match asset.asset_type { AssetType::Token => 50.0 + (i as f64 * 10.0), AssetType::Artwork => 500.0 + (i as f64 * 100.0), @@ -365,10 +393,13 @@ impl MarketplaceController { AssetType::Bond => 1500.0 + (i as f64 * 300.0), AssetType::Other => 800.0 + (i as f64 * 150.0), }; - + let mut listing = Listing::new( format!("{} for Sale", asset.name), - format!("This is a great opportunity to own {}. {}", asset.name, asset.description), + format!( + "This is a great opportunity to own {}. {}", + asset.name, asset.description + ), asset.id.clone(), asset.name.clone(), asset.asset_type.clone(), @@ -381,21 +412,21 @@ impl MarketplaceController { vec!["digital".to_string(), "asset".to_string()], asset.image_url.clone(), ); - + // Make some listings featured if i % 5 == 0 { listing.set_featured(true); } - + listings.push(listing); } - + // Create some auction listings for i in 0..4 { let asset_index = (i + 6) % assets.len(); let asset = &assets[asset_index]; let user_index = i % user_ids.len(); - + let starting_price = match asset.asset_type { AssetType::Token => 40.0 + (i as f64 * 5.0), AssetType::Artwork => 400.0 + (i as f64 * 50.0), @@ -406,7 +437,7 @@ impl MarketplaceController { AssetType::Bond => 1200.0 + (i as f64 * 250.0), AssetType::Other => 600.0 + (i as f64 * 120.0), }; - + let mut listing = Listing::new( format!("Auction: {}", asset.name), format!("Bid on this amazing {}. {}", asset.name, asset.description), @@ -422,12 +453,13 @@ impl MarketplaceController { vec!["auction".to_string(), "bidding".to_string()], asset.image_url.clone(), ); - + // Add some bids to the auctions let num_bids = 2 + (i % 3); for j in 0..num_bids { let bidder_index = (j + 1) % user_ids.len(); - if bidder_index != user_index { // Ensure seller isn't bidding + if bidder_index != user_index { + // Ensure seller isn't bidding let bid_amount = starting_price * (1.0 + (0.1 * (j + 1) as f64)); let _ = listing.add_bid( user_ids[bidder_index].to_string(), @@ -437,21 +469,21 @@ impl MarketplaceController { ); } } - + // Make some listings featured if i % 3 == 0 { listing.set_featured(true); } - + listings.push(listing); } - + // Create some exchange listings for i in 0..3 { let asset_index = (i + 10) % assets.len(); let asset = &assets[asset_index]; let user_index = i % user_ids.len(); - + let value = match asset.asset_type { AssetType::Token => 60.0 + (i as f64 * 15.0), AssetType::Artwork => 600.0 + (i as f64 * 150.0), @@ -462,33 +494,36 @@ impl MarketplaceController { AssetType::Bond => 1800.0 + (i as f64 * 350.0), AssetType::Other => 1000.0 + (i as f64 * 200.0), }; - + let listing = Listing::new( format!("Trade: {}", asset.name), - format!("Looking to exchange {} for another asset of similar value. Interested in NFTs and tokens.", asset.name), + format!( + "Looking to exchange {} for another asset of similar value. Interested in NFTs and tokens.", + asset.name + ), asset.id.clone(), asset.name.clone(), asset.asset_type.clone(), user_ids[user_index].to_string(), user_names[user_index].to_string(), - value, // Estimated value for exchange + value, // Estimated value for exchange "USD".to_string(), ListingType::Exchange, Some(Utc::now() + Duration::days(60)), vec!["exchange".to_string(), "trade".to_string()], asset.image_url.clone(), ); - + listings.push(listing); } - + // Create some sold listings for i in 0..5 { let asset_index = (i + 13) % assets.len(); let asset = &assets[asset_index]; let seller_index = i % user_ids.len(); let buyer_index = (i + 1) % user_ids.len(); - + let price = match asset.asset_type { AssetType::Token => 55.0 + (i as f64 * 12.0), AssetType::Artwork => 550.0 + (i as f64 * 120.0), @@ -499,9 +534,9 @@ impl MarketplaceController { AssetType::Bond => 1650.0 + (i as f64 * 330.0), AssetType::Other => 900.0 + (i as f64 * 180.0), }; - - let sale_price = price * 0.95; // Slight discount on sale - + + let sale_price = price * 0.95; // Slight discount on sale + let mut listing = Listing::new( format!("{} - SOLD", asset.name), format!("This {} was sold recently.", asset.name), @@ -517,27 +552,27 @@ impl MarketplaceController { vec!["sold".to_string()], asset.image_url.clone(), ); - + // Mark as sold let _ = listing.mark_as_sold( user_ids[buyer_index].to_string(), user_names[buyer_index].to_string(), sale_price, ); - + // Set sold date to be sometime in the past let days_ago = i as i64 + 1; listing.sold_at = Some(Utc::now() - Duration::days(days_ago)); - + listings.push(listing); } - + // Create a few cancelled listings for i in 0..2 { let asset_index = (i + 18) % assets.len(); let asset = &assets[asset_index]; let user_index = i % user_ids.len(); - + let price = match asset.asset_type { AssetType::Token => 45.0 + (i as f64 * 8.0), AssetType::Artwork => 450.0 + (i as f64 * 80.0), @@ -548,7 +583,7 @@ impl MarketplaceController { AssetType::Bond => 1350.0 + (i as f64 * 270.0), AssetType::Other => 750.0 + (i as f64 * 150.0), }; - + let mut listing = Listing::new( format!("{} - Cancelled", asset.name), format!("This listing for {} was cancelled.", asset.name), @@ -564,13 +599,13 @@ impl MarketplaceController { vec!["cancelled".to_string()], asset.image_url.clone(), ); - + // Cancel the listing let _ = listing.cancel(); - + listings.push(listing); } - + listings } } diff --git a/actix_mvc_app/src/models/asset.rs b/actix_mvc_app/src/models/asset.rs index f2ed183..0b94b22 100644 --- a/actix_mvc_app/src/models/asset.rs +++ b/actix_mvc_app/src/models/asset.rs @@ -112,6 +112,7 @@ pub struct Asset { pub external_url: Option, } +#[allow(dead_code)] impl Asset { /// Creates a new asset pub fn new( diff --git a/actix_mvc_app/src/models/contract.rs b/actix_mvc_app/src/models/contract.rs index ef936a5..e3f3035 100644 --- a/actix_mvc_app/src/models/contract.rs +++ b/actix_mvc_app/src/models/contract.rs @@ -85,6 +85,7 @@ pub struct ContractSigner { pub comments: Option, } +#[allow(dead_code)] impl ContractSigner { /// Creates a new contract signer pub fn new(name: String, email: String) -> Self { @@ -123,6 +124,7 @@ pub struct ContractRevision { pub comments: Option, } +#[allow(dead_code)] impl ContractRevision { /// Creates a new contract revision pub fn new(version: u32, content: String, created_by: String, comments: Option) -> Self { @@ -166,6 +168,7 @@ pub struct Contract { pub toc: Option>, } +#[allow(dead_code)] impl Contract { /// Creates a new contract pub fn new(title: String, description: String, contract_type: ContractType, created_by: String, organization_id: Option) -> Self { diff --git a/actix_mvc_app/src/models/defi.rs b/actix_mvc_app/src/models/defi.rs index d1986b0..8b9b5a3 100644 --- a/actix_mvc_app/src/models/defi.rs +++ b/actix_mvc_app/src/models/defi.rs @@ -14,6 +14,7 @@ pub enum DefiPositionStatus { Cancelled } +#[allow(dead_code)] impl DefiPositionStatus { pub fn as_str(&self) -> &str { match self { @@ -35,6 +36,7 @@ pub enum DefiPositionType { Collateral, } +#[allow(dead_code)] impl DefiPositionType { pub fn as_str(&self) -> &str { match self { @@ -95,6 +97,7 @@ pub struct DefiDatabase { receiving_positions: HashMap, } +#[allow(dead_code)] impl DefiDatabase { pub fn new() -> Self { Self { diff --git a/actix_mvc_app/src/models/flow.rs b/actix_mvc_app/src/models/flow.rs index 6feab01..2293c4a 100644 --- a/actix_mvc_app/src/models/flow.rs +++ b/actix_mvc_app/src/models/flow.rs @@ -110,6 +110,7 @@ pub struct FlowStep { pub logs: Vec, } +#[allow(dead_code)] impl FlowStep { /// Creates a new flow step pub fn new(name: String, description: String, order: u32) -> Self { @@ -189,6 +190,7 @@ pub struct FlowLog { pub timestamp: DateTime, } +#[allow(dead_code)] impl FlowLog { /// Creates a new flow log pub fn new(message: String) -> Self { @@ -231,6 +233,7 @@ pub struct Flow { pub current_step: Option, } +#[allow(dead_code)] impl Flow { /// Creates a new flow pub fn new(name: &str, description: &str, flow_type: FlowType, owner_id: &str, owner_name: &str) -> Self { diff --git a/actix_mvc_app/src/models/governance.rs b/actix_mvc_app/src/models/governance.rs index 1c4f1f3..486bef3 100644 --- a/actix_mvc_app/src/models/governance.rs +++ b/actix_mvc_app/src/models/governance.rs @@ -75,6 +75,7 @@ pub struct Proposal { pub voting_ends_at: Option>, } +#[allow(dead_code)] impl Proposal { /// Creates a new proposal pub fn new(creator_id: i32, creator_name: String, title: String, description: String) -> Self { @@ -140,6 +141,7 @@ pub struct Vote { pub updated_at: DateTime, } +#[allow(dead_code)] impl Vote { /// Creates a new vote pub fn new(proposal_id: String, voter_id: i32, voter_name: String, vote_type: VoteType, comment: Option) -> Self { @@ -200,6 +202,7 @@ pub struct VotingResults { pub total_votes: usize, } +#[allow(dead_code)] impl VotingResults { /// Creates a new empty voting results object pub fn new(proposal_id: String) -> Self { diff --git a/actix_mvc_app/src/models/marketplace.rs b/actix_mvc_app/src/models/marketplace.rs index 784a53b..d502140 100644 --- a/actix_mvc_app/src/models/marketplace.rs +++ b/actix_mvc_app/src/models/marketplace.rs @@ -1,7 +1,7 @@ +use crate::models::asset::AssetType; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::models::asset::{Asset, AssetType}; /// Status of a marketplace listing #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -12,6 +12,7 @@ pub enum ListingStatus { Expired, } +#[allow(dead_code)] impl ListingStatus { pub fn as_str(&self) -> &str { match self { @@ -63,6 +64,7 @@ pub enum BidStatus { Cancelled, } +#[allow(dead_code)] impl BidStatus { pub fn as_str(&self) -> &str { match self { @@ -103,6 +105,7 @@ pub struct Listing { pub image_url: Option, } +#[allow(dead_code)] impl Listing { /// Creates a new listing pub fn new( @@ -150,7 +153,13 @@ impl Listing { } /// Adds a bid to the listing - pub fn add_bid(&mut self, bidder_id: String, bidder_name: String, amount: f64, currency: String) -> Result<(), String> { + pub fn add_bid( + &mut self, + bidder_id: String, + bidder_name: String, + amount: f64, + currency: String, + ) -> Result<(), String> { if self.status != ListingStatus::Active { return Err("Listing is not active".to_string()); } @@ -160,7 +169,10 @@ impl Listing { } if currency != self.currency { - return Err(format!("Currency mismatch: expected {}, got {}", self.currency, currency)); + return Err(format!( + "Currency mismatch: expected {}, got {}", + self.currency, currency + )); } // Check if bid amount is higher than current highest bid or starting price @@ -193,13 +205,19 @@ impl Listing { /// Gets the highest bid on the listing pub fn highest_bid(&self) -> Option<&Bid> { - self.bids.iter() + self.bids + .iter() .filter(|b| b.status == BidStatus::Active) .max_by(|a, b| a.amount.partial_cmp(&b.amount).unwrap()) } /// Marks the listing as sold - pub fn mark_as_sold(&mut self, buyer_id: String, buyer_name: String, sale_price: f64) -> Result<(), String> { + pub fn mark_as_sold( + &mut self, + buyer_id: String, + buyer_name: String, + sale_price: f64, + ) -> Result<(), String> { if self.status != ListingStatus::Active { return Err("Listing is not active".to_string()); } @@ -257,11 +275,13 @@ impl MarketplaceStatistics { let mut listings_by_type = std::collections::HashMap::new(); let mut sales_by_asset_type = std::collections::HashMap::new(); - let active_listings = listings.iter() + let active_listings = listings + .iter() .filter(|l| l.status == ListingStatus::Active) .count(); - let sold_listings = listings.iter() + let sold_listings = listings + .iter() .filter(|l| l.status == ListingStatus::Sold) .count(); diff --git a/actix_mvc_app/src/models/mod.rs b/actix_mvc_app/src/models/mod.rs index 2e9448b..d225c4b 100644 --- a/actix_mvc_app/src/models/mod.rs +++ b/actix_mvc_app/src/models/mod.rs @@ -1,17 +1,16 @@ // Export models -pub mod user; -pub mod ticket; -pub mod calendar; -pub mod governance; -pub mod flow; -pub mod contract; pub mod asset; -pub mod marketplace; +pub mod calendar; +pub mod contract; pub mod defi; +pub mod flow; +pub mod governance; +pub mod marketplace; +pub mod ticket; +pub mod user; // Re-export models for easier imports -pub use user::User; -pub use ticket::{Ticket, TicketComment, TicketStatus, TicketPriority}; pub use calendar::{CalendarEvent, CalendarViewMode}; -pub use marketplace::{Listing, ListingStatus, ListingType, Bid, BidStatus, MarketplaceStatistics}; -pub use defi::{DefiPosition, DefiPositionType, DefiPositionStatus, ProvidingPosition, ReceivingPosition, DEFI_DB, initialize_mock_data}; +pub use defi::initialize_mock_data; +pub use ticket::{Ticket, TicketComment, TicketPriority, TicketStatus}; +pub use user::User; diff --git a/actix_mvc_app/src/models/ticket.rs b/actix_mvc_app/src/models/ticket.rs index 5e02d91..e8caeed 100644 --- a/actix_mvc_app/src/models/ticket.rs +++ b/actix_mvc_app/src/models/ticket.rs @@ -76,6 +76,7 @@ pub struct Ticket { pub assigned_to: Option, } +#[allow(dead_code)] impl Ticket { /// Creates a new ticket pub fn new(user_id: i32, title: String, description: String, priority: TicketPriority) -> Self { diff --git a/actix_mvc_app/src/models/user.rs b/actix_mvc_app/src/models/user.rs index aec201f..a2af576 100644 --- a/actix_mvc_app/src/models/user.rs +++ b/actix_mvc_app/src/models/user.rs @@ -4,6 +4,7 @@ use bcrypt::{hash, verify, DEFAULT_COST}; /// Represents a user in the system #[derive(Debug, Clone, Serialize, Deserialize)] +#[allow(dead_code)] pub struct User { /// Unique identifier for the user pub id: Option, @@ -31,6 +32,7 @@ pub enum UserRole { Admin, } +#[allow(dead_code)] impl User { /// Creates a new user with default values pub fn new(name: String, email: String) -> Self { @@ -125,6 +127,7 @@ impl User { /// Represents user login credentials #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct LoginCredentials { pub email: String, pub password: String, @@ -132,6 +135,7 @@ pub struct LoginCredentials { /// Represents user registration data #[derive(Debug, Deserialize)] +#[allow(dead_code)] pub struct RegistrationData { pub name: String, pub email: String, diff --git a/actix_mvc_app/src/utils/mod.rs b/actix_mvc_app/src/utils/mod.rs index 129cdbc..6bbaaa0 100644 --- a/actix_mvc_app/src/utils/mod.rs +++ b/actix_mvc_app/src/utils/mod.rs @@ -1,7 +1,7 @@ -use actix_web::{error, Error, HttpResponse}; +use actix_web::{Error, HttpResponse}; use chrono::{DateTime, Utc}; -use tera::{self, Context, Function, Tera, Value}; use std::error::Error as StdError; +use tera::{self, Context, Function, Tera, Value}; // Export modules pub mod redis_service; @@ -11,6 +11,7 @@ pub use redis_service::RedisCalendarService; /// Error type for template rendering #[derive(Debug)] +#[allow(dead_code)] pub struct TemplateError { pub message: String, pub details: String, @@ -46,7 +47,7 @@ impl Function for NowFunction { }; let now = Utc::now(); - + // Special case for just getting the year if args.get("year").and_then(|v| v.as_bool()).unwrap_or(false) { return Ok(Value::String(now.format("%Y").to_string())); @@ -68,14 +69,10 @@ impl Function for FormatDateFunction { None => { return Err(tera::Error::msg( "The 'timestamp' argument must be a valid timestamp", - )) + )); } }, - None => { - return Err(tera::Error::msg( - "The 'timestamp' argument is required", - )) - } + None => return Err(tera::Error::msg("The 'timestamp' argument is required")), }; let format = match args.get("format") { @@ -89,23 +86,21 @@ impl Function for FormatDateFunction { // Convert timestamp to DateTime using the non-deprecated method let datetime = match DateTime::from_timestamp(timestamp, 0) { Some(dt) => dt, - None => { - return Err(tera::Error::msg( - "Failed to convert timestamp to datetime", - )) - } + None => return Err(tera::Error::msg("Failed to convert timestamp to datetime")), }; - + Ok(Value::String(datetime.format(format).to_string())) } } /// Formats a date for display +#[allow(dead_code)] pub fn format_date(date: &DateTime, format: &str) -> String { date.format(format).to_string() } /// Truncates a string to a maximum length and adds an ellipsis if truncated +#[allow(dead_code)] pub fn truncate_string(s: &str, max_length: usize) -> String { if s.len() <= max_length { s.to_string() @@ -124,38 +119,41 @@ pub fn render_template( ctx: &Context, ) -> Result { println!("DEBUG: Attempting to render template: {}", template_name); - + // Print all context keys for debugging let mut keys = Vec::new(); for (key, _) in ctx.clone().into_json().as_object().unwrap().iter() { keys.push(key.clone()); } println!("DEBUG: Context keys: {:?}", keys); - + match tmpl.render(template_name, ctx) { Ok(content) => { println!("DEBUG: Successfully rendered template: {}", template_name); Ok(HttpResponse::Ok().content_type("text/html").body(content)) - }, + } Err(e) => { // Log the error with more details - println!("DEBUG: Template rendering error for {}: {}", template_name, e); + println!( + "DEBUG: Template rendering error for {}: {}", + template_name, e + ); println!("DEBUG: Error details: {:?}", e); - + // Print the error cause chain for better debugging let mut current_error: Option<&dyn StdError> = Some(&e); let mut error_chain = Vec::new(); - + while let Some(error) = current_error { error_chain.push(format!("{}", error)); current_error = error.source(); } - + println!("DEBUG: Error chain: {:?}", error_chain); - + // Log the error log::error!("Template rendering error: {}", e); - + // Create a simple error response with more detailed information let error_html = format!( r#" @@ -187,9 +185,9 @@ pub fn render_template( e, error_chain.join("\n") ); - + println!("DEBUG: Returning simple error page"); - + Ok(HttpResponse::InternalServerError() .content_type("text/html") .body(error_html)) @@ -207,4 +205,4 @@ mod tests { assert_eq!(truncate_string("Hello, world!", 5), "Hello..."); assert_eq!(truncate_string("", 5), ""); } -} \ No newline at end of file +}