freezone/platform/src/components/accounting/overview_tab.rs
2025-06-27 04:13:31 +02:00

207 lines
13 KiB
Rust

use yew::prelude::*;
use crate::components::accounting::models::*;
#[derive(Properties, PartialEq)]
pub struct OverviewTabProps {
pub state: UseStateHandle<AccountingState>,
}
#[function_component(OverviewTab)]
pub fn overview_tab(props: &OverviewTabProps) -> Html {
let state = &props.state;
// Calculate totals
let total_revenue: f64 = state.revenue_entries.iter().map(|r| r.total_amount).sum();
let total_expenses: f64 = state.expense_entries.iter().map(|e| e.total_amount).sum();
let net_profit = total_revenue - total_expenses;
let pending_revenue: f64 = state.revenue_entries.iter()
.filter(|r| r.payment_status == PaymentStatus::Pending)
.map(|r| r.total_amount)
.sum();
let pending_expenses: f64 = state.expense_entries.iter()
.filter(|e| e.payment_status == PaymentStatus::Pending)
.map(|e| e.total_amount)
.sum();
html! {
<div class="animate-fade-in-up">
// Key Statistics Cards
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card border-warning shadow-soft card-hover">
<div class="card-body p-4">
<div class="d-flex align-items-center justify-content-between">
<div>
<h6 class="text-warning mb-1">{"Pending Items"}</h6>
<h3 class="mb-0 fw-bold text-dark">{format!("${:.2}", pending_revenue + pending_expenses)}</h3>
</div>
<div class="bg-warning bg-opacity-10 rounded-circle p-3">
<i class="bi bi-clock text-warning fs-4"></i>
</div>
</div>
<div class="mt-3">
<small class="text-muted">{format!("${:.2} revenue, ${:.2} expenses", pending_revenue, pending_expenses)}</small>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-info shadow-soft card-hover">
<div class="card-body p-4">
<div class="d-flex align-items-center justify-content-between">
<div>
<h6 class="text-info mb-1">{"Avg Invoice Value"}</h6>
<h3 class="mb-0 fw-bold text-dark">{format!("${:.2}", total_revenue / state.revenue_entries.len() as f64)}</h3>
</div>
<div class="bg-info bg-opacity-10 rounded-circle p-3">
<i class="bi bi-receipt text-info fs-4"></i>
</div>
</div>
<div class="mt-3">
<small class="text-muted">{format!("{} invoices total", state.revenue_entries.len())}</small>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-success shadow-soft card-hover">
<div class="card-body p-4">
<div class="d-flex align-items-center justify-content-between">
<div>
<h6 class="text-success mb-1">{"Tax Deductible"}</h6>
<h3 class="mb-0 fw-bold text-dark">{format!("${:.2}", state.expense_entries.iter().filter(|e| e.is_deductible).map(|e| e.total_amount).sum::<f64>())}</h3>
</div>
<div class="bg-success bg-opacity-10 rounded-circle p-3">
<i class="bi bi-receipt-cutoff text-success fs-4"></i>
</div>
</div>
<div class="mt-3">
<small class="text-muted">{"100% of expenses deductible"}</small>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-primary shadow-soft card-hover">
<div class="card-body p-4">
<div class="d-flex align-items-center justify-content-between">
<div>
<h6 class="text-primary mb-1">{"Collection Rate"}</h6>
<h3 class="mb-0 fw-bold text-dark">{"85.2%"}</h3>
</div>
<div class="bg-primary bg-opacity-10 rounded-circle p-3">
<i class="bi bi-percent text-primary fs-4"></i>
</div>
</div>
<div class="mt-3">
<small class="text-muted">{"Above industry avg"}</small>
</div>
</div>
</div>
</div>
</div>
// Recent Transactions
<div class="row g-4">
<div class="col-12">
<div class="card shadow-soft border-0">
<div class="card-header bg-white border-bottom-0 py-3">
<h5 class="mb-0 fw-bold">{"Recent Transactions"}</h5>
<small class="text-muted">{"Latest payments made and received"}</small>
</div>
<div class="card-body p-0">
{if state.payment_transactions.is_empty() {
html! {
<div class="text-center py-5">
<i class="bi bi-credit-card fs-1 text-muted mb-3 d-block"></i>
<h6 class="text-muted">{"No transactions recorded yet"}</h6>
<p class="text-muted mb-0">{"Transactions will appear here once you record payments"}</p>
</div>
}
} else {
html! {
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="bg-light">
<tr>
<th class="border-0 py-3 px-4">{"Date"}</th>
<th class="border-0 py-3">{"Type"}</th>
<th class="border-0 py-3">{"Reference"}</th>
<th class="border-0 py-3">{"Amount"}</th>
<th class="border-0 py-3">{"Method"}</th>
<th class="border-0 py-3">{"Status"}</th>
<th class="border-0 py-3">{"Notes"}</th>
</tr>
</thead>
<tbody>
{for state.payment_transactions.iter().take(10).map(|transaction| {
let (transaction_type, reference, amount_color) = if let Some(invoice_id) = &transaction.invoice_id {
("Revenue", invoice_id.clone(), "text-success")
} else if let Some(expense_id) = &transaction.expense_id {
("Expense", expense_id.clone(), "text-danger")
} else {
("Unknown", "N/A".to_string(), "text-muted")
};
html! {
<tr>
<td class="py-3 px-4">
<div class="fw-semibold">{&transaction.date}</div>
<small class="text-muted">{&transaction.id}</small>
</td>
<td class="py-3">
<span class={format!("badge bg-{} bg-opacity-10 text-{}",
if transaction_type == "Revenue" { "success" } else { "danger" },
if transaction_type == "Revenue" { "success" } else { "danger" }
)}>
{transaction_type}
</span>
</td>
<td class="py-3">
<div class="fw-semibold">{&reference}</div>
{if let Some(hash) = &transaction.transaction_hash {
html! { <small class="text-muted"><code>{&hash[..12]}{"..."}</code></small> }
} else if let Some(ref_num) = &transaction.reference_number {
html! { <small class="text-muted">{ref_num}</small> }
} else {
html! {}
}}
</td>
<td class="py-3">
<div class={format!("fw-bold {}", amount_color)}>
{if transaction_type == "Revenue" { "+" } else { "-" }}
{format!("${:.2}", transaction.amount)}
</div>
</td>
<td class="py-3">
<div class="d-flex align-items-center">
<i class={format!("bi bi-{} text-{} me-2", transaction.payment_method.get_icon(), transaction.payment_method.get_color())}></i>
<span class="small">{transaction.payment_method.to_string()}</span>
</div>
</td>
<td class="py-3">
<span class={format!("badge bg-{}", transaction.status.get_color())}>
{transaction.status.to_string()}
</span>
</td>
<td class="py-3">
<span class="text-muted">{&transaction.notes}</span>
</td>
</tr>
}
})}
</tbody>
</table>
</div>
}
}}
</div>
</div>
</div>
</div>
</div>
}
}