From 4a5436a3b40e20cc57b922117f78572916f5c2fe Mon Sep 17 00:00:00 2001 From: kushal-saha Date: Fri, 8 May 2026 07:20:59 +0000 Subject: [PATCH] chore: restructure views and add Livewire components for dashboard functionality - remove flux dashboard - add DTOs for the connecter services Deps: - spatie/laravel-data - blade-icons (lucide-icons) - tailwind-merge --- .../ConnectedServiceCollectionData.php | 14 + .../ConnectedServiceData.php | 24 + .../ConnectedServicesData.php | 18 + app/Enums/ConnectionStatus.php | 13 + app/Enums/ConnectionTechTypes.php | 11 + composer.json | 7 +- composer.lock | 686 ++++++++++++++++-- config/data.php | 217 ++++++ .../⚡all-connected-services.blade.php | 86 +++ .../⚡connected-services.blade.php | 37 + .../components/dashboard-sidebar.blade.php | 192 +++++ .../components/dashboard-stats.blade.php | 18 + .../components/dashbord-topbar.blade.php | 12 + .../components/placeholder-pattern.blade.php | 12 - .../views/components/shared/button.blade.php | 3 + .../views/components/shared/card.blade.php | 3 + .../components/shared/tabs/tab.blade.php | 45 ++ .../components/shared/tabs/tabs.blade.php | 37 + resources/views/dashboard.blade.php | 18 - resources/views/layouts/app/sidebar.blade.php | 113 +-- resources/views/pages/⚡dashboard.blade.php | 20 + resources/views/partials/head.blade.php | 2 +- routes/web.php | 8 +- tests/Feature/Auth/PasswordResetTest.php | 12 +- 24 files changed, 1415 insertions(+), 193 deletions(-) create mode 100644 app/Data/Ui/ConnectedServices/ConnectedServiceCollectionData.php create mode 100644 app/Data/Ui/ConnectedServices/ConnectedServiceData.php create mode 100644 app/Data/Ui/ConnectedServices/ConnectedServicesData.php create mode 100644 app/Enums/ConnectionStatus.php create mode 100644 app/Enums/ConnectionTechTypes.php create mode 100644 config/data.php create mode 100644 resources/views/components/connected-services/⚡all-connected-services.blade.php create mode 100644 resources/views/components/connected-services/⚡connected-services.blade.php create mode 100644 resources/views/components/dashboard-sidebar.blade.php create mode 100644 resources/views/components/dashboard-stats.blade.php create mode 100644 resources/views/components/dashbord-topbar.blade.php delete mode 100644 resources/views/components/placeholder-pattern.blade.php create mode 100644 resources/views/components/shared/button.blade.php create mode 100644 resources/views/components/shared/card.blade.php create mode 100644 resources/views/components/shared/tabs/tab.blade.php create mode 100644 resources/views/components/shared/tabs/tabs.blade.php delete mode 100644 resources/views/dashboard.blade.php create mode 100644 resources/views/pages/⚡dashboard.blade.php diff --git a/app/Data/Ui/ConnectedServices/ConnectedServiceCollectionData.php b/app/Data/Ui/ConnectedServices/ConnectedServiceCollectionData.php new file mode 100644 index 0000000..68539b7 --- /dev/null +++ b/app/Data/Ui/ConnectedServices/ConnectedServiceCollectionData.php @@ -0,0 +1,14 @@ + + */ +final class ConnectedServiceCollectionData extends Collection {} diff --git a/app/Data/Ui/ConnectedServices/ConnectedServiceData.php b/app/Data/Ui/ConnectedServices/ConnectedServiceData.php new file mode 100644 index 0000000..2b2c10e --- /dev/null +++ b/app/Data/Ui/ConnectedServices/ConnectedServiceData.php @@ -0,0 +1,24 @@ + DATE_ATOM, + + /* + * When transforming or casting dates, the following timezone will be used to + * convert the date to the correct timezone. If set to null no timezone will + * be passed. + */ + 'date_timezone' => null, + + /* + * It is possible to enable certain features of the package, these would otherwise + * be breaking changes, and thus they are disabled by default. In the next major + * version of the package, these features will be enabled by default. + */ + 'features' => [ + 'cast_and_transform_iterables' => false, + + /* + * When trying to set a computed property value, the package will throw an exception. + * You can disable this behaviour by setting this option to true, which will then just + * ignore the value being passed into the computed property and recalculate it. + */ + 'ignore_exception_when_trying_to_set_computed_property_value' => false, + ], + + /* + * Global transformers will take complex types and transform them into simple + * types. + */ + 'transformers' => [ + DateTimeInterface::class => DateTimeInterfaceTransformer::class, + Arrayable::class => ArrayableTransformer::class, + BackedEnum::class => EnumTransformer::class, + ], + + /* + * Global casts will cast values into complex types when creating a data + * object from simple types. + */ + 'casts' => [ + DateTimeInterface::class => DateTimeInterfaceCast::class, + BackedEnum::class => EnumCast::class, + // Enumerable::class => Spatie\LaravelData\Casts\EnumerableCast::class, + ], + + /* + * Rule inferrers can be configured here. They will automatically add + * validation rules to properties of a data object based upon + * the type of the property. + */ + 'rule_inferrers' => [ + SometimesRuleInferrer::class, + NullableRuleInferrer::class, + RequiredRuleInferrer::class, + BuiltInTypesRuleInferrer::class, + AttributesRuleInferrer::class, + ], + + /* + * Normalizers return an array representation of the payload, or null if + * it cannot normalize the payload. The normalizers below are used for + * every data object, unless overridden in a specific data object class. + */ + 'normalizers' => [ + ModelNormalizer::class, + // Spatie\LaravelData\Normalizers\FormRequestNormalizer::class, + ArrayableNormalizer::class, + ObjectNormalizer::class, + ArrayNormalizer::class, + JsonNormalizer::class, + ], + + /* + * Data objects can be wrapped into a key like 'data' when used as a resource, + * this key can be set globally here for all data objects. You can pass in + * `null` if you want to disable wrapping. + */ + 'wrap' => null, + + /* + * Adds a specific caster to the Symphony VarDumper component which hides + * some properties from data objects and collections when being dumped + * by `dump` or `dd`. Can be 'enabled', 'disabled' or 'development' + * which will only enable the caster locally. + */ + 'var_dumper_caster_mode' => 'development', + + /* + * It is possible to skip the PHP reflection analysis of data objects + * when running in production. This will speed up the package. You + * can configure where data objects are stored and which cache + * store should be used. + * + * Structures are cached forever as they'll become stale when your + * application is deployed with changes. You can set a duration + * in seconds if you want the cache to clear after a certain + * timeframe. + */ + 'structure_caching' => [ + 'enabled' => true, + 'directories' => [app_path('Data')], + 'cache' => [ + 'store' => env('CACHE_STORE', env('CACHE_DRIVER', 'file')), + 'prefix' => 'laravel-data', + 'duration' => null, + ], + 'reflection_discovery' => [ + 'enabled' => true, + 'base_path' => base_path(), + 'root_namespace' => null, + ], + ], + + /* + * A data object can be validated when created using a factory or when calling the from + * method. By default, only when a request is passed the data is being validated. This + * behaviour can be changed to always validate or to completely disable validation. + */ + 'validation_strategy' => ValidationStrategy::OnlyRequests->value, + + /* + * A data object can map the names of its properties when transforming (output) or when + * creating (input). By default, the package will not map any names. You can set a + * global strategy here, or override it on a specific data object. + */ + 'name_mapping_strategy' => [ + 'input' => null, + 'output' => null, + ], + + /* + * When using an invalid include, exclude, only or except partial, the package will + * throw an exception. You can disable this behaviour by setting this option to true. + */ + 'ignore_invalid_partials' => false, + + /* + * When transforming a nested chain of data objects, the package can end up in an infinite + * loop when including a recursive relationship. The max transformation depth can be + * set as a safety measure to prevent this from happening. When set to null, the + * package will not enforce a maximum depth. + */ + 'max_transformation_depth' => null, + + /* + * When the maximum transformation depth is reached, the package will throw an exception. + * You can disable this behaviour by setting this option to true which will return an + * empty array. + */ + 'throw_when_max_transformation_depth_reached' => true, + + /* + * When using the `make:data` command, the package will use these settings to generate + * the data classes. You can override these settings by passing options to the command. + */ + 'commands' => [ + + /* + * Provides default configuration for the `make:data` command. These settings can be overridden with options + * passed directly to the `make:data` command for generating single Data classes, or if not set they will + * automatically fall back to these defaults. See `php artisan make:data --help` for more information + */ + 'make' => [ + + /* + * The default namespace for generated Data classes. This exists under the application's root namespace, + * so the default 'Data` will end up as '\App\Data', and generated Data classes will be placed in the + * app/Data/ folder. Data classes can live anywhere, but this is where `make:data` will put them. + */ + 'namespace' => 'Data', + + /* + * This suffix will be appended to all data classes generated by make:data, so that they are less likely + * to conflict with other related classes, controllers or models with a similar name without resorting + * to adding an alias for the Data object. Set to a blank string (not null) to disable. + */ + 'suffix' => 'Data', + ], + ], + + /* + * When using Livewire, the package allows you to enable or disable the synths + * these synths will automatically handle the data objects and their + * properties when used in a Livewire component. + */ + 'livewire' => [ + 'enable_synths' => false, + ], +]; diff --git a/resources/views/components/connected-services/⚡all-connected-services.blade.php b/resources/views/components/connected-services/⚡all-connected-services.blade.php new file mode 100644 index 0000000..f7c4d6c --- /dev/null +++ b/resources/views/components/connected-services/⚡all-connected-services.blade.php @@ -0,0 +1,86 @@ +connectedServices = new ConnectedServicesData( + services: new ConnectedServiceCollectionData([ + new ConnectedServiceData( + name: 'Microsoft Outlook', + icon: 'home', + type: ConnectionTechTypes::OIDC, + connectionService: 'Azure AD', + status: ConnectionStatus::Connected + ), + new ConnectedServiceData( + name: 'Microsoft Teams', + icon: 'home', + type: ConnectionTechTypes::OIDC, + connectionService: 'Azure AD', + status: ConnectionStatus::Pending + ), + new ConnectedServiceData( + name: 'Keka HR', + icon: 'home', + type: ConnectionTechTypes::SAML, + connectionService: 'KeKA', + status: ConnectionStatus::Disconnected + ), + new ConnectedServiceData( + name: 'Trutimer', + icon: 'home', + type: ConnectionTechTypes::SAML, + connectionService: 'Redmine', + status: ConnectionStatus::Error + ), + ]) + ); + } +}; +?> + +
+ @foreach ($connectedServices->services as $service) + +
+
+ + Outline + + +
+
+

{{$service->name}}

+

{{$service->type->name}} - {{$service->connectionService}}

+
+
+
+
+
$service->status === ConnectionStatus::Connected, + 'bg-gray-400' => $service->status === ConnectionStatus::Disconnected, + 'bg-yellow-400' => $service->status === ConnectionStatus::Pending, + 'bg-red-400' => $service->status === ConnectionStatus::Error, + ]) + >
+

{{ucfirst($service->status->name)}}

+
+ Revoke +
+
+ @endforeach +
diff --git a/resources/views/components/connected-services/⚡connected-services.blade.php b/resources/views/components/connected-services/⚡connected-services.blade.php new file mode 100644 index 0000000..c6caf28 --- /dev/null +++ b/resources/views/components/connected-services/⚡connected-services.blade.php @@ -0,0 +1,37 @@ + + +
+ +
+

+ + {{ __('Connected Services') }} +

+ +
+ + {{ __('Add service') }} +
+
+
+ + + + + +
Tricks
+
+ +
Musics
+
+
+
+
diff --git a/resources/views/components/dashboard-sidebar.blade.php b/resources/views/components/dashboard-sidebar.blade.php new file mode 100644 index 0000000..9dff3e1 --- /dev/null +++ b/resources/views/components/dashboard-sidebar.blade.php @@ -0,0 +1,192 @@ +active = $active; + $this->collapsed = session('sidebar_collapsed', false); + } + + public function toggle(): void + { + $this->collapsed = !$this->collapsed; + session(['sidebar_collapsed' => $this->collapsed]); + } + + public function navItems(): array + { + return [ + [ + 'key' => 'dashboard', + 'label' => 'Dashboard', + 'icon' => 'lucide-house', + 'route' => 'home', + ], + [ + 'key' => 'users', + 'label' => 'Users', + 'icon' => 'lucide-users', + 'route' => 'home', + ], + ]; + } +}; +?> + diff --git a/resources/views/components/dashboard-stats.blade.php b/resources/views/components/dashboard-stats.blade.php new file mode 100644 index 0000000..88d5e1d --- /dev/null +++ b/resources/views/components/dashboard-stats.blade.php @@ -0,0 +1,18 @@ + + +

Total Users

+

284

+
+ +

Active Roles

+

4

+
+ +

Services

+

8

+
+ +

Revoked Today

+

7

+
+
diff --git a/resources/views/components/dashbord-topbar.blade.php b/resources/views/components/dashbord-topbar.blade.php new file mode 100644 index 0000000..10c522a --- /dev/null +++ b/resources/views/components/dashbord-topbar.blade.php @@ -0,0 +1,12 @@ +
+
+

Admin Panel

+
+
+ + + +
+
+
+
diff --git a/resources/views/components/placeholder-pattern.blade.php b/resources/views/components/placeholder-pattern.blade.php deleted file mode 100644 index 8a434f0..0000000 --- a/resources/views/components/placeholder-pattern.blade.php +++ /dev/null @@ -1,12 +0,0 @@ -@props([ - 'id' => uniqid(), -]) - - - - - - - - - diff --git a/resources/views/components/shared/button.blade.php b/resources/views/components/shared/button.blade.php new file mode 100644 index 0000000..51f30af --- /dev/null +++ b/resources/views/components/shared/button.blade.php @@ -0,0 +1,3 @@ + diff --git a/resources/views/components/shared/card.blade.php b/resources/views/components/shared/card.blade.php new file mode 100644 index 0000000..c8c00b1 --- /dev/null +++ b/resources/views/components/shared/card.blade.php @@ -0,0 +1,3 @@ +
twMerge('rounded-xl border border-gray-200 p-4 shadow-xs bg-white')}}> + {{$slot}} +
diff --git a/resources/views/components/shared/tabs/tab.blade.php b/resources/views/components/shared/tabs/tab.blade.php new file mode 100644 index 0000000..605b7ea --- /dev/null +++ b/resources/views/components/shared/tabs/tab.blade.php @@ -0,0 +1,45 @@ +@php use Illuminate\Support\Facades\Blade; @endphp +@props([ + 'name', + 'label' => null, + 'disabled' => false, + 'hidden' => false, +]) + +@php + $fromLabel = $label ?? ''; + + $labelHtml = Blade::render(" +
'{$disabled}' + ])> + {$fromLabel} +
+ "); +@endphp + +
class("tab-content py-5 px-1") }} +> + {{ $slot }} +
diff --git a/resources/views/components/shared/tabs/tabs.blade.php b/resources/views/components/shared/tabs/tabs.blade.php new file mode 100644 index 0000000..3b63bf3 --- /dev/null +++ b/resources/views/components/shared/tabs/tabs.blade.php @@ -0,0 +1,37 @@ +@props([ + 'selected' => null, + 'labelClass' => 'px-4 py-3 cursor-pointer', + 'activeClass' => 'font-medium border-b border-b-gray-400', + 'labelDivClass' => 'border-b border-b-base-content/10 flex overflow-x-auto', + 'tabsClass' => 'relative w-full', +]) + +
+
+ +
+ +
except(['wire:model', 'wire:model.live'])->class(['block']) }}> + {{ $slot }} +
+
diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php deleted file mode 100644 index 8f08c05..0000000 --- a/resources/views/dashboard.blade.php +++ /dev/null @@ -1,18 +0,0 @@ - -
-
-
- -
-
- -
-
- -
-
-
- -
-
-
diff --git a/resources/views/layouts/app/sidebar.blade.php b/resources/views/layouts/app/sidebar.blade.php index 6db2170..37b6cff 100644 --- a/resources/views/layouts/app/sidebar.blade.php +++ b/resources/views/layouts/app/sidebar.blade.php @@ -1,101 +1,18 @@ - - @include('partials.head') - - - - - - - - - - - - {{ __('Dashboard') }} - - - - - - - - - {{ __('Repository') }} - - - - {{ __('Documentation') }} - - - - - - - - - - - - - - - - -
-
- - -
- {{ auth()->user()->name }} - {{ auth()->user()->email }} -
-
-
-
- - - - - - {{ __('Settings') }} - - - - - -
- @csrf - - {{ __('Log out') }} - -
-
-
-
- - {{ $slot }} - - @persist('toast') - - - - @endpersist - - @fluxScripts - + + @include('partials.head') + + +
+ +
+ +
+ {{ $slot }} +
+
+
+@livewireScripts + diff --git a/resources/views/pages/⚡dashboard.blade.php b/resources/views/pages/⚡dashboard.blade.php new file mode 100644 index 0000000..ae40408 --- /dev/null +++ b/resources/views/pages/⚡dashboard.blade.php @@ -0,0 +1,20 @@ + + +
+ + +
+ diff --git a/resources/views/partials/head.blade.php b/resources/views/partials/head.blade.php index 8e9a491..a6bdc44 100644 --- a/resources/views/partials/head.blade.php +++ b/resources/views/partials/head.blade.php @@ -12,4 +12,4 @@ @fonts @vite(['resources/css/app.css', 'resources/js/app.js']) -@fluxAppearance +@livewireStyles diff --git a/routes/web.php b/routes/web.php index 12e932e..91a29c1 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,11 +1,11 @@ name('home'); -Route::middleware(['auth', 'verified'])->group(function () { - Route::view('dashboard', 'dashboard')->name('dashboard'); -}); +Route::livewire('dashboard', 'pages::dashboard')->name('dashboard'); -require __DIR__.'/settings.php'; +require __DIR__ . '/settings.php'; diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php index 9f55aba..44672b0 100644 --- a/tests/Feature/Auth/PasswordResetTest.php +++ b/tests/Feature/Auth/PasswordResetTest.php @@ -1,21 +1,23 @@ skipUnlessFortifyHas(Features::resetPasswords()); }); -test('reset password link screen can be rendered', function () { +test('reset password link screen can be rendered', function (): void { $response = $this->get(route('password.request')); $response->assertOk(); }); -test('reset password link can be requested', function () { +test('reset password link can be requested', function (): void { Notification::fake(); $user = User::factory()->create(); @@ -25,7 +27,7 @@ Notification::assertSentTo($user, ResetPassword::class); }); -test('reset password screen can be rendered', function () { +test('reset password screen can be rendered', function (): void { Notification::fake(); $user = User::factory()->create(); @@ -41,7 +43,7 @@ }); }); -test('password can be reset with valid token', function () { +test('password can be reset with valid token', function (): void { Notification::fake(); $user = User::factory()->create();