diff --git a/app/Exceptions/PermissionConfigurationException.php b/app/Exceptions/PermissionConfigurationException.php new file mode 100644 index 0000000..1df315a --- /dev/null +++ b/app/Exceptions/PermissionConfigurationException.php @@ -0,0 +1,21 @@ +can(PermissionsEnum::VIEW_USERS->value); } - public function view(User $user, User $model): bool { + public function view(User $user): bool { return $user->can(PermissionsEnum::VIEW_USERS->value); } @@ -49,7 +49,7 @@ public function delete(User $user, User $model): bool { return $user->can(PermissionsEnum::DELETE_USERS->value); } - public function restore(User $user, User $model): bool { + public function restore(User $user): bool { return $user->can(PermissionsEnum::RESTORE_USERS->value); } } diff --git a/config/database.php b/config/database.php index 3a71a93..a756577 100644 --- a/config/database.php +++ b/config/database.php @@ -4,6 +4,8 @@ use Illuminate\Support\Str; +$DEFAULT_HOST = '127.0.0.1'; + return [ /* @@ -47,7 +49,7 @@ 'mysql' => [ 'driver' => 'mysql', 'url' => env('DB_URL'), - 'host' => env('DB_HOST', '127.0.0.1'), + 'host' => env('DB_HOST', $DEFAULT_HOST), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'laravel'), 'username' => env('DB_USERNAME', 'root'), @@ -67,7 +69,7 @@ 'mariadb' => [ 'driver' => 'mariadb', 'url' => env('DB_URL'), - 'host' => env('DB_HOST', '127.0.0.1'), + 'host' => env('DB_HOST', $DEFAULT_HOST), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'laravel'), 'username' => env('DB_USERNAME', 'root'), @@ -87,7 +89,7 @@ 'pgsql' => [ 'driver' => 'pgsql', 'url' => env('DB_URL'), - 'host' => env('DB_HOST', '127.0.0.1'), + 'host' => env('DB_HOST', $DEFAULT_HOST), 'port' => env('DB_PORT', '5432'), 'database' => env('DB_DATABASE', 'laravel'), 'username' => env('DB_USERNAME', 'root'), @@ -155,7 +157,7 @@ 'default' => [ 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), + 'host' => env('REDIS_HOST', $DEFAULT_HOST), 'username' => env('REDIS_USERNAME'), 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), @@ -164,7 +166,7 @@ 'cache' => [ 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), + 'host' => env('REDIS_HOST', $DEFAULT_HOST), 'username' => env('REDIS_USERNAME'), 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), diff --git a/config/logging.php b/config/logging.php index 562ef8d..2abfd13 100644 --- a/config/logging.php +++ b/config/logging.php @@ -7,6 +7,8 @@ use Monolog\Handler\SyslogUdpHandler; use Monolog\Processor\PsrLogMessageProcessor; +$DEFAULT_LOG_PATH = 'logs/laravel.log'; + return [ /* @@ -62,14 +64,14 @@ 'single' => [ 'driver' => 'single', - 'path' => storage_path('logs/laravel.log'), + 'path' => storage_path($DEFAULT_LOG_PATH), 'level' => env('LOG_LEVEL', 'debug'), 'replace_placeholders' => true, ], 'daily' => [ 'driver' => 'daily', - 'path' => storage_path('logs/laravel.log'), + 'path' => storage_path($DEFAULT_LOG_PATH), 'level' => env('LOG_LEVEL', 'debug'), 'days' => env('LOG_DAILY_DAYS', 14), 'replace_placeholders' => true, @@ -126,7 +128,7 @@ ], 'emergency' => [ - 'path' => storage_path('logs/laravel.log'), + 'path' => storage_path($DEFAULT_LOG_PATH), ], ], diff --git a/database/migrations/2025_03_19_061321_create_permission_tables.php b/database/migrations/2025_03_19_061321_create_permission_tables.php index 05ab13c..9966d62 100644 --- a/database/migrations/2025_03_19_061321_create_permission_tables.php +++ b/database/migrations/2025_03_19_061321_create_permission_tables.php @@ -1,5 +1,6 @@ RolesEnum::ADMINISTRATOR->value, 'guard_name' => 'web']); $user_manager_role = Role::firstOrCreate(['name' => RolesEnum::USER_MANAGER->value, 'guard_name' => 'web']); - $registered_user_role = Role::firstOrCreate(['name' => RolesEnum::REGISTERED_USER->value, 'guard_name' => 'web']); + Role::firstOrCreate(['name' => RolesEnum::REGISTERED_USER->value, 'guard_name' => 'web']); // flush cache after creating roles and permissions app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions(); diff --git a/resources/js/app.tsx b/resources/js/app.tsx index cbc8483..65f3ca9 100644 --- a/resources/js/app.tsx +++ b/resources/js/app.tsx @@ -5,7 +5,7 @@ import { createRoot } from 'react-dom/client'; import { initializeTheme } from './hooks/use-appearance'; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; +const appName = import.meta.env.VITE_APP_NAME ?? 'Laravel'; createInertiaApp({ title: (title) => `${title} - ${appName}`, diff --git a/resources/js/components/app-content.tsx b/resources/js/components/app-content.tsx index e9a81f8..0f16ae5 100644 --- a/resources/js/components/app-content.tsx +++ b/resources/js/components/app-content.tsx @@ -5,7 +5,7 @@ interface AppContentProps extends React.ComponentProps<'main'> { variant?: 'header' | 'sidebar'; } -export function AppContent({ variant = 'header', children, ...props }: AppContentProps) { +export function AppContent({ variant = 'header', children, ...props }: Readonly) { if (variant === 'sidebar') { return {children}; } diff --git a/resources/js/components/app-header.tsx b/resources/js/components/app-header.tsx index d7dca70..9ad310d 100644 --- a/resources/js/components/app-header.tsx +++ b/resources/js/components/app-header.tsx @@ -52,7 +52,7 @@ interface AppHeaderProps { breadcrumbs?: BreadcrumbItem[]; } -export function AppHeader({ breadcrumbs = [] }: AppHeaderProps) { +export function AppHeader({ breadcrumbs = [] }: Readonly) { const page = usePage(); const { auth } = page.props; const getInitials = useInitials(); @@ -136,9 +136,9 @@ export function AppHeader({ breadcrumbs = [] }: AppHeaderProps) {
- {mainNavItems.map((item, index) => ( + {mainNavItems.map((item) => ( ) {...props} src="/images/logo.png" alt="App Logo" - className={`w-auto ${props.className || ''}`} + className={`w-auto ${props.className ?? ''}`} /> ); } diff --git a/resources/js/components/app-shell.tsx b/resources/js/components/app-shell.tsx index b17fe3b..fbd20b0 100644 --- a/resources/js/components/app-shell.tsx +++ b/resources/js/components/app-shell.tsx @@ -6,7 +6,7 @@ interface AppShellProps { variant?: 'header' | 'sidebar'; } -export function AppShell({ children, variant = 'header' }: AppShellProps) { +export function AppShell({ children, variant = 'header' }: Readonly) { const [isOpen, setIsOpen] = useState(() => typeof window !== 'undefined' ? localStorage.getItem('sidebar') !== 'false' : true ); diff --git a/resources/js/components/app-sidebar-header.tsx b/resources/js/components/app-sidebar-header.tsx index 724529e..f80146a 100644 --- a/resources/js/components/app-sidebar-header.tsx +++ b/resources/js/components/app-sidebar-header.tsx @@ -2,7 +2,9 @@ import { Breadcrumbs } from '@/components/breadcrumbs'; import { SidebarTrigger } from '@/components/ui/sidebar'; import { type BreadcrumbItem as BreadcrumbItemType } from '@/types'; -export function AppSidebarHeader({ breadcrumbs = [] }: { breadcrumbs?: BreadcrumbItemType[] }) { +export function AppSidebarHeader({ + breadcrumbs = [], +}: Readonly<{ breadcrumbs?: BreadcrumbItemType[] }>) { return (
diff --git a/resources/js/components/breadcrumbs.tsx b/resources/js/components/breadcrumbs.tsx index a5d9f19..e4422df 100644 --- a/resources/js/components/breadcrumbs.tsx +++ b/resources/js/components/breadcrumbs.tsx @@ -10,7 +10,7 @@ import { type BreadcrumbItem as BreadcrumbItemType } from '@/types'; import { Link } from '@inertiajs/react'; import { Fragment } from 'react'; -export function Breadcrumbs({ breadcrumbs }: { breadcrumbs: BreadcrumbItemType[] }) { +export function Breadcrumbs({ breadcrumbs }: Readonly<{ breadcrumbs: BreadcrumbItemType[] }>) { return ( <> {breadcrumbs.length > 0 && ( diff --git a/resources/js/components/heading-small.tsx b/resources/js/components/heading-small.tsx index 1409941..4ac059c 100644 --- a/resources/js/components/heading-small.tsx +++ b/resources/js/components/heading-small.tsx @@ -1,10 +1,10 @@ export default function HeadingSmall({ title, description, -}: { +}: Readonly<{ title: string; description?: string; -}) { +}>) { return (

{title}

diff --git a/resources/js/components/heading.tsx b/resources/js/components/heading.tsx index 879a1da..28ca3ea 100644 --- a/resources/js/components/heading.tsx +++ b/resources/js/components/heading.tsx @@ -1,4 +1,7 @@ -export default function Heading({ title, description }: { title: string; description?: string }) { +export default function Heading({ + title, + description, +}: Readonly<{ title: string; description?: string }>) { return (

{title}

diff --git a/resources/js/components/icon.tsx b/resources/js/components/icon.tsx index ee53466..ae204b7 100644 --- a/resources/js/components/icon.tsx +++ b/resources/js/components/icon.tsx @@ -6,6 +6,6 @@ interface IconProps extends Omit { iconNode: ComponentType; } -export function Icon({ iconNode: IconComponent, className, ...props }: IconProps) { +export function Icon({ iconNode: IconComponent, className, ...props }: Readonly) { return ; } diff --git a/resources/js/components/nav-footer.tsx b/resources/js/components/nav-footer.tsx index 8d18df0..695af22 100644 --- a/resources/js/components/nav-footer.tsx +++ b/resources/js/components/nav-footer.tsx @@ -17,7 +17,7 @@ export function NavFooter({ items: NavItem[]; }) { return ( - + {items.map((item) => ( diff --git a/resources/js/components/nav-main.tsx b/resources/js/components/nav-main.tsx index 8b79c95..ebad446 100644 --- a/resources/js/components/nav-main.tsx +++ b/resources/js/components/nav-main.tsx @@ -17,16 +17,18 @@ interface NavMainProps { items: NavItemType[]; } -export function NavMain({ items }: NavMainProps) { +export function NavMain({ items }: Readonly) { const { url } = usePage(); const { state } = useSidebar(); - const [openGroups, setOpenGroups] = useState(() => { + const [openGroups, setOpenGroups] = useState(() => { // Initialize with groups that have defaultOpen set to true // or if they contain the active route return items - .map((item, index) => { + .map((item) => { + const itemKey = item.href || item.title; + // Check if item is set to defaultOpen - if (item.defaultOpen) return index; + if (item.defaultOpen) return itemKey; // Check if any child route is active if ( @@ -40,12 +42,12 @@ export function NavMain({ items }: NavMainProps) { return false; }) ) { - return index; + return itemKey; } - return -1; + return null; }) - .filter((index) => index !== -1); + .filter((key): key is string => key !== null); }); // Helper function to check if a route is active @@ -70,25 +72,27 @@ export function NavMain({ items }: NavMainProps) { }, [openGroups]); // Only handle child clicks for highlighting - const handleChildClick = (parentIndex: number) => { - if (!openGroups.includes(parentIndex)) { - setOpenGroups([...openGroups, parentIndex]); + const handleChildClick = (itemKey: string) => { + if (!openGroups.includes(itemKey)) { + setOpenGroups([...openGroups, itemKey]); } }; return ( - {items.map((item, index) => - item.children ? ( + {items.map((item) => { + const itemKey = item.href || item.title; + + return item.children ? ( { setOpenGroups( open - ? [...openGroups, index] - : openGroups.filter((i) => i !== index) + ? [...openGroups, itemKey] + : openGroups.filter((key) => key !== itemKey) ); }} > @@ -106,36 +110,39 @@ export function NavMain({ items }: NavMainProps) { {state !== 'collapsed' && ( - {item.children.map((child, childIndex) => ( - - handleChildClick(index)} - > - { + const childKey = child.href || child.title; + return ( + + handleChildClick(itemKey)} > - {child.icon && ( - - )} - {child.title} - - - - ))} + + {child.icon && ( + + )} + {child.title} + + + + ); + })} )} ) : ( - + - ) - )} + ); + })} ); } diff --git a/resources/js/components/ui/breadcrumb.tsx b/resources/js/components/ui/breadcrumb.tsx index eb88f32..a4de799 100644 --- a/resources/js/components/ui/breadcrumb.tsx +++ b/resources/js/components/ui/breadcrumb.tsx @@ -53,8 +53,6 @@ function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) { return ( ) { const patternId = useId(); return ( diff --git a/resources/js/components/ui/toggle-group.tsx b/resources/js/components/ui/toggle-group.tsx index e8b41e8..191539d 100644 --- a/resources/js/components/ui/toggle-group.tsx +++ b/resources/js/components/ui/toggle-group.tsx @@ -20,6 +20,8 @@ function ToggleGroup({ ...props }: React.ComponentProps & VariantProps) { + const contextValue = React.useMemo(() => ({ variant, size }), [variant, size]) + return ( - + {children} @@ -51,12 +53,12 @@ function ToggleGroupItem({ return ( ) { const getInitials = useInitials(); return ( diff --git a/resources/js/components/user-menu-content.tsx b/resources/js/components/user-menu-content.tsx index a5ce059..1eea48d 100644 --- a/resources/js/components/user-menu-content.tsx +++ b/resources/js/components/user-menu-content.tsx @@ -15,7 +15,7 @@ interface UserMenuContentProps { user: User; } -export function UserMenuContent({ user }: UserMenuContentProps) { +export function UserMenuContent({ user }: Readonly) { const cleanup = useMobileNavigation(); const { t } = useTranslations(); diff --git a/resources/js/hooks/use-appearance.tsx b/resources/js/hooks/use-appearance.tsx index 39c4b57..5b2cdf6 100644 --- a/resources/js/hooks/use-appearance.tsx +++ b/resources/js/hooks/use-appearance.tsx @@ -64,7 +64,7 @@ export function useAppearance() { useEffect(() => { const savedAppearance = localStorage.getItem('appearance') as Appearance | null; - updateAppearance(savedAppearance || 'light'); + updateAppearance(savedAppearance ?? 'light'); return () => mediaQuery()?.removeEventListener('change', handleSystemThemeChange); }, [updateAppearance]); diff --git a/resources/js/layouts/auth-layout.tsx b/resources/js/layouts/auth-layout.tsx index 7852690..a135473 100644 --- a/resources/js/layouts/auth-layout.tsx +++ b/resources/js/layouts/auth-layout.tsx @@ -5,11 +5,11 @@ export default function AuthLayout({ title, description, ...props -}: { +}: Readonly<{ children: React.ReactNode; title: string; description: string; -}) { +}>) { return ( {children} diff --git a/resources/js/layouts/auth/auth-card-layout.tsx b/resources/js/layouts/auth/auth-card-layout.tsx index 3c280e5..46d45bf 100644 --- a/resources/js/layouts/auth/auth-card-layout.tsx +++ b/resources/js/layouts/auth/auth-card-layout.tsx @@ -8,7 +8,6 @@ export default function AuthCardLayout({ title, description, }: PropsWithChildren<{ - name?: string; title?: string; description?: string; }>) { diff --git a/resources/js/layouts/auth/auth-simple-layout.tsx b/resources/js/layouts/auth/auth-simple-layout.tsx index 94f4aa2..037a4e6 100644 --- a/resources/js/layouts/auth/auth-simple-layout.tsx +++ b/resources/js/layouts/auth/auth-simple-layout.tsx @@ -3,7 +3,6 @@ import { Link } from '@inertiajs/react'; import { type PropsWithChildren } from 'react'; interface AuthLayoutProps { - name?: string; title?: string; description?: string; } diff --git a/resources/js/pages/auth/forgot-password.tsx b/resources/js/pages/auth/forgot-password.tsx index 49759ed..0ae60bf 100644 --- a/resources/js/pages/auth/forgot-password.tsx +++ b/resources/js/pages/auth/forgot-password.tsx @@ -10,7 +10,7 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import AuthLayout from '@/layouts/auth-layout'; -export default function ForgotPassword({ status }: { status?: string }) { +export default function ForgotPassword({ status }: Readonly<{ status?: string }>) { const { data, setData, post, processing, errors } = useForm>({ email: '', }); diff --git a/resources/js/pages/auth/login.tsx b/resources/js/pages/auth/login.tsx index ddcfa3c..5cd0eea 100644 --- a/resources/js/pages/auth/login.tsx +++ b/resources/js/pages/auth/login.tsx @@ -27,7 +27,12 @@ interface LoginProps { redirectTo?: string; } -export default function Login({ status, canResetPassword, token, redirectTo }: LoginProps) { +export default function Login({ + status, + canResetPassword, + token, + redirectTo, +}: Readonly) { const form = useForm>({ email: '', password: '', @@ -150,7 +155,6 @@ export default function Login({ status, canResetPassword, token, redirectTo }: L type="email" required autoFocus - tabIndex={1} autoComplete="email" value={data.email} onChange={(e) => setData('email', e.target.value)} @@ -167,7 +171,6 @@ export default function Login({ status, canResetPassword, token, redirectTo }: L id="password" type="password" required - tabIndex={2} autoComplete="current-password" value={data.password} onChange={(e) => setData('password', e.target.value)} @@ -179,7 +182,6 @@ export default function Login({ status, canResetPassword, token, redirectTo }: L {t('auth.login.forgot_password')} @@ -193,7 +195,6 @@ export default function Login({ status, canResetPassword, token, redirectTo }: L name="remember" checked={data.remember} onClick={() => setData('remember', !data.remember)} - tabIndex={3} />
@@ -214,7 +215,6 @@ export default function Login({ status, canResetPassword, token, redirectTo }: L
Don't have an account?{' '} - - Sign up - + Sign up
diff --git a/resources/js/pages/auth/register.tsx b/resources/js/pages/auth/register.tsx index af18967..68dac6e 100644 --- a/resources/js/pages/auth/register.tsx +++ b/resources/js/pages/auth/register.tsx @@ -48,7 +48,6 @@ export default function Register() { type="text" required autoFocus - tabIndex={1} autoComplete="name" value={data.name} onChange={(e) => setData('name', e.target.value)} @@ -64,7 +63,6 @@ export default function Register() { id="email" type="email" required - tabIndex={2} autoComplete="email" value={data.email} onChange={(e) => setData('email', e.target.value)} @@ -80,7 +78,6 @@ export default function Register() { id="password" type="password" required - tabIndex={3} autoComplete="new-password" value={data.password} onChange={(e) => setData('password', e.target.value)} @@ -96,7 +93,6 @@ export default function Register() { id="password_confirmation" type="password" required - tabIndex={4} autoComplete="new-password" value={data.password_confirmation} onChange={(e) => setData('password_confirmation', e.target.value)} @@ -106,12 +102,7 @@ export default function Register() {
- diff --git a/resources/js/pages/auth/reset-password.tsx b/resources/js/pages/auth/reset-password.tsx index 723abea..97c009b 100644 --- a/resources/js/pages/auth/reset-password.tsx +++ b/resources/js/pages/auth/reset-password.tsx @@ -19,7 +19,7 @@ type ResetPasswordForm = { password_confirmation: string; }; -export default function ResetPassword({ token, email }: ResetPasswordProps) { +export default function ResetPassword({ token, email }: Readonly) { const form = useForm>({ token: token, email: email, diff --git a/resources/js/pages/auth/verify-email.tsx b/resources/js/pages/auth/verify-email.tsx index 89950e6..98786a6 100644 --- a/resources/js/pages/auth/verify-email.tsx +++ b/resources/js/pages/auth/verify-email.tsx @@ -7,7 +7,7 @@ import TextLink from '@/components/text-link'; import { Button } from '@/components/ui/button'; import AuthLayout from '@/layouts/auth-layout'; -export default function VerifyEmail({ status }: { status?: string }) { +export default function VerifyEmail({ status }: Readonly<{ status?: string }>) { const { post, processing } = useForm({}); const submit: FormEventHandler = (e) => { diff --git a/resources/js/pages/settings/profile.tsx b/resources/js/pages/settings/profile.tsx index 43c16de..f4bc697 100644 --- a/resources/js/pages/settings/profile.tsx +++ b/resources/js/pages/settings/profile.tsx @@ -21,10 +21,10 @@ interface ProfileForm { export default function Profile({ mustVerifyEmail, status, -}: { +}: Readonly<{ mustVerifyEmail: boolean; status?: string; -}) { +}>) { const { t } = useTranslations(); const { auth } = usePage().props; diff --git a/resources/js/pages/users/components/delete-user-modal.tsx b/resources/js/pages/users/components/delete-user-modal.tsx index 50c6932..098409f 100644 --- a/resources/js/pages/users/components/delete-user-modal.tsx +++ b/resources/js/pages/users/components/delete-user-modal.tsx @@ -18,7 +18,7 @@ interface DeleteUserModalProps { onClose: () => void; } -export function DeleteUserModal({ user, open, onClose }: DeleteUserModalProps) { +export function DeleteUserModal({ user, open, onClose }: Readonly) { const [isDeleting, setIsDeleting] = useState(false); const { t } = useTranslations(); diff --git a/resources/js/pages/users/components/restore-user-modal.tsx b/resources/js/pages/users/components/restore-user-modal.tsx index 75b7809..6aa14d1 100644 --- a/resources/js/pages/users/components/restore-user-modal.tsx +++ b/resources/js/pages/users/components/restore-user-modal.tsx @@ -18,7 +18,7 @@ interface RestoreUserModalProps { onClose: () => void; } -export function RestoreUserModal({ user, open, onClose }: RestoreUserModalProps) { +export function RestoreUserModal({ user, open, onClose }: Readonly) { const { t } = useTranslations(); const form = useForm({}); diff --git a/resources/js/pages/users/components/user-form.tsx b/resources/js/pages/users/components/user-form.tsx index 1b5e786..4ebbb79 100644 --- a/resources/js/pages/users/components/user-form.tsx +++ b/resources/js/pages/users/components/user-form.tsx @@ -26,7 +26,7 @@ interface UserFormProps { }[]; } -export function UserForm({ user, action, roles }: UserFormProps) { +export function UserForm({ user, action, roles }: Readonly) { const { t } = useTranslations(); const { data, setData, post, put, processing, errors } = useForm({ name: user?.name ?? '', diff --git a/resources/js/pages/users/create.tsx b/resources/js/pages/users/create.tsx index 9cd6925..d9b0e91 100644 --- a/resources/js/pages/users/create.tsx +++ b/resources/js/pages/users/create.tsx @@ -10,7 +10,7 @@ interface CreateUserProps { }[]; } -export default function Create({ roles }: CreateUserProps) { +export default function Create({ roles }: Readonly) { const { t } = useTranslations(); return ( diff --git a/resources/js/pages/users/edit.tsx b/resources/js/pages/users/edit.tsx index 8333bec..a617e58 100644 --- a/resources/js/pages/users/edit.tsx +++ b/resources/js/pages/users/edit.tsx @@ -12,7 +12,7 @@ interface EditUserProps { }[]; } -export default function Edit({ user, roles }: EditUserProps) { +export default function Edit({ user, roles }: Readonly) { const { t } = useTranslations(); return ( diff --git a/resources/js/pages/users/index.tsx b/resources/js/pages/users/index.tsx index 5e78c92..e0d91ac 100644 --- a/resources/js/pages/users/index.tsx +++ b/resources/js/pages/users/index.tsx @@ -1,6 +1,6 @@ import AppLayout from '@/layouts/app-layout'; import { PageProps, User, Auth } from '@/types/index.d'; -import { Link } from '@inertiajs/react'; +import { Link, Head, usePage, useForm } from '@inertiajs/react'; import { Button } from '@/components/ui/button'; import { Table, @@ -12,8 +12,6 @@ import { } from '@/components/ui/table'; import { Plus, Pencil, Trash, MoreHorizontal, Eye, ArrowUpCircle } from 'lucide-react'; import { useTranslations } from '@/hooks/use-translations'; -import { usePage } from '@inertiajs/react'; -import { Head } from '@inertiajs/react'; import { DropdownMenu, DropdownMenuContent, @@ -25,7 +23,6 @@ import { DeleteUserModal } from './components/delete-user-modal'; import { RestoreUserModal } from './components/restore-user-modal'; import { cn } from '@/lib/utils'; import { Input } from '@/components/ui/input'; -import { useForm } from '@inertiajs/react'; import { useDebouncedCallback } from 'use-debounce'; import { formatDate } from '@/utils/format'; interface UsersIndexProps extends PageProps { diff --git a/resources/js/pages/users/show.tsx b/resources/js/pages/users/show.tsx index 120833f..625e3ec 100644 --- a/resources/js/pages/users/show.tsx +++ b/resources/js/pages/users/show.tsx @@ -7,7 +7,7 @@ interface ShowUserProps extends PageProps { user: User; } -export default function Show({ user }: ShowUserProps) { +export default function Show({ user }: Readonly) { const { t } = useTranslations(); return ( diff --git a/resources/js/ssr.tsx b/resources/js/ssr.tsx index c4f5cec..44e2486 100644 --- a/resources/js/ssr.tsx +++ b/resources/js/ssr.tsx @@ -5,7 +5,7 @@ import ReactDOMServer from 'react-dom/server'; import { type RouteName, route } from 'ziggy-js'; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; +const appName = import.meta.env.VITE_APP_NAME ?? 'Laravel'; createServer((page) => createInertiaApp({ diff --git a/resources/scss/_fonts.scss b/resources/scss/_fonts.scss index 8d41075..6d70840 100644 --- a/resources/scss/_fonts.scss +++ b/resources/scss/_fonts.scss @@ -10,6 +10,7 @@ // This will significantly reduce the size of the exported font-families and as // a result will speed-up the loading of your website. In essence this SCSS file // which should be used as a guide, replaces the following single line of code: +// NOSONAR - this is for showing purpose only // @import '@fontsource-variable/open-sans'; // Use the SCSS of each font which is required by the theme: diff --git a/resources/scss/_reset.scss b/resources/scss/_reset.scss index f50ff2e..d484056 100644 --- a/resources/scss/_reset.scss +++ b/resources/scss/_reset.scss @@ -11,10 +11,6 @@ box-sizing: border-box; } -// * { -// margin: 0; -// } - body { line-height: 1.5; -webkit-font-smoothing: antialiased; diff --git a/routes/settings.php b/routes/settings.php index 802a89d..7d6187e 100644 --- a/routes/settings.php +++ b/routes/settings.php @@ -7,15 +7,18 @@ use Illuminate\Support\Facades\Route; use Inertia\Inertia; -Route::middleware('auth')->group(function (): void { - Route::redirect('settings', 'settings/profile'); +$SETTINGS_PROFILE_ROUTE = 'settings/profile'; +$SETTINGS_PASSWORD_ROUTE = 'settings/password'; - Route::get('settings/profile', [ProfileController::class, 'edit'])->name('profile.edit'); - Route::patch('settings/profile', [ProfileController::class, 'update'])->name('profile.update'); - Route::delete('settings/profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); +Route::middleware('auth')->group(function () use ($SETTINGS_PROFILE_ROUTE, $SETTINGS_PASSWORD_ROUTE): void { + Route::redirect('settings', $SETTINGS_PROFILE_ROUTE); - Route::get('settings/password', [PasswordController::class, 'edit'])->name('password.edit'); - Route::put('settings/password', [PasswordController::class, 'update'])->name('password.update'); + Route::get($SETTINGS_PROFILE_ROUTE, [ProfileController::class, 'edit'])->name('profile.edit'); + Route::patch($SETTINGS_PROFILE_ROUTE, [ProfileController::class, 'update'])->name('profile.update'); + Route::delete($SETTINGS_PROFILE_ROUTE, [ProfileController::class, 'destroy'])->name('profile.destroy'); + + Route::get($SETTINGS_PASSWORD_ROUTE, [PasswordController::class, 'edit'])->name('password.edit'); + Route::put($SETTINGS_PASSWORD_ROUTE, [PasswordController::class, 'update'])->name('password.update'); Route::get('settings/appearance', fn () => Inertia::render('settings/appearance'))->name('appearance'); }); diff --git a/routes/web.php b/routes/web.php index 44da501..89f7bfd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -21,5 +21,7 @@ Route::put('/users/{user}/restore', UserRestoreController::class)->name('users.restore')->withTrashed(); }); +// NOSONAR - this comes from Laravel require __DIR__ . '/settings.php'; +// NOSONAR - this comes from Laravel require __DIR__ . '/auth.php'; diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php index 87ffc8a..0acd0c3 100644 --- a/tests/Feature/Auth/AuthenticationTest.php +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -6,6 +6,8 @@ use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; +$LOGIN_ROUTE = '/login'; + uses(RefreshDatabase::class); beforeEach(function (): void { @@ -14,7 +16,7 @@ }); test('login screen can be rendered', function (): void { - $response = $this->get('/login'); + $response = $this->get($LOGIN_ROUTE); $response->assertStatus(200); }); @@ -23,11 +25,11 @@ $user = User::factory()->create()->assignRole(RolesEnum::REGISTERED_USER->value); // First get the login page to obtain CSRF token - $response = $this->get('/login'); + $this->get($LOGIN_ROUTE); // Then attempt login with the token $response = $this->withSession(['_token' => csrf_token()]) - ->post('/login', [ + ->post($LOGIN_ROUTE, [ 'email' => $user->email, 'password' => 'password', '_token' => csrf_token(), @@ -44,7 +46,7 @@ test('users can not authenticate with invalid password', function (): void { $user = User::factory()->create(); - $response = $this->post('/login', [ + $response = $this->post($LOGIN_ROUTE, [ 'email' => $user->email, 'password' => 'wrong-password', '_token' => csrf_token(),