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
-
- - - - - - - - - - - - - - {milestones.map((milestone, index) => ( - - - - - ))} - -
DESCRIPTION{unit.toUpperCase()}
CURRENT - {Math.floor(stats.totalShares).toLocaleString()} -
{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 ( -
-
- ADD MILESTONE -
-
- - setData('target', e.target.value)} - className="bg-black border-red-500 text-red-400 focus:border-red-300 font-mono text-sm rounded-none border-2 focus:ring-0 focus:outline-none placeholder:text-red-400/40 transition-all glow-red" - /> - -
- -
- - setData('description', e.target.value)} - className="bg-black border-red-500 text-red-400 focus:border-red-300 font-mono text-sm rounded-none border-2 focus:ring-0 focus:outline-none placeholder:text-red-400/40 transition-all glow-red" - /> - -
- -
- - {onCancel && ( - - )} -
-
-
-
- ); -} 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']); - } -}