mirror of
https://github.com/wahyd4/links.git
synced 2026-08-08 21:04:53 +10:00
Add fire planning mini app
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
# FIRE Planning Calculator - Educational Enhancements
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
This document describes the educational content and explanations added to the FIRE Planning mini app to help users understand the calculations and methodology.
|
||||
|
||||
---
|
||||
|
||||
## 1. Educational Info Section (Top of Page)
|
||||
|
||||
**Location:** Below the header, before the input form
|
||||
|
||||
**Content:**
|
||||
- **Green highlighted box** with lightbulb icon
|
||||
- Explanation of FIRE (Financial Independence, Retire Early) movement
|
||||
- Detailed explanation of the **4% Safe Withdrawal Rate Rule**
|
||||
- Formula breakdown: **FIRE Target = Annual Expenses × 25**
|
||||
- Practical example: A$80,000/year expenses → A$2,000,000 target
|
||||
- Trinity Study reference
|
||||
|
||||
**Purpose:** Educate users before they enter data
|
||||
|
||||
---
|
||||
|
||||
## 2. Calculation Methodology Box
|
||||
|
||||
**Location:** In results section, above summary cards
|
||||
|
||||
**Content:**
|
||||
- **Blue highlighted box** explaining step-by-step calculations
|
||||
- Shows formulas with **real-time calculated values**:
|
||||
1. Annual Savings = Income × Savings Rate
|
||||
2. FIRE Target (4% Rule) = Annual Expenses × 25
|
||||
3. Yearly Growth = (Net Worth + Savings) × (1 + Return Rate)
|
||||
4. Inflation-Adjusted Target = FIRE Target × (1 + Inflation)^years
|
||||
5. Progress % = (Net Worth ÷ Adjusted Target) × 100%
|
||||
|
||||
**Purpose:** Transparency in how numbers are calculated
|
||||
|
||||
---
|
||||
|
||||
## 3. Enhanced Summary Cards
|
||||
|
||||
**Added to each card:**
|
||||
- **Subtitle explanations** below the main value
|
||||
- Context about what each number means
|
||||
- Additional details like:
|
||||
- "Based on 4% rule (25× expenses)"
|
||||
- "Adjusted for X% annual inflation"
|
||||
- "In Y years (target year)"
|
||||
- Suggestions when FIRE not achieved
|
||||
|
||||
**Purpose:** Help users interpret each metric
|
||||
|
||||
---
|
||||
|
||||
## 4. Scenario Analysis Explanation
|
||||
|
||||
**Location:** Above scenario tabs
|
||||
|
||||
**Content:**
|
||||
- Explanation of why different scenarios matter
|
||||
- What each scenario represents:
|
||||
- **Conservative (6%)**: Bonds/safer investments
|
||||
- **Neutral (8%)**: Balanced portfolio
|
||||
- **Optimistic (10%)**: Aggressive stock-heavy portfolio
|
||||
- Note about historical market variance
|
||||
|
||||
**Purpose:** Help users understand risk and return trade-offs
|
||||
|
||||
---
|
||||
|
||||
## 5. Chart Descriptions
|
||||
|
||||
**Added before each chart:**
|
||||
|
||||
### Net Worth Growth Chart
|
||||
- Explains green line (projected net worth with compound growth)
|
||||
- Explains red dashed line (inflation-adjusted FIRE target)
|
||||
- Clear indication of what "crossing" means (FIRE achieved!)
|
||||
|
||||
### Progress Chart
|
||||
- Explains the percentage calculation
|
||||
- Notes that 100% = FIRE achieved
|
||||
|
||||
### Projection Table
|
||||
- Explains what each column represents
|
||||
- Notes that green rows = FIRE achieved years
|
||||
|
||||
**Purpose:** Make visualizations self-explanatory
|
||||
|
||||
---
|
||||
|
||||
## 6. FAQ Section (Bottom of Page)
|
||||
|
||||
**Location:** Below all results and charts
|
||||
|
||||
**6 Comprehensive FAQs:**
|
||||
|
||||
### Q1: Why multiply annual expenses by 25?
|
||||
- Explains 4% rule mathematics
|
||||
- Provides concrete example
|
||||
- Shows the inverse relationship (1 ÷ 0.04 = 25)
|
||||
|
||||
### Q2: What's a realistic return rate?
|
||||
- Historical S&P 500 returns (~10% nominal)
|
||||
- Real returns after inflation (7-8%)
|
||||
- Different investor profiles (conservative/balanced/aggressive)
|
||||
- Recommended ranges (6-10%)
|
||||
|
||||
### Q3: How does compound growth work?
|
||||
- Formula: New Net Worth = (Previous + Savings) × (1 + Return)
|
||||
- Step-by-step numerical example
|
||||
- Explains exponential growth concept
|
||||
|
||||
### Q4: Why does FIRE target increase over time?
|
||||
- Inflation explanation
|
||||
- Purchasing power concept
|
||||
- Formula: Future = Current × (1 + Inflation)^years
|
||||
- Example: A$100k → A$145k in 15 years at 2.5% inflation
|
||||
|
||||
### Q5: Should I include my home in net worth?
|
||||
- Distinction between total net worth and investable assets
|
||||
- Why primary residence doesn't generate FIRE cash flow
|
||||
- Recommendation to track both separately
|
||||
|
||||
### Q6: Important Assumptions & Limitations
|
||||
- **Bulleted list of caveats:**
|
||||
- Constant savings/income assumption
|
||||
- Taxes and healthcare not included
|
||||
- Market volatility considerations
|
||||
- Superannuation/401k exclusion
|
||||
- Disclaimer: Planning tool, not financial advice
|
||||
|
||||
**Purpose:** Address common questions and provide education
|
||||
|
||||
---
|
||||
|
||||
## Visual Design
|
||||
|
||||
All educational sections use:
|
||||
- **Color-coded backgrounds:**
|
||||
- Green gradient for FIRE/4% rule intro
|
||||
- Blue for calculation methodology
|
||||
- Yellow/cream for FAQ section
|
||||
- White cards for individual FAQs
|
||||
- **Icons** for visual interest and quick recognition
|
||||
- **Proper hierarchy** with headings and spacing
|
||||
- **Readable font sizes** (text-sm for detailed content)
|
||||
- **Responsive design** that works on mobile
|
||||
|
||||
---
|
||||
|
||||
## User Journey
|
||||
|
||||
1. **Learn first** - Educational intro sets context
|
||||
2. **Input data** - Clear help text on each field
|
||||
3. **Calculate** - See methodology explanation
|
||||
4. **Understand results** - Enhanced summary cards with context
|
||||
5. **Visualize** - Charts with descriptions
|
||||
6. **Deep dive** - Detailed table
|
||||
7. **Learn more** - FAQ section for deeper understanding
|
||||
|
||||
---
|
||||
|
||||
## Key Formulas Highlighted
|
||||
|
||||
1. **FIRE Target** = Annual Expenses × 25
|
||||
2. **Annual Savings** = Income × Savings Rate
|
||||
3. **Compound Growth** = (Net Worth + Savings) × (1 + Return Rate)
|
||||
4. **Inflation Adjustment** = Target × (1 + Inflation Rate)^years
|
||||
5. **Progress** = (Net Worth ÷ Adjusted Target) × 100%
|
||||
|
||||
---
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Transparency** - Users understand where numbers come from
|
||||
✅ **Education** - Learn FIRE principles while planning
|
||||
✅ **Confidence** - Make informed decisions with full context
|
||||
✅ **Trust** - Clear methodology builds credibility
|
||||
✅ **Self-service** - FAQ answers common questions without support
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements (Optional)
|
||||
|
||||
- Interactive tooltips on hover for input fields
|
||||
- Video tutorials or animated explanations
|
||||
- Links to Trinity Study and FIRE movement resources
|
||||
- Glossary of financial terms
|
||||
- Share/export functionality with explanations included
|
||||
- Multi-language support for international users
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** October 11, 2025
|
||||
**Version:** 1.1 (Educational Enhancement Release)
|
||||
@@ -101,11 +101,8 @@ Blog-like content management:
|
||||
- Tag categorization
|
||||
- Publication status tracking
|
||||
|
||||
#### IPTVCollection & IPTVChannel
|
||||
IPTV playlist management:
|
||||
- M3U playlist parsing
|
||||
- Channel metadata
|
||||
- Category organization
|
||||
#### mini_apps
|
||||
A collection of mini apps which can be located at mini_apps_views.py
|
||||
|
||||
#### Analytics Models
|
||||
- **ClickLog**: Individual click tracking with timestamps
|
||||
|
||||
@@ -25,7 +25,16 @@ class MiniAppsListView(TemplateView):
|
||||
'thumbnail': 'https://images.unsplash.com/photo-1589254065878-42c9da997008?w=400&h=300&fit=crop',
|
||||
'icon': 'fas fa-volume-up',
|
||||
'color': '#8e44ad'
|
||||
},{
|
||||
},
|
||||
{
|
||||
'name': 'FIRE Planning',
|
||||
'description': 'Plan your Financial Independence Retire Early (FIRE) journey with interactive calculators, projections, and visualizations.',
|
||||
'url': 'mini-apps-fire-planning',
|
||||
'thumbnail': 'https://images.unsplash.com/photo-1579621970563-ebec7560ff3e?w=400&h=300&fit=crop',
|
||||
'icon': 'fas fa-chart-line',
|
||||
'color': '#27ae60'
|
||||
},
|
||||
{
|
||||
'name': 'Weather Dashboard',
|
||||
'description': 'Real-time weather information with beautiful visualizations and forecasts.',
|
||||
'url': '#',
|
||||
@@ -55,3 +64,12 @@ class TTSView(TemplateView):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['page_title'] = 'Text-to-Speech'
|
||||
return context
|
||||
|
||||
|
||||
class FIREPlanningView(TemplateView):
|
||||
template_name = 'links/mini_apps/fire_planning.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['page_title'] = 'FIRE Planning Calculator'
|
||||
return context
|
||||
|
||||
@@ -0,0 +1,963 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% trans "FIRE Planning Calculator" %} - {% trans "Mini Apps" %}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.fire-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.fire-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.fire-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.fire-subtitle {
|
||||
color: #6b7280;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.input-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.input-field:focus {
|
||||
outline: none;
|
||||
border-color: #27ae60;
|
||||
box-shadow: 0 0 0 3px rgba(39, 174, 96, 0.1);
|
||||
}
|
||||
|
||||
.btn-calculate {
|
||||
background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
|
||||
color: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
width: 100%;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.btn-calculate:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 20px rgba(39, 174, 96, 0.3);
|
||||
}
|
||||
|
||||
.btn-reset {
|
||||
background: #6b7280;
|
||||
color: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
width: 100%;
|
||||
font-size: 1rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-reset:hover {
|
||||
background: #4b5563;
|
||||
}
|
||||
|
||||
.results-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
padding: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.results-card.show {
|
||||
display: block;
|
||||
animation: fadeIn 0.5s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.summary-card {
|
||||
background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
|
||||
padding: 1.5rem;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.summary-card.success {
|
||||
background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
|
||||
}
|
||||
|
||||
.summary-card.warning {
|
||||
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
margin-bottom: 2rem;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f3f4f6;
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
border-bottom: 2px solid #e5e7eb;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
.fire-achieved {
|
||||
background: #d1fae5 !important;
|
||||
}
|
||||
|
||||
.input-help {
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.scenario-tabs {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.scenario-tab {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #e5e7eb;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.scenario-tab.active {
|
||||
background: #27ae60;
|
||||
color: white;
|
||||
border-color: #27ae60;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-title i {
|
||||
margin-right: 0.5rem;
|
||||
color: #27ae60;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="fire-container">
|
||||
<!-- Header -->
|
||||
<div class="fire-header">
|
||||
<h1 class="fire-title">
|
||||
<i class="fas fa-chart-line"></i>
|
||||
FIRE Planning Calculator
|
||||
</h1>
|
||||
<p class="fire-subtitle">
|
||||
Plan Your Financial Independence and Early Retirement Journey
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Educational Info Section -->
|
||||
<div class="input-card mb-6" style="background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);">
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0 mr-4">
|
||||
<i class="fas fa-lightbulb text-4xl text-green-600"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-gray-800 mb-3">
|
||||
<i class="fas fa-graduation-cap mr-2"></i>
|
||||
Understanding FIRE & The 4% Rule
|
||||
</h2>
|
||||
<div class="text-gray-700 space-y-2 text-sm leading-relaxed">
|
||||
<p>
|
||||
<strong>FIRE (Financial Independence, Retire Early)</strong> is a movement focused on achieving financial freedom through aggressive saving and smart investing.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The 4% Rule:</strong> Based on the Trinity Study, this rule suggests you can safely withdraw 4% of your investment portfolio annually in retirement, adjusted for inflation, with a high probability your money will last 30+ years.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Your FIRE Target = Annual Expenses × 25</strong> (because 1 ÷ 0.04 = 25)
|
||||
</p>
|
||||
<p class="pt-2 border-t border-green-300 mt-3">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
<strong>Example:</strong> If you need A$80,000/year to live comfortably, your FIRE target is A$2,000,000.
|
||||
At 4% withdrawal, A$2M × 4% = A$80,000 per year.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Input Form -->
|
||||
<div class="input-card">
|
||||
<h2 class="section-title">
|
||||
<i class="fas fa-edit"></i>
|
||||
Your Financial Details
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="currentAge">Current Age</label>
|
||||
<input type="number" id="currentAge" class="input-field" value="35" min="18" max="100">
|
||||
<div class="input-help">Your current age in years</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="targetAge">Target FIRE Age</label>
|
||||
<input type="number" id="targetAge" class="input-field" value="50" min="18" max="100">
|
||||
<div class="input-help">Age you want to achieve financial independence</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="currentNetWorth">Current Net Worth (excl. home) - AUD</label>
|
||||
<input type="number" id="currentNetWorth" class="input-field" value="300000" step="1000">
|
||||
<div class="input-help">Investable assets excluding primary residence</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="totalNetWorth">Total Net Worth (incl. home) - AUD</label>
|
||||
<input type="number" id="totalNetWorth" class="input-field" value="900000" step="1000">
|
||||
<div class="input-help">Total net worth including property</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="annualExpenses">Annual Expenses (post-retirement) - AUD</label>
|
||||
<input type="number" id="annualExpenses" class="input-field" value="200000" step="1000">
|
||||
<div class="input-help">Expected yearly spending after FIRE</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="annualIncome">Annual Income (including RSU) - AUD</label>
|
||||
<input type="number" id="annualIncome" class="input-field" value="300000" step="1000">
|
||||
<div class="input-help">Total yearly income before tax</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="savingsRate">Savings Rate (%)</label>
|
||||
<input type="number" id="savingsRate" class="input-field" value="40" min="0" max="100" step="1">
|
||||
<div class="input-help">Percentage of income saved annually</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="returnRate">Expected Investment Return (%)</label>
|
||||
<input type="number" id="returnRate" class="input-field" value="8" min="0" max="30" step="0.1">
|
||||
<div class="input-help">Average annual investment return rate</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label class="input-label" for="inflationRate">Inflation Rate (%)</label>
|
||||
<input type="number" id="inflationRate" class="input-field" value="2.5" min="0" max="20" step="0.1">
|
||||
<div class="input-help">Expected average annual inflation</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<button class="btn-calculate" onclick="calculateFIRE()">
|
||||
<i class="fas fa-calculator mr-2"></i>
|
||||
Calculate FIRE Plan
|
||||
</button>
|
||||
<button class="btn-reset" onclick="resetForm()">
|
||||
<i class="fas fa-redo mr-2"></i>
|
||||
Reset to Defaults
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Results Summary -->
|
||||
<div id="resultsSection" class="results-card">
|
||||
<h2 class="section-title">
|
||||
<i class="fas fa-trophy"></i>
|
||||
Your FIRE Journey Summary
|
||||
</h2>
|
||||
|
||||
<div class="summary-grid" id="summaryCards">
|
||||
<!-- Summary cards will be inserted here by JavaScript -->
|
||||
</div>
|
||||
|
||||
<!-- Calculation Methodology Explanation -->
|
||||
<div class="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6 rounded">
|
||||
<h4 class="font-semibold text-blue-900 mb-2 flex items-center">
|
||||
<i class="fas fa-calculator mr-2"></i>
|
||||
How We Calculate Your FIRE Journey
|
||||
</h4>
|
||||
<div class="text-sm text-blue-800 space-y-1">
|
||||
<p><strong>1. Annual Savings:</strong> Income × Savings Rate = <span id="calcAnnualSavings" class="font-mono bg-blue-100 px-1 rounded">...</span></p>
|
||||
<p><strong>2. FIRE Target (4% Rule):</strong> Annual Expenses × 25 = <span id="calcFireTarget" class="font-mono bg-blue-100 px-1 rounded">...</span></p>
|
||||
<p><strong>3. Yearly Growth:</strong> (Net Worth + Annual Savings) × (1 + Return Rate)</p>
|
||||
<p><strong>4. Inflation-Adjusted Target:</strong> FIRE Target × (1 + Inflation)<sup>years</sup></p>
|
||||
<p><strong>5. Progress:</strong> (Current Net Worth ÷ Inflation-Adjusted Target) × 100%</p>
|
||||
<p class="pt-2 text-xs italic">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
FIRE is achieved when your net worth reaches or exceeds your inflation-adjusted target (≥100% progress)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scenario Comparison Tabs -->
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-semibold mb-2">Scenario Analysis</h3>
|
||||
<p class="text-sm text-gray-600 mb-3">
|
||||
<i class="fas fa-chart-line mr-1"></i>
|
||||
Compare different return rate scenarios. Historical stock market returns vary, so it's wise to plan for multiple outcomes.
|
||||
<strong>Conservative (6%)</strong> = bonds/safer investments,
|
||||
<strong>Neutral (8%)</strong> = balanced portfolio,
|
||||
<strong>Optimistic (10%)</strong> = aggressive stock-heavy portfolio.
|
||||
</p>
|
||||
<div class="scenario-tabs">
|
||||
<button class="scenario-tab active" onclick="switchScenario('neutral')">
|
||||
Neutral (8%)
|
||||
</button>
|
||||
<button class="scenario-tab" onclick="switchScenario('conservative')">
|
||||
Conservative (6%)
|
||||
</button>
|
||||
<button class="scenario-tab" onclick="switchScenario('optimistic')">
|
||||
Optimistic (10%)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Net Worth Growth Chart -->
|
||||
<div class="chart-container">
|
||||
<h3 class="chart-title">
|
||||
<i class="fas fa-chart-area mr-2"></i>
|
||||
Net Worth Projection vs FIRE Target
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 mb-4">
|
||||
<strong>Green line:</strong> Your projected net worth growth (investments + annual savings × compound returns).
|
||||
<strong>Red dashed line:</strong> FIRE target adjusted for inflation.
|
||||
When green crosses above red, you've achieved FIRE! 🎉
|
||||
</p>
|
||||
<canvas id="netWorthChart"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Progress Chart -->
|
||||
<div class="chart-container">
|
||||
<h3 class="chart-title">
|
||||
<i class="fas fa-percentage mr-2"></i>
|
||||
FIRE Progress Over Time
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 mb-4">
|
||||
Your FIRE progress as a percentage. 100% means you've reached your target!
|
||||
This shows: (Current Net Worth ÷ Inflation-Adjusted FIRE Target) × 100%
|
||||
</p>
|
||||
<canvas id="progressChart"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Detailed Table -->
|
||||
<div class="chart-container">
|
||||
<h3 class="chart-title">
|
||||
<i class="fas fa-table mr-2"></i>
|
||||
Year-by-Year Projection
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 mb-4">
|
||||
Detailed breakdown of your FIRE journey.
|
||||
<span class="bg-green-100 px-2 py-1 rounded text-xs font-semibold">Green rows</span> indicate years where you've achieved FIRE (≥100% progress).
|
||||
</p>
|
||||
<div class="overflow-x-auto">
|
||||
<table id="projectionTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Age</th>
|
||||
<th>Year</th>
|
||||
<th>Net Worth</th>
|
||||
<th>FIRE Target</th>
|
||||
<th>Progress</th>
|
||||
<th>Annual Savings</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="projectionTableBody">
|
||||
<!-- Table rows will be inserted here by JavaScript -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Educational FAQ Section -->
|
||||
<div class="input-card" style="background: linear-gradient(135deg, #fff9e6 0%, #fff3cd 100%);">
|
||||
<h2 class="section-title">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
Frequently Asked Questions
|
||||
</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<h3 class="font-bold text-gray-800 mb-2 flex items-center">
|
||||
<i class="fas fa-calculator text-yellow-600 mr-2"></i>
|
||||
Why multiply annual expenses by 25?
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
The 4% rule (based on historical market data) suggests you can safely withdraw 4% of your portfolio annually.
|
||||
Since 1 ÷ 0.04 = 25, you need 25 times your annual expenses. For example: need A$60,000/year → target A$1,500,000 portfolio.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<h3 class="font-bold text-gray-800 mb-2 flex items-center">
|
||||
<i class="fas fa-chart-line text-green-600 mr-2"></i>
|
||||
What's a realistic return rate?
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
Historical stock market returns (S&P 500) average ~10% annually before inflation. After inflation (~2-3%), real returns are ~7-8%.
|
||||
Conservative investors might use 6%, balanced 8%, aggressive 10%. Diversified portfolios typically aim for 7-9%.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<h3 class="font-bold text-gray-800 mb-2 flex items-center">
|
||||
<i class="fas fa-piggy-bank text-blue-600 mr-2"></i>
|
||||
How does compound growth work?
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
Each year: <strong>New Net Worth = (Previous Net Worth + Annual Savings) × (1 + Return Rate)</strong>.
|
||||
Example: Start with A$100,000, save A$30,000/year at 8% return → Year 1: (A$100k + A$30k) × 1.08 = A$140,400.
|
||||
This compounds over time, creating exponential growth!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<h3 class="font-bold text-gray-800 mb-2 flex items-center">
|
||||
<i class="fas fa-fire text-red-600 mr-2"></i>
|
||||
Why does the FIRE target increase over time?
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
Inflation! A$100,000 today won't have the same purchasing power in 15 years. At 2.5% inflation, A$100,000 becomes ~A$145,000 in 15 years.
|
||||
The calculator adjusts your target using: <strong>Future Target = Current Target × (1 + Inflation Rate)<sup>years</sup></strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<h3 class="font-bold text-gray-800 mb-2 flex items-center">
|
||||
<i class="fas fa-home text-purple-600 mr-2"></i>
|
||||
Should I include my home in net worth?
|
||||
</h3>
|
||||
<p class="text-sm text-gray-700">
|
||||
For FIRE calculations, use "Net Worth excl. home" - only investable assets that generate income.
|
||||
Your primary residence doesn't produce cash flow for living expenses (though it does have value and reduces housing costs).
|
||||
Track total net worth separately for overall wealth monitoring.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg p-4 shadow-sm">
|
||||
<h3 class="font-bold text-gray-800 mb-2 flex items-center">
|
||||
<i class="fas fa-exclamation-triangle text-orange-600 mr-2"></i>
|
||||
Important Assumptions & Limitations
|
||||
</h3>
|
||||
<ul class="text-sm text-gray-700 space-y-1 ml-5 list-disc">
|
||||
<li>Assumes constant savings rate and income (real life varies!)</li>
|
||||
<li>Doesn't account for taxes, healthcare costs, or unexpected expenses</li>
|
||||
<li>Market returns fluctuate - use scenario analysis for different outcomes</li>
|
||||
<li>Superannuation/401k considerations not included (consult financial advisor)</li>
|
||||
<li>This is a planning tool, not financial advice - do your own research!</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chart.js Library -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
||||
|
||||
<script>
|
||||
let netWorthChart = null;
|
||||
let progressChart = null;
|
||||
let currentScenario = 'neutral';
|
||||
let calculationData = {};
|
||||
|
||||
// Format currency in AUD
|
||||
function formatCurrency(value) {
|
||||
return new Intl.NumberFormat('en-AU', {
|
||||
style: 'currency',
|
||||
currency: 'AUD',
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
// Format percentage
|
||||
function formatPercentage(value) {
|
||||
return value.toFixed(1) + '%';
|
||||
}
|
||||
|
||||
// Get input values
|
||||
function getInputs() {
|
||||
return {
|
||||
currentAge: parseInt(document.getElementById('currentAge').value),
|
||||
targetAge: parseInt(document.getElementById('targetAge').value),
|
||||
currentNetWorth: parseFloat(document.getElementById('currentNetWorth').value),
|
||||
totalNetWorth: parseFloat(document.getElementById('totalNetWorth').value),
|
||||
annualExpenses: parseFloat(document.getElementById('annualExpenses').value),
|
||||
annualIncome: parseFloat(document.getElementById('annualIncome').value),
|
||||
savingsRate: parseFloat(document.getElementById('savingsRate').value) / 100,
|
||||
returnRate: parseFloat(document.getElementById('returnRate').value) / 100,
|
||||
inflationRate: parseFloat(document.getElementById('inflationRate').value) / 100
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate FIRE projections
|
||||
function calculateProjections(inputs, returnRate) {
|
||||
const years = inputs.targetAge - inputs.currentAge;
|
||||
const projections = [];
|
||||
|
||||
let netWorth = inputs.currentNetWorth;
|
||||
let fireTarget = inputs.annualExpenses * 25; // 4% rule
|
||||
const annualSavings = inputs.annualIncome * inputs.savingsRate;
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
for (let i = 0; i <= years; i++) {
|
||||
const age = inputs.currentAge + i;
|
||||
const year = currentYear + i;
|
||||
|
||||
// Adjust FIRE target for inflation
|
||||
const adjustedFireTarget = fireTarget * Math.pow(1 + inputs.inflationRate, i);
|
||||
|
||||
// Calculate progress
|
||||
const progress = (netWorth / adjustedFireTarget) * 100;
|
||||
|
||||
projections.push({
|
||||
age: age,
|
||||
year: year,
|
||||
netWorth: netWorth,
|
||||
fireTarget: adjustedFireTarget,
|
||||
progress: progress,
|
||||
annualSavings: annualSavings
|
||||
});
|
||||
|
||||
// Grow net worth for next year
|
||||
if (i < years) {
|
||||
netWorth = (netWorth + annualSavings) * (1 + returnRate);
|
||||
}
|
||||
}
|
||||
|
||||
return projections;
|
||||
}
|
||||
|
||||
// Find FIRE achievement year
|
||||
function findFIREYear(projections) {
|
||||
for (let i = 0; i < projections.length; i++) {
|
||||
if (projections[i].progress >= 100) {
|
||||
return projections[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate FIRE
|
||||
function calculateFIRE() {
|
||||
const inputs = getInputs();
|
||||
|
||||
// Validate inputs
|
||||
if (inputs.targetAge <= inputs.currentAge) {
|
||||
alert('Target FIRE Age must be greater than Current Age');
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate for all scenarios
|
||||
calculationData = {
|
||||
neutral: calculateProjections(inputs, inputs.returnRate),
|
||||
conservative: calculateProjections(inputs, 0.06),
|
||||
optimistic: calculateProjections(inputs, 0.10),
|
||||
inputs: inputs
|
||||
};
|
||||
|
||||
// Show results
|
||||
document.getElementById('resultsSection').classList.add('show');
|
||||
|
||||
// Update summary
|
||||
updateSummary(calculationData.neutral, inputs);
|
||||
|
||||
// Update charts
|
||||
updateCharts(currentScenario);
|
||||
|
||||
// Update table
|
||||
updateTable(calculationData.neutral);
|
||||
|
||||
// Scroll to results
|
||||
document.getElementById('resultsSection').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// Update summary cards
|
||||
function updateSummary(projections, inputs) {
|
||||
const fireYear = findFIREYear(projections);
|
||||
const finalProjection = projections[projections.length - 1];
|
||||
const initialFireTarget = inputs.annualExpenses * 25;
|
||||
const annualSavings = inputs.annualIncome * inputs.savingsRate;
|
||||
|
||||
// Update calculation explanations
|
||||
document.getElementById('calcAnnualSavings').textContent =
|
||||
`${formatCurrency(inputs.annualIncome)} × ${formatPercentage(inputs.savingsRate * 100)} = ${formatCurrency(annualSavings)}`;
|
||||
document.getElementById('calcFireTarget').textContent =
|
||||
`${formatCurrency(inputs.annualExpenses)} × 25 = ${formatCurrency(initialFireTarget)}`;
|
||||
|
||||
let summaryHTML = `
|
||||
<div class="summary-card">
|
||||
<div class="summary-label">FIRE Target (Today)</div>
|
||||
<div class="summary-value">${formatCurrency(initialFireTarget)}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">Based on 4% rule (25× expenses)</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-card">
|
||||
<div class="summary-label">FIRE Target (At Age ${inputs.targetAge})</div>
|
||||
<div class="summary-value">${formatCurrency(finalProjection.fireTarget)}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">Adjusted for ${formatPercentage(inputs.inflationRate * 100)} annual inflation</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (fireYear) {
|
||||
summaryHTML += `
|
||||
<div class="summary-card success">
|
||||
<div class="summary-label">🎉 FIRE Achieved at Age</div>
|
||||
<div class="summary-value">${fireYear.age}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">In ${fireYear.age - inputs.currentAge} years (${fireYear.year})</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-card success">
|
||||
<div class="summary-label">Net Worth at FIRE</div>
|
||||
<div class="summary-value">${formatCurrency(fireYear.netWorth)}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">${formatPercentage(fireYear.progress)} of target reached</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
summaryHTML += `
|
||||
<div class="summary-card warning">
|
||||
<div class="summary-label">⚠️ FIRE Not Achieved by Age ${inputs.targetAge}</div>
|
||||
<div class="summary-value">${formatPercentage(finalProjection.progress)}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">Consider: higher savings rate or later retirement age</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-card">
|
||||
<div class="summary-label">Projected Net Worth</div>
|
||||
<div class="summary-value">${formatCurrency(finalProjection.netWorth)}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">At age ${inputs.targetAge}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
summaryHTML += `
|
||||
<div class="summary-card">
|
||||
<div class="summary-label">Annual Savings</div>
|
||||
<div class="summary-value">${formatCurrency(annualSavings)}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">${formatPercentage(inputs.savingsRate * 100)} of ${formatCurrency(inputs.annualIncome)} income</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-card">
|
||||
<div class="summary-label">Years to FIRE</div>
|
||||
<div class="summary-value">${fireYear ? (fireYear.age - inputs.currentAge) : 'N/A'}</div>
|
||||
<div class="text-xs text-gray-600 mt-1">${fireYear ? `Target year: ${fireYear.year}` : 'Extend timeline or adjust inputs'}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.getElementById('summaryCards').innerHTML = summaryHTML;
|
||||
}
|
||||
|
||||
// Update charts
|
||||
function updateCharts(scenario) {
|
||||
const projections = calculationData[scenario];
|
||||
|
||||
const ages = projections.map(p => p.age);
|
||||
const netWorths = projections.map(p => p.netWorth);
|
||||
const fireTargets = projections.map(p => p.fireTarget);
|
||||
const progress = projections.map(p => Math.min(p.progress, 150)); // Cap at 150% for chart
|
||||
|
||||
// Net Worth Chart
|
||||
if (netWorthChart) {
|
||||
netWorthChart.destroy();
|
||||
}
|
||||
|
||||
const ctx1 = document.getElementById('netWorthChart').getContext('2d');
|
||||
netWorthChart = new Chart(ctx1, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ages,
|
||||
datasets: [{
|
||||
label: 'Projected Net Worth',
|
||||
data: netWorths,
|
||||
borderColor: '#27ae60',
|
||||
backgroundColor: 'rgba(39, 174, 96, 0.1)',
|
||||
borderWidth: 3,
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}, {
|
||||
label: 'FIRE Target',
|
||||
data: fireTargets,
|
||||
borderColor: '#e74c3c',
|
||||
backgroundColor: 'rgba(231, 76, 60, 0.1)',
|
||||
borderWidth: 3,
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
borderDash: [5, 5]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return context.dataset.label + ': ' + formatCurrency(context.parsed.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return formatCurrency(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Age'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Progress Chart
|
||||
if (progressChart) {
|
||||
progressChart.destroy();
|
||||
}
|
||||
|
||||
const ctx2 = document.getElementById('progressChart').getContext('2d');
|
||||
progressChart = new Chart(ctx2, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ages,
|
||||
datasets: [{
|
||||
label: 'FIRE Progress (%)',
|
||||
data: progress,
|
||||
borderColor: '#3498db',
|
||||
backgroundColor: 'rgba(52, 152, 219, 0.2)',
|
||||
borderWidth: 3,
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return 'Progress: ' + formatPercentage(context.parsed.y);
|
||||
}
|
||||
}
|
||||
},
|
||||
annotation: {
|
||||
annotations: {
|
||||
line1: {
|
||||
type: 'line',
|
||||
yMin: 100,
|
||||
yMax: 100,
|
||||
borderColor: '#27ae60',
|
||||
borderWidth: 2,
|
||||
borderDash: [10, 5],
|
||||
label: {
|
||||
content: 'FIRE Achieved',
|
||||
enabled: true,
|
||||
position: 'end'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
max: 150,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return value + '%';
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Age'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update projection table
|
||||
function updateTable(projections) {
|
||||
let tableHTML = '';
|
||||
|
||||
projections.forEach((proj, index) => {
|
||||
const rowClass = proj.progress >= 100 ? 'fire-achieved' : '';
|
||||
tableHTML += `
|
||||
<tr class="${rowClass}">
|
||||
<td>${proj.age}</td>
|
||||
<td>${proj.year}</td>
|
||||
<td>${formatCurrency(proj.netWorth)}</td>
|
||||
<td>${formatCurrency(proj.fireTarget)}</td>
|
||||
<td>${formatPercentage(proj.progress)}</td>
|
||||
<td>${formatCurrency(proj.annualSavings)}</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
document.getElementById('projectionTableBody').innerHTML = tableHTML;
|
||||
}
|
||||
|
||||
// Switch scenario
|
||||
function switchScenario(scenario) {
|
||||
currentScenario = scenario;
|
||||
|
||||
// Update active tab
|
||||
document.querySelectorAll('.scenario-tab').forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
});
|
||||
event.target.classList.add('active');
|
||||
|
||||
// Update charts with new scenario
|
||||
updateCharts(scenario);
|
||||
|
||||
// Update table with new scenario
|
||||
updateTable(calculationData[scenario]);
|
||||
|
||||
// Update summary
|
||||
updateSummary(calculationData[scenario], calculationData.inputs);
|
||||
}
|
||||
|
||||
// Reset form
|
||||
function resetForm() {
|
||||
document.getElementById('currentAge').value = 35;
|
||||
document.getElementById('targetAge').value = 50;
|
||||
document.getElementById('currentNetWorth').value = 300000;
|
||||
document.getElementById('totalNetWorth').value = 900000;
|
||||
document.getElementById('annualExpenses').value = 200000;
|
||||
document.getElementById('annualIncome').value = 300000;
|
||||
document.getElementById('savingsRate').value = 40;
|
||||
document.getElementById('returnRate').value = 8;
|
||||
document.getElementById('inflationRate').value = 2.5;
|
||||
|
||||
document.getElementById('resultsSection').classList.remove('show');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -51,6 +51,7 @@ urlpatterns = [
|
||||
path('ui/mini-apps/', mini_apps_views.MiniAppsListView.as_view(), name='mini-apps-list'),
|
||||
path('ui/mini-apps/image-gallery/', mini_apps_views.ImageGalleryView.as_view(), name='mini-apps-image-gallery'),
|
||||
path('ui/mini-apps/tts/', mini_apps_views.TTSView.as_view(), name='mini-apps-tts'),
|
||||
path('ui/mini-apps/fire-planning/', mini_apps_views.FIREPlanningView.as_view(), name='mini-apps-fire-planning'),
|
||||
|
||||
# TTS API
|
||||
path('ui/tts-api/', views.generate_tts_api, name='tts-api'),
|
||||
|
||||
Reference in New Issue
Block a user