initial commit
This commit is contained in:
460
platform/src/views/companies_view.rs
Normal file
460
platform/src/views/companies_view.rs
Normal file
@@ -0,0 +1,460 @@
|
||||
use yew::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use crate::routing::AppView;
|
||||
use crate::components::{ViewComponent, EmptyState, RegistrationWizard};
|
||||
use crate::models::*;
|
||||
use crate::services::{CompanyService, CompanyRegistration, RegistrationStatus};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct CompaniesViewProps {
|
||||
pub on_navigate: Option<Callback<AppView>>,
|
||||
#[prop_or_default]
|
||||
pub show_registration: bool,
|
||||
#[prop_or_default]
|
||||
pub registration_success: Option<u32>,
|
||||
#[prop_or_default]
|
||||
pub registration_failure: bool,
|
||||
}
|
||||
|
||||
pub enum CompaniesViewMsg {
|
||||
LoadCompanies,
|
||||
CompaniesLoaded(Vec<Company>),
|
||||
LoadRegistrations,
|
||||
RegistrationsLoaded(Vec<CompanyRegistration>),
|
||||
SwitchToCompany(String),
|
||||
ShowRegistration,
|
||||
RegistrationComplete(Company),
|
||||
BackToCompanies,
|
||||
ViewCompany(u32),
|
||||
ContinueRegistration(CompanyRegistration),
|
||||
StartNewRegistration,
|
||||
DeleteRegistration(u32),
|
||||
ShowNewRegistrationForm,
|
||||
HideNewRegistrationForm,
|
||||
}
|
||||
|
||||
pub struct CompaniesView {
|
||||
companies: Vec<Company>,
|
||||
registrations: Vec<CompanyRegistration>,
|
||||
loading: bool,
|
||||
show_registration: bool,
|
||||
current_registration: Option<CompanyRegistration>,
|
||||
show_new_registration_form: bool,
|
||||
}
|
||||
|
||||
impl Component for CompaniesView {
|
||||
type Message = CompaniesViewMsg;
|
||||
type Properties = CompaniesViewProps;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
// Load companies and registrations on component creation
|
||||
ctx.link().send_message(CompaniesViewMsg::LoadCompanies);
|
||||
ctx.link().send_message(CompaniesViewMsg::LoadRegistrations);
|
||||
|
||||
Self {
|
||||
companies: Vec::new(),
|
||||
registrations: Vec::new(),
|
||||
loading: true,
|
||||
show_registration: ctx.props().show_registration,
|
||||
current_registration: None,
|
||||
show_new_registration_form: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
CompaniesViewMsg::LoadCompanies => {
|
||||
self.loading = true;
|
||||
// Load companies from service
|
||||
let companies = CompanyService::get_companies();
|
||||
ctx.link().send_message(CompaniesViewMsg::CompaniesLoaded(companies));
|
||||
false
|
||||
}
|
||||
CompaniesViewMsg::CompaniesLoaded(companies) => {
|
||||
self.companies = companies;
|
||||
self.loading = false;
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::LoadRegistrations => {
|
||||
// Load actual registrations from service
|
||||
let registrations = CompanyService::get_registrations();
|
||||
ctx.link().send_message(CompaniesViewMsg::RegistrationsLoaded(registrations));
|
||||
false
|
||||
}
|
||||
CompaniesViewMsg::RegistrationsLoaded(registrations) => {
|
||||
self.registrations = registrations;
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::SwitchToCompany(company_id) => {
|
||||
// Navigate to company view
|
||||
if let Some(on_navigate) = &ctx.props().on_navigate {
|
||||
if let Ok(id) = company_id.parse::<u32>() {
|
||||
on_navigate.emit(AppView::CompanyView(id));
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
CompaniesViewMsg::ShowRegistration => {
|
||||
self.show_registration = true;
|
||||
self.current_registration = None; // Start fresh registration
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::StartNewRegistration => {
|
||||
self.show_registration = true;
|
||||
self.current_registration = None; // Start fresh registration
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::ContinueRegistration(registration) => {
|
||||
self.show_registration = true;
|
||||
self.current_registration = Some(registration);
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::RegistrationComplete(company) => {
|
||||
// Add new company to list and clear current registration
|
||||
let company_id = company.id;
|
||||
self.companies.push(company);
|
||||
self.current_registration = None;
|
||||
self.show_registration = false;
|
||||
|
||||
// Navigate to registration success step
|
||||
if let Some(on_navigate) = &ctx.props().on_navigate {
|
||||
on_navigate.emit(AppView::EntitiesRegisterSuccess(company_id));
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::ViewCompany(company_id) => {
|
||||
// Navigate to company view
|
||||
if let Some(on_navigate) = &ctx.props().on_navigate {
|
||||
on_navigate.emit(AppView::CompanyView(company_id));
|
||||
}
|
||||
false
|
||||
}
|
||||
CompaniesViewMsg::BackToCompanies => {
|
||||
self.show_registration = false;
|
||||
self.current_registration = None;
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::DeleteRegistration(registration_id) => {
|
||||
// Remove registration from list
|
||||
self.registrations.retain(|r| r.id != registration_id);
|
||||
|
||||
// Update storage
|
||||
let _ = CompanyService::save_registrations(&self.registrations);
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::ShowNewRegistrationForm => {
|
||||
self.show_new_registration_form = true;
|
||||
true
|
||||
}
|
||||
CompaniesViewMsg::HideNewRegistrationForm => {
|
||||
self.show_new_registration_form = false;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let link = ctx.link();
|
||||
|
||||
// Check if we should show success state
|
||||
if ctx.props().registration_success.is_some() {
|
||||
// Show success state
|
||||
html! {
|
||||
<ViewComponent
|
||||
title={Some("Registration Successful".to_string())}
|
||||
description={Some("Your company registration has been completed successfully".to_string())}
|
||||
>
|
||||
<RegistrationWizard
|
||||
on_registration_complete={link.callback(CompaniesViewMsg::RegistrationComplete)}
|
||||
on_back_to_companies={link.callback(|_| CompaniesViewMsg::BackToCompanies)}
|
||||
success_company_id={ctx.props().registration_success}
|
||||
show_failure={false}
|
||||
force_fresh_start={false}
|
||||
continue_registration={None}
|
||||
continue_step={None}
|
||||
/>
|
||||
</ViewComponent>
|
||||
}
|
||||
} else if self.show_registration {
|
||||
// Registration view
|
||||
html! {
|
||||
<ViewComponent
|
||||
title={Some("Register New Company".to_string())}
|
||||
description={Some("Complete the registration process to create your new company".to_string())}
|
||||
>
|
||||
<RegistrationWizard
|
||||
on_registration_complete={link.callback(CompaniesViewMsg::RegistrationComplete)}
|
||||
on_back_to_companies={link.callback(|_| CompaniesViewMsg::BackToCompanies)}
|
||||
success_company_id={None}
|
||||
show_failure={ctx.props().registration_failure}
|
||||
force_fresh_start={self.current_registration.is_none()}
|
||||
continue_registration={self.current_registration.as_ref().map(|r| r.form_data.clone())}
|
||||
continue_step={self.current_registration.as_ref().map(|r| r.current_step)}
|
||||
/>
|
||||
</ViewComponent>
|
||||
}
|
||||
} else {
|
||||
// Main companies view with unified table
|
||||
html! {
|
||||
<ViewComponent
|
||||
title={Some("Companies".to_string())}
|
||||
description={Some("Manage your companies and registrations".to_string())}
|
||||
>
|
||||
{self.render_companies_content(ctx)}
|
||||
</ViewComponent>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CompaniesView {
|
||||
fn render_companies_content(&self, ctx: &Context<Self>) -> Html {
|
||||
let link = ctx.link();
|
||||
|
||||
if self.loading {
|
||||
return html! {
|
||||
<div class="text-center py-5">
|
||||
<div class="spinner-border text-primary mb-3" role="status">
|
||||
<span class="visually-hidden">{"Loading..."}</span>
|
||||
</div>
|
||||
<p class="text-muted">{"Loading companies..."}</p>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
|
||||
if self.companies.is_empty() && self.registrations.is_empty() {
|
||||
return html! {
|
||||
<div class="text-center py-5">
|
||||
<EmptyState
|
||||
icon={"building".to_string()}
|
||||
title={"No companies found".to_string()}
|
||||
description={"Create and manage your owned companies and corporate entities for business operations.".to_string()}
|
||||
primary_action={None}
|
||||
secondary_action={None}
|
||||
/>
|
||||
<div class="mt-4">
|
||||
<button
|
||||
class="btn btn-success btn-lg"
|
||||
onclick={link.callback(|_| CompaniesViewMsg::StartNewRegistration)}
|
||||
>
|
||||
<i class="bi bi-plus-circle me-2"></i>{"Register Your First Company"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
|
||||
html! {
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{self.render_companies_table(ctx)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn render_companies_table(&self, ctx: &Context<Self>) -> Html {
|
||||
let link = ctx.link();
|
||||
|
||||
html! {
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-building me-2"></i>{"Companies & Registrations"}
|
||||
</h5>
|
||||
<small class="text-muted">
|
||||
{format!("{} companies, {} pending registrations", self.companies.len(), self.registrations.len())}
|
||||
</small>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-success"
|
||||
onclick={link.callback(|_| CompaniesViewMsg::StartNewRegistration)}
|
||||
>
|
||||
<i class="bi bi-plus-circle me-2"></i>{"New Registration"}
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>{"Name"}</th>
|
||||
<th>{"Type"}</th>
|
||||
<th>{"Status"}</th>
|
||||
<th>{"Date"}</th>
|
||||
<th>{"Progress"}</th>
|
||||
<th class="text-end">{"Actions"}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
// Render active companies first
|
||||
{for self.companies.iter().map(|company| {
|
||||
let company_id = company.id;
|
||||
let on_view = {
|
||||
let link = link.clone();
|
||||
Callback::from(move |e: MouseEvent| {
|
||||
e.prevent_default();
|
||||
link.emit(CompaniesViewMsg::ViewCompany(company_id));
|
||||
})
|
||||
};
|
||||
let on_switch = {
|
||||
let link = link.clone();
|
||||
Callback::from(move |e: MouseEvent| {
|
||||
e.prevent_default();
|
||||
link.emit(CompaniesViewMsg::SwitchToCompany(company_id.to_string()));
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<tr key={format!("company-{}", company.id)}>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-building text-success me-2"></i>
|
||||
<strong>{&company.name}</strong>
|
||||
</div>
|
||||
</td>
|
||||
<td>{company.company_type.to_string()}</td>
|
||||
<td>
|
||||
<span class={company.status.get_badge_class()}>
|
||||
{company.status.to_string()}
|
||||
</span>
|
||||
</td>
|
||||
<td>{&company.incorporation_date}</td>
|
||||
<td>
|
||||
<span class="badge bg-success">{"Complete"}</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group">
|
||||
<button
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
onclick={on_view}
|
||||
title="View company details"
|
||||
>
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-primary"
|
||||
onclick={on_switch}
|
||||
title="Switch to this entity"
|
||||
>
|
||||
<i class="bi bi-box-arrow-in-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
})}
|
||||
|
||||
// Render pending registrations
|
||||
{for self.registrations.iter().map(|registration| {
|
||||
let registration_clone = registration.clone();
|
||||
let registration_id = registration.id;
|
||||
let can_continue = matches!(registration.status, RegistrationStatus::Draft | RegistrationStatus::PendingPayment | RegistrationStatus::PaymentFailed);
|
||||
|
||||
let on_continue = {
|
||||
let link = link.clone();
|
||||
let reg = registration_clone.clone();
|
||||
Callback::from(move |e: MouseEvent| {
|
||||
e.prevent_default();
|
||||
link.emit(CompaniesViewMsg::ContinueRegistration(reg.clone()));
|
||||
})
|
||||
};
|
||||
|
||||
let on_delete = {
|
||||
let link = link.clone();
|
||||
Callback::from(move |e: MouseEvent| {
|
||||
e.prevent_default();
|
||||
if web_sys::window()
|
||||
.unwrap()
|
||||
.confirm_with_message("Are you sure you want to delete this registration?")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
link.emit(CompaniesViewMsg::DeleteRegistration(registration_id));
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<tr key={format!("registration-{}", registration.id)}>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-file-earmark-text text-warning me-2"></i>
|
||||
{®istration.company_name}
|
||||
<small class="text-muted ms-2">{"(Registration)"}</small>
|
||||
</div>
|
||||
</td>
|
||||
<td>{registration.company_type.to_string()}</td>
|
||||
<td>
|
||||
<span class={format!("badge {}", registration.status.get_badge_class())}>
|
||||
{registration.status.to_string()}
|
||||
</span>
|
||||
</td>
|
||||
<td>{®istration.created_at}</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="progress me-2" style="width: 60px; height: 8px;">
|
||||
<div
|
||||
class="progress-bar bg-info"
|
||||
style={format!("width: {}%", (registration.current_step as f32 / 5.0 * 100.0))}
|
||||
></div>
|
||||
</div>
|
||||
<small class="text-muted">{format!("{}/5", registration.current_step)}</small>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group">
|
||||
{if can_continue {
|
||||
html! {
|
||||
<button
|
||||
class="btn btn-sm btn-success"
|
||||
onclick={on_continue}
|
||||
title="Continue registration"
|
||||
>
|
||||
<i class="bi bi-play-circle"></i>
|
||||
</button>
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
<button
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
disabled={true}
|
||||
title="Registration complete"
|
||||
>
|
||||
<i class="bi bi-check-circle"></i>
|
||||
</button>
|
||||
}
|
||||
}}
|
||||
<button
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
onclick={on_delete}
|
||||
title="Delete registration"
|
||||
>
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(CompaniesViewWrapper)]
|
||||
pub fn companies_view(props: &CompaniesViewProps) -> Html {
|
||||
html! {
|
||||
<CompaniesView
|
||||
on_navigate={props.on_navigate.clone()}
|
||||
show_registration={props.show_registration}
|
||||
registration_success={props.registration_success}
|
||||
registration_failure={props.registration_failure}
|
||||
/>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user