From d053fc428cd066645f081b7c40dcad87bcc20834 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Wed, 29 Jul 2026 00:00:19 +0700 Subject: [PATCH] feat: enhance user profile update functionality and improve sidebar user display --- .../Settings/ProfileController.php | 16 ++++-- app/Http/Middleware/HandleInertiaRequests.php | 2 +- app/Models/User.php | 10 ++++ .../js/components/app-sidebar-header.tsx | 4 ++ resources/js/components/app-sidebar.tsx | 25 +-------- resources/js/components/nav-main.tsx | 4 +- resources/js/components/nav-user.tsx | 55 ++++++------------- resources/js/hooks/use-initials.tsx | 4 ++ resources/js/hooks/use-mobile.ts | 1 + resources/js/pages/auth/login.tsx | 2 +- 10 files changed, 53 insertions(+), 70 deletions(-) diff --git a/app/Http/Controllers/Settings/ProfileController.php b/app/Http/Controllers/Settings/ProfileController.php index d19f963..dae699c 100644 --- a/app/Http/Controllers/Settings/ProfileController.php +++ b/app/Http/Controllers/Settings/ProfileController.php @@ -30,13 +30,21 @@ public function edit(Request $request): Response */ public function update(ProfileUpdateRequest $request): RedirectResponse { - $request->user()->fill($request->validated()); + $validated = $request->validated(); + $user = $request->user(); - if ($request->user()->isDirty('email')) { - $request->user()->email_verified_at = null; + $user->fill(['email' => $validated['email']]); + + if ($user->isDirty('email')) { + $user->email_verified_at = null; } - $request->user()->save(); + $user->save(); + + $user->userProfile()->updateOrCreate( + [], + ['full_name' => $validated['name']], + ); Inertia::flash('toast', ['type' => 'success', 'message' => __('Profile updated.')]); diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index f4cc770..b0f2200 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -39,7 +39,7 @@ public function share(Request $request): array ...parent::share($request), 'name' => config('app.name'), 'auth' => [ - 'user' => $request->user(), + 'user' => $request->user()?->load('userProfile'), ], 'sidebarOpen' => ! $request->hasCookie('sidebar_state') || $request->cookie('sidebar_state') === 'true', ]; diff --git a/app/Models/User.php b/app/Models/User.php index 81f5fb3..b8b453e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -7,6 +7,7 @@ use Database\Factories\UserFactory; use Illuminate\Database\Eloquent\Attributes\Scope; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; @@ -22,6 +23,8 @@ class User extends Authenticatable { use HasFactory, HasRoles, Notifiable, SoftDeletes, TwoFactorAuthenticatable; + protected $with = ['userProfile']; + protected function casts(): array { return [ @@ -39,6 +42,13 @@ protected function active(Builder $query): void $query->where('is_active', true); } + protected function fullName(): Attribute + { + return Attribute::make( + get: fn() => $this->userProfile?->full_name ?? '', + ); + } + public function attendances(): HasMany { return $this->hasMany(Attendance::class); diff --git a/resources/js/components/app-sidebar-header.tsx b/resources/js/components/app-sidebar-header.tsx index 9d53d6c..3d97cac 100644 --- a/resources/js/components/app-sidebar-header.tsx +++ b/resources/js/components/app-sidebar-header.tsx @@ -1,4 +1,5 @@ import { Breadcrumbs } from '@/components/breadcrumbs'; +import { NavUser } from '@/components/nav-user'; import { SidebarTrigger } from '@/components/ui/sidebar'; import type { BreadcrumbItem as BreadcrumbItemType } from '@/types'; @@ -13,6 +14,9 @@ export function AppSidebarHeader({ +
+ +
); } diff --git a/resources/js/components/app-sidebar.tsx b/resources/js/components/app-sidebar.tsx index a38aa1d..c0b6b13 100644 --- a/resources/js/components/app-sidebar.tsx +++ b/resources/js/components/app-sidebar.tsx @@ -1,13 +1,10 @@ import { Link } from '@inertiajs/react'; -import { BookOpen, FolderGit2, LayoutGrid } from 'lucide-react'; +import { LayoutGrid } from 'lucide-react'; import AppLogo from '@/components/app-logo'; -import { NavFooter } from '@/components/nav-footer'; import { NavMain } from '@/components/nav-main'; -import { NavUser } from '@/components/nav-user'; import { Sidebar, SidebarContent, - SidebarFooter, SidebarHeader, SidebarMenu, SidebarMenuButton, @@ -18,25 +15,12 @@ import type { NavItem } from '@/types'; const mainNavItems: NavItem[] = [ { - title: 'Dashboard', + title: 'Dasbor', href: dashboard(), icon: LayoutGrid, }, ]; -const footerNavItems: NavItem[] = [ - { - title: 'Repository', - href: 'https://github.com/laravel/react-starter-kit', - icon: FolderGit2, - }, - { - title: 'Documentation', - href: 'https://laravel.com/docs/starter-kits#react', - icon: BookOpen, - }, -]; - export function AppSidebar() { return ( @@ -55,11 +39,6 @@ export function AppSidebar() { - - - - - ); } diff --git a/resources/js/components/nav-main.tsx b/resources/js/components/nav-main.tsx index 54095ac..2eb96d1 100644 --- a/resources/js/components/nav-main.tsx +++ b/resources/js/components/nav-main.tsx @@ -1,10 +1,9 @@ import { Link } from '@inertiajs/react'; import { SidebarGroup, - SidebarGroupLabel, SidebarMenu, SidebarMenuButton, - SidebarMenuItem, + SidebarMenuItem } from '@/components/ui/sidebar'; import { useCurrentUrl } from '@/hooks/use-current-url'; import type { NavItem } from '@/types'; @@ -14,7 +13,6 @@ export function NavMain({ items = [] }: { items: NavItem[] }) { return ( - Platform {items.map((item) => ( diff --git a/resources/js/components/nav-user.tsx b/resources/js/components/nav-user.tsx index a407239..2678bd2 100644 --- a/resources/js/components/nav-user.tsx +++ b/resources/js/components/nav-user.tsx @@ -5,54 +5,33 @@ import { DropdownMenuContent, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from '@/components/ui/sidebar'; import { UserInfo } from '@/components/user-info'; import { UserMenuContent } from '@/components/user-menu-content'; -import { useIsMobile } from '@/hooks/use-mobile'; export function NavUser() { const { auth } = usePage().props; - const { state } = useSidebar(); - const isMobile = useIsMobile(); if (!auth.user) { return null; } return ( - - - - - - - - - - - - - - - + + + + + + + + ); } diff --git a/resources/js/hooks/use-initials.tsx b/resources/js/hooks/use-initials.tsx index 407309f..701886d 100644 --- a/resources/js/hooks/use-initials.tsx +++ b/resources/js/hooks/use-initials.tsx @@ -8,6 +8,10 @@ function getInitial(name: string): string { export function useInitials(): GetInitialsFn { return useCallback((fullName: string): string => { + if (!fullName) { +return ''; +} + const names = fullName.trim().split(/\s+/u).filter(Boolean); if (names.length === 0) { diff --git a/resources/js/hooks/use-mobile.ts b/resources/js/hooks/use-mobile.ts index 2b0fe1d..77ab35a 100644 --- a/resources/js/hooks/use-mobile.ts +++ b/resources/js/hooks/use-mobile.ts @@ -12,6 +12,7 @@ export function useIsMobile() { } mql.addEventListener("change", onChange) setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + return () => mql.removeEventListener("change", onChange) }, []) diff --git a/resources/js/pages/auth/login.tsx b/resources/js/pages/auth/login.tsx index 4ac7d59..0848e34 100644 --- a/resources/js/pages/auth/login.tsx +++ b/resources/js/pages/auth/login.tsx @@ -1,3 +1,4 @@ +import { Form, Head } from '@inertiajs/react'; import InputError from '@/components/input-error'; import PasswordInput from '@/components/password-input'; import { Button } from '@/components/ui/button'; @@ -6,7 +7,6 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Spinner } from '@/components/ui/spinner'; import { store } from '@/routes/login'; -import { Form, Head } from '@inertiajs/react'; export default function Login() { return (