Skip to content

Commit 4f05c5a

Browse files
committed
feat: Implement breadcrumb navigation across settings pages and add confirm dialog functionality
1 parent c781241 commit 4f05c5a

File tree

26 files changed

+515
-76
lines changed

26 files changed

+515
-76
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ These projects were the main inspiration for creating this starter kit. I’ve a
4444

4545
I prefer explicit event handling over “magic” behavior. With custom events, you can keep logic clean and encapsulated inside dedicated listener classes.
4646

47-
6. **Custom Helpers**: Instead of a typical `helpers.php` file, this kit organizes helpers as **Traits**, **Services**, **Support** classes, and **Facades**.
47+
6. **Custom Helpers**: This kit organizes helpers as **Traits**, **Services**, **Support** classes, and **Facades**.
4848
- A small `helpers.php` file is still included for global functions where class imports aren’t possible.
4949

5050
---
@@ -111,6 +111,15 @@ Now as the app is all setup correctly you can start working on it:
111111
composer dev
112112
```
113113

114+
**Note**: `composer dev` may give you an error about the `pail` command, specially on Windows machines, as the `pail` command is not supported on windows.
115+
116+
To make it work you can use alternative command:
117+
118+
```bash
119+
# Start development server on Windows
120+
composer dev:win
121+
```
122+
114123
### Check on browser
115124

116125
After running your development server you can visit to [http://localhost:8000](http://localhost:8000) or [http://127.0.0.1:8000](http://127.0.0.1:8000) on your favorite browser to see the app in action.

app/Http/Controllers/Settings/GlobalConfigController.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use App\Http\Requests\Settings\UpdateGlobalConfigRequest;
1111
use App\Http\Resources\GlobalConfigResource;
1212
use App\Models\GlobalConfig;
13+
use App\Support\BreadcrumbItem;
14+
use App\Traits\HasBreadcrumbs;
1315
use Error;
1416
use Exception;
1517
use Illuminate\Http\RedirectResponse;
@@ -22,6 +24,8 @@
2224

2325
final class GlobalConfigController
2426
{
27+
use HasBreadcrumbs;
28+
2529
public function index(Request $request): Response|JsonResource
2630
{
2731
if ($request->wantsJson()) {
@@ -31,6 +35,14 @@ public function index(Request $request): Response|JsonResource
3135
return Inertia::render('settings/global-configs/index', [
3236
'title' => 'Global Configurations',
3337
'query' => $request->query(),
38+
'breadcrumbs' => $this->withBreadcrumbs([
39+
BreadcrumbItem::make()
40+
->labeled('Settings')
41+
->to(route('global-configs.index')),
42+
BreadcrumbItem::make()
43+
->labeled('Global Configuration')
44+
->to(route('global-configs.index')),
45+
]),
3446
]);
3547
}
3648

@@ -39,6 +51,14 @@ public function create(): Response
3951
return Inertia::render('settings/global-configs/form', [
4052
'title' => 'Create Global Config',
4153
'types' => GlobalConfigType::values(),
54+
'breadcrumbs' => $this->withBreadcrumbs([
55+
BreadcrumbItem::make('Settings')
56+
->to(route('global-configs.index')),
57+
BreadcrumbItem::make('Global Configuration')
58+
->to(route('global-configs.index')),
59+
BreadcrumbItem::make('Create Global Configuration')
60+
->to(route('global-configs.create')),
61+
]),
4262
]);
4363
}
4464

@@ -67,6 +87,14 @@ public function edit(GlobalConfig $globalConfig): Response
6787
'title' => 'Update Global Config',
6888
'globalConfig' => new GlobalConfigResource($globalConfig),
6989
'types' => GlobalConfigType::values(),
90+
'breadcrumbs' => $this->withBreadcrumbs([
91+
BreadcrumbItem::make('Settings')
92+
->to(route('global-configs.index')),
93+
BreadcrumbItem::make('Global Configuration')
94+
->to(route('global-configs.index')),
95+
BreadcrumbItem::make('Update Global Configuration')
96+
->to(route('global-configs.edit', [$globalConfig])),
97+
]),
7098
]);
7199
}
72100

app/Http/Controllers/Settings/PasswordController.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace App\Http\Controllers\Settings;
66

77
use App\Models\User;
8+
use App\Support\BreadcrumbItem;
9+
use App\Traits\HasBreadcrumbs;
810
use Illuminate\Container\Attributes\CurrentUser;
911
use Illuminate\Http\RedirectResponse;
1012
use Illuminate\Http\Request;
@@ -15,12 +17,22 @@
1517

1618
final class PasswordController
1719
{
20+
use HasBreadcrumbs;
21+
1822
/**
1923
* Show the user's password settings page.
2024
*/
2125
public function edit(): Response
2226
{
23-
return Inertia::render('settings/password');
27+
return Inertia::render('settings/password', [
28+
'title' => 'Password',
29+
'breadcrumbs' => $this->withBreadcrumbs([
30+
BreadcrumbItem::make('Settings')
31+
->to(route('global-configs.index')),
32+
BreadcrumbItem::make('Password')
33+
->to(route('password.edit')),
34+
]),
35+
]);
2436
}
2537

2638
/**

app/Http/Controllers/Settings/ProfileController.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use App\Http\Requests\Settings\ProfileUpdateRequest;
88
use App\Models\User;
9+
use App\Support\BreadcrumbItem;
10+
use App\Traits\HasBreadcrumbs;
911
use Illuminate\Container\Attributes\CurrentUser;
1012
use Illuminate\Contracts\Auth\MustVerifyEmail;
1113
use Illuminate\Http\RedirectResponse;
@@ -16,6 +18,8 @@
1618

1719
final class ProfileController
1820
{
21+
use HasBreadcrumbs;
22+
1923
/**
2024
* Show the user's profile settings page.
2125
*/
@@ -24,6 +28,12 @@ public function edit(Request $request): Response
2428
return Inertia::render('settings/profile', [
2529
'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail,
2630
'status' => $request->session()->get('status'),
31+
'breadcrumbs' => $this->withBreadcrumbs([
32+
BreadcrumbItem::make('Settings')
33+
->to(route('global-configs.index')),
34+
BreadcrumbItem::make('Profile settings')
35+
->to(route('profile.edit')),
36+
]),
2737
]);
2838
}
2939

app/Http/Controllers/Settings/TwoFactorAuthenticationController.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use App\Http\Requests\Settings\TwoFactorAuthenticationRequest;
88
use App\Models\User;
9+
use App\Support\BreadcrumbItem;
10+
use App\Traits\HasBreadcrumbs;
911
use Illuminate\Container\Attributes\CurrentUser;
1012
use Illuminate\Routing\Controllers\HasMiddleware;
1113
use Illuminate\Routing\Controllers\Middleware;
@@ -15,6 +17,8 @@
1517

1618
final class TwoFactorAuthenticationController implements HasMiddleware
1719
{
20+
use HasBreadcrumbs;
21+
1822
/**
1923
* Get the middleware that should be assigned to the controller.
2024
*/
@@ -35,6 +39,12 @@ public function show(TwoFactorAuthenticationRequest $request, #[CurrentUser] Use
3539
return Inertia::render('settings/two-factor', [
3640
'twoFactorEnabled' => $user->hasEnabledTwoFactorAuthentication() ?: false,
3741
'requiresConfirmation' => Features::optionEnabled(Features::twoFactorAuthentication(), 'confirm'),
42+
'breadcrumbs' => $this->withBreadcrumbs([
43+
BreadcrumbItem::make('Settings')
44+
->to(route('global-configs.index')),
45+
BreadcrumbItem::make('Two-Factor Authentication')
46+
->to(route('two-factor.show')),
47+
]),
3848
]);
3949
}
4050
}

app/Support/BreadcrumbItem.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Support;
6+
7+
use Illuminate\Contracts\Support\Arrayable;
8+
9+
/**
10+
* @implements Arrayable<string, mixed>
11+
*/
12+
final class BreadcrumbItem implements Arrayable
13+
{
14+
private string $label = '';
15+
16+
private ?string $url = null;
17+
18+
private bool $current = false;
19+
20+
public static function make(?string $label = null): self
21+
{
22+
$instance = new self;
23+
24+
if ($label !== null && $label !== '' && $label !== '0') {
25+
$instance->label = $label;
26+
}
27+
28+
return $instance;
29+
}
30+
31+
public function labeled(string $label): static
32+
{
33+
$this->label = $label;
34+
35+
return $this;
36+
}
37+
38+
public function to(?string $url): static
39+
{
40+
$this->url = $url;
41+
42+
return $this;
43+
}
44+
45+
public function url(?string $url): static
46+
{
47+
return $this->to($url);
48+
}
49+
50+
public function isCurrent(bool $current = true): static
51+
{
52+
$this->current = $current;
53+
54+
return $this;
55+
}
56+
57+
/**
58+
* @return array{url: string|null, label: string, current: bool}
59+
*/
60+
public function toArray(): array
61+
{
62+
return [
63+
'url' => $this->url,
64+
'label' => $this->label,
65+
'current' => $this->current,
66+
];
67+
}
68+
}

app/Traits/HasBreadcrumbs.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Traits;
6+
7+
use App\Support\BreadcrumbItem;
8+
9+
trait HasBreadcrumbs
10+
{
11+
/**
12+
* Summary of withBreadcrumbs
13+
*
14+
* @param BreadcrumbItem[] $items
15+
* @return array<int, array{url: string|null, label: string, current: bool}>
16+
*/
17+
public function withBreadcrumbs(array $items): array
18+
{
19+
/** @var array<int, array{url: string|null, label: string, current: bool}> $breadcrumbs */
20+
$breadcrumbs = collect($items)->map(fn ($item): array => $item->toArray())->toArray();
21+
22+
return $breadcrumbs;
23+
}
24+
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"dev:ssr": [
6868
"npm run build:ssr",
6969
"Composer\\Config::disableProcessTimeout",
70-
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"php artisan inertia:start-ssr\" --names=server,queue,logs,ssr --kill-others"
70+
"npx concurrently -c \"#93c5fd,#c4b5fd,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan inertia:start-ssr\" --names=server,queue,ssr --kill-others"
7171
],
7272
"post-autoload-dump": [
7373
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",

package-lock.json

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"dependencies": {
2323
"@headlessui/react": "^2.2.9",
2424
"@inertiajs/react": "^2.2.8",
25+
"@radix-ui/react-alert-dialog": "^1.1.15",
2526
"@radix-ui/react-avatar": "^1.1.10",
2627
"@radix-ui/react-checkbox": "^1.3.3",
2728
"@radix-ui/react-collapsible": "^1.1.12",

0 commit comments

Comments
 (0)