Add company management module with registration and entity switching

This commit is contained in:
timurgordon 2025-05-05 13:58:51 +03:00
parent 2760f00a30
commit 9468595395
20 changed files with 2538 additions and 76 deletions

151
actix_mvc_app/Cargo.lock generated
View File

@ -107,6 +107,45 @@ dependencies = [
"syn",
]
[[package]]
name = "actix-multipart"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d974dd6c4f78d102d057c672dcf6faa618fafa9df91d44f9c466688fc1275a3a"
dependencies = [
"actix-multipart-derive",
"actix-utils",
"actix-web",
"bytes",
"derive_more 0.99.19",
"futures-core",
"futures-util",
"httparse",
"local-waker",
"log",
"memchr",
"mime",
"rand 0.8.5",
"serde",
"serde_json",
"serde_plain",
"tempfile",
"tokio",
]
[[package]]
name = "actix-multipart-derive"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a0a77f836d869f700e5b47ac7c3c8b9c8bc82e4aec861954c6198abee3ebd4d"
dependencies = [
"darling",
"parse-size",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "actix-router"
version = "0.5.3"
@ -247,6 +286,7 @@ version = "0.1.0"
dependencies = [
"actix-files",
"actix-identity",
"actix-multipart",
"actix-session",
"actix-web",
"bcrypt",
@ -255,6 +295,7 @@ dependencies = [
"dotenv",
"env_logger",
"futures",
"futures-util",
"jsonwebtoken",
"lazy_static",
"log",
@ -823,6 +864,41 @@ dependencies = [
"cipher",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "deranged"
version = "0.4.0"
@ -947,6 +1023,22 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "flate2"
version = "1.1.1"
@ -1397,6 +1489,12 @@ dependencies = [
"syn",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.0.3"
@ -1564,6 +1662,12 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litemap"
version = "0.7.5"
@ -1760,6 +1864,12 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "parse-size"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
[[package]]
name = "parse-zoneinfo"
version = "0.3.1"
@ -2152,6 +2262,19 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "rustversion"
version = "1.0.20"
@ -2217,6 +2340,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_plain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50"
dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.8"
@ -2356,6 +2488,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
@ -2384,6 +2522,19 @@ dependencies = [
"syn",
]
[[package]]
name = "tempfile"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
dependencies = [
"fastrand",
"getrandom 0.3.2",
"once_cell",
"rustix",
"windows-sys 0.59.0",
]
[[package]]
name = "tera"
version = "1.20.0"

View File

@ -4,6 +4,8 @@ version = "0.1.0"
edition = "2024"
[dependencies]
actix-multipart = "0.6.1"
futures-util = "0.3.30"
actix-web = "4.5.1"
actix-files = "0.6.5"
tera = "1.19.1"

View File

@ -0,0 +1,245 @@
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;
// Form structs for company operations
#[derive(Debug, Deserialize)]
pub struct CompanyRegistrationForm {
pub company_name: String,
pub company_type: String,
pub shareholders: String,
pub company_purpose: Option<String>,
}
pub struct CompanyController;
impl CompanyController {
// Display the company management dashboard
pub async fn index(tmpl: web::Data<Tera>, req: HttpRequest) -> Result<HttpResponse> {
let mut context = Context::new();
println!("DEBUG: Starting Company dashboard rendering");
// Add active_page for navigation highlighting
context.insert("active_page", &"company");
// Parse query parameters
let query_string = req.query_string();
// Check for success message
if let Some(pos) = query_string.find("success=") {
let start = pos + 8; // length of "success="
let end = query_string[start..].find('&').map_or(query_string.len(), |e| e + start);
let success = &query_string[start..end];
let decoded = urlencoding::decode(success).unwrap_or_else(|_| success.into());
context.insert("success", &decoded);
}
// Check for entity context
if let Some(pos) = query_string.find("entity=") {
let start = pos + 7; // length of "entity="
let end = query_string[start..].find('&').map_or(query_string.len(), |e| e + start);
let 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 entity_name = &query_string[start..end];
let decoded_name = urlencoding::decode(entity_name).unwrap_or_else(|_| entity_name.into());
context.insert("entity_name", &decoded_name);
println!("DEBUG: Entity context set to {} ({})", entity, decoded_name);
}
}
println!("DEBUG: Rendering Company dashboard template");
let response = render_template(&tmpl, "company/index.html", &context);
println!("DEBUG: Finished rendering Company dashboard template");
response
}
// View company details
pub async fn view_company(tmpl: web::Data<Tera>, path: web::Path<String>) -> Result<HttpResponse> {
let company_id = path.into_inner();
let mut context = Context::new();
println!("DEBUG: Viewing company details for {}", company_id);
// Add active_page for navigation highlighting
context.insert("active_page", &"company");
context.insert("company_id", &company_id);
// In a real application, we would fetch company data from a database
// For now, we'll use mock data based on the company_id
match company_id.as_str() {
"company1" => {
context.insert("company_name", &"Zanzibar Digital Solutions");
context.insert("company_type", &"Startup FZC");
context.insert("status", &"Active");
context.insert("registration_date", &"2025-04-01");
context.insert("purpose", &"Digital solutions and blockchain development");
context.insert("plan", &"Startup FZC - $50/month");
context.insert("next_billing", &"2025-06-01");
context.insert("payment_method", &"Credit Card (****4582)");
// Shareholders data
let shareholders = vec![
("John Smith", "60%"),
("Sarah Johnson", "40%"),
];
context.insert("shareholders", &shareholders);
// Contracts data
let contracts = vec![
("Articles of Incorporation", "Signed"),
("Terms & Conditions", "Signed"),
("Digital Asset Issuance", "Signed"),
];
context.insert("contracts", &contracts);
},
"company2" => {
context.insert("company_name", &"Blockchain Innovations Ltd");
context.insert("company_type", &"Growth FZC");
context.insert("status", &"Active");
context.insert("registration_date", &"2025-03-15");
context.insert("purpose", &"Blockchain technology research and development");
context.insert("plan", &"Growth FZC - $100/month");
context.insert("next_billing", &"2025-06-15");
context.insert("payment_method", &"Bank Transfer");
// Shareholders data
let shareholders = vec![
("Michael Chen", "35%"),
("Aisha Patel", "35%"),
("David Okonkwo", "30%"),
];
context.insert("shareholders", &shareholders);
// Contracts data
let contracts = vec![
("Articles of Incorporation", "Signed"),
("Terms & Conditions", "Signed"),
("Digital Asset Issuance", "Signed"),
("Physical Asset Holding", "Signed"),
];
context.insert("contracts", &contracts);
},
"company3" => {
context.insert("company_name", &"Sustainable Energy Cooperative");
context.insert("company_type", &"Cooperative FZC");
context.insert("status", &"Pending");
context.insert("registration_date", &"2025-05-01");
context.insert("purpose", &"Renewable energy production and distribution");
context.insert("plan", &"Cooperative FZC - $200/month");
context.insert("next_billing", &"Pending Activation");
context.insert("payment_method", &"Pending");
// Shareholders data
let shareholders = vec![
("Community Energy Group", "40%"),
("Green Future Initiative", "30%"),
("Sustainable Living Collective", "30%"),
];
context.insert("shareholders", &shareholders);
// Contracts data
let contracts = vec![
("Articles of Incorporation", "Signed"),
("Terms & Conditions", "Signed"),
("Cooperative Governance", "Pending"),
];
context.insert("contracts", &contracts);
},
_ => {
// If company_id is not recognized, redirect to company index
return Ok(HttpResponse::Found()
.append_header(("Location", "/company"))
.finish());
}
}
println!("DEBUG: Rendering company view template");
let response = render_template(&tmpl, "company/view.html", &context);
println!("DEBUG: Finished rendering company view template");
response
}
// Switch to entity context
pub async fn switch_entity(path: web::Path<String>) -> Result<HttpResponse> {
let company_id = path.into_inner();
println!("DEBUG: Switching to entity context for {}", company_id);
// Get company name based on ID (in a real app, this would come from a database)
let company_name = match company_id.as_str() {
"company1" => "Zanzibar Digital Solutions",
"company2" => "Blockchain Innovations Ltd",
"company3" => "Sustainable Energy Cooperative",
_ => "Unknown Company"
};
// In a real application, we would set a session/cookie for the current entity
// Here we'll redirect back to the company page with a success message and entity parameter
let success_message = format!("Switched to {} entity context", company_name);
let encoded_message = urlencoding::encode(&success_message);
Ok(HttpResponse::Found()
.append_header(("Location", format!("/company?success={}&entity={}&entity_name={}",
encoded_message, company_id, urlencoding::encode(company_name))))
.finish())
}
// Process company registration
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;
println!("DEBUG: Processing company registration request");
let mut fields: HashMap<String, String> = HashMap::new();
let mut files = Vec::new();
// Parse multipart form
while let Some(Ok(mut field)) = form.next().await {
let mut value = Vec::new();
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
value.extend_from_slice(&data);
}
// Get field name from content disposition
let cd = field.content_disposition();
if let Some(name) = cd.get_name() {
if name == "company_docs" {
files.push(value); // Just collect files in memory for now
} else {
fields.insert(name.to_string(), String::from_utf8_lossy(&value).to_string());
}
}
}
// Extract company details
let company_name = fields.get("company_name").cloned().unwrap_or_default();
let company_type = fields.get("company_type").cloned().unwrap_or_default();
let shareholders = fields.get("shareholders").cloned().unwrap_or_default();
// Log received fields (mock DB insert)
println!("[Company Registration] Name: {}, Type: {}, Shareholders: {}, Files: {}",
company_name, company_type, shareholders, files.len());
// Create success message
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))))
.finish())
}
}

View File

@ -9,5 +9,6 @@ pub mod contract;
pub mod asset;
pub mod defi;
pub mod marketplace;
pub mod company;
// Re-export controllers for easier imports

View File

@ -10,6 +10,7 @@ use crate::controllers::contract::ContractController;
use crate::controllers::asset::AssetController;
use crate::controllers::marketplace::MarketplaceController;
use crate::controllers::defi::DefiController;
use crate::controllers::company::CompanyController;
use crate::middleware::JwtAuth;
use crate::SESSION_KEY;
@ -133,6 +134,14 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
.route("/swap", web::post().to(DefiController::swap_tokens))
.route("/collateral", web::post().to(DefiController::create_collateral))
)
// Company routes
.service(
web::scope("/company")
.route("", web::get().to(CompanyController::index))
.route("/register", web::post().to(CompanyController::register))
.route("/view/{id}", web::get().to(CompanyController::view_company))
.route("/switch/{id}", web::get().to(CompanyController::switch_entity))
)
);
// Keep the /protected scope for any future routes that should be under that path

View File

@ -0,0 +1,173 @@
// Company data (would be loaded from backend in production)
var companyData = {
'company1': {
name: 'Zanzibar Digital Solutions',
type: 'Startup FZC',
status: 'Active',
registrationDate: '2025-04-01',
purpose: 'Digital solutions and blockchain development',
plan: 'Startup FZC - $50/month',
nextBilling: '2025-06-01',
paymentMethod: 'Credit Card (****4582)',
shareholders: [
{ name: 'John Smith', percentage: '60%' },
{ name: 'Sarah Johnson', percentage: '40%' }
],
contracts: [
{ name: 'Articles of Incorporation', status: 'Signed' },
{ name: 'Terms & Conditions', status: 'Signed' },
{ name: 'Digital Asset Issuance', status: 'Signed' }
]
},
'company2': {
name: 'Blockchain Innovations Ltd',
type: 'Growth FZC',
status: 'Active',
registrationDate: '2025-03-15',
purpose: 'Blockchain technology research and development',
plan: 'Growth FZC - $100/month',
nextBilling: '2025-06-15',
paymentMethod: 'Bank Transfer',
shareholders: [
{ name: 'Michael Chen', percentage: '35%' },
{ name: 'Aisha Patel', percentage: '35%' },
{ name: 'David Okonkwo', percentage: '30%' }
],
contracts: [
{ name: 'Articles of Incorporation', status: 'Signed' },
{ name: 'Terms & Conditions', status: 'Signed' },
{ name: 'Digital Asset Issuance', status: 'Signed' },
{ name: 'Physical Asset Holding', status: 'Signed' }
]
},
'company3': {
name: 'Sustainable Energy Cooperative',
type: 'Cooperative FZC',
status: 'Pending',
registrationDate: '2025-05-01',
purpose: 'Renewable energy production and distribution',
plan: 'Cooperative FZC - $200/month',
nextBilling: 'Pending Activation',
paymentMethod: 'Pending',
shareholders: [
{ name: 'Community Energy Group', percentage: '40%' },
{ name: 'Green Future Initiative', percentage: '30%' },
{ name: 'Sustainable Living Collective', percentage: '30%' }
],
contracts: [
{ name: 'Articles of Incorporation', status: 'Signed' },
{ name: 'Terms & Conditions', status: 'Signed' },
{ name: 'Cooperative Governance', status: 'Pending' }
]
}
};
// Current company ID for modal
var currentCompanyId = null;
// View company details function
function viewCompanyDetails(companyId) {
// Store current company ID
currentCompanyId = companyId;
// Get company data
const company = companyData[companyId];
if (!company) return;
// Update modal title
document.getElementById('companyDetailsModalLabel').innerHTML =
`<i class="bi bi-building me-2"></i>${company.name} Details`;
// Update general information
document.getElementById('modal-company-name').textContent = company.name;
document.getElementById('modal-company-type').textContent = company.type;
document.getElementById('modal-registration-date').textContent = company.registrationDate;
// Update status with appropriate badge
const statusBadge = company.status === 'Active' ?
`<span class="badge bg-success">${company.status}</span>` :
`<span class="badge bg-warning text-dark">${company.status}</span>`;
document.getElementById('modal-status').innerHTML = statusBadge;
document.getElementById('modal-purpose').textContent = company.purpose;
// Update billing information
document.getElementById('modal-plan').textContent = company.plan;
document.getElementById('modal-next-billing').textContent = company.nextBilling;
document.getElementById('modal-payment-method').textContent = company.paymentMethod;
// Update shareholders table
const shareholdersTable = document.getElementById('modal-shareholders');
shareholdersTable.innerHTML = '';
company.shareholders.forEach(shareholder => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${shareholder.name}</td>
<td>${shareholder.percentage}</td>
`;
shareholdersTable.appendChild(row);
});
// Update contracts table
const contractsTable = document.getElementById('modal-contracts');
contractsTable.innerHTML = '';
company.contracts.forEach(contract => {
const row = document.createElement('tr');
const statusBadge = contract.status === 'Signed' ?
`<span class="badge bg-success">${contract.status}</span>` :
`<span class="badge bg-warning text-dark">${contract.status}</span>`;
row.innerHTML = `
<td>${contract.name}</td>
<td>${statusBadge}</td>
<td><button class="btn btn-sm btn-outline-primary" onclick="viewContract('${contract.name.toLowerCase().replace(/\s+/g, '-')}')">View</button></td>
`;
contractsTable.appendChild(row);
});
// Show the modal
const modal = new bootstrap.Modal(document.getElementById('companyDetailsModal'));
modal.show();
}
// Switch to entity function
function switchToEntity(companyId) {
const company = companyData[companyId];
if (!company) return;
// In a real application, this would redirect to the entity context
// For now, we'll just show an alert
alert(`Switching to ${company.name} entity context. All UI will now reflect this entity's governance, billing, and other features.`);
// This would typically involve:
// 1. Setting a session/cookie for the current entity
// 2. Redirecting to the dashboard with that entity context
// window.location.href = `/dashboard?entity=${companyId}`;
}
// Switch to entity from modal
function switchToEntityFromModal() {
if (currentCompanyId) {
switchToEntity(currentCompanyId);
// Close the modal
const modal = bootstrap.Modal.getInstance(document.getElementById('companyDetailsModal'));
modal.hide();
}
}
// View contract function
function viewContract(contractId) {
// In a real application, this would open the contract document
// For now, we'll just show an alert
alert(`Viewing contract: ${contractId.replace(/-/g, ' ')}`);
// This would typically involve:
// 1. Fetching the contract document from the server
// 2. Opening it in a viewer or new tab
// window.open(`/contracts/view/${contractId}`, '_blank');
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
console.log('Company management script loaded');
});

View File

@ -64,7 +64,7 @@
<button class="navbar-toggler d-md-none me-2" type="button" id="sidebarToggle" aria-label="Toggle navigation">
<i class="bi bi-list text-white"></i>
</button>
<h5 class="mb-0">Zanzibar Digital Freezone</h5>
<h5 class="mb-0">Zanzibar Digital Freezone {% if entity_name %}| <span class="text-info">{{ entity_name }}</span>{% endif %}</h5>
</div>
<div class="d-none d-md-flex">
<ul class="navbar-nav flex-row">
@ -155,6 +155,11 @@
<i class="bi bi-bank me-2"></i> DeFi Platform
</a>
</li>
<li class="nav-item">
<a class="nav-link d-flex align-items-center ps-3 py-2 {% if active_page == 'company' %}active fw-bold border-start border-4 border-primary bg-light{% endif %}" href="/company">
<i class="bi bi-building me-2"></i> Companies
</a>
</li>
<li class="nav-item">
<a class="nav-link d-flex align-items-center ps-3 py-2 {% if active_page == 'marketplace' %}active fw-bold border-start border-4 border-primary bg-light{% endif %}" href="/marketplace">
<i class="bi bi-shop me-2"></i> Marketplace
@ -206,6 +211,33 @@
</footer>
</div>
<!-- Toast container for notifications -->
<div class="toast-container position-fixed bottom-0 end-0 p-3">
{% if success %}
<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header bg-success text-white">
<strong class="me-auto">Success</strong>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
{{ success }}
</div>
</div>
{% endif %}
{% if error %}
<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header bg-danger text-white">
<strong class="me-auto">Error</strong>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
{{ error }}
</div>
</div>
{% endif %}
</div>
<script src="/static/js/bootstrap.bundle.min.js"></script>
<script src="https://unpkg.com/unpoly@3.7.2/unpoly.min.js"></script>
<script src="https://unpkg.com/unpoly@3.7.2/unpoly-bootstrap5.min.js"></script>
@ -214,6 +246,17 @@
document.getElementById('sidebarToggle').addEventListener('click', function() {
document.getElementById('sidebar').classList.toggle('show');
});
// Auto-hide toasts after 5 seconds
document.addEventListener('DOMContentLoaded', function() {
const toasts = document.querySelectorAll('.toast.show');
toasts.forEach(toast => {
setTimeout(() => {
const bsToast = new bootstrap.Toast(toast);
bsToast.hide();
}, 5000);
});
});
</script>
{% block extra_js %}{% endblock %}
</body>

View File

@ -0,0 +1,111 @@
{% extends "base.html" %}
{% block title %}Company Management{% endblock %}
{% block head %}
{{ super() }}
<style>
.toast {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
}
</style>
{% endblock %}
{% block content %}
<!-- Toast notification for success messages -->
{% if success_message %}
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="true" data-bs-delay="5000">
<div class="toast-header bg-success text-white">
<i class="bi bi-check-circle me-2"></i>
<strong class="me-auto">Success</strong>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
{{ success_message }}
</div>
</div>
</div>
{% endif %}
<div class="container-fluid py-4">
<h2 class="mb-4">Company & Legal Entity Management (Freezone)</h2>
<!-- Company Management Tabs -->
<div class="mb-4">
<div class="card-body">
<ul class="nav nav-tabs" id="companyTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="manage-tab" data-bs-toggle="tab" data-bs-target="#manage" type="button" role="tab" aria-controls="manage" aria-selected="true">
<i class="bi bi-building me-1"></i> Manage Companies
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="register-tab" data-bs-toggle="tab" data-bs-target="#register" type="button" role="tab" aria-controls="register" aria-selected="false">
<i class="bi bi-file-earmark-plus me-1"></i> Register New Company
</button>
</li>
</ul>
<div class="tab-content mt-3" id="companyTabsContent">
<div class="tab-pane fade show active" id="manage" role="tabpanel" aria-labelledby="manage-tab">
{% include "company/manage.html" %}
</div>
<div class="tab-pane fade" id="register" role="tabpanel" aria-labelledby="register-tab">
{% include "company/register.html" %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="/static/js/company.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Show toast if success message exists
const urlParams = new URLSearchParams(window.location.search);
const successMessage = urlParams.get('success');
if (successMessage) {
const toastEl = document.querySelector('.toast');
if (toastEl) {
const toastBody = toastEl.querySelector('.toast-body');
toastBody.textContent = decodeURIComponent(successMessage);
const toast = new bootstrap.Toast(toastEl);
toast.show();
// Auto-hide after 5 seconds
setTimeout(function() {
toast.hide();
}, 5000);
}
}
// Handle tab tracking in URL
const tabParam = urlParams.get('tab');
if (tabParam) {
const tabButton = document.querySelector(`button[data-bs-target="#${tabParam}"]`);
if (tabButton) {
const tab = new bootstrap.Tab(tabButton);
tab.show();
}
}
// Update URL when tab changes
const tabButtons = document.querySelectorAll('button[data-bs-toggle="tab"]');
tabButtons.forEach(function(button) {
button.addEventListener('shown.bs.tab', function(event) {
const targetId = event.target.getAttribute('data-bs-target').substring(1);
const url = new URL(window.location);
url.searchParams.set('tab', targetId);
window.history.replaceState({}, '', url);
});
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,193 @@
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<i class="bi bi-building me-1"></i> Your Companies
</div>
<div class="card-body">
<!-- Company list table -->
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Status</th>
<th>Date Registered</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Example rows -->
<tr>
<td>Zanzibar Digital Solutions</td>
<td>Startup FZC</td>
<td><span class="badge bg-success">Active</span></td>
<td>2025-04-01</td>
<td>
<div class="btn-group">
<a href="/company/view/company1" class="btn btn-sm btn-outline-primary"><i class="bi bi-eye"></i> View</a>
<a href="/company/switch/company1" class="btn btn-sm btn-primary"><i class="bi bi-box-arrow-in-right"></i> Switch to Entity</a>
</div>
</td>
</tr>
<tr>
<td>Blockchain Innovations Ltd</td>
<td>Growth FZC</td>
<td><span class="badge bg-success">Active</span></td>
<td>2025-03-15</td>
<td>
<div class="btn-group">
<a href="/company/view/company2" class="btn btn-sm btn-outline-primary"><i class="bi bi-eye"></i> View</a>
<a href="/company/switch/company2" class="btn btn-sm btn-primary"><i class="bi bi-box-arrow-in-right"></i> Switch to Entity</a>
</div>
</td>
</tr>
<tr>
<td>Sustainable Energy Cooperative</td>
<td>Cooperative FZC</td>
<td><span class="badge bg-warning text-dark">Pending</span></td>
<td>2025-05-01</td>
<td>
<div class="btn-group">
<a href="/company/view/company3" class="btn btn-sm btn-outline-primary"><i class="bi bi-eye"></i> View</a>
<a href="/company/switch/company3" class="btn btn-sm btn-primary"><i class="bi bi-box-arrow-in-right"></i> Switch to Entity</a>
</div>
</td>
</tr>
<!-- More rows dynamically rendered here -->
</tbody>
</table>
</div>
</div>
<!-- Company Details Modal -->
<div class="modal fade" id="companyDetailsModal" tabindex="-1" aria-labelledby="companyDetailsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-light">
<h5 class="modal-title" id="companyDetailsModalLabel"><i class="bi bi-building me-2"></i>Company Details</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="companyDetailsContent">
<!-- Company details will be loaded here -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card h-100">
<div class="card-header">General Information</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<th>Company Name:</th>
<td id="modal-company-name">Zanzibar Digital Solutions</td>
</tr>
<tr>
<th>Type:</th>
<td id="modal-company-type">Startup FZC</td>
</tr>
<tr>
<th>Registration Date:</th>
<td id="modal-registration-date">2025-04-01</td>
</tr>
<tr>
<th>Status:</th>
<td id="modal-status"><span class="badge bg-success">Active</span></td>
</tr>
<tr>
<th>Purpose:</th>
<td id="modal-purpose">Digital solutions and blockchain development</td>
</tr>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-header">Billing Information</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<th>Plan:</th>
<td id="modal-plan">Startup FZC - $50/month</td>
</tr>
<tr>
<th>Next Billing:</th>
<td id="modal-next-billing">2025-06-01</td>
</tr>
<tr>
<th>Payment Method:</th>
<td id="modal-payment-method">Credit Card (****4582)</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6">
<div class="card h-100">
<div class="card-header">Shareholders</div>
<div class="card-body">
<table class="table table-sm">
<thead>
<tr>
<th>Name</th>
<th>Percentage</th>
</tr>
</thead>
<tbody id="modal-shareholders">
<tr>
<td>John Smith</td>
<td>60%</td>
</tr>
<tr>
<td>Sarah Johnson</td>
<td>40%</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-header">Contracts</div>
<div class="card-body">
<table class="table table-sm">
<thead>
<tr>
<th>Contract</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody id="modal-contracts">
<tr>
<td>Articles of Incorporation</td>
<td><span class="badge bg-success">Signed</span></td>
<td><button class="btn btn-sm btn-outline-primary">View</button></td>
</tr>
<tr>
<td>Terms & Conditions</td>
<td><span class="badge bg-success">Signed</span></td>
<td><button class="btn btn-sm btn-outline-primary">View</button></td>
</tr>
<tr>
<td>Digital Asset Issuance</td>
<td><span class="badge bg-success">Signed</span></td>
<td><button class="btn btn-sm btn-outline-primary">View</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="switchToEntityFromModal()"><i class="bi bi-box-arrow-in-right me-1"></i>Switch to Entity</button>
</div>
</div>
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
<ul class="nav nav-tabs" id="companyTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="manage-tab" data-bs-toggle="tab" data-bs-target="#manage" type="button" role="tab" aria-controls="manage" aria-selected="true">
<i class="bi bi-building me-1"></i> Manage Companies
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="register-tab" data-bs-toggle="tab" data-bs-target="#register" type="button" role="tab" aria-controls="register" aria-selected="false">
<i class="bi bi-file-earmark-plus me-1"></i> Register New Company
</button>
</li>
</ul>
<div class="tab-content mt-4" id="companyTabsContent">
<div class="tab-pane fade show active" id="manage" role="tabpanel" aria-labelledby="manage-tab">
{% include "company/manage.html" %}
</div>
<div class="tab-pane fade" id="register" role="tabpanel" aria-labelledby="register-tab">
{% include "company/register.html" %}
</div>
</div>

View File

@ -0,0 +1,177 @@
{% extends "base.html" %}
{% block title %}{{ company_name }} - Company Details{% endblock %}
{% block head %}
{{ super() }}
<style>
.badge-signed {
background-color: #198754;
color: white;
}
.badge-pending {
background-color: #ffc107;
color: #212529;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2><i class="bi bi-building me-2"></i>{{ company_name }}</h2>
<div>
<a href="/company" class="btn btn-outline-secondary me-2"><i class="bi bi-arrow-left me-1"></i>Back to Companies</a>
<a href="/company/switch/{{ company_id }}" class="btn btn-primary"><i class="bi bi-box-arrow-in-right me-1"></i>Switch to Entity</a>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="bi bi-info-circle me-2"></i>General Information</h5>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<th style="width: 30%">Company Name:</th>
<td>{{ company_name }}</td>
</tr>
<tr>
<th>Type:</th>
<td>{{ company_type }}</td>
</tr>
<tr>
<th>Registration Date:</th>
<td>{{ registration_date }}</td>
</tr>
<tr>
<th>Status:</th>
<td>
{% if status == "Active" %}
<span class="badge bg-success">{{ status }}</span>
{% else %}
<span class="badge bg-warning text-dark">{{ status }}</span>
{% endif %}
</td>
</tr>
<tr>
<th>Purpose:</th>
<td>{{ purpose }}</td>
</tr>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="bi bi-credit-card me-2"></i>Billing Information</h5>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<th style="width: 30%">Plan:</th>
<td>{{ plan }}</td>
</tr>
<tr>
<th>Next Billing:</th>
<td>{{ next_billing }}</td>
</tr>
<tr>
<th>Payment Method:</th>
<td>{{ payment_method }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="bi bi-people me-2"></i>Shareholders</h5>
</div>
<div class="card-body">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Percentage</th>
</tr>
</thead>
<tbody>
{% for shareholder in shareholders %}
<tr>
<td>{{ shareholder.0 }}</td>
<td>{{ shareholder.1 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="bi bi-file-earmark-text me-2"></i>Contracts</h5>
</div>
<div class="card-body">
<table class="table table-striped">
<thead>
<tr>
<th>Contract</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for contract in contracts %}
<tr>
<td>{{ contract.0 }}</td>
<td>
{% if contract.1 == "Signed" %}
<span class="badge bg-success">{{ contract.1 }}</span>
{% else %}
<span class="badge bg-warning text-dark">{{ contract.1 }}</span>
{% endif %}
</td>
<td>
<a href="/contracts/view/{{ contract.0 | lower | replace(from=' ', to='-') }}" class="btn btn-sm btn-outline-primary">View</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header bg-light">
<h5 class="mb-0"><i class="bi bi-gear me-2"></i>Actions</h5>
</div>
<div class="card-body">
<div class="d-flex gap-2">
<a href="/company/edit/{{ company_id }}" class="btn btn-outline-primary"><i class="bi bi-pencil me-1"></i>Edit Company</a>
<a href="/company/documents/{{ company_id }}" class="btn btn-outline-secondary"><i class="bi bi-file-earmark me-1"></i>Manage Documents</a>
<a href="/company/switch/{{ company_id }}" class="btn btn-primary"><i class="bi bi-box-arrow-in-right me-1"></i>Switch to Entity</a>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script>
document.addEventListener('DOMContentLoaded', function() {
console.log('Company view page loaded');
});
</script>
{% endblock %}

View File

@ -24,11 +24,11 @@
<option value="" selected disabled>Choose an asset</option>
<!-- Tokens -->
<optgroup label="Tokens">
<option value="TFT" data-type="token" data-value="5000" data-amount="10000" data-unit="TFT" data-img="/static/img/tokens/tft.png">
ThreeFold Token (TFT) - 10,000 TFT ($5,000)
<option value="TFT" data-type="token" data-value="5000" data-amount="10000" data-unit="TFT">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i> ThreeFold Token (TFT) - 10,000 TFT ($5,000)
</option>
<option value="ZDFZ" data-type="token" data-value="2500" data-amount="5000" data-unit="ZDFZ" data-img="/static/img/tokens/zdfz.png">
Zanzibar Token (ZDFZ) - 5,000 ZDFZ ($2,500)
<option value="ZDFZ" data-type="token" data-value="2500" data-amount="5000" data-unit="ZDFZ">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i> Zanzibar Token (ZDFZ) - 5,000 ZDFZ ($2,500)
</option>
</optgroup>
<!-- Digital Assets -->
@ -180,7 +180,7 @@
<tr>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/tft.png" alt="TFT" width="20" height="20" class="rounded-circle me-1">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
2,000 TFT
</div>
</td>
@ -232,7 +232,7 @@
<tr>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="20" height="20" class="rounded-circle me-1">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
1,000 ZDFZ
</div>
</td>

View File

@ -179,7 +179,7 @@
<tr>
<td>
<div class="d-flex align-items-center">
<div class="token-icon me-2"></div>
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
{{ position.base.asset_name }}
</div>
</td>
@ -232,14 +232,14 @@
<tr>
<td>
<div class="d-flex align-items-center">
<div class="token-icon me-2"></div>
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
{{ position.base.asset_name }}
</div>
</td>
<td>{{ position.base.amount }} {{ position.base.asset_symbol }}</td>
<td>
<div class="d-flex align-items-center">
<div class="token-icon me-2"></div>
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
{{ position.collateral_amount }} {{ position.collateral_asset_symbol }}
</div>
</td>

View File

@ -34,8 +34,8 @@
<td>
<div class="d-flex align-items-center">
<div class="position-relative me-2">
<img src="/static/img/tokens/tft.png" alt="TFT" width="24" height="24" class="rounded-circle">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle position-absolute" style="left: 15px; top: 0;">
</div>
TFT-ZDFZ
</div>
@ -56,8 +56,8 @@
<td>
<div class="d-flex align-items-center">
<div class="position-relative me-2">
<img src="/static/img/tokens/tft.png" alt="TFT" width="24" height="24" class="rounded-circle">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="24" height="24" class="rounded-circle position-absolute" style="left: 15px; top: 0;">
</div>
TFT-USDT
</div>
@ -76,13 +76,7 @@
</tr>
<tr>
<td>
<div class="d-flex align-items-center">
<div class="position-relative me-2">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="24" height="24" class="rounded-circle position-absolute" style="left: 15px; top: 0;">
</div>
ZDFZ-USDT
</div>
ZDFZ-USDT
</td>
<td>$850,000</td>
<td>$32,000</td>
@ -117,13 +111,7 @@
<div class="col-md-6 col-lg-4 mb-4">
<div class="card h-100">
<div class="card-header bg-light">
<div class="d-flex align-items-center">
<div class="position-relative me-2">
<img src="/static/img/tokens/tft.png" alt="TFT" width="24" height="24" class="rounded-circle">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle position-absolute" style="left: 15px; top: 0;">
</div>
TFT-ZDFZ
</div>
TFT-ZDFZ
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-2">
@ -163,13 +151,7 @@
<div class="col-md-6 col-lg-4 mb-4">
<div class="card h-100">
<div class="card-header bg-light">
<div class="d-flex align-items-center">
<div class="position-relative me-2">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="24" height="24" class="rounded-circle position-absolute" style="left: 15px; top: 0;">
</div>
ZDFZ-USDT
</div>
ZDFZ-USDT
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-2">

View File

@ -156,7 +156,7 @@ This is a compliant version of the previous lending_borrowing.html tab. All term
<tr>
<td>
<div class="d-flex align-items-center">
<div class="token-icon me-2"></div>
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
{{ position.base.asset_name }}
</div>
</td>
@ -211,16 +211,13 @@ This is a compliant version of the previous lending_borrowing.html tab. All term
<tr>
<td>
<div class="d-flex align-items-center">
<div class="token-icon me-2"></div>
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
{{ position.base.asset_name }}
</div>
</td>
<td>{{ position.base.amount }} {{ position.base.asset_symbol }}</td>
<td>
<div class="d-flex align-items-center">
<div class="token-icon me-2"></div>
{{ position.collateral_amount }} {{ position.collateral_asset_symbol }}
</div>
{{ position.collateral_amount }} {{ position.collateral_asset_symbol }}
</td>
<td>
<div class="d-flex align-items-center">

View File

@ -22,7 +22,6 @@
<div class="card h-100">
<div class="card-header bg-primary text-white">
<div class="d-flex align-items-center">
<img src="/static/img/tokens/tft.png" alt="TFT" width="24" height="24" class="rounded-circle me-2">
<h6 class="mb-0">ThreeFold Token (TFT)</h6>
</div>
</div>
@ -80,7 +79,6 @@
<div class="card h-100">
<div class="card-header bg-success text-white">
<div class="d-flex align-items-center">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle me-2">
<h6 class="mb-0">Zanzibar Token (ZDFZ)</h6>
</div>
</div>
@ -221,7 +219,7 @@
<tr>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/tft.png" alt="TFT" width="24" height="24" class="rounded-circle me-2">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
ThreeFold Token (TFT)
</div>
</td>
@ -239,7 +237,7 @@
<tr>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle me-2">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
Zanzibar Token (ZDFZ)
</div>
</td>

View File

@ -29,27 +29,26 @@
</div>
<div class="dropdown">
<button class="btn btn-outline-primary dropdown-toggle d-flex align-items-center" type="button" id="fromTokenDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<img src="/static/img/tokens/tft.png" alt="TFT" width="24" height="24" class="rounded-circle me-2" id="fromTokenImg">
<span id="fromTokenSymbol">TFT</span>
</button>
<input type="hidden" name="from_token" id="fromTokenInput" value="TFT">
<ul class="dropdown-menu" aria-labelledby="fromTokenDropdown">
<li><a class="dropdown-item d-flex align-items-center" href="#" data-token="TFT" data-img="/static/img/tokens/tft.png" data-balance="10000">
<img src="/static/img/tokens/tft.png" alt="TFT" width="24" height="24" class="rounded-circle me-2">
<li><a class="dropdown-item d-flex align-items-center" href="#" data-token="TFT" data-balance="10000">
<div>
<div>ThreeFold Token</div>
<small class="text-muted">Balance: 10,000 TFT</small>
</div>
</a></li>
<li><a class="dropdown-item d-flex align-items-center" href="#" data-token="ZDFZ" data-img="/static/img/tokens/zdfz.png" data-balance="5000">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle me-2">
<div>
<div>Zanzibar Token</div>
<small class="text-muted">Balance: 5,000 ZDFZ</small>
</div>
</a></li>
<li><a class="dropdown-item d-flex align-items-center" href="#" data-token="USDT" data-img="/static/img/tokens/usdt.png" data-balance="2500">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="24" height="24" class="rounded-circle me-2">
<div>
<div>Tether USD</div>
<small class="text-muted">Balance: 2,500 USDT</small>
@ -82,21 +81,21 @@
<input type="number" class="form-control form-control-lg border-0" id="swapToAmount" name="to_amount" placeholder="0.0" readonly>
<div class="dropdown">
<button class="btn btn-outline-primary dropdown-toggle d-flex align-items-center" type="button" id="toTokenDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle me-2" id="toTokenImg">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
<span id="toTokenSymbol">ZDFZ</span>
</button>
<input type="hidden" name="to_token" id="toTokenInput" value="ZDFZ">
<ul class="dropdown-menu" aria-labelledby="toTokenDropdown">
<li><a class="dropdown-item d-flex align-items-center" href="#" data-token="TFT" data-img="/static/img/tokens/tft.png">
<img src="/static/img/tokens/tft.png" alt="TFT" width="24" height="24" class="rounded-circle me-2">
<div>ThreeFold Token</div>
</a></li>
<li><a class="dropdown-item d-flex align-items-center" href="#" data-token="ZDFZ" data-img="/static/img/tokens/zdfz.png">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="24" height="24" class="rounded-circle me-2">
<div>Zanzibar Token</div>
</a></li>
<li><a class="dropdown-item d-flex align-items-center" href="#" data-token="USDT" data-img="/static/img/tokens/usdt.png">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="24" height="24" class="rounded-circle me-2">
<div>Tether USD</div>
</a></li>
</ul>
@ -163,13 +162,13 @@
<td>2025-04-15 14:32</td>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/tft.png" alt="TFT" width="20" height="20" class="rounded-circle me-1">
500 TFT
</div>
</td>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="20" height="20" class="rounded-circle me-1">
250 ZDFZ
</div>
</td>
@ -179,13 +178,13 @@
<td>2025-04-14 09:17</td>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="20" height="20" class="rounded-circle me-1">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
1,000 USDT
</div>
</td>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/tft.png" alt="TFT" width="20" height="20" class="rounded-circle me-1">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
2,000 TFT
</div>
</td>
@ -195,13 +194,13 @@
<td>2025-04-12 16:45</td>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="20" height="20" class="rounded-circle me-1">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
100 ZDFZ
</div>
</td>
<td>
<div class="d-flex align-items-center">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="20" height="20" class="rounded-circle me-1">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
50 USDT
</div>
</td>
@ -234,8 +233,8 @@
<td>
<div class="d-flex align-items-center">
<div class="position-relative me-2">
<img src="/static/img/tokens/tft.png" alt="TFT" width="20" height="20" class="rounded-circle">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="20" height="20" class="rounded-circle position-absolute" style="left: 10px; top: 0;">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
</div>
TFT/ZDFZ
</div>
@ -248,8 +247,8 @@
<td>
<div class="d-flex align-items-center">
<div class="position-relative me-2">
<img src="/static/img/tokens/tft.png" alt="TFT" width="20" height="20" class="rounded-circle">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="20" height="20" class="rounded-circle position-absolute" style="left: 10px; top: 0;">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
</div>
TFT/USDT
</div>
@ -262,8 +261,8 @@
<td>
<div class="d-flex align-items-center">
<div class="position-relative me-2">
<img src="/static/img/tokens/zdfz.png" alt="ZDFZ" width="20" height="20" class="rounded-circle">
<img src="/static/img/tokens/usdt.png" alt="USDT" width="20" height="20" class="rounded-circle position-absolute" style="left: 10px; top: 0;">
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
<i class="bi bi-coin me-2" style="font-size: 1.5rem;"></i>
</div>
ZDFZ/USDT
</div>

View File

@ -0,0 +1,173 @@
// Company data (would be loaded from backend in production)
var companyData = {
'company1': {
name: 'Zanzibar Digital Solutions',
type: 'Startup FZC',
status: 'Active',
registrationDate: '2025-04-01',
purpose: 'Digital solutions and blockchain development',
plan: 'Startup FZC - $50/month',
nextBilling: '2025-06-01',
paymentMethod: 'Credit Card (****4582)',
shareholders: [
{ name: 'John Smith', percentage: '60%' },
{ name: 'Sarah Johnson', percentage: '40%' }
],
contracts: [
{ name: 'Articles of Incorporation', status: 'Signed' },
{ name: 'Terms & Conditions', status: 'Signed' },
{ name: 'Digital Asset Issuance', status: 'Signed' }
]
},
'company2': {
name: 'Blockchain Innovations Ltd',
type: 'Growth FZC',
status: 'Active',
registrationDate: '2025-03-15',
purpose: 'Blockchain technology research and development',
plan: 'Growth FZC - $100/month',
nextBilling: '2025-06-15',
paymentMethod: 'Bank Transfer',
shareholders: [
{ name: 'Michael Chen', percentage: '35%' },
{ name: 'Aisha Patel', percentage: '35%' },
{ name: 'David Okonkwo', percentage: '30%' }
],
contracts: [
{ name: 'Articles of Incorporation', status: 'Signed' },
{ name: 'Terms & Conditions', status: 'Signed' },
{ name: 'Digital Asset Issuance', status: 'Signed' },
{ name: 'Physical Asset Holding', status: 'Signed' }
]
},
'company3': {
name: 'Sustainable Energy Cooperative',
type: 'Cooperative FZC',
status: 'Pending',
registrationDate: '2025-05-01',
purpose: 'Renewable energy production and distribution',
plan: 'Cooperative FZC - $200/month',
nextBilling: 'Pending Activation',
paymentMethod: 'Pending',
shareholders: [
{ name: 'Community Energy Group', percentage: '40%' },
{ name: 'Green Future Initiative', percentage: '30%' },
{ name: 'Sustainable Living Collective', percentage: '30%' }
],
contracts: [
{ name: 'Articles of Incorporation', status: 'Signed' },
{ name: 'Terms & Conditions', status: 'Signed' },
{ name: 'Cooperative Governance', status: 'Pending' }
]
}
};
// Current company ID for modal
var currentCompanyId = null;
// View company details function
function viewCompanyDetails(companyId) {
// Store current company ID
currentCompanyId = companyId;
// Get company data
const company = companyData[companyId];
if (!company) return;
// Update modal title
document.getElementById('companyDetailsModalLabel').innerHTML =
`<i class="bi bi-building me-2"></i>${company.name} Details`;
// Update general information
document.getElementById('modal-company-name').textContent = company.name;
document.getElementById('modal-company-type').textContent = company.type;
document.getElementById('modal-registration-date').textContent = company.registrationDate;
// Update status with appropriate badge
const statusBadge = company.status === 'Active' ?
`<span class="badge bg-success">${company.status}</span>` :
`<span class="badge bg-warning text-dark">${company.status}</span>`;
document.getElementById('modal-status').innerHTML = statusBadge;
document.getElementById('modal-purpose').textContent = company.purpose;
// Update billing information
document.getElementById('modal-plan').textContent = company.plan;
document.getElementById('modal-next-billing').textContent = company.nextBilling;
document.getElementById('modal-payment-method').textContent = company.paymentMethod;
// Update shareholders table
const shareholdersTable = document.getElementById('modal-shareholders');
shareholdersTable.innerHTML = '';
company.shareholders.forEach(shareholder => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${shareholder.name}</td>
<td>${shareholder.percentage}</td>
`;
shareholdersTable.appendChild(row);
});
// Update contracts table
const contractsTable = document.getElementById('modal-contracts');
contractsTable.innerHTML = '';
company.contracts.forEach(contract => {
const row = document.createElement('tr');
const statusBadge = contract.status === 'Signed' ?
`<span class="badge bg-success">${contract.status}</span>` :
`<span class="badge bg-warning text-dark">${contract.status}</span>`;
row.innerHTML = `
<td>${contract.name}</td>
<td>${statusBadge}</td>
<td><button class="btn btn-sm btn-outline-primary" onclick="viewContract('${contract.name.toLowerCase().replace(/\s+/g, '-')}')">View</button></td>
`;
contractsTable.appendChild(row);
});
// Show the modal
const modal = new bootstrap.Modal(document.getElementById('companyDetailsModal'));
modal.show();
}
// Switch to entity function
function switchToEntity(companyId) {
const company = companyData[companyId];
if (!company) return;
// In a real application, this would redirect to the entity context
// For now, we'll just show an alert
alert(`Switching to ${company.name} entity context. All UI will now reflect this entity's governance, billing, and other features.`);
// This would typically involve:
// 1. Setting a session/cookie for the current entity
// 2. Redirecting to the dashboard with that entity context
// window.location.href = `/dashboard?entity=${companyId}`;
}
// Switch to entity from modal
function switchToEntityFromModal() {
if (currentCompanyId) {
switchToEntity(currentCompanyId);
// Close the modal
const modal = bootstrap.Modal.getInstance(document.getElementById('companyDetailsModal'));
modal.hide();
}
}
// View contract function
function viewContract(contractId) {
// In a real application, this would open the contract document
// For now, we'll just show an alert
alert(`Viewing contract: ${contractId.replace(/-/g, ' ')}`);
// This would typically involve:
// 1. Fetching the contract document from the server
// 2. Opening it in a viewer or new tab
// window.open(`/contracts/view/${contractId}`, '_blank');
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
console.log('Company management script loaded');
});

View File

@ -411,18 +411,9 @@ document.addEventListener('DOMContentLoaded', function() {
if (liquidationUnitElement) liquidationUnitElement.textContent = assetUnit;
// Calculate collateral value
const amount = parseFloat(collateralAmountInput.value) || 0;
let collateralValue = 0;
if (assetType === 'token') {
// For tokens, calculate based on token price
const tokenPrice = assetValue / assetAmount;
collateralValue = amount * tokenPrice;
} else {
// For NFTs and other assets, use the full value if amount is 1
collateralValue = amount === 1 ? assetValue : 0;
liquidationPriceElement.value = (borrowedValue * 1.2).toFixed(2);
}
if (collateralValueElement) collateralValueElement.value = collateralValue.toFixed(2);
// Calculate max loan amount (75% of collateral value)