diff --git a/app/Http/Controllers/Pricing/PricingController.php b/app/Http/Controllers/Pricing/PricingController.php
index cdd34a3..bb07156 100644
--- a/app/Http/Controllers/Pricing/PricingController.php
+++ b/app/Http/Controllers/Pricing/PricingController.php
@@ -18,7 +18,7 @@ public function current(): JsonResponse
]);
}
- public function update(Request $request): JsonResponse
+ public function update(Request $request)
{
$validated = $request->validate([
'date' => 'required|date|before_or_equal:today',
@@ -27,11 +27,7 @@ public function update(Request $request): JsonResponse
$assetPrice = AssetPrice::updatePrice($validated['date'], $validated['price']);
- return response()->json([
- 'success' => true,
- 'message' => 'Asset price updated successfully!',
- 'data' => $assetPrice,
- ]);
+ return back()->with('success', 'Asset price updated successfully!');
}
public function history(Request $request): JsonResponse
diff --git a/resources/js/components/Display/InlineForm.tsx b/resources/js/components/Display/InlineForm.tsx
index 949de4d..9d6b693 100644
--- a/resources/js/components/Display/InlineForm.tsx
+++ b/resources/js/components/Display/InlineForm.tsx
@@ -1,13 +1,15 @@
import AddMilestoneForm from '@/components/Milestones/AddMilestoneForm';
import AddPurchaseForm from '@/components/Transactions/AddPurchaseForm';
+import UpdatePriceForm from '@/components/Pricing/UpdatePriceForm';
import { cn } from '@/lib/utils';
import { X } from 'lucide-react';
interface InlineFormProps {
- type: 'purchase' | 'milestone' | null;
+ type: 'purchase' | 'milestone' | 'price' | null;
onClose: () => void;
onPurchaseSuccess?: () => void;
onMilestoneSuccess?: () => void;
+ onPriceSuccess?: () => void;
className?: string;
}
@@ -16,11 +18,12 @@ export default function InlineForm({
onClose,
onPurchaseSuccess,
onMilestoneSuccess,
+ onPriceSuccess,
className
}: InlineFormProps) {
if (!type) return null;
- const title = type === 'purchase' ? 'ADD PURCHASE' : 'ADD MILESTONE';
+ const title = type === 'purchase' ? 'ADD PURCHASE' : type === 'milestone' ? 'ADD MILESTONE' : 'UPDATE PRICE';
return (
- ) : (
+ ) : type === 'milestone' ? (
{
if (onMilestoneSuccess) onMilestoneSuccess();
onClose();
}}
/>
+ ) : (
+ {
+ if (onPriceSuccess) onPriceSuccess();
+ onClose();
+ }}
+ />
)}
diff --git a/resources/js/components/Display/StatsBox.tsx b/resources/js/components/Display/StatsBox.tsx
index b88d0bf..4d11085 100644
--- a/resources/js/components/Display/StatsBox.tsx
+++ b/resources/js/components/Display/StatsBox.tsx
@@ -1,5 +1,6 @@
import { cn } from '@/lib/utils';
import { Plus } from 'lucide-react';
+import { useState } from 'react';
interface Milestone {
target: number;
@@ -21,6 +22,7 @@ interface StatsBoxProps {
className?: string;
onAddPurchase?: () => void;
onAddMilestone?: () => void;
+ onUpdatePrice?: () => void;
}
export default function StatsBox({
@@ -28,8 +30,10 @@ export default function StatsBox({
milestones = [],
className,
onAddPurchase,
- onAddMilestone
+ onAddMilestone,
+ onUpdatePrice
}: StatsBoxProps) {
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('de-DE', {
style: 'currency',
@@ -50,149 +54,141 @@ export default function StatsBox({
return (
- {/* Current Price */}
- {stats.currentPrice && (
-
-
- current price
-
-
- {formatCurrencyDetailed(stats.currentPrice)}
-
-
- )}
-
- {/* Portfolio Stats Grid */}
-
- {/* Total Investment */}
-
-
Total Investment
-
- {formatCurrency(stats.totalInvestment)}
-
-
-
- {/* Current Value */}
- {stats.currentValue && (
-
-
Current Value
-
- {formatCurrency(stats.currentValue)}
-
-
- )}
-
- {/* Average Cost */}
-
-
Avg Cost/Share
-
- {formatCurrencyDetailed(stats.averageCostPerShare)}
-
-
-
- {/* Profit/Loss */}
- {stats.profitLoss !== undefined && (
-
-
P&L
-
= 0 ? "text-green-400" : "text-red-400"
- )}>
- {stats.profitLoss >= 0 ? '+' : ''}{formatCurrency(stats.profitLoss)}
-
-
- )}
-
-
- {/* Withdrawal Estimates */}
- {stats.currentValue && (
-
-
Annual Withdrawal (Safe)
-
-
-
3% Rule
-
- {formatCurrency(stats.currentValue * 0.03)}
+
+ {/* STATS Title and Current Price */}
+
+
+ STATS
+
+
+ {stats.currentPrice && (
+
+ VWCE: {formatCurrencyDetailed(stats.currentPrice)}
-
-
-
4% Rule
-
- {formatCurrency(stats.currentValue * 0.04)}
-
-
-
-
- )}
-
- {/* Milestones */}
- {milestones.length > 0 && (
-
-
Milestones
-
- {milestones.map((milestone, index) => {
- const isReached = stats.totalShares >= milestone.target;
- return (
-
-
-
-
- {milestone.target.toLocaleString()}
-
-
-
- {milestone.description}
-
+ )}
+
+ {/* Action Dropdown */}
+
+
+
+ {/* Dropdown Menu */}
+ {isDropdownOpen && (
+
+ {onAddPurchase && (
+
+ )}
+ {onAddMilestone && (
+
+ )}
+ {onUpdatePrice && (
+
+ )}
- );
- })}
+ )}
+
- )}
- {/* Action Buttons */}
+ {/* Milestone Table */}
-
- {/* Add Purchase Button */}
- {onAddPurchase && (
-
- )}
-
- {/* Add Milestone Button */}
- {onAddMilestone && (
-
- )}
+
MILESTONES
+
+
+
+
+ | DESCRIPTION |
+ SHARES |
+ SWR 3% |
+ SWR 4% |
+
+
+
+ {/* Create combined array with current position and milestones, sorted by target */}
+ {[
+ ...milestones.map(m => ({ ...m, isCurrent: false })),
+ {
+ target: stats.totalShares,
+ description: 'CURRENT',
+ created_at: '',
+ isCurrent: true
+ }
+ ]
+ .sort((a, b) => a.target - b.target)
+ .map((item, index) => {
+ const swr3 = stats.currentPrice ? item.target * stats.currentPrice * 0.03 : 0;
+ const swr4 = stats.currentPrice ? item.target * stats.currentPrice * 0.04 : 0;
+
+ return (
+ = item.target
+ ? "text-green-400/80"
+ : "text-red-400/70"
+ )}
+ >
+ |
+ {item.isCurrent ? (
+ {item.description}
+ ) : (
+ item.description
+ )}
+ |
+
+ {Math.floor(item.target).toLocaleString()}
+ |
+
+ {stats.currentPrice ? formatCurrency(swr3) : 'N/A'}
+ |
+
+ {stats.currentPrice ? formatCurrency(swr4) : 'N/A'}
+ |
+
+ );
+ })}
+
+
+
);
}
\ No newline at end of file
diff --git a/resources/js/components/Pricing/UpdatePriceForm.tsx b/resources/js/components/Pricing/UpdatePriceForm.tsx
index b4944e5..bd18cb9 100644
--- a/resources/js/components/Pricing/UpdatePriceForm.tsx
+++ b/resources/js/components/Pricing/UpdatePriceForm.tsx
@@ -16,9 +16,10 @@ interface PriceUpdateFormData {
interface UpdatePriceFormProps {
currentPrice?: number;
className?: string;
+ onSuccess?: () => void;
}
-export default function UpdatePriceForm({ currentPrice, className }: UpdatePriceFormProps) {
+export default function UpdatePriceForm({ currentPrice, className, onSuccess }: UpdatePriceFormProps) {
const { data, setData, post, processing, errors } = useForm
({
date: new Date().toISOString().split('T')[0], // Today's date in YYYY-MM-DD format
price: currentPrice?.toString() || '',
@@ -31,6 +32,7 @@ export default function UpdatePriceForm({ currentPrice, className }: UpdatePrice
onSuccess: () => {
// Keep the date, reset only price if needed
// User might want to update same day multiple times
+ if (onSuccess) onSuccess();
},
});
};
diff --git a/resources/js/pages/dashboard.tsx b/resources/js/pages/dashboard.tsx
index 4275e3b..1727698 100644
--- a/resources/js/pages/dashboard.tsx
+++ b/resources/js/pages/dashboard.tsx
@@ -35,7 +35,7 @@ export default function Dashboard() {
const [milestones, setMilestones] = useState([]);
const [showProgressBar, setShowProgressBar] = useState(false);
const [showStatsBox, setShowStatsBox] = useState(false);
- const [activeForm, setActiveForm] = useState<'purchase' | 'milestone' | null>(null);
+ const [activeForm, setActiveForm] = useState<'purchase' | 'milestone' | 'price' | null>(null);
const [loading, setLoading] = useState(true);
// Fetch purchase summary, current price, and milestones
@@ -98,6 +98,19 @@ export default function Dashboard() {
}
};
+ // Refresh price data after successful update
+ const handlePriceSuccess = async () => {
+ try {
+ const priceResponse = await fetch('/pricing/current');
+ if (priceResponse.ok) {
+ const price = await priceResponse.json();
+ setPriceData(price);
+ }
+ } catch (error) {
+ console.error('Failed to refresh price data:', error);
+ }
+ };
+
// Calculate portfolio stats
const currentValue = priceData.current_price
@@ -180,6 +193,7 @@ export default function Dashboard() {
milestones={milestones}
onAddPurchase={() => setActiveForm('purchase')}
onAddMilestone={() => setActiveForm('milestone')}
+ onUpdatePrice={() => setActiveForm('price')}
/>
@@ -190,6 +204,7 @@ export default function Dashboard() {
onClose={() => setActiveForm(null)}
onPurchaseSuccess={handlePurchaseSuccess}
onMilestoneSuccess={handleMilestoneSuccess}
+ onPriceSuccess={handlePriceSuccess}
/>