diff --git a/app/Http/Controllers/PersonalSubscriptionController.php b/app/Http/Controllers/PersonalSubscriptionController.php new file mode 100644 index 00000000..d7d7f1bd --- /dev/null +++ b/app/Http/Controllers/PersonalSubscriptionController.php @@ -0,0 +1,44 @@ + $request->user() + ->personalSubscriptions() + ->orderBy('next_billing_date') + ->get(), + ]); + } + + public function store(Request $request) + { + $validated = $request->validate([ + 'name' => ['required', 'string', 'max:255'], + 'amount' => ['required', 'numeric', 'min:0'], + 'currency' => ['required', 'string', 'size:3'], + 'billing_cycle' => ['required', 'in:weekly,monthly,biweekly,yearly'], + 'next_billing_date' => ['required', 'date'], + 'color' => ['nullable', 'string', 'max:20'], + ]); + + $request->user()->personalSubscriptions()->create($validated); + + return back(); + } + + public function destroy(Request $request, PersonalSubscription $personalSubscription) + { + abort_unless($personalSubscription->user_id === $request->user()->id, 403); + $personalSubscription->delete(); + + return back(); + } +} diff --git a/app/Models/PersonalSubscription.php b/app/Models/PersonalSubscription.php new file mode 100644 index 00000000..c94f1bee --- /dev/null +++ b/app/Models/PersonalSubscription.php @@ -0,0 +1,22 @@ + 'date', + 'amount' => 'decimal:2', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 2b09ecc8..b83285b3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -521,4 +521,9 @@ class User extends Authenticatable implements HasLocalePreference, MustVerifyEma return "{$timestamp}_{$this->getKey()}_{$originalEmail}"; } + + public function personalSubscriptions() + { + return $this->hasMany(PersonalSubscription::class); + } } diff --git a/database/migrations/2026_06_16_210355_create_personal_subscriptions_table.php b/database/migrations/2026_06_16_210355_create_personal_subscriptions_table.php new file mode 100644 index 00000000..d69a7a08 --- /dev/null +++ b/database/migrations/2026_06_16_210355_create_personal_subscriptions_table.php @@ -0,0 +1,34 @@ +id(); + $table->foreignUuid('user_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->decimal('amount', 8, 2); + $table->string('currency', 3)->default('EUR'); + $table->enum('billing_cycle', ['weekly', 'monthly', 'biweekly', 'yearly'])->default('monthly'); + $table->date('next_billing_date'); + $table->string('color')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_subscriptions'); + } +}; diff --git a/resources/js/providers/menu-item-provider.tsx b/resources/js/providers/menu-item-provider.tsx index 602db216..dfdf58c9 100644 --- a/resources/js/providers/menu-item-provider.tsx +++ b/resources/js/providers/menu-item-provider.tsx @@ -1,9 +1,11 @@ import { index as accountsIndex } from '@/actions/App/Http/Controllers/AccountController'; import { index as budgetsIndex } from '@/actions/App/Http/Controllers/BudgetController'; import { index as transactionsIndex } from '@/actions/App/Http/Controllers/TransactionController'; +import { index as PersonalSubscriptionIndex } from '@/actions/App/Http/Controllers/PersonalSubscriptionController'; import { cashflow, dashboard } from '@/routes'; import { Features, NavItem } from '@/types'; import { + CalendarSync, CreditCard, LayoutGrid, PiggyBank, @@ -26,6 +28,13 @@ const mobileLabels: Record> = { transactions: 'Movim.', budgets: 'Presup.', }, + fr: { + dashboard: 'Accueil', + cashflow: 'Trésorerie', + accounts: 'Comptes', + transactions: 'Mouvements', + budgets: 'Budget', + }, }; function getMobileLabel(key: string, locale: string): string { @@ -75,6 +84,13 @@ export function getMainNavItems(features: Features, locale: string): NavItem[] { href: budgetsIndex(), icon: PiggyBank, }, + { + type: 'nav-item', + title: 'Subscriptions', + mobileTitle: getMobileLabel('budgets', locale), + href: PersonalSubscriptionIndex(), + icon: CalendarSync, + }, ); return items; diff --git a/routes/web.php b/routes/web.php index 69fba6b0..41ba7e09 100644 --- a/routes/web.php +++ b/routes/web.php @@ -28,6 +28,7 @@ use App\Http\Controllers\SitemapController; use App\Http\Controllers\SubscriptionController; use App\Http\Controllers\TransactionController; use App\Http\Controllers\UserLeadController; +use App\Http\Controllers\PersonalSubscriptionController; use App\Models\Bank; use App\Services\LandingAuthOverrideService; use Illuminate\Http\Request; @@ -194,4 +195,9 @@ Route::middleware(['auth', 'verified', 'onboarded', 'subscribed'])->group(functi Route::delete('budgets/{budget}', [BudgetController::class, 'destroy'])->name('budgets.destroy'); }); +Route::middleware(['auth'])->group(function () { + Route::resource('personal-subscriptions', PersonalSubscriptionController::class) + ->only(['index', 'store', 'destroy']); +}); + require __DIR__.'/settings.php';