fix: Remove warnings
This commit is contained in:
parent
e4e403e231
commit
60198dc2d4
@ -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()?;
|
||||
|
@ -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<String>,
|
||||
@ -80,10 +83,19 @@ impl AssetController {
|
||||
.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();
|
||||
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.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
|
||||
})
|
||||
@ -106,10 +118,8 @@ impl AssetController {
|
||||
let assets = Self::get_mock_assets();
|
||||
println!("DEBUG: Generated {} mock assets", assets.len());
|
||||
|
||||
let assets_data: Vec<serde_json::Map<String, serde_json::Value>> = assets
|
||||
.iter()
|
||||
.map(|a| Self::asset_to_json(a))
|
||||
.collect();
|
||||
let assets_data: Vec<serde_json::Map<String, serde_json::Value>> =
|
||||
assets.iter().map(|a| Self::asset_to_json(a)).collect();
|
||||
|
||||
// Add active_page for navigation highlighting
|
||||
context.insert("active_page", &"assets");
|
||||
@ -132,10 +142,8 @@ impl AssetController {
|
||||
let assets = Self::get_mock_assets();
|
||||
println!("DEBUG: Generated {} mock assets", assets.len());
|
||||
|
||||
let assets_data: Vec<serde_json::Map<String, serde_json::Value>> = assets
|
||||
.iter()
|
||||
.map(|a| Self::asset_to_json(a))
|
||||
.collect();
|
||||
let assets_data: Vec<serde_json::Map<String, serde_json::Value>> =
|
||||
assets.iter().map(|a| Self::asset_to_json(a)).collect();
|
||||
|
||||
// Add active_page for navigation highlighting
|
||||
context.insert("active_page", &"assets");
|
||||
@ -177,9 +185,20 @@ impl AssetController {
|
||||
.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();
|
||||
@ -190,7 +209,7 @@ impl AssetController {
|
||||
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())
|
||||
@ -216,7 +235,7 @@ impl AssetController {
|
||||
("Share", "Share"),
|
||||
("Bond", "Bond"),
|
||||
("IntellectualProperty", "Intellectual Property"),
|
||||
("Other", "Other")
|
||||
("Other", "Other"),
|
||||
];
|
||||
|
||||
context.insert("asset_types", &asset_types);
|
||||
@ -237,7 +256,9 @@ impl AssetController {
|
||||
// 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
|
||||
@ -253,7 +274,9 @@ impl AssetController {
|
||||
// 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
|
||||
@ -269,7 +292,9 @@ impl AssetController {
|
||||
// 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
|
||||
@ -284,7 +309,9 @@ impl AssetController {
|
||||
// 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
|
||||
@ -325,118 +352,233 @@ impl AssetController {
|
||||
fn asset_to_json(asset: &Asset) -> serde_json::Map<String, serde_json::Value> {
|
||||
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<serde_json::Value> = asset.valuation_history.iter()
|
||||
let valuation_history: Vec<serde_json::Value> = 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<serde_json::Value> = asset.transaction_history.iter()
|
||||
let transaction_history: Vec<serde_json::Value> = 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
|
||||
@ -481,14 +623,31 @@ impl AssetController {
|
||||
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",
|
||||
@ -550,9 +709,24 @@ impl AssetController {
|
||||
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",
|
||||
@ -610,14 +784,31 @@ impl AssetController {
|
||||
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",
|
||||
@ -675,14 +866,31 @@ impl AssetController {
|
||||
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",
|
||||
@ -740,14 +948,31 @@ impl AssetController {
|
||||
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",
|
||||
|
@ -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<String, jsonwebtoken::errors::Error> {
|
||||
|
@ -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,
|
||||
@ -32,7 +32,9 @@ impl CompanyController {
|
||||
// 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);
|
||||
@ -41,16 +43,21 @@ impl CompanyController {
|
||||
// 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);
|
||||
}
|
||||
@ -63,7 +70,10 @@ impl CompanyController {
|
||||
}
|
||||
|
||||
// View company details
|
||||
pub async fn view_company(tmpl: web::Data<Tera>, path: web::Path<String>) -> Result<HttpResponse> {
|
||||
pub async fn view_company(
|
||||
tmpl: web::Data<Tera>,
|
||||
path: web::Path<String>,
|
||||
) -> Result<HttpResponse> {
|
||||
let company_id = path.into_inner();
|
||||
let mut context = Context::new();
|
||||
|
||||
@ -87,10 +97,7 @@ impl CompanyController {
|
||||
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
|
||||
@ -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");
|
||||
@ -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");
|
||||
@ -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()
|
||||
@ -179,7 +186,7 @@ impl CompanyController {
|
||||
"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
|
||||
@ -188,16 +195,21 @@ impl CompanyController {
|
||||
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<HttpResponse> {
|
||||
use actix_web::{http::header};
|
||||
pub async fn register(mut form: actix_multipart::Multipart) -> Result<HttpResponse> {
|
||||
use actix_web::http::header;
|
||||
use futures_util::stream::StreamExt as _;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -220,7 +232,10 @@ impl CompanyController {
|
||||
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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,15 +246,26 @@ impl CompanyController {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
@ -49,11 +53,12 @@ impl ContractController {
|
||||
context.insert("recent_contracts", &recent_contracts);
|
||||
|
||||
// Add pending signature contracts
|
||||
let pending_signature_contracts: Vec<serde_json::Map<String, serde_json::Value>> = contracts
|
||||
.iter()
|
||||
.filter(|c| c.status == ContractStatus::PendingSignatures)
|
||||
.map(|c| Self::contract_to_json(c))
|
||||
.collect();
|
||||
let pending_signature_contracts: Vec<serde_json::Map<String, serde_json::Value>> =
|
||||
contracts
|
||||
.iter()
|
||||
.filter(|c| c.status == ContractStatus::PendingSignatures)
|
||||
.map(|c| Self::contract_to_json(c))
|
||||
.collect();
|
||||
|
||||
context.insert("pending_signature_contracts", &pending_signature_contracts);
|
||||
|
||||
@ -110,7 +115,7 @@ impl ContractController {
|
||||
pub async fn detail(
|
||||
tmpl: web::Data<Tera>,
|
||||
path: web::Path<String>,
|
||||
query: Query<HashMap<String, String>>
|
||||
query: Query<HashMap<String, String>>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let contract_id = path.into_inner();
|
||||
let mut context = Context::new();
|
||||
@ -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<TocItem>, 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,9 +191,12 @@ 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);
|
||||
}
|
||||
@ -181,11 +205,19 @@ impl ContractController {
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -208,7 +240,7 @@ impl ContractController {
|
||||
("Employment", "Employment Contract"),
|
||||
("NDA", "Non-Disclosure Agreement"),
|
||||
("SLA", "Service Level Agreement"),
|
||||
("Other", "Other")
|
||||
("Other", "Other"),
|
||||
];
|
||||
|
||||
context.insert("contract_types", &contract_types);
|
||||
@ -224,7 +256,9 @@ impl ContractController {
|
||||
// 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
|
||||
@ -232,46 +266,99 @@ impl ContractController {
|
||||
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<serde_json::Value> = contract.signers.iter()
|
||||
let signers: Vec<serde_json::Value> = 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)
|
||||
@ -281,91 +368,212 @@ impl ContractController {
|
||||
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<serde_json::Value> = contract.revisions.iter()
|
||||
let revisions: Vec<serde_json::Value> = 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()));
|
||||
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));
|
||||
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()));
|
||||
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));
|
||||
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
|
||||
@ -554,7 +762,6 @@ impl ContractController {
|
||||
]),
|
||||
};
|
||||
|
||||
|
||||
// Add potential signers to contract 3 (still in draft)
|
||||
contract3.signers.push(ContractSigner {
|
||||
id: "signer-006".to_string(),
|
||||
@ -576,54 +783,52 @@ impl ContractController {
|
||||
|
||||
// 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
|
||||
|
@ -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,
|
||||
@ -116,7 +122,10 @@ impl DefiController {
|
||||
}
|
||||
|
||||
// Process providing request
|
||||
pub async fn create_providing(_tmpl: web::Data<Tera>, form: web::Form<ProvidingForm>) -> Result<HttpResponse> {
|
||||
pub async fn create_providing(
|
||||
_tmpl: web::Data<Tera>,
|
||||
form: web::Form<ProvidingForm>,
|
||||
) -> Result<HttpResponse> {
|
||||
println!("DEBUG: Processing providing request: {:?}", form);
|
||||
|
||||
// Get the asset obligationails (in a real app, this would come from a database)
|
||||
@ -134,7 +143,8 @@ impl DefiController {
|
||||
_ => 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 {
|
||||
@ -164,9 +174,15 @@ impl DefiController {
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -177,7 +193,10 @@ impl DefiController {
|
||||
}
|
||||
|
||||
// Process receiving request
|
||||
pub async fn create_receiving(_tmpl: web::Data<Tera>, form: web::Form<ReceivingForm>) -> Result<HttpResponse> {
|
||||
pub async fn create_receiving(
|
||||
_tmpl: web::Data<Tera>,
|
||||
form: web::Form<ReceivingForm>,
|
||||
) -> Result<HttpResponse> {
|
||||
println!("DEBUG: Processing receiving request: {:?}", form);
|
||||
|
||||
// Get the asset obligationails (in a real app, this would come from a database)
|
||||
@ -196,11 +215,13 @@ impl DefiController {
|
||||
};
|
||||
|
||||
// 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
|
||||
@ -238,10 +259,15 @@ impl DefiController {
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -252,22 +278,33 @@ impl DefiController {
|
||||
}
|
||||
|
||||
// Process liquidity provision
|
||||
pub async fn add_liquidity(_tmpl: web::Data<Tera>, form: web::Form<LiquidityForm>) -> Result<HttpResponse> {
|
||||
pub async fn add_liquidity(
|
||||
_tmpl: web::Data<Tera>,
|
||||
form: web::Form<LiquidityForm>,
|
||||
) -> Result<HttpResponse> {
|
||||
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<Tera>, form: web::Form<StakingForm>) -> Result<HttpResponse> {
|
||||
pub async fn create_staking(
|
||||
_tmpl: web::Data<Tera>,
|
||||
form: web::Form<StakingForm>,
|
||||
) -> Result<HttpResponse> {
|
||||
println!("DEBUG: Processing staking request: {:?}", form);
|
||||
|
||||
// In a real application, this would create a staking position in the database
|
||||
@ -276,27 +313,41 @@ impl DefiController {
|
||||
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<Tera>, form: web::Form<SwapForm>) -> Result<HttpResponse> {
|
||||
pub async fn swap_tokens(
|
||||
_tmpl: web::Data<Tera>,
|
||||
form: web::Form<SwapForm>,
|
||||
) -> Result<HttpResponse> {
|
||||
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<Tera>, form: web::Form<CollateralForm>) -> Result<HttpResponse> {
|
||||
pub async fn create_collateral(
|
||||
_tmpl: web::Data<Tera>,
|
||||
form: web::Form<CollateralForm>,
|
||||
) -> Result<HttpResponse> {
|
||||
println!("DEBUG: Processing collateral creation: {:?}", form);
|
||||
|
||||
// In a real application, this would create a collateral position in the database
|
||||
@ -309,11 +360,16 @@ impl DefiController {
|
||||
_ => "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())
|
||||
}
|
||||
|
||||
@ -322,12 +378,32 @@ impl DefiController {
|
||||
let mut stats = serde_json::Map::new();
|
||||
|
||||
// Handle Option<Number> 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
|
||||
}
|
||||
@ -336,25 +412,61 @@ impl DefiController {
|
||||
fn asset_to_json(asset: &Asset) -> serde_json::Map<String, serde_json::Value> {
|
||||
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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
@ -43,13 +43,15 @@ impl MarketplaceController {
|
||||
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();
|
||||
|
||||
@ -58,7 +60,8 @@ impl MarketplaceController {
|
||||
let recent_listings = recent_listings.into_iter().take(8).collect::<Vec<_>>();
|
||||
|
||||
// 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();
|
||||
|
||||
@ -87,27 +90,34 @@ impl MarketplaceController {
|
||||
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)
|
||||
}
|
||||
@ -120,9 +130,8 @@ impl MarketplaceController {
|
||||
|
||||
// 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);
|
||||
@ -131,7 +140,10 @@ impl MarketplaceController {
|
||||
}
|
||||
|
||||
// Display listing details
|
||||
pub async fn listing_detail(tmpl: web::Data<Tera>, path: web::Path<String>) -> Result<HttpResponse> {
|
||||
pub async fn listing_detail(
|
||||
tmpl: web::Data<Tera>,
|
||||
path: web::Path<String>,
|
||||
) -> Result<HttpResponse> {
|
||||
let listing_id = path.into_inner();
|
||||
let mut context = Context::new();
|
||||
|
||||
@ -142,15 +154,19 @@ impl MarketplaceController {
|
||||
|
||||
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 {
|
||||
@ -186,17 +202,21 @@ impl MarketplaceController {
|
||||
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)
|
||||
}
|
||||
@ -215,7 +235,8 @@ impl MarketplaceController {
|
||||
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(),
|
||||
@ -223,9 +244,9 @@ impl MarketplaceController {
|
||||
};
|
||||
|
||||
// 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() {
|
||||
@ -273,13 +294,14 @@ impl MarketplaceController {
|
||||
}
|
||||
|
||||
// Submit a bid on an auction listing
|
||||
#[allow(dead_code)]
|
||||
pub async fn submit_bid(
|
||||
tmpl: web::Data<Tera>,
|
||||
_tmpl: web::Data<Tera>,
|
||||
path: web::Path<String>,
|
||||
form: web::Form<BidForm>,
|
||||
_form: web::Form<BidForm>,
|
||||
) -> Result<HttpResponse> {
|
||||
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
|
||||
@ -289,13 +311,16 @@ impl MarketplaceController {
|
||||
|
||||
// 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<Tera>,
|
||||
_tmpl: web::Data<Tera>,
|
||||
path: web::Path<String>,
|
||||
form: web::Form<PurchaseForm>,
|
||||
) -> Result<HttpResponse> {
|
||||
@ -305,7 +330,10 @@ impl MarketplaceController {
|
||||
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());
|
||||
}
|
||||
|
||||
@ -324,7 +352,7 @@ impl MarketplaceController {
|
||||
|
||||
// Cancel a listing
|
||||
pub async fn cancel_listing(
|
||||
tmpl: web::Data<Tera>,
|
||||
_tmpl: web::Data<Tera>,
|
||||
path: web::Path<String>,
|
||||
) -> Result<HttpResponse> {
|
||||
let _listing_id = path.into_inner();
|
||||
@ -368,7 +396,10 @@ impl MarketplaceController {
|
||||
|
||||
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(),
|
||||
@ -427,7 +458,8 @@ impl MarketplaceController {
|
||||
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(),
|
||||
@ -465,13 +497,16 @@ impl MarketplaceController {
|
||||
|
||||
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)),
|
||||
@ -500,7 +535,7 @@ impl MarketplaceController {
|
||||
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),
|
||||
|
@ -112,6 +112,7 @@ pub struct Asset {
|
||||
pub external_url: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Asset {
|
||||
/// Creates a new asset
|
||||
pub fn new(
|
||||
|
@ -85,6 +85,7 @@ pub struct ContractSigner {
|
||||
pub comments: Option<String>,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl ContractRevision {
|
||||
/// Creates a new contract revision
|
||||
pub fn new(version: u32, content: String, created_by: String, comments: Option<String>) -> Self {
|
||||
@ -166,6 +168,7 @@ pub struct Contract {
|
||||
pub toc: Option<Vec<TocItem>>,
|
||||
}
|
||||
|
||||
#[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<String>) -> Self {
|
||||
|
@ -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<String, ReceivingPosition>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl DefiDatabase {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
@ -110,6 +110,7 @@ pub struct FlowStep {
|
||||
pub logs: Vec<FlowLog>,
|
||||
}
|
||||
|
||||
#[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<Utc>,
|
||||
}
|
||||
|
||||
#[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<FlowStep>,
|
||||
}
|
||||
|
||||
#[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 {
|
||||
|
@ -75,6 +75,7 @@ pub struct Proposal {
|
||||
pub voting_ends_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[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<Utc>,
|
||||
}
|
||||
|
||||
#[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<String>) -> 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 {
|
||||
|
@ -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<String>,
|
||||
}
|
||||
|
||||
#[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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -76,6 +76,7 @@ pub struct Ticket {
|
||||
pub assigned_to: Option<i32>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Ticket {
|
||||
/// Creates a new ticket
|
||||
pub fn new(user_id: i32, title: String, description: String, priority: TicketPriority) -> Self {
|
||||
|
@ -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<i32>,
|
||||
@ -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,
|
||||
|
@ -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,
|
||||
@ -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,11 +86,7 @@ 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()))
|
||||
@ -101,11 +94,13 @@ impl Function for FormatDateFunction {
|
||||
}
|
||||
|
||||
/// Formats a date for display
|
||||
#[allow(dead_code)]
|
||||
pub fn format_date(date: &DateTime<Utc>, 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()
|
||||
@ -136,10 +131,13 @@ pub fn render_template(
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user