portal, platform, and server fixes

This commit is contained in:
Timur Gordon
2025-06-30 17:01:40 +02:00
parent 1c96fa4087
commit a5b46bffb1
59 changed files with 9158 additions and 1057 deletions

View File

@@ -13,7 +13,7 @@ This guide covers the complete production setup for the Stripe Elements integrat
- **Comprehensive error handling** and user guidance
### ✅ 2. Backend Server (`src/bin/server.rs`)
- **Payment intent creation endpoint**: `/company/create-payment-intent`
- **Payment intent creation endpoint**: `/api/company/create-payment-intent`
- **Webhook handling**: `/webhooks/stripe`
- **Payment success page**: `/company/payment-success`
- **Health check**: `/api/health`
@@ -232,7 +232,7 @@ cargo build --release --features server
curl http://127.0.0.1:8080/api/health
# Test payment intent creation
curl -X POST http://127.0.0.1:8080/company/create-payment-intent \
curl -X POST http://127.0.0.1:8080/api/company/create-payment-intent \
-H "Content-Type: application/json" \
-d '{"company_name":"Test","company_type":"Single FZC","payment_plan":"monthly","final_agreement":true,"agreements":["terms"]}'

View File

@@ -183,7 +183,7 @@
// Create payment intent on server
window.createPaymentIntent = async function(formDataJson) {
console.log('💳 Creating payment intent for company registration...');
console.log('🔧 Server endpoint: /company/create-payment-intent');
console.log('🔧 Server endpoint: /api/company/create-payment-intent');
try {
// Parse the JSON string from Rust
@@ -201,7 +201,7 @@
final_agreement: formData.final_agreement
});
const response = await fetch('http://127.0.0.1:3001/company/create-payment-intent', {
const response = await fetch('http://127.0.0.1:3001/api/company/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -424,7 +424,7 @@
};
console.log('✅ Stripe integration ready for company registration payments');
console.log('🔧 Server endpoint: /company/create-payment-intent');
console.log('🔧 Server endpoint: /api/company/create-payment-intent');
console.log('💡 Navigate to Entities → Register Company → Step 4 to process payments');
// Add a test function for manual payment testing

View File

@@ -40,7 +40,7 @@ echo "✅ Build successful!"
echo ""
echo "🌐 Starting server on http://${HOST:-127.0.0.1}:${PORT:-8080}"
echo "📊 Health check: http://${HOST:-127.0.0.1}:${PORT:-8080}/api/health"
echo "💳 Payment endpoint: http://${HOST:-127.0.0.1}:${PORT:-8080}/company/create-payment-intent"
echo "💳 Payment endpoint: http://${HOST:-127.0.0.1}:${PORT:-8080}/api/company/create-payment-intent"
echo ""
echo "🧪 To test the integration:"
echo " 1. Open http://${HOST:-127.0.0.1}:${PORT:-8080} in your browser"

View File

@@ -491,7 +491,7 @@ async fn main() -> anyhow::Result<()> {
let app = Router::new()
// API routes
.route("/api/health", get(health_check))
.route("/company/create-payment-intent", post(create_payment_intent))
.route("/api/company/create-payment-intent", post(create_payment_intent))
.route("/resident/create-payment-intent", post(create_resident_payment_intent))
.route("/company/payment-success", get(payment_success))
.route("/company/payment-failure", get(payment_failure))
@@ -516,7 +516,7 @@ async fn main() -> anyhow::Result<()> {
info!("Starting server on {}", addr);
info!("Health check: http://{}/api/health", addr);
info!("Payment endpoint: http://{}/company/create-payment-intent", addr);
info!("Payment endpoint: http://{}/api/company/create-payment-intent", addr);
// Start the server
let listener = tokio::net::TcpListener::bind(&addr).await?;

View File

@@ -534,8 +534,8 @@ pub fn expenses_tab(props: &ExpensesTabProps) -> Html {
// Expense Actions and Table
<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">
<div class="card shadow-soft" style="border: none;">
<div class="card-header bg-white py-3" style="border-bottom: none;">
<div class="d-flex justify-content-between align-items-center">
<div>
<h5 class="mb-0 fw-bold">{"Expense Entries"}</h5>

View File

@@ -87,7 +87,7 @@ pub fn financial_reports_tab(props: &FinancialReportsTabProps) -> Html {
</button>
</div>
<div class="card shadow-soft border-0">
<div class="card shadow-soft" style="border: none;">
<div class="card-body">
if state.financial_reports.is_empty() {
<div class="text-center py-5">

View File

@@ -28,7 +28,7 @@ pub fn overview_tab(props: &OverviewTabProps) -> Html {
// 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 shadow-soft card-hover" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center justify-content-between">
<div>
@@ -47,7 +47,7 @@ pub fn overview_tab(props: &OverviewTabProps) -> Html {
</div>
<div class="col-md-3">
<div class="card border-info shadow-soft card-hover">
<div class="card shadow-soft card-hover" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center justify-content-between">
<div>
@@ -66,7 +66,7 @@ pub fn overview_tab(props: &OverviewTabProps) -> Html {
</div>
<div class="col-md-3">
<div class="card border-success shadow-soft card-hover">
<div class="card shadow-soft card-hover" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center justify-content-between">
<div>
@@ -85,7 +85,7 @@ pub fn overview_tab(props: &OverviewTabProps) -> Html {
</div>
<div class="col-md-3">
<div class="card border-primary shadow-soft card-hover">
<div class="card shadow-soft card-hover" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center justify-content-between">
<div>
@@ -107,8 +107,8 @@ pub fn overview_tab(props: &OverviewTabProps) -> Html {
// 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">
<div class="card shadow-soft" style="border: none;">
<div class="card-header bg-white py-3" style="border-bottom: none;">
<h5 class="mb-0 fw-bold">{"Recent Transactions"}</h5>
<small class="text-muted">{"Latest payments made and received"}</small>
</div>

View File

@@ -520,8 +520,8 @@ pub fn revenue_tab(props: &RevenueTabProps) -> Html {
// Revenue Actions and Table
<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">
<div class="card shadow-soft" style="border: none;">
<div class="card-header bg-white py-3" style="border-bottom: none;">
<div class="d-flex justify-content-between align-items-center">
<div>
<h5 class="mb-0 fw-bold">{"Revenue Entries"}</h5>

View File

@@ -23,8 +23,8 @@ pub fn tax_tab(props: &TaxTabProps) -> Html {
<div class="row g-4">
<div class="col-lg-8">
<div class="card shadow-soft border-0">
<div class="card-header bg-white border-bottom-0 py-3">
<div class="card shadow-soft" style="border: none;">
<div class="card-header bg-white py-3" style="border-bottom: none;">
<h5 class="mb-0 fw-bold">{"Tax Summary"}</h5>
</div>
<div class="card-body">
@@ -62,8 +62,8 @@ pub fn tax_tab(props: &TaxTabProps) -> Html {
</div>
<div class="col-lg-4">
<div class="card shadow-soft border-0">
<div class="card-header bg-white border-bottom-0 py-3">
<div class="card shadow-soft" style="border: none;">
<div class="card-header bg-white py-3" style="border-bottom: none;">
<h5 class="mb-0 fw-bold">{"Tax Actions"}</h5>
</div>
<div class="card-body">

View File

@@ -121,13 +121,13 @@ pub fn inbox(props: &InboxProps) -> Html {
<style>
{r#"
.inbox-card {
border: 1px solid #e9ecef;
border: none;
border-radius: 12px;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.inbox-card:hover {
border-color: #dee2e6;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
}
.notification-item {
border-radius: 8px;

View File

@@ -50,11 +50,41 @@ pub fn header(props: &HeaderProps) -> Html {
<i class="bi bi-list"></i>
</button>
<div class="d-flex align-items-center">
<i class="bi bi-building-gear text-primary fs-4 me-2"></i>
<div>
<h5 class="mb-0 fw-bold">{"Zanzibar Digital Freezone"}</h5>
// Enhanced title with better typography
<div class="ml-4 d-flex align-items-baseline">
<h4 class="mb-0 me-2" style="
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
font-weight: 700;
font-size: 1.35rem;
background: linear-gradient(135deg, #0099FF 0%, #00CC66 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: -0.02em;
line-height: 1.2;
">
{"Zanzibar"}
</h4>
</div>
<div style="
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
font-weight: 500;
font-size: 0.9rem;
color: #6c757d;
letter-spacing: 0.3px;
margin-top: -2px;
">
{"DIGITAL FREEZONE"}
</div>
{if let Some(entity) = entity_name {
html! { <small class="text-info">{entity}</small> }
html! {
<small class="text-info d-block" style="
font-size: 0.75rem;
font-weight: 500;
margin-top: 1px;
">{entity}</small>
}
} else {
html! {}
}}

View File

@@ -143,8 +143,31 @@ impl ResidentLandingOverlay {
<div class="mb-4">
<i class="bi bi-globe2" style="font-size: 4rem; opacity: 0.9;"></i>
</div>
<h1 class="display-4 fw-bold mb-4">
{"Zanzibar Digital Freezone"}
<h1 class="display-4 mb-4" style="
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
font-weight: 700;
letter-spacing: -0.02em;
line-height: 1.1;
">
<span style="
background: linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.8) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
">
{"Zanzibar"}
</span>
<br/>
<span style="
font-size: 0.7em;
font-weight: 500;
letter-spacing: 2px;
color: rgba(255,255,255,0.9);
text-transform: uppercase;
">
{"Digital Freezone"}
</span>
</h1>
<h2 class="h3 mb-4 text-white-75">
{"Your Gateway to Digital Residency"}

View File

@@ -42,7 +42,7 @@ pub fn view_component(props: &ViewComponentProps) -> Html {
};
html! {
<div class="container-fluid" style="max-width: 1100px;">
<div class="container-fluid" style="max-width: 1400px;">
<div class="px-3 px-md-4 px-lg-5 px-xl-6">
// Breadcrumbs (if provided)
if let Some(breadcrumbs) = &props.breadcrumbs {
@@ -69,7 +69,7 @@ pub fn view_component(props: &ViewComponentProps) -> Html {
// Left side: Title and description
<div>
if let Some(title) = &props.title {
<h2 class="mb-1 fw-bold">{title}</h2>
<h2 class="mb-1">{title}</h2>
}
if let Some(description) = &props.description {
<p class="text-muted mb-0">{description}</p>
@@ -87,8 +87,8 @@ pub fn view_component(props: &ViewComponentProps) -> Html {
// Modern tabs navigation (if provided)
if let Some(tabs) = &props.tabs {
<div class="mb-0">
<ul class="nav nav-tabs border-bottom-0" role="tablist">
<div class="mb-4">
<div class="bg-white rounded-3 shadow-sm p-2 d-inline-flex">
{for tabs.keys().map(|tab_name| {
let is_active = *active_tab == *tab_name;
let tab_name_clone = tab_name.clone();
@@ -102,34 +102,27 @@ pub fn view_component(props: &ViewComponentProps) -> Html {
};
html! {
<li class="nav-item" role="presentation">
<button
class={classes!(
"nav-link",
"px-3",
"py-2",
"small",
"border",
"border-bottom-0",
"bg-light",
"text-muted",
if is_active {
"active bg-white text-dark border-primary border-bottom-0"
} else {
"border-light"
}
)}
type="button"
role="tab"
onclick={on_click}
style={if is_active { "margin-bottom: -1px; z-index: 1; position: relative;" } else { "" }}
>
{tab_name}
</button>
</li>
<button
class={classes!(
"btn",
"btn-sm",
"me-1",
"border-0",
"small",
if is_active {
"bg-light text-dark"
} else {
"bg-transparent text-muted"
}
)}
type="button"
onclick={on_click}
>
{tab_name}
</button>
}
})}
</ul>
</div>
</div>
}
} else {
@@ -199,7 +192,7 @@ pub fn view_component(props: &ViewComponentProps) -> Html {
// Tab Content (if tabs are provided)
if let Some(tabs) = &props.tabs {
<div class="tab-content border border-top-0 rounded-bottom bg-white p-4">
<div class="tab-content">
{for tabs.iter().map(|(tab_name, content)| {
let is_active = *active_tab == *tab_name;
html! {

View File

@@ -123,7 +123,7 @@ impl AppView {
AppView::Login => "Login".to_string(),
AppView::Home => "Home".to_string(),
AppView::Administration => "Administration".to_string(),
AppView::PersonAdministration => "Administration".to_string(),
AppView::PersonAdministration => "Settings".to_string(),
AppView::Business => "Business".to_string(),
AppView::Accounting => "Accounting".to_string(),
AppView::Contracts => "Contracts".to_string(),

View File

@@ -261,7 +261,7 @@ impl CompaniesView {
let link = ctx.link();
html! {
<div class="card border-0 shadow-sm">
<div class="card shadow-sm" style="border: none;">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="d-flex align-items-center">

View File

@@ -297,7 +297,7 @@ impl ContractsViewComponent {
// Filters Section
<div class="row mb-4">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card shadow-sm" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-3">
<div class="bg-primary bg-opacity-10 rounded-3 p-2 me-3">
@@ -348,7 +348,7 @@ impl ContractsViewComponent {
// Contracts Table
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card shadow-sm" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-3">
<div class="bg-success bg-opacity-10 rounded-3 p-2 me-3">
@@ -449,7 +449,7 @@ impl ContractsViewComponent {
html! {
<div class="row">
<div class="col-lg-8">
<div class="card border-0 shadow-sm">
<div class="card shadow-sm" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-4">
<div class="bg-primary bg-opacity-10 rounded-3 p-2 me-3">
@@ -541,7 +541,7 @@ Payment will be made according to the following schedule:
</div>
<div class="col-lg-4">
<div class="card border-0 shadow-sm mb-4">
<div class="card shadow-sm mb-4" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-3">
<div class="bg-info bg-opacity-10 rounded-3 p-2 me-3">
@@ -560,7 +560,7 @@ Payment will be made according to the following schedule:
</div>
</div>
<div class="card border-0 shadow-sm">
<div class="card shadow-sm" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-3">
<div class="bg-warning bg-opacity-10 rounded-3 p-2 me-3">

View File

@@ -152,6 +152,7 @@ impl Component for EntitiesView {
<ViewComponent
title={Some("Registration Successful".to_string())}
description={Some("Your company registration has been completed successfully".to_string())}
use_modern_header={true}
>
<RegistrationWizard
on_registration_complete={link.callback(EntitiesViewMsg::RegistrationComplete)}
@@ -170,6 +171,7 @@ impl Component for EntitiesView {
<ViewComponent
title={Some("Register New Company".to_string())}
description={Some("Complete the registration process to create your new company".to_string())}
use_modern_header={true}
>
<RegistrationWizard
on_registration_complete={link.callback(EntitiesViewMsg::RegistrationComplete)}
@@ -198,6 +200,7 @@ impl Component for EntitiesView {
description={Some("Manage your companies and registrations".to_string())}
tabs={Some(tabs)}
default_tab={Some("Companies".to_string())}
use_modern_header={true}
/>
}
}
@@ -255,7 +258,7 @@ impl EntitiesView {
<div class="row">
<div class="col-12">
// Header with new registration button
<div class="card mb-4">
<div class="card mb-4 shadow-sm" style="border: none;">
<div class="card-body text-center py-4">
<div class="mb-3">
<i class="bi bi-plus-circle-fill text-success" style="font-size: 3rem;"></i>
@@ -290,7 +293,7 @@ impl EntitiesView {
if self.registrations.is_empty() {
return html! {
<div class="card">
<div class="card shadow-sm" style="border: none;">
<div class="card-header">
<h5 class="mb-0">
<i class="bi bi-file-earmark-text me-2"></i>{"Pending Registrations"}
@@ -306,7 +309,7 @@ impl EntitiesView {
}
html! {
<div class="card">
<div class="card shadow-sm" style="border: none;">
<div class="card-header d-flex justify-content-between align-items-center">
<div>
<h5 class="mb-0">

View File

@@ -273,7 +273,7 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html
// Account Settings Tab (Person-specific)
tabs.insert("Account Settings".to_string(), html! {
<div class="card border-0 shadow-sm">
<div class="card shadow-sm" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-4">
<div class="bg-primary bg-opacity-10 rounded-3 p-3 me-3">
@@ -327,7 +327,7 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html
// Privacy & Security Tab
tabs.insert("Privacy & Security".to_string(), html! {
<div class="card border-0 shadow-sm">
<div class="card shadow-sm" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-4">
<div class="bg-success bg-opacity-10 rounded-3 p-3 me-3">
@@ -393,7 +393,7 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html
<div class="row">
// Subscription Tier Pane
<div class="col-lg-4 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card shadow-sm h-100" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-3">
<div class="bg-warning bg-opacity-10 rounded-3 p-2 me-3">
@@ -446,7 +446,7 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html
<div class="col-lg-8">
// Payments Table Pane
<div class="card border-0 shadow-sm mb-4">
<div class="card shadow-sm mb-4" style="border: none;">
<div class="card-body p-4">
<div class="d-flex align-items-center mb-3">
<div class="bg-info bg-opacity-10 rounded-3 p-2 me-3">
@@ -491,7 +491,7 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html
</div>
// Payment Methods Pane
<div class="card border-0 shadow-sm">
<div class="card shadow-sm" style="border: none;">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="d-flex align-items-center">
@@ -516,7 +516,7 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html
<div class="row">
{for billing_api.payment_methods.iter().map(|method| html! {
<div class="col-md-6 mb-3">
<div class="card border">
<div class="card shadow-sm" style="border: none;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex align-items-center">
@@ -578,26 +578,13 @@ pub fn person_administration_view(props: &PersonAdministrationViewProps) -> Html
html! {
<>
<div class="container-fluid px-3 px-md-4 px-lg-5 px-xl-6">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="h3 mb-1">{"Settings"}</h1>
<p class="text-muted mb-0">{"Manage your account settings and preferences"}</p>
</div>
</div>
<ViewComponent
title={None::<String>}
description={None::<String>}
tabs={Some(tabs)}
default_tab={Some("Account Settings".to_string())}
use_modern_header={true}
/>
</div>
</div>
</div>
<ViewComponent
title={Some("Settings".to_string())}
description={Some("Manage your account settings and preferences".to_string())}
tabs={Some(tabs)}
default_tab={Some("Account Settings".to_string())}
use_modern_header={true}
/>
// Plan Selection Modal
if *show_plan_modal {

View File

@@ -77,6 +77,7 @@ body {
z-index: 1030;
background-color: #212529 !important;
color: white;
border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;
}
.header .container-fluid {

View File

@@ -155,7 +155,7 @@ window.createPaymentIntent = async function(formDataJson) {
console.log('Form data:', formData);
const response = await fetch('/company/create-payment-intent', {
const response = await fetch('/api/company/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',