Skip to content

Commit 24a314c

Browse files
committed
feat: enhance bill management features with improved pagination, updated trial handling, and new language support
1 parent f73e809 commit 24a314c

File tree

15 files changed

+386
-116
lines changed

15 files changed

+386
-116
lines changed

app/Http/Controllers/BillController.php

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ public function index()
3535
if (str_contains($search, ':')) {
3636
[$column, $value] = explode(':', request('search', ''));
3737
if ($column && $value && in_fillable($column, Bill::class)) {
38-
return $query->where($column, 'like', '%' . $value . '%');
38+
return $query->where($column, 'like', '%'.$value.'%');
3939
}
4040
}
4141

42-
$query->where('title', 'like', '%' . $search . '%');
42+
$query->where('title', 'like', '%'.$search.'%');
4343
})
4444
->when(request('status'), function ($query) {
4545
if (request('status') === 'upcoming') {
46-
$query->upcoming();
46+
$query->upcoming(7);
4747

4848
return;
4949
}
@@ -71,21 +71,24 @@ public function index()
7171
$billsQuery->orderBy('due_date', 'asc');
7272
}
7373

74-
$bills = $billsQuery->paginate(20)->withQueryString();
74+
$bills = $billsQuery
75+
->paginate(15)
76+
->onEachSide(1)
77+
->withQueryString();
7578

7679
$currentMonthBills = Bill::query()
7780
->currentMonth()
7881
->get();
7982

83+
$upcomingCount = Bill::query()->upcoming(7)->count();
84+
8085
return inertia('Bills/Index', [
8186
'bills' => $bills,
82-
'total_unpaid' => $currentMonthBills->filter(fn($item) => ! $item->isPaid())->sum('amount'),
83-
'unpaid_count' => $currentMonthBills->filter(fn($item) => ! $item->isPaid())->count(),
84-
'upcoming_count' => $currentMonthBills
85-
->filter(fn($item) => $item->isUpcoming())
86-
->count(),
87+
'total_unpaid' => $currentMonthBills->filter(fn ($item) => ! $item->isPaid())->sum('amount'),
88+
'unpaid_count' => $currentMonthBills->filter(fn ($item) => ! $item->isPaid())->count(),
89+
'upcoming_count' => $upcomingCount,
8790
'paid_count' => $currentMonthBills
88-
->filter(fn($item) => $item->isPaid())
91+
->filter(fn ($item) => $item->isPaid())
8992
->count(),
9093
'categories' => Category::all(),
9194
]);

app/Models/Bill.php

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,11 @@ protected function checkUniqueness(string $destination, string $slug): string
115115

116116
while (
117117
self::where($destination, $slug)
118-
->where('user_id', $this->user_id)
119-
->where('team_id', $this->team_id)
120-
->exists()
118+
->where('user_id', $this->user_id)
119+
->where('team_id', $this->team_id)
120+
->exists()
121121
) {
122-
$slug = $originalSlug.'-'.$counter++.Str::random(6);
122+
$slug = $originalSlug . '-' . $counter++ . Str::random(6);
123123
}
124124

125125
return $slug;
@@ -256,16 +256,6 @@ public function isPaid()
256256
return $this->status === 'paid';
257257
}
258258

259-
/**
260-
* Check if the due date is upcoming.
261-
*
262-
* @return bool
263-
*/
264-
public function isUpcoming()
265-
{
266-
return $this->isUpcomingIn(7);
267-
}
268-
269259
/**
270260
* Check if the due date is upcoming.
271261
*
@@ -277,7 +267,9 @@ public function isUpcomingIn($days = 1)
277267
$days = 0;
278268
}
279269

280-
return $this->due_date->lte(now()->addDays(intval($days)));
270+
return $this->due_date->lte(now()->addDays(intval($days))) &&
271+
$this->due_date->gte(now()) &&
272+
$this->status === 'unpaid';
281273
}
282274

283275
/**
@@ -391,6 +383,20 @@ public function scopePaid($query)
391383
return $query->where('status', 'paid');
392384
}
393385

386+
/**
387+
* Scope a query to only include upcoming bills.
388+
*/
389+
public function scopeUpcoming($query, $days = 7)
390+
{
391+
$now = now();
392+
393+
return $query->whereBetween(
394+
'due_date',
395+
[$now, $now->copy()->addDays(intval($days))]
396+
)
397+
->where('status', 'unpaid');
398+
}
399+
394400
/**
395401
* Get all tags
396402
*
@@ -403,7 +409,7 @@ public static function getAllTags()
403409
->pluck('tags')
404410
->filter()
405411
->flatten()
406-
->map(fn ($tag) => strtolower(trim($tag)))
412+
->map(fn($tag) => strtolower(trim($tag)))
407413
->unique()
408414
->values();
409415
}
@@ -415,28 +421,16 @@ public function createUniqueTags()
415421
{
416422
if ($this->tags) {
417423
$this->tags = array_map(
418-
fn ($item) => strtolower(trim($item)),
424+
fn($item) => strtolower(trim($item)),
419425
$this->tags
420426
);
421427

422-
$this->tags = array_filter($this->tags, fn ($tag) => ! empty($tag));
428+
$this->tags = array_filter($this->tags, fn($tag) => ! empty($tag));
423429

424430
$this->tags = array_values(array_unique($this->tags));
425431
}
426432
}
427433

428-
/**
429-
* Scope a query to only include upcoming bills.
430-
*/
431-
public function scopeUpcoming($query, $days = 7)
432-
{
433-
$now = now();
434-
435-
return $query->where('due_date', '>=', $now)
436-
->where('due_date', '<=', $now->copy()->addDays(intval($days)))
437-
->where('status', 'unpaid');
438-
}
439-
440434
public function markAsPaid()
441435
{
442436
$this->update([

database/factories/BillFactory.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,24 @@ public function definition(): array
6363
'recurrence_period' => $isRecurring ? fake()->randomElement(['weekly', 'monthly', 'yearly']) : null,
6464
'payment_url' => fake()->optional(0.4)->url(),
6565
'tags' => fake()->optional(0.5)->randomElements($tags, fake()->numberBetween(1, 3)),
66-
'has_trial' => false,
66+
'has_trial' => $hasTrial,
6767
'trial_start_date' => null,
6868
'trial_end_date' => null,
6969
];
7070

7171
// Add trial dates if has_trial is true
7272
if ($hasTrial && $definition['status'] === 'unpaid') {
7373
$trialStart = fake()->dateTimeBetween('-1 month', '+1 week');
74+
$trialEnd = fake()->dateTimeBetween($trialStart, '+1 month');
75+
7476
$definition['has_trial'] = true;
7577
$definition['trial_start_date'] = $trialStart;
76-
$definition['trial_end_date'] = fake()->dateTimeBetween($trialStart, '+1 month');
78+
$definition['trial_end_date'] = $trialEnd;
79+
$definition['due_date'] = $trialEnd;
80+
}
81+
82+
if (now()->greaterThan($dueDate) && $definition['status'] === 'unpaid') {
83+
$definition['status'] = 'overdue';
7784
}
7885

7986
return $definition;
@@ -84,7 +91,7 @@ public function definition(): array
8491
*/
8592
public function paid(): static
8693
{
87-
return $this->state(fn (array $attributes) => [
94+
return $this->state(fn(array $attributes) => [
8895
'status' => 'paid',
8996
]);
9097
}
@@ -94,7 +101,7 @@ public function paid(): static
94101
*/
95102
public function unpaid(): static
96103
{
97-
return $this->state(fn (array $attributes) => [
104+
return $this->state(fn(array $attributes) => [
98105
'status' => 'unpaid',
99106
]);
100107
}
@@ -104,7 +111,7 @@ public function unpaid(): static
104111
*/
105112
public function recurring(string $period = 'monthly'): static
106113
{
107-
return $this->state(fn (array $attributes) => [
114+
return $this->state(fn(array $attributes) => [
108115
'is_recurring' => true,
109116
'recurrence_period' => $period,
110117
]);
@@ -117,11 +124,13 @@ public function withTrial(): static
117124
{
118125
return $this->state(function (array $attributes) {
119126
$trialStart = fake()->dateTimeBetween('-1 month', '+1 week');
127+
$trialEnd = fake()->dateTimeBetween($trialStart, '+1 month');
120128

121129
return [
122130
'has_trial' => true,
123131
'trial_start_date' => $trialStart,
124-
'trial_end_date' => fake()->dateTimeBetween($trialStart, '+1 month'),
132+
'trial_end_date' => $trialEnd,
133+
'due_date' => $trialEnd,
125134
];
126135
});
127136
}

lang/en/auth.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Authentication Language Lines
8+
|--------------------------------------------------------------------------
9+
|
10+
| The following language lines are used during authentication for various
11+
| messages that we need to display to the user. You are free to modify
12+
| these language lines according to your application's requirements.
13+
|
14+
*/
15+
16+
'failed' => 'These credentials do not match our records.',
17+
'password' => 'The provided password is incorrect.',
18+
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
19+
20+
];

lang/en/pagination.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Pagination Language Lines
8+
|--------------------------------------------------------------------------
9+
|
10+
| The following language lines are used by the paginator library to build
11+
| the simple pagination links. You are free to change them to anything
12+
| you want to customize your views to better match your application.
13+
|
14+
*/
15+
16+
'previous' => 'Previous',
17+
'next' => 'Next',
18+
19+
];

lang/en/passwords.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Password Reset Language Lines
8+
|--------------------------------------------------------------------------
9+
|
10+
| The following language lines are the default lines which match reasons
11+
| that are given by the password broker for a password update attempt
12+
| outcome such as failure due to an invalid password / reset token.
13+
|
14+
*/
15+
16+
'reset' => 'Your password has been reset.',
17+
'sent' => 'We have emailed your password reset link.',
18+
'throttled' => 'Please wait before retrying.',
19+
'token' => 'This password reset token is invalid.',
20+
'user' => "We can't find a user with that email address.",
21+
22+
];

0 commit comments

Comments
 (0)