261 lines
14 KiB
Rust
261 lines
14 KiB
Rust
use yew::prelude::*;
|
|
use web_sys::HtmlInputElement;
|
|
use wasm_bindgen::JsCast;
|
|
use crate::components::accounting::models::*;
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct FinancialReportsTabProps {
|
|
pub state: UseStateHandle<AccountingState>,
|
|
}
|
|
|
|
#[function_component(FinancialReportsTab)]
|
|
pub fn financial_reports_tab(props: &FinancialReportsTabProps) -> Html {
|
|
let state = &props.state;
|
|
let show_report_modal = use_state(|| false);
|
|
let report_type = use_state(|| ReportType::ProfitLoss);
|
|
let start_date = use_state(|| "".to_string());
|
|
let end_date = use_state(|| "".to_string());
|
|
|
|
let on_generate_report = {
|
|
let state = state.clone();
|
|
let show_report_modal = show_report_modal.clone();
|
|
let report_type = report_type.clone();
|
|
let start_date = start_date.clone();
|
|
let end_date = end_date.clone();
|
|
|
|
Callback::from(move |_| {
|
|
if start_date.is_empty() || end_date.is_empty() {
|
|
web_sys::window()
|
|
.unwrap()
|
|
.alert_with_message("Please select both start and end dates")
|
|
.unwrap();
|
|
return;
|
|
}
|
|
|
|
let new_report = FinancialReport {
|
|
id: state.financial_reports.len() + 1,
|
|
report_type: (*report_type).clone(),
|
|
period_start: (*start_date).clone(),
|
|
period_end: (*end_date).clone(),
|
|
generated_date: js_sys::Date::new_0().to_iso_string().as_string().unwrap()[..10].to_string(),
|
|
status: "Generated".to_string(),
|
|
};
|
|
|
|
let mut new_state = (*state).clone();
|
|
new_state.financial_reports.push(new_report);
|
|
state.set(new_state);
|
|
show_report_modal.set(false);
|
|
})
|
|
};
|
|
|
|
let on_export_report = {
|
|
Callback::from(move |report_id: usize| {
|
|
// Create CSV content for the report
|
|
let csv_content = format!(
|
|
"Financial Report Export\nReport ID: {}\nGenerated: {}\n\nThis is a placeholder for the actual report data.",
|
|
report_id,
|
|
js_sys::Date::new_0().to_iso_string().as_string().unwrap()
|
|
);
|
|
|
|
// Create and download the file
|
|
let blob = web_sys::Blob::new_with_str_sequence(&js_sys::Array::of1(&csv_content.into())).unwrap();
|
|
let url = web_sys::Url::create_object_url_with_blob(&blob).unwrap();
|
|
|
|
let document = web_sys::window().unwrap().document().unwrap();
|
|
let a = document.create_element("a").unwrap();
|
|
a.set_attribute("href", &url).unwrap();
|
|
a.set_attribute("download", &format!("financial_report_{}.csv", report_id)).unwrap();
|
|
a.dyn_ref::<web_sys::HtmlElement>().unwrap().click();
|
|
|
|
web_sys::Url::revoke_object_url(&url).unwrap();
|
|
})
|
|
};
|
|
|
|
html! {
|
|
<div class="animate-fade-in-up">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h4 class="mb-0">{"Financial Reports"}</h4>
|
|
<button
|
|
class="btn btn-primary"
|
|
onclick={
|
|
let show_report_modal = show_report_modal.clone();
|
|
Callback::from(move |_| show_report_modal.set(true))
|
|
}
|
|
>
|
|
<i class="bi bi-plus-lg me-2"></i>
|
|
{"Generate Report"}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="card shadow-soft" style="border: none;">
|
|
<div class="card-body">
|
|
if state.financial_reports.is_empty() {
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-file-earmark-text display-1 text-muted mb-3"></i>
|
|
<h5 class="text-muted">{"No reports generated yet"}</h5>
|
|
<p class="text-muted">{"Generate your first financial report to get started"}</p>
|
|
</div>
|
|
} else {
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>{"Report Type"}</th>
|
|
<th>{"Period"}</th>
|
|
<th>{"Generated"}</th>
|
|
<th>{"Status"}</th>
|
|
<th>{"Actions"}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{for state.financial_reports.iter().map(|report| {
|
|
let report_id = report.id;
|
|
let on_export = on_export_report.clone();
|
|
html! {
|
|
<tr>
|
|
<td>
|
|
<span class="badge bg-primary">{format!("{:?}", report.report_type)}</span>
|
|
</td>
|
|
<td>{format!("{} to {}", report.period_start, report.period_end)}</td>
|
|
<td>{&report.generated_date}</td>
|
|
<td>
|
|
<span class="badge bg-success">{&report.status}</span>
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<button
|
|
class="btn btn-outline-primary"
|
|
onclick={
|
|
Callback::from(move |_| {
|
|
web_sys::window()
|
|
.unwrap()
|
|
.alert_with_message("Report preview feature coming soon!")
|
|
.unwrap();
|
|
})
|
|
}
|
|
>
|
|
<i class="bi bi-eye"></i>
|
|
</button>
|
|
<button
|
|
class="btn btn-outline-success"
|
|
onclick={move |_| on_export.emit(report_id)}
|
|
>
|
|
<i class="bi bi-download"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
}
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
// Report Generation Modal
|
|
if *show_report_modal {
|
|
<div class="modal show d-block" tabindex="-1" style="background-color: rgba(0,0,0,0.5);">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">{"Generate Financial Report"}</h5>
|
|
<button
|
|
type="button"
|
|
class="btn-close"
|
|
onclick={
|
|
let show_report_modal = show_report_modal.clone();
|
|
Callback::from(move |_| show_report_modal.set(false))
|
|
}
|
|
></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">{"Report Type"}</label>
|
|
<select
|
|
class="form-select"
|
|
onchange={
|
|
let report_type = report_type.clone();
|
|
Callback::from(move |e: Event| {
|
|
let target = e.target_dyn_into::<HtmlInputElement>().unwrap();
|
|
let value = match target.value().as_str() {
|
|
"ProfitLoss" => ReportType::ProfitLoss,
|
|
"BalanceSheet" => ReportType::BalanceSheet,
|
|
"CashFlow" => ReportType::CashFlow,
|
|
"TaxSummary" => ReportType::TaxSummary,
|
|
_ => ReportType::ProfitLoss,
|
|
};
|
|
report_type.set(value);
|
|
})
|
|
}
|
|
>
|
|
<option value="ProfitLoss">{"Profit & Loss"}</option>
|
|
<option value="BalanceSheet">{"Balance Sheet"}</option>
|
|
<option value="CashFlow">{"Cash Flow"}</option>
|
|
<option value="TaxSummary">{"Tax Summary"}</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{"Start Date"}</label>
|
|
<input
|
|
type="date"
|
|
class="form-control"
|
|
value={(*start_date).clone()}
|
|
onchange={
|
|
let start_date = start_date.clone();
|
|
Callback::from(move |e: Event| {
|
|
let target = e.target_dyn_into::<HtmlInputElement>().unwrap();
|
|
start_date.set(target.value());
|
|
})
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">{"End Date"}</label>
|
|
<input
|
|
type="date"
|
|
class="form-control"
|
|
value={(*end_date).clone()}
|
|
onchange={
|
|
let end_date = end_date.clone();
|
|
Callback::from(move |e: Event| {
|
|
let target = e.target_dyn_into::<HtmlInputElement>().unwrap();
|
|
end_date.set(target.value());
|
|
})
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button
|
|
type="button"
|
|
class="btn btn-secondary"
|
|
onclick={
|
|
let show_report_modal = show_report_modal.clone();
|
|
Callback::from(move |_| show_report_modal.set(false))
|
|
}
|
|
>
|
|
{"Cancel"}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary"
|
|
onclick={on_generate_report}
|
|
>
|
|
{"Generate Report"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
} |