diff --git a/website/app/Http/Controllers/Account/BillingController.php b/website/app/Http/Controllers/Account/BillingController.php index 99e085d..ea095fb 100644 --- a/website/app/Http/Controllers/Account/BillingController.php +++ b/website/app/Http/Controllers/Account/BillingController.php @@ -142,6 +142,39 @@ class BillingController extends Controller ]); } + public function upcomingRenewals(Request $request): Response + { + $user = $request->user(); + + $renewals = $user->subscriptions() + ->with('plan') + ->whereIn('stripe_status', ['active', 'trialing']) + ->whereNotNull('current_period_end') + ->orderBy('current_period_end') + ->get() + ->map(function ($subscription) use ($user) { + $service = $user->services() + ->where('subscription_id', $subscription->id) + ->first(); + + return [ + 'id' => $subscription->id, + 'plan_name' => $subscription->plan?->name ?? $subscription->type, + 'plan_price' => $subscription->plan?->price, + 'billing_cycle' => $subscription->plan?->billing_cycle, + 'renewal_date' => $subscription->current_period_end, + 'status' => $subscription->stripe_status, + 'auto_renew' => $service?->auto_renew ?? true, + 'service_id' => $service?->id, + 'days_until_renewal' => now()->diffInDays($subscription->current_period_end, false), + ]; + }); + + return Inertia::render('Billing/UpcomingRenewals', [ + 'renewals' => $renewals, + ]); + } + public function setupIntent(Request $request): JsonResponse { $user = $request->user(); diff --git a/website/resources/ts/Pages/Billing/UpcomingRenewals.vue b/website/resources/ts/Pages/Billing/UpcomingRenewals.vue new file mode 100644 index 0000000..adae17c --- /dev/null +++ b/website/resources/ts/Pages/Billing/UpcomingRenewals.vue @@ -0,0 +1,259 @@ + + + diff --git a/website/resources/ts/navigation/account.ts b/website/resources/ts/navigation/account.ts index fa4c29f..db85b68 100644 --- a/website/resources/ts/navigation/account.ts +++ b/website/resources/ts/navigation/account.ts @@ -9,6 +9,7 @@ export const accountNavItems: VerticalNavItems = [ { heading: 'Billing' }, { title: 'Billing', to: '/billing', icon: 'tabler-credit-card' }, { title: 'Payment Methods', to: '/billing/payment-methods', icon: 'tabler-wallet' }, + { title: 'Renewals', to: '/billing/renewals', icon: 'tabler-calendar-repeat' }, { title: 'Plans', to: '/plans', icon: 'tabler-package' }, { heading: 'Support' }, diff --git a/website/resources/ts/types/index.ts b/website/resources/ts/types/index.ts index c14a280..3709387 100644 --- a/website/resources/ts/types/index.ts +++ b/website/resources/ts/types/index.ts @@ -205,4 +205,39 @@ export interface TicketReply { } } +export interface AuditLog { + id: number + user_id: number | null + admin_id: number | null + action: string + resource_type: string | null + resource_id: number | null + ip_address: string | null + user_agent: string | null + changes: Record | null + created_at: string + user?: { + id: number + name: string + email: string + } + admin?: { + id: number + name: string + email: string + } +} + +export interface UpcomingRenewal { + id: number + plan_name: string + plan_price: string | null + billing_cycle: string | null + renewal_date: string + status: string + auto_renew: boolean + service_id: number | null + days_until_renewal: number +} + export type StatusColor = 'success' | 'error' | 'warning' | 'info' | 'secondary' diff --git a/website/routes/account.php b/website/routes/account.php index 7b1a2ee..72ba0ef 100644 --- a/website/routes/account.php +++ b/website/routes/account.php @@ -53,6 +53,7 @@ Route::get('/billing/payment-methods', [BillingController::class, 'paymentMethod Route::get('/billing/invoices', [BillingController::class, 'invoices'])->name('account.billing.invoices'); Route::get('/billing/invoices/{invoice}/download', [BillingController::class, 'downloadInvoice'])->name('account.billing.invoices.download'); Route::get('/billing/transactions', [BillingController::class, 'transactions'])->name('account.billing.transactions'); +Route::get('/billing/renewals', [BillingController::class, 'upcomingRenewals'])->name('account.billing.renewals'); Route::post('/billing/setup-intent', [BillingController::class, 'setupIntent'])->name('account.billing.setup-intent'); // Support Tickets