diff --git a/app/Http/Controllers/Milestones/MilestoneController.php b/app/Http/Controllers/Milestones/MilestoneController.php
deleted file mode 100644
index dfe79fd..0000000
--- a/app/Http/Controllers/Milestones/MilestoneController.php
+++ /dev/null
@@ -1,43 +0,0 @@
-validate([
- 'target' => 'required|integer|min:1',
- 'description' => 'required|string|max:255',
- ]);
-
- $tracker = User::default()->tracker;
-
- if (! $tracker) {
- return back()->withErrors(['tracker' => 'No tracker found. Please complete onboarding first.']);
- }
-
- $tracker->milestones()->create($validated);
-
- return back()->with('success', 'Milestone created successfully');
- }
-
- public function index(): JsonResponse
- {
- $tracker = User::default()->tracker;
-
- if (! $tracker) {
- return response()->json([]);
- }
-
- return response()->json($tracker->milestones()->orderBy('target')->get());
- }
-}
diff --git a/app/Models/Milestone.php b/app/Models/Milestone.php
deleted file mode 100644
index fb7a9c5..0000000
--- a/app/Models/Milestone.php
+++ /dev/null
@@ -1,28 +0,0 @@
- 'integer',
- ];
-
- public function tracker(): BelongsTo
- {
- return $this->belongsTo(Tracker::class);
- }
-}
diff --git a/app/Models/Tracker.php b/app/Models/Tracker.php
index b7170d6..fb3edf5 100644
--- a/app/Models/Tracker.php
+++ b/app/Models/Tracker.php
@@ -40,9 +40,4 @@ public function entries(): HasMany
{
return $this->hasMany(Entry::class);
}
-
- public function milestones(): HasMany
- {
- return $this->hasMany(Milestone::class);
- }
}
diff --git a/app/Models/User.php b/app/Models/User.php
index 031123f..f08a6ed 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -47,18 +47,8 @@ public static function default(): self
]);
}
- public function hasCompletedOnboarding(): bool
- {
- return $this->hasEntries() && $this->hasMilestones();
- }
-
public function hasEntries(): bool
{
return (bool) $this->tracker?->entries()->exists();
}
-
- public function hasMilestones(): bool
- {
- return (bool) $this->tracker?->milestones()->exists();
- }
}
diff --git a/database/migrations/2026_08_15_000001_drop_milestones_table.php b/database/migrations/2026_08_15_000001_drop_milestones_table.php
new file mode 100644
index 0000000..7461c21
--- /dev/null
+++ b/database/migrations/2026_08_15_000001_drop_milestones_table.php
@@ -0,0 +1,26 @@
+id();
+ $table->foreignId('tracker_id')->constrained()->cascadeOnDelete();
+ $table->integer('target');
+ $table->string('description');
+ $table->timestamps();
+ });
+ }
+};
diff --git a/resources/js/components/Display/InlineForm.tsx b/resources/js/components/Display/InlineForm.tsx
index 777a09d..5069e11 100644
--- a/resources/js/components/Display/InlineForm.tsx
+++ b/resources/js/components/Display/InlineForm.tsx
@@ -1,28 +1,25 @@
import AddEntryForm from '@/components/Transactions/AddEntryForm';
-import AddMilestoneForm from '@/components/Milestones/AddMilestoneForm';
import { cn } from '@/lib/utils';
-type FormType = 'purchase' | 'milestone';
-
interface InlineFormProps {
- type: FormType | null;
+ open: boolean;
unit?: string;
onClose: () => void;
- onSuccess?: (type: FormType) => void;
+ onSuccess?: () => void;
className?: string;
}
export default function InlineForm({
- type,
+ open,
unit = 'units',
onClose,
onSuccess,
className,
}: InlineFormProps) {
- if (!type) return null;
+ if (!open) return null;
const handleSuccess = () => {
- if (onSuccess) onSuccess(type);
+ onSuccess?.();
onClose();
};
@@ -36,18 +33,11 @@ export default function InlineForm({
>
- {type === 'purchase' ? (
-
- ) : (
-
- )}
+
diff --git a/resources/js/components/Display/ProgressBar.tsx b/resources/js/components/Display/ProgressBar.tsx
deleted file mode 100644
index 06cb5d0..0000000
--- a/resources/js/components/Display/ProgressBar.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { cn } from '@/lib/utils';
-import type { Milestone } from '@/types/domain';
-
-interface ProgressBarProps {
- currentQuantity: number;
- milestones: Milestone[];
- selectedMilestoneIndex?: number;
- className?: string;
- onClick?: () => void;
-}
-
-export default function ProgressBar({
- currentQuantity,
- milestones,
- selectedMilestoneIndex = 0,
- className,
- onClick
-}: ProgressBarProps) {
- const selectedMilestone = milestones.length > 0 && selectedMilestoneIndex < milestones.length
- ? milestones[selectedMilestoneIndex]
- : null;
-
- const progressPercentage = selectedMilestone
- ? Math.min((currentQuantity / selectedMilestone.target) * 100, 100)
- : 0;
- return (
-
- {/* Progress Bar Container */}
-
- {/* Old-school progress bar with overlaid text */}
-
- {/* Inner container */}
-
- {/* Progress fill */}
-
-
- {/* Text overlay */}
- {selectedMilestone && (
-
- {/* Base text (red on black background) */}
-
- {progressPercentage.toFixed(1)}%
-
-
- )}
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/resources/js/components/Display/StatsBox.tsx b/resources/js/components/Display/StatsBox.tsx
deleted file mode 100644
index bcc19af..0000000
--- a/resources/js/components/Display/StatsBox.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import { cn } from '@/lib/utils';
-import { Plus, ChevronRight } from 'lucide-react';
-import { useState } from 'react';
-import ComponentTitle from '@/components/ui/ComponentTitle';
-import type { Milestone } from '@/types/domain';
-
-interface StatsBoxProps {
- stats: {
- totalShares: number;
- };
- unit?: string;
- milestones?: Milestone[];
- selectedMilestoneIndex?: number;
- onMilestoneSelect?: (index: number) => void;
- className?: string;
- onAddPurchase?: () => void;
- onAddMilestone?: () => void;
-}
-
-export default function StatsBox({
- stats,
- unit = 'units',
- milestones = [],
- selectedMilestoneIndex = 0,
- onMilestoneSelect,
- className,
- onAddPurchase,
- onAddMilestone,
-}: StatsBoxProps) {
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
-
- const handleCycleMilestone = () => {
- if (milestones.length === 0 || !onMilestoneSelect) return;
- const nextIndex = (selectedMilestoneIndex + 1) % milestones.length;
- onMilestoneSelect(nextIndex);
- };
-
- return (
-
-
-
-
Stats
-
-
- {/* Action Dropdown */}
-
-
-
- {isDropdownOpen && (
-
- {onAddPurchase && (
-
- )}
- {onAddMilestone && (
-
- )}
-
- )}
-
-
- {/* Milestone Cycle Button */}
- {milestones.length > 1 && (
-
- )}
-
-
-
- {/* Milestone Table */}
-
-
MILESTONES
-
-
-
-
- | DESCRIPTION |
- {unit.toUpperCase()} |
-
-
-
-
- | CURRENT |
-
- {Math.floor(stats.totalShares).toLocaleString()}
- |
-
-
- {milestones.map((milestone, index) => (
-
- | {milestone.description} |
-
- {Math.floor(milestone.target).toLocaleString()}
- |
-
- ))}
-
-
-
-
-
-
- );
-}
diff --git a/resources/js/components/Milestones/AddMilestoneForm.tsx b/resources/js/components/Milestones/AddMilestoneForm.tsx
deleted file mode 100644
index 8433bf0..0000000
--- a/resources/js/components/Milestones/AddMilestoneForm.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { Button } from '@/components/ui/button';
-import { Input } from '@/components/ui/input';
-import { Label } from '@/components/ui/label';
-import InputError from '@/components/InputError';
-import { useForm } from '@inertiajs/react';
-import { LoaderCircle } from 'lucide-react';
-import { FormEventHandler } from 'react';
-import ComponentTitle from '@/components/ui/ComponentTitle';
-
-interface MilestoneFormData {
- target: string;
- description: string;
- [key: string]: string;
-}
-
-interface AddMilestoneFormProps {
- onSuccess?: () => void;
- onCancel?: () => void;
-}
-
-export default function AddMilestoneForm({ onSuccess, onCancel }: AddMilestoneFormProps) {
- const { data, setData, post, processing, errors, reset } = useForm({
- target: '',
- description: '',
- });
-
- const submit: FormEventHandler = (e) => {
- e.preventDefault();
-
- post(route('milestones.store'), {
- onSuccess: () => {
- reset();
- if (onSuccess) {
- onSuccess();
- }
- },
- });
- };
-
- return (
-
- );
-}
diff --git a/resources/js/pages/dashboard.tsx b/resources/js/pages/dashboard.tsx
index b380a5d..ca73103 100644
--- a/resources/js/pages/dashboard.tsx
+++ b/resources/js/pages/dashboard.tsx
@@ -1,28 +1,21 @@
import LedDisplay from '@/components/Display/LedDisplay';
import InlineForm from '@/components/Display/InlineForm';
-import ProgressBar from '@/components/Display/ProgressBar';
-import StatsBox from '@/components/Display/StatsBox';
import OnboardingFlow from '@/components/Onboarding/OnboardingFlow';
import TerminalSpinner from '@/components/ui/TerminalSpinner';
import { Head } from '@inertiajs/react';
import { useCallback, useEffect, useState } from 'react';
-import type { Milestone, Tracker } from '@/types/domain';
+import type { Tracker } from '@/types/domain';
export default function Dashboard() {
const [totalShares, setTotalShares] = useState(0);
- const [milestones, setMilestones] = useState([]);
- const [selectedMilestoneIndex, setSelectedMilestoneIndex] = useState(0);
- const [showProgressBar, setShowProgressBar] = useState(false);
- const [showStatsBox, setShowStatsBox] = useState(false);
- const [activeForm, setActiveForm] = useState<'purchase' | 'milestone' | null>(null);
+ const [formOpen, setFormOpen] = useState(false);
const [loading, setLoading] = useState(true);
const [needsOnboarding, setNeedsOnboarding] = useState(false);
const [tracker, setTracker] = useState(null);
const loadData = useCallback(async () => {
- const [entriesResponse, milestonesResponse, trackerResponse] = await Promise.all([
+ const [entriesResponse, trackerResponse] = await Promise.all([
fetch('/entries/summary'),
- fetch('/milestones'),
fetch('/tracker'),
]);
@@ -31,10 +24,6 @@ export default function Dashboard() {
setTotalShares(entries.total_quantity);
}
- if (milestonesResponse.ok) {
- setMilestones(await milestonesResponse.json());
- }
-
let trackerExists = false;
if (trackerResponse.ok) {
@@ -60,7 +49,7 @@ export default function Dashboard() {
fetchData();
}, [loadData]);
- const handlePurchaseSuccess = async () => {
+ const handleEntrySuccess = async () => {
try {
const entriesResponse = await fetch('/entries/summary');
if (entriesResponse.ok) {
@@ -72,23 +61,6 @@ export default function Dashboard() {
}
};
- const handleMilestoneSuccess = async () => {
- try {
- const milestonesResponse = await fetch('/milestones');
- if (milestonesResponse.ok) {
- const milestonesData = await milestonesResponse.json();
- setMilestones(milestonesData);
- setSelectedMilestoneIndex(0);
- }
- } catch (error) {
- console.error('Failed to refresh milestone data:', error);
- }
- };
-
- const handleMilestoneSelect = (index: number) => {
- setSelectedMilestoneIndex(index);
- };
-
const handleOnboardingComplete = loadData;
if (loading) {
@@ -100,23 +72,10 @@ export default function Dashboard() {
);
}
- const handleLedClick = () => {
- const newShowProgressBar = !showProgressBar;
- setShowProgressBar(newShowProgressBar);
- if (!newShowProgressBar) {
- setShowStatsBox(false);
- }
- };
-
- const handleProgressClick = () => {
- setShowStatsBox(!showStatsBox);
- setActiveForm(null);
- };
-
if (needsOnboarding) {
return (
<>
-
+
>
);
@@ -131,42 +90,16 @@ export default function Dashboard() {
setFormOpen((open) => !open)}
/>
-
-
-
- setActiveForm('purchase')}
- onAddMilestone={() => setActiveForm('milestone')}
- />
-
-
-
- setActiveForm(null)}
- onSuccess={(type) => {
- if (type === 'purchase') handlePurchaseSuccess();
- else if (type === 'milestone') handleMilestoneSuccess();
- }}
- />
-
+ setFormOpen(false)}
+ onSuccess={handleEntrySuccess}
+ />
>
diff --git a/resources/js/types/domain.ts b/resources/js/types/domain.ts
index f6ad9e9..6acb030 100644
--- a/resources/js/types/domain.ts
+++ b/resources/js/types/domain.ts
@@ -1,10 +1,3 @@
-export interface Milestone {
- id?: number;
- target: number;
- description: string;
- created_at: string;
-}
-
export interface TrackerAsset {
id: number;
symbol: string;
diff --git a/routes/web.php b/routes/web.php
index dbbe194..ae89b5d 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -1,7 +1,6 @@
name('for-date');
});
-// Milestone routes
-Route::prefix('milestones')->name('milestones.')->group(function () {
- Route::get('/', [MilestoneController::class, 'index'])->name('index');
- Route::post('/', [MilestoneController::class, 'store'])->name('store');
-});
-
require __DIR__.'/auth.php';
diff --git a/tests/Feature/.gitkeep b/tests/Feature/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/tests/Feature/MilestoneTest.php b/tests/Feature/MilestoneTest.php
deleted file mode 100644
index 62d7fd1..0000000
--- a/tests/Feature/MilestoneTest.php
+++ /dev/null
@@ -1,73 +0,0 @@
- User::default()->id,
- 'label' => 'Test',
- 'unit' => 'units',
- ]);
- }
-
- public function test_can_create_milestone(): void
- {
- $tracker = $this->tracker();
-
- $milestone = Milestone::create([
- 'tracker_id' => $tracker->id,
- 'target' => 1500,
- 'description' => 'First milestone',
- ]);
-
- $this->assertDatabaseHas('milestones', [
- 'target' => 1500,
- 'description' => 'First milestone',
- ]);
-
- $this->assertEquals(1500, $milestone->target);
- $this->assertEquals('First milestone', $milestone->description);
- }
-
- public function test_can_fetch_milestones_via_api(): void
- {
- $tracker = $this->tracker();
- Milestone::create(['tracker_id' => $tracker->id, 'target' => 1500, 'description' => 'First milestone']);
- Milestone::create(['tracker_id' => $tracker->id, 'target' => 3000, 'description' => 'Second milestone']);
-
- $response = $this->get('/milestones');
-
- $response->assertStatus(200);
- $response->assertJsonCount(2);
- $response->assertJson([
- ['target' => 1500, 'description' => 'First milestone'],
- ['target' => 3000, 'description' => 'Second milestone'],
- ]);
- }
-
- public function test_milestones_ordered_by_target(): void
- {
- $tracker = $this->tracker();
- Milestone::create(['tracker_id' => $tracker->id, 'target' => 3000, 'description' => 'Third']);
- Milestone::create(['tracker_id' => $tracker->id, 'target' => 1000, 'description' => 'First']);
- Milestone::create(['tracker_id' => $tracker->id, 'target' => 2000, 'description' => 'Second']);
-
- $response = $this->get('/milestones');
-
- $milestones = $response->json();
- $this->assertEquals(1000, $milestones[0]['target']);
- $this->assertEquals(2000, $milestones[1]['target']);
- $this->assertEquals(3000, $milestones[2]['target']);
- }
-}