Compare commits
6 Commits
e1cf5c05b5
...
renovate/r
Author | SHA1 | Date | |
---|---|---|---|
b5d8b50205 | |||
fc452ebd4c | |||
201c1ee523 | |||
1c1b842a15 | |||
aba4e4a7f6 | |||
4dcd24f1fd |
@@ -4,7 +4,7 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- '**' # matches every branch
|
||||
- "**" # matches every branch
|
||||
|
||||
jobs:
|
||||
lint_and_typecheck:
|
||||
@@ -15,14 +15,17 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: pnpm install
|
||||
|
||||
- name: Run check
|
||||
run: npm run check
|
||||
run: pnpm run check
|
||||
|
@@ -29,7 +29,6 @@ The project’s code is structured using React/Next.js with TypeScript, focusing
|
||||
The calculator models your FIRE journey in two phases:
|
||||
|
||||
1. **Accumulation:**
|
||||
|
||||
- Your starting capital is grown by your expected CAGR (~7% by default).
|
||||
- Monthly savings are added for each year until retirement.
|
||||
- Every variable can be adjusted live (capital, savings, age, growth, inflation, spending, target retirement).
|
||||
@@ -58,11 +57,11 @@ To run locally:
|
||||
```
|
||||
2. **Install dependencies**
|
||||
```bash
|
||||
npm install
|
||||
pnpm install
|
||||
```
|
||||
3. **Run the app**
|
||||
```bash
|
||||
npm run dev
|
||||
pnpm run dev
|
||||
```
|
||||
4. Visit [http://localhost:3000](http://localhost:3000) and unleash the fire.
|
||||
|
||||
|
7649
package-lock.json
generated
7649
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.56.1",
|
||||
"recharts": "^2.15.3",
|
||||
"recharts": "^3.0.0",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"zod": "^4.0.0"
|
||||
},
|
||||
@@ -41,6 +41,7 @@
|
||||
"@types/react-dom": "19.1.6",
|
||||
"eslint": "9.31.0",
|
||||
"eslint-config-next": "15.3.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"postcss": "8.5.6",
|
||||
"prettier": "3.6.2",
|
||||
"prettier-plugin-tailwindcss": "0.6.14",
|
||||
@@ -52,5 +53,5 @@
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "7.39.3"
|
||||
},
|
||||
"packageManager": "npm@11.4.2"
|
||||
"packageManager": "pnpm@10.13.1"
|
||||
}
|
||||
|
5070
pnpm-lock.yaml
generated
Normal file
5070
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
pnpm-workspace.yaml
Normal file
6
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
ignoredBuiltDependencies:
|
||||
- unrs-resolver
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- '@tailwindcss/oxide'
|
||||
- sharp
|
@@ -120,7 +120,7 @@ export default function FireCalculatorForm() {
|
||||
const [showing4percent, setShowing4percent] = useState(false);
|
||||
|
||||
// Initialize form with default values
|
||||
const form = useForm<FormValues>({
|
||||
const form = useForm<z.input<typeof formSchema>, undefined, FormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
startingCapital: 50000,
|
||||
@@ -258,11 +258,18 @@ export default function FireCalculatorForm() {
|
||||
<Input
|
||||
placeholder="e.g., 10000"
|
||||
type="number"
|
||||
{...field}
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
value={field.value as number | string | undefined}
|
||||
onChange={(e) => {
|
||||
field.onChange(
|
||||
e.target.value === ""
|
||||
? undefined
|
||||
: Number(e.target.value),
|
||||
);
|
||||
void form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -279,11 +286,18 @@ export default function FireCalculatorForm() {
|
||||
<Input
|
||||
placeholder="e.g., 500"
|
||||
type="number"
|
||||
{...field}
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
value={field.value as number | string | undefined}
|
||||
onChange={(e) => {
|
||||
field.onChange(
|
||||
e.target.value === ""
|
||||
? undefined
|
||||
: Number(e.target.value),
|
||||
);
|
||||
void form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -300,11 +314,18 @@ export default function FireCalculatorForm() {
|
||||
<Input
|
||||
placeholder="e.g., 30"
|
||||
type="number"
|
||||
{...field}
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
value={field.value as number | string | undefined}
|
||||
onChange={(e) => {
|
||||
field.onChange(
|
||||
e.target.value === ""
|
||||
? undefined
|
||||
: Number(e.target.value),
|
||||
);
|
||||
void form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -321,11 +342,18 @@ export default function FireCalculatorForm() {
|
||||
<Input
|
||||
placeholder="e.g., 90"
|
||||
type="number"
|
||||
{...field}
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
value={field.value as number | string | undefined}
|
||||
onChange={(e) => {
|
||||
field.onChange(
|
||||
e.target.value === ""
|
||||
? undefined
|
||||
: Number(e.target.value),
|
||||
);
|
||||
void form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -343,11 +371,18 @@ export default function FireCalculatorForm() {
|
||||
placeholder="e.g., 7"
|
||||
type="number"
|
||||
step="0.1"
|
||||
{...field}
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
value={field.value as number | string | undefined}
|
||||
onChange={(e) => {
|
||||
field.onChange(
|
||||
e.target.value === ""
|
||||
? undefined
|
||||
: Number(e.target.value),
|
||||
);
|
||||
void form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -365,11 +400,18 @@ export default function FireCalculatorForm() {
|
||||
placeholder="e.g., 2"
|
||||
type="number"
|
||||
step="0.1"
|
||||
{...field}
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
value={field.value as number | string | undefined}
|
||||
onChange={(e) => {
|
||||
field.onChange(
|
||||
e.target.value === ""
|
||||
? undefined
|
||||
: Number(e.target.value),
|
||||
);
|
||||
void form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -388,11 +430,18 @@ export default function FireCalculatorForm() {
|
||||
<Input
|
||||
placeholder="e.g., 2000"
|
||||
type="number"
|
||||
{...field}
|
||||
onChange={(value) => {
|
||||
field.onChange(value);
|
||||
value={field.value as number | string | undefined}
|
||||
onChange={(e) => {
|
||||
field.onChange(
|
||||
e.target.value === ""
|
||||
? undefined
|
||||
: Number(e.target.value),
|
||||
);
|
||||
void form.handleSubmit(onSubmit)();
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
ref={field.ref}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -406,11 +455,13 @@ export default function FireCalculatorForm() {
|
||||
name="retirementAge"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Retirement Age: {field.value}</FormLabel>
|
||||
<FormLabel>
|
||||
Retirement Age: {field.value as number}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Slider
|
||||
name="retirementAge"
|
||||
value={[field.value]}
|
||||
value={[field.value as number]}
|
||||
min={25}
|
||||
max={75}
|
||||
step={1}
|
||||
@@ -563,8 +614,8 @@ export default function FireCalculatorForm() {
|
||||
<ReferenceLine
|
||||
x={
|
||||
irlYear +
|
||||
(form.getValues("retirementAge") -
|
||||
form.getValues("currentAge"))
|
||||
(Number(form.getValues("retirementAge")) -
|
||||
Number(form.getValues("currentAge")))
|
||||
}
|
||||
stroke="var(--primary)"
|
||||
strokeWidth={2}
|
||||
@@ -579,7 +630,7 @@ export default function FireCalculatorForm() {
|
||||
x={
|
||||
irlYear +
|
||||
(result.retirementAge4percent -
|
||||
form.getValues("currentAge"))
|
||||
Number(form.getValues("currentAge")))
|
||||
}
|
||||
stroke="var(--secondary)"
|
||||
strokeWidth={1}
|
||||
@@ -642,8 +693,8 @@ export default function FireCalculatorForm() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-3xl font-bold">
|
||||
{form.getValues("lifeExpectancy") -
|
||||
form.getValues("retirementAge")}
|
||||
{Number(form.getValues("lifeExpectancy")) -
|
||||
Number(form.getValues("retirementAge"))}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -674,7 +725,7 @@ export default function FireCalculatorForm() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-3xl font-bold">
|
||||
{form.getValues("lifeExpectancy") -
|
||||
{Number(form.getValues("lifeExpectancy")) -
|
||||
(result.retirementAge4percent ?? 0)}
|
||||
</p>
|
||||
</CardContent>
|
||||
|
Reference in New Issue
Block a user