diff --git a/.github/workflows/browser-tests.yml b/.github/workflows/browser-tests.yml deleted file mode 100644 index d69408ecd..000000000 --- a/.github/workflows/browser-tests.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: browser-tests - -on: - push: - branches: - - pest-ci - - develop - - main - pull_request: - branches: - - pest-ci - - develop - - main - -jobs: - ci: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.5 - tools: composer:v2 - coverage: xdebug - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: '22' - - - name: Install Node Dependencies - run: npm i - - - name: Install Playwright Dependencies - run: npm install playwright@latest - - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - - name: Add `laravel-labs/starter-kit-browser-tests` Repository - run: | - composer config repositories.browser-tests '{"type": "vcs", "url": "https://github.com/laravel-labs/starter-kit-browser-tests"}' --file composer.json - composer remove "phpunit/phpunit" --dev --no-update - composer require "laravel-labs/starter-kit-browser-tests:dev-main@dev" --dev --no-update - - - name: Install Dependencies - run: composer install --no-interaction --prefer-dist --optimize-autoloader - - - name: Copy Environment File - run: cp .env.example .env - - - name: Generate Application Key - run: php artisan key:generate - - - name: Setup Test Environment - run: | - cp vendor/laravel-labs/starter-kit-browser-tests/phpunit.xml.dist . - rm phpunit.xml - rm -Rf tests/ - cp -rf vendor/laravel-labs/starter-kit-browser-tests/tests/ tests/ - - - name: Build Assets - run: npm run build - - - name: Tests - run: php vendor/bin/pest diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a03395865..619061fc7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,10 +5,14 @@ on: branches: - develop - main + - master + - workos pull_request: branches: - develop - main + - master + - workos permissions: contents: write @@ -17,7 +21,7 @@ jobs: quality: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index eac012da5..696e4d761 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,10 +5,14 @@ on: branches: - develop - main + - master + - workos pull_request: branches: - develop - main + - master + - workos jobs: ci: @@ -39,14 +43,14 @@ jobs: - name: Install Dependencies run: composer install --no-interaction --prefer-dist --optimize-autoloader - - name: Build Assets - run: npm run build - - name: Copy Environment File run: cp .env.example .env - name: Generate Application Key run: php artisan key:generate + - name: Build Assets + run: npm run build + - name: Tests run: ./vendor/bin/phpunit diff --git a/.gitignore b/.gitignore index afac65771..550440344 100644 --- a/.gitignore +++ b/.gitignore @@ -4,11 +4,11 @@ /public/build /public/hot /public/storage +/storage/*.key +/storage/pail /resources/js/actions /resources/js/routes /resources/js/wayfinder -/storage/*.key -/storage/pail /vendor .DS_Store .env diff --git a/.prettierrc b/.prettierrc index 1d21a94dc..92805fad1 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,12 +5,12 @@ "htmlWhitespaceSensitivity": "css", "printWidth": 80, "plugins": [ - "prettier-plugin-organize-imports", "prettier-plugin-tailwindcss" ], "tailwindFunctions": [ "clsx", - "cn" + "cn", + "cva" ], "tailwindStylesheet": "resources/css/app.css", "tabWidth": 4, diff --git a/app/Http/Controllers/Settings/ProfileController.php b/app/Http/Controllers/Settings/ProfileController.php index 5b8e48fd7..c827bfed8 100644 --- a/app/Http/Controllers/Settings/ProfileController.php +++ b/app/Http/Controllers/Settings/ProfileController.php @@ -26,7 +26,7 @@ public function edit(Request $request): Response } /** - * Update the user's profile settings. + * Update the user's profile information. */ public function update(ProfileUpdateRequest $request): RedirectResponse { @@ -42,7 +42,7 @@ public function update(ProfileUpdateRequest $request): RedirectResponse } /** - * Delete the user's account. + * Delete the user's profile. */ public function destroy(ProfileDeleteRequest $request): RedirectResponse { diff --git a/database/migrations/2025_08_26_100418_add_two_factor_columns_to_users_table.php b/database/migrations/2025_08_14_170933_add_two_factor_columns_to_users_table.php similarity index 100% rename from database/migrations/2025_08_26_100418_add_two_factor_columns_to_users_table.php rename to database/migrations/2025_08_14_170933_add_two_factor_columns_to_users_table.php diff --git a/package.json b/package.json index 997f00791..22b0422b7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "eslint-plugin-react": "^7.37.3", "eslint-plugin-react-hooks": "^7.0.0", "prettier": "^3.4.2", - "prettier-plugin-organize-imports": "^4.1.0", "prettier-plugin-tailwindcss": "^0.6.11", "typescript-eslint": "^8.23.0" }, diff --git a/public/logo.svg b/public/logo.svg deleted file mode 100644 index 3b7c63d99..000000000 --- a/public/logo.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/resources/js/components/alert-error.tsx b/resources/js/components/alert-error.tsx index 8cc228bd4..a9eacd313 100644 --- a/resources/js/components/alert-error.tsx +++ b/resources/js/components/alert-error.tsx @@ -1,5 +1,5 @@ -import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { AlertCircleIcon } from 'lucide-react'; +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; export default function AlertError({ errors, diff --git a/resources/js/components/app-content.tsx b/resources/js/components/app-content.tsx index e5c9989b3..4e6dd4643 100644 --- a/resources/js/components/app-content.tsx +++ b/resources/js/components/app-content.tsx @@ -1,5 +1,5 @@ -import { SidebarInset } from '@/components/ui/sidebar'; import * as React from 'react'; +import { SidebarInset } from '@/components/ui/sidebar'; type Props = React.ComponentProps<'main'> & { variant?: 'header' | 'sidebar'; diff --git a/resources/js/components/app-header.tsx b/resources/js/components/app-header.tsx index e9b34295f..fc6e4c7d8 100644 --- a/resources/js/components/app-header.tsx +++ b/resources/js/components/app-header.tsx @@ -1,3 +1,5 @@ +import { Link, usePage } from '@inertiajs/react'; +import { BookOpen, Folder, LayoutGrid, Menu, Search } from 'lucide-react'; import { Breadcrumbs } from '@/components/breadcrumbs'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Button } from '@/components/ui/button'; @@ -31,8 +33,6 @@ import { useInitials } from '@/hooks/use-initials'; import { cn, toUrl } from '@/lib/utils'; import { dashboard } from '@/routes'; import type { BreadcrumbItem, NavItem, SharedData } from '@/types'; -import { Link, usePage } from '@inertiajs/react'; -import { BookOpen, Folder, LayoutGrid, Menu, Search } from 'lucide-react'; import AppLogo from './app-logo'; import AppLogoIcon from './app-logo-icon'; diff --git a/resources/js/components/app-shell.tsx b/resources/js/components/app-shell.tsx index c32a6046c..37bf9dd98 100644 --- a/resources/js/components/app-shell.tsx +++ b/resources/js/components/app-shell.tsx @@ -1,7 +1,7 @@ -import { SidebarProvider } from '@/components/ui/sidebar'; -import type { SharedData } from '@/types'; import { usePage } from '@inertiajs/react'; import type { ReactNode } from 'react'; +import { SidebarProvider } from '@/components/ui/sidebar'; +import type { SharedData } from '@/types'; type Props = { children: ReactNode; diff --git a/resources/js/components/app-sidebar.tsx b/resources/js/components/app-sidebar.tsx index a973ba22e..a2d04acd5 100644 --- a/resources/js/components/app-sidebar.tsx +++ b/resources/js/components/app-sidebar.tsx @@ -1,3 +1,5 @@ +import { Link } from '@inertiajs/react'; +import { BookOpen, Folder, LayoutGrid } from 'lucide-react'; import { NavFooter } from '@/components/nav-footer'; import { NavMain } from '@/components/nav-main'; import { NavUser } from '@/components/nav-user'; @@ -12,8 +14,6 @@ import { } from '@/components/ui/sidebar'; import { dashboard } from '@/routes'; import type { NavItem } from '@/types'; -import { Link } from '@inertiajs/react'; -import { BookOpen, Folder, LayoutGrid } from 'lucide-react'; import AppLogo from './app-logo'; const mainNavItems: NavItem[] = [ diff --git a/resources/js/components/appearance-tabs.tsx b/resources/js/components/appearance-tabs.tsx index 0a30d23d8..b01386248 100644 --- a/resources/js/components/appearance-tabs.tsx +++ b/resources/js/components/appearance-tabs.tsx @@ -1,9 +1,9 @@ -import type { Appearance } from '@/hooks/use-appearance'; -import { useAppearance } from '@/hooks/use-appearance'; -import { cn } from '@/lib/utils'; import type { LucideIcon } from 'lucide-react'; import { Monitor, Moon, Sun } from 'lucide-react'; import type { HTMLAttributes } from 'react'; +import type { Appearance } from '@/hooks/use-appearance'; +import { useAppearance } from '@/hooks/use-appearance'; +import { cn } from '@/lib/utils'; export default function AppearanceToggleTab({ className = '', diff --git a/resources/js/components/breadcrumbs.tsx b/resources/js/components/breadcrumbs.tsx index 43ea0a9e8..95414f215 100644 --- a/resources/js/components/breadcrumbs.tsx +++ b/resources/js/components/breadcrumbs.tsx @@ -1,3 +1,5 @@ +import { Link } from '@inertiajs/react'; +import { Fragment } from 'react'; import { Breadcrumb, BreadcrumbItem, @@ -7,8 +9,6 @@ import { BreadcrumbSeparator, } from '@/components/ui/breadcrumb'; import type { BreadcrumbItem as BreadcrumbItemType } from '@/types'; -import { Link } from '@inertiajs/react'; -import { Fragment } from 'react'; export function Breadcrumbs({ breadcrumbs, diff --git a/resources/js/components/delete-user.tsx b/resources/js/components/delete-user.tsx index 7fbd03d5c..a196d54e1 100644 --- a/resources/js/components/delete-user.tsx +++ b/resources/js/components/delete-user.tsx @@ -1,3 +1,5 @@ +import { Form } from '@inertiajs/react'; +import { useRef } from 'react'; import ProfileController from '@/actions/App/Http/Controllers/Settings/ProfileController'; import Heading from '@/components/heading'; import InputError from '@/components/input-error'; @@ -13,8 +15,6 @@ import { } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; -import { Form } from '@inertiajs/react'; -import { useRef } from 'react'; export default function DeleteUser() { const passwordInput = useRef(null); diff --git a/resources/js/components/input-error.tsx b/resources/js/components/input-error.tsx index 9b36b312f..f323cee94 100644 --- a/resources/js/components/input-error.tsx +++ b/resources/js/components/input-error.tsx @@ -1,5 +1,5 @@ -import { cn } from '@/lib/utils'; import type { HTMLAttributes } from 'react'; +import { cn } from '@/lib/utils'; export default function InputError({ message, diff --git a/resources/js/components/nav-footer.tsx b/resources/js/components/nav-footer.tsx index b19d1596e..8430e495b 100644 --- a/resources/js/components/nav-footer.tsx +++ b/resources/js/components/nav-footer.tsx @@ -1,3 +1,4 @@ +import type { ComponentPropsWithoutRef } from 'react'; import { SidebarGroup, SidebarGroupContent, @@ -7,7 +8,6 @@ import { } from '@/components/ui/sidebar'; import { toUrl } from '@/lib/utils'; import type { NavItem } from '@/types'; -import type { ComponentPropsWithoutRef } from 'react'; export function NavFooter({ items, diff --git a/resources/js/components/nav-main.tsx b/resources/js/components/nav-main.tsx index 018410d97..54095ac73 100644 --- a/resources/js/components/nav-main.tsx +++ b/resources/js/components/nav-main.tsx @@ -1,3 +1,4 @@ +import { Link } from '@inertiajs/react'; import { SidebarGroup, SidebarGroupLabel, @@ -7,7 +8,6 @@ import { } from '@/components/ui/sidebar'; import { useCurrentUrl } from '@/hooks/use-current-url'; import type { NavItem } from '@/types'; -import { Link } from '@inertiajs/react'; export function NavMain({ items = [] }: { items: NavItem[] }) { const { isCurrentUrl } = useCurrentUrl(); diff --git a/resources/js/components/nav-user.tsx b/resources/js/components/nav-user.tsx index f8c5f0b78..f3470e3d3 100644 --- a/resources/js/components/nav-user.tsx +++ b/resources/js/components/nav-user.tsx @@ -1,3 +1,5 @@ +import { usePage } from '@inertiajs/react'; +import { ChevronsUpDown } from 'lucide-react'; import { DropdownMenu, DropdownMenuContent, @@ -13,8 +15,6 @@ import { UserInfo } from '@/components/user-info'; import { UserMenuContent } from '@/components/user-menu-content'; import { useIsMobile } from '@/hooks/use-mobile'; import type { SharedData } from '@/types'; -import { usePage } from '@inertiajs/react'; -import { ChevronsUpDown } from 'lucide-react'; export function NavUser() { const { auth } = usePage().props; diff --git a/resources/js/components/text-link.tsx b/resources/js/components/text-link.tsx index 78d06b397..08ad2694e 100644 --- a/resources/js/components/text-link.tsx +++ b/resources/js/components/text-link.tsx @@ -1,6 +1,6 @@ -import { cn } from '@/lib/utils'; import { Link } from '@inertiajs/react'; import type { ComponentProps } from 'react'; +import { cn } from '@/lib/utils'; type Props = ComponentProps; diff --git a/resources/js/components/two-factor-recovery-codes.tsx b/resources/js/components/two-factor-recovery-codes.tsx index 6468df878..637a4b268 100644 --- a/resources/js/components/two-factor-recovery-codes.tsx +++ b/resources/js/components/two-factor-recovery-codes.tsx @@ -1,3 +1,6 @@ +import { Form } from '@inertiajs/react'; +import { Eye, EyeOff, LockKeyhole, RefreshCw } from 'lucide-react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { Button } from '@/components/ui/button'; import { Card, @@ -7,9 +10,6 @@ import { CardTitle, } from '@/components/ui/card'; import { regenerateRecoveryCodes } from '@/routes/two-factor'; -import { Form } from '@inertiajs/react'; -import { Eye, EyeOff, LockKeyhole, RefreshCw } from 'lucide-react'; -import { useCallback, useEffect, useRef, useState } from 'react'; import AlertError from './alert-error'; type Props = { diff --git a/resources/js/components/two-factor-setup-modal.tsx b/resources/js/components/two-factor-setup-modal.tsx index 84ea60deb..6f1cfbc99 100644 --- a/resources/js/components/two-factor-setup-modal.tsx +++ b/resources/js/components/two-factor-setup-modal.tsx @@ -1,3 +1,7 @@ +import { Form } from '@inertiajs/react'; +import { REGEXP_ONLY_DIGITS } from 'input-otp'; +import { Check, Copy, ScanLine } from 'lucide-react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import InputError from '@/components/input-error'; import { Button } from '@/components/ui/button'; import { @@ -16,10 +20,6 @@ import { useAppearance } from '@/hooks/use-appearance'; import { useClipboard } from '@/hooks/use-clipboard'; import { OTP_MAX_LENGTH } from '@/hooks/use-two-factor-auth'; import { confirm } from '@/routes/two-factor'; -import { Form } from '@inertiajs/react'; -import { REGEXP_ONLY_DIGITS } from 'input-otp'; -import { Check, Copy, ScanLine } from 'lucide-react'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import AlertError from './alert-error'; import { Spinner } from './ui/spinner'; diff --git a/resources/js/components/user-menu-content.tsx b/resources/js/components/user-menu-content.tsx index b1578c6c2..adbda7150 100644 --- a/resources/js/components/user-menu-content.tsx +++ b/resources/js/components/user-menu-content.tsx @@ -1,3 +1,5 @@ +import { Link, router } from '@inertiajs/react'; +import { LogOut, Settings } from 'lucide-react'; import { DropdownMenuGroup, DropdownMenuItem, @@ -9,8 +11,6 @@ import { useMobileNavigation } from '@/hooks/use-mobile-navigation'; import { logout } from '@/routes'; import { edit } from '@/routes/profile'; import type { User } from '@/types'; -import { Link, router } from '@inertiajs/react'; -import { LogOut, Settings } from 'lucide-react'; type Props = { user: User; diff --git a/resources/js/hooks/use-current-url.ts b/resources/js/hooks/use-current-url.ts index 8aaa0a3a1..495f249a5 100644 --- a/resources/js/hooks/use-current-url.ts +++ b/resources/js/hooks/use-current-url.ts @@ -1,6 +1,6 @@ -import { toUrl } from '@/lib/utils'; import type { InertiaLinkProps } from '@inertiajs/react'; import { usePage } from '@inertiajs/react'; +import { toUrl } from '@/lib/utils'; export type IsCurrentUrlFn = ( urlToCheck: NonNullable, diff --git a/resources/js/hooks/use-two-factor-auth.ts b/resources/js/hooks/use-two-factor-auth.ts index 8f09080a9..1903d88b2 100644 --- a/resources/js/hooks/use-two-factor-auth.ts +++ b/resources/js/hooks/use-two-factor-auth.ts @@ -1,6 +1,6 @@ +import { useCallback, useMemo, useState } from 'react'; import { qrCode, recoveryCodes, secretKey } from '@/routes/two-factor'; import type { TwoFactorSecretKey, TwoFactorSetupData } from '@/types'; -import { useCallback, useMemo, useState } from 'react'; export type UseTwoFactorAuthReturn = { qrCodeSvg: string | null; diff --git a/resources/js/layouts/auth/auth-card-layout.tsx b/resources/js/layouts/auth/auth-card-layout.tsx index f8aad9523..851ac5f63 100644 --- a/resources/js/layouts/auth/auth-card-layout.tsx +++ b/resources/js/layouts/auth/auth-card-layout.tsx @@ -1,3 +1,5 @@ +import { Link } from '@inertiajs/react'; +import type { PropsWithChildren } from 'react'; import AppLogoIcon from '@/components/app-logo-icon'; import { Card, @@ -7,8 +9,6 @@ import { CardTitle, } from '@/components/ui/card'; import { home } from '@/routes'; -import { Link } from '@inertiajs/react'; -import type { PropsWithChildren } from 'react'; export default function AuthCardLayout({ children, diff --git a/resources/js/layouts/auth/auth-simple-layout.tsx b/resources/js/layouts/auth/auth-simple-layout.tsx index a81715bde..b1396ae8b 100644 --- a/resources/js/layouts/auth/auth-simple-layout.tsx +++ b/resources/js/layouts/auth/auth-simple-layout.tsx @@ -1,7 +1,7 @@ +import { Link } from '@inertiajs/react'; import AppLogoIcon from '@/components/app-logo-icon'; import { home } from '@/routes'; import type { AuthLayoutProps } from '@/types'; -import { Link } from '@inertiajs/react'; export default function AuthSimpleLayout({ children, diff --git a/resources/js/layouts/auth/auth-split-layout.tsx b/resources/js/layouts/auth/auth-split-layout.tsx index 4bac15d80..8cf412460 100644 --- a/resources/js/layouts/auth/auth-split-layout.tsx +++ b/resources/js/layouts/auth/auth-split-layout.tsx @@ -1,7 +1,7 @@ +import { Link, usePage } from '@inertiajs/react'; import AppLogoIcon from '@/components/app-logo-icon'; import { home } from '@/routes'; import type { AuthLayoutProps, SharedData } from '@/types'; -import { Link, usePage } from '@inertiajs/react'; export default function AuthSplitLayout({ children, diff --git a/resources/js/layouts/settings/layout.tsx b/resources/js/layouts/settings/layout.tsx index 715831f8b..4ef9c9a74 100644 --- a/resources/js/layouts/settings/layout.tsx +++ b/resources/js/layouts/settings/layout.tsx @@ -1,3 +1,5 @@ +import { Link } from '@inertiajs/react'; +import type { PropsWithChildren } from 'react'; import Heading from '@/components/heading'; import { Button } from '@/components/ui/button'; import { Separator } from '@/components/ui/separator'; @@ -8,8 +10,6 @@ import { edit } from '@/routes/profile'; import { show } from '@/routes/two-factor'; import { edit as editPassword } from '@/routes/user-password'; import type { NavItem } from '@/types'; -import { Link } from '@inertiajs/react'; -import type { PropsWithChildren } from 'react'; const sidebarNavItems: NavItem[] = [ { diff --git a/resources/js/pages/auth/confirm-password.tsx b/resources/js/pages/auth/confirm-password.tsx index b9ae2690d..13ec2b98b 100644 --- a/resources/js/pages/auth/confirm-password.tsx +++ b/resources/js/pages/auth/confirm-password.tsx @@ -1,3 +1,4 @@ +import { Form, Head } from '@inertiajs/react'; import InputError from '@/components/input-error'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -5,7 +6,6 @@ import { Label } from '@/components/ui/label'; import { Spinner } from '@/components/ui/spinner'; import AuthLayout from '@/layouts/auth-layout'; import { store } from '@/routes/password/confirm'; -import { Form, Head } from '@inertiajs/react'; export default function ConfirmPassword() { return ( diff --git a/resources/js/pages/auth/forgot-password.tsx b/resources/js/pages/auth/forgot-password.tsx index 811624058..e3803c40f 100644 --- a/resources/js/pages/auth/forgot-password.tsx +++ b/resources/js/pages/auth/forgot-password.tsx @@ -1,4 +1,6 @@ // Components +import { Form, Head } from '@inertiajs/react'; +import { LoaderCircle } from 'lucide-react'; import InputError from '@/components/input-error'; import TextLink from '@/components/text-link'; import { Button } from '@/components/ui/button'; @@ -7,8 +9,6 @@ import { Label } from '@/components/ui/label'; import AuthLayout from '@/layouts/auth-layout'; import { login } from '@/routes'; import { email } from '@/routes/password'; -import { Form, Head } from '@inertiajs/react'; -import { LoaderCircle } from 'lucide-react'; export default function ForgotPassword({ status }: { status?: string }) { return ( diff --git a/resources/js/pages/auth/login.tsx b/resources/js/pages/auth/login.tsx index 186ee770e..685118fce 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 TextLink from '@/components/text-link'; import { Button } from '@/components/ui/button'; @@ -9,7 +10,6 @@ import AuthLayout from '@/layouts/auth-layout'; import { register } from '@/routes'; import { store } from '@/routes/login'; import { request } from '@/routes/password'; -import { Form, Head } from '@inertiajs/react'; type Props = { status?: string; diff --git a/resources/js/pages/auth/register.tsx b/resources/js/pages/auth/register.tsx index 9fac70bc6..22f862738 100644 --- a/resources/js/pages/auth/register.tsx +++ b/resources/js/pages/auth/register.tsx @@ -1,3 +1,4 @@ +import { Form, Head } from '@inertiajs/react'; import InputError from '@/components/input-error'; import TextLink from '@/components/text-link'; import { Button } from '@/components/ui/button'; @@ -7,7 +8,6 @@ import { Spinner } from '@/components/ui/spinner'; import AuthLayout from '@/layouts/auth-layout'; import { login } from '@/routes'; import { store } from '@/routes/register'; -import { Form, Head } from '@inertiajs/react'; export default function Register() { return ( diff --git a/resources/js/pages/auth/reset-password.tsx b/resources/js/pages/auth/reset-password.tsx index bb84e3f74..605a6e24e 100644 --- a/resources/js/pages/auth/reset-password.tsx +++ b/resources/js/pages/auth/reset-password.tsx @@ -1,3 +1,4 @@ +import { Form, Head } from '@inertiajs/react'; import InputError from '@/components/input-error'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -5,7 +6,6 @@ import { Label } from '@/components/ui/label'; import { Spinner } from '@/components/ui/spinner'; import AuthLayout from '@/layouts/auth-layout'; import { update } from '@/routes/password'; -import { Form, Head } from '@inertiajs/react'; type Props = { token: string; diff --git a/resources/js/pages/auth/two-factor-challenge.tsx b/resources/js/pages/auth/two-factor-challenge.tsx index c08724b0c..011d0c746 100644 --- a/resources/js/pages/auth/two-factor-challenge.tsx +++ b/resources/js/pages/auth/two-factor-challenge.tsx @@ -1,3 +1,6 @@ +import { Form, Head } from '@inertiajs/react'; +import { REGEXP_ONLY_DIGITS } from 'input-otp'; +import { useMemo, useState } from 'react'; import InputError from '@/components/input-error'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -9,9 +12,6 @@ import { import { OTP_MAX_LENGTH } from '@/hooks/use-two-factor-auth'; import AuthLayout from '@/layouts/auth-layout'; import { store } from '@/routes/two-factor/login'; -import { Form, Head } from '@inertiajs/react'; -import { REGEXP_ONLY_DIGITS } from 'input-otp'; -import { useMemo, useState } from 'react'; export default function TwoFactorChallenge() { const [showRecoveryInput, setShowRecoveryInput] = useState(false); diff --git a/resources/js/pages/auth/verify-email.tsx b/resources/js/pages/auth/verify-email.tsx index 03ac55793..6117a1e82 100644 --- a/resources/js/pages/auth/verify-email.tsx +++ b/resources/js/pages/auth/verify-email.tsx @@ -1,11 +1,11 @@ // Components +import { Form, Head } from '@inertiajs/react'; import TextLink from '@/components/text-link'; import { Button } from '@/components/ui/button'; import { Spinner } from '@/components/ui/spinner'; import AuthLayout from '@/layouts/auth-layout'; import { logout } from '@/routes'; import { send } from '@/routes/verification'; -import { Form, Head } from '@inertiajs/react'; export default function VerifyEmail({ status }: { status?: string }) { return ( diff --git a/resources/js/pages/dashboard.tsx b/resources/js/pages/dashboard.tsx index 95e8a9636..74c0780d2 100644 --- a/resources/js/pages/dashboard.tsx +++ b/resources/js/pages/dashboard.tsx @@ -1,8 +1,8 @@ +import { Head } from '@inertiajs/react'; import { PlaceholderPattern } from '@/components/ui/placeholder-pattern'; import AppLayout from '@/layouts/app-layout'; import { dashboard } from '@/routes'; import type { BreadcrumbItem } from '@/types'; -import { Head } from '@inertiajs/react'; const breadcrumbs: BreadcrumbItem[] = [ { diff --git a/resources/js/pages/settings/appearance.tsx b/resources/js/pages/settings/appearance.tsx index acc852524..d826f30c5 100644 --- a/resources/js/pages/settings/appearance.tsx +++ b/resources/js/pages/settings/appearance.tsx @@ -1,10 +1,10 @@ +import { Head } from '@inertiajs/react'; import AppearanceTabs from '@/components/appearance-tabs'; import Heading from '@/components/heading'; import AppLayout from '@/layouts/app-layout'; import SettingsLayout from '@/layouts/settings/layout'; import { edit as editAppearance } from '@/routes/appearance'; import type { BreadcrumbItem } from '@/types'; -import { Head } from '@inertiajs/react'; const breadcrumbs: BreadcrumbItem[] = [ { diff --git a/resources/js/pages/settings/password.tsx b/resources/js/pages/settings/password.tsx index 18f46b082..3d6bc2879 100644 --- a/resources/js/pages/settings/password.tsx +++ b/resources/js/pages/settings/password.tsx @@ -1,3 +1,6 @@ +import { Transition } from '@headlessui/react'; +import { Form, Head } from '@inertiajs/react'; +import { useRef } from 'react'; import PasswordController from '@/actions/App/Http/Controllers/Settings/PasswordController'; import Heading from '@/components/heading'; import InputError from '@/components/input-error'; @@ -8,9 +11,6 @@ import AppLayout from '@/layouts/app-layout'; import SettingsLayout from '@/layouts/settings/layout'; import { edit } from '@/routes/user-password'; import type { BreadcrumbItem } from '@/types'; -import { Transition } from '@headlessui/react'; -import { Form, Head } from '@inertiajs/react'; -import { useRef } from 'react'; const breadcrumbs: BreadcrumbItem[] = [ { diff --git a/resources/js/pages/settings/profile.tsx b/resources/js/pages/settings/profile.tsx index 834c613c4..ebab68171 100644 --- a/resources/js/pages/settings/profile.tsx +++ b/resources/js/pages/settings/profile.tsx @@ -1,3 +1,5 @@ +import { Transition } from '@headlessui/react'; +import { Form, Head, Link, usePage } from '@inertiajs/react'; import ProfileController from '@/actions/App/Http/Controllers/Settings/ProfileController'; import DeleteUser from '@/components/delete-user'; import Heading from '@/components/heading'; @@ -10,8 +12,6 @@ import SettingsLayout from '@/layouts/settings/layout'; import { edit } from '@/routes/profile'; import { send } from '@/routes/verification'; import type { BreadcrumbItem, SharedData } from '@/types'; -import { Transition } from '@headlessui/react'; -import { Form, Head, Link, usePage } from '@inertiajs/react'; const breadcrumbs: BreadcrumbItem[] = [ { diff --git a/resources/js/pages/settings/two-factor.tsx b/resources/js/pages/settings/two-factor.tsx index 31922e791..d6cece497 100644 --- a/resources/js/pages/settings/two-factor.tsx +++ b/resources/js/pages/settings/two-factor.tsx @@ -1,3 +1,6 @@ +import { Form, Head } from '@inertiajs/react'; +import { ShieldBan, ShieldCheck } from 'lucide-react'; +import { useState } from 'react'; import Heading from '@/components/heading'; import TwoFactorRecoveryCodes from '@/components/two-factor-recovery-codes'; import TwoFactorSetupModal from '@/components/two-factor-setup-modal'; @@ -8,9 +11,6 @@ import AppLayout from '@/layouts/app-layout'; import SettingsLayout from '@/layouts/settings/layout'; import { disable, enable, show } from '@/routes/two-factor'; import type { BreadcrumbItem } from '@/types'; -import { Form, Head } from '@inertiajs/react'; -import { ShieldBan, ShieldCheck } from 'lucide-react'; -import { useState } from 'react'; type Props = { requiresConfirmation?: boolean; diff --git a/resources/js/pages/welcome.tsx b/resources/js/pages/welcome.tsx index dd58e2c26..b6425dbf3 100644 --- a/resources/js/pages/welcome.tsx +++ b/resources/js/pages/welcome.tsx @@ -1,6 +1,6 @@ +import { Head, Link, usePage } from '@inertiajs/react'; import { dashboard, login, register } from '@/routes'; import type { SharedData } from '@/types'; -import { Head, Link, usePage } from '@inertiajs/react'; export default function Welcome({ canRegister = true, diff --git a/routes/web.php b/routes/web.php index 11e61c220..0a20f55ea 100644 --- a/routes/web.php +++ b/routes/web.php @@ -10,10 +10,8 @@ ]); })->name('home'); -Route::middleware(['auth', 'verified'])->group(function () { - Route::get('dashboard', function () { - return Inertia::render('dashboard'); - })->name('dashboard'); -}); +Route::get('dashboard', function () { + return Inertia::render('dashboard'); +})->middleware(['auth', 'verified'])->name('dashboard'); require __DIR__.'/settings.php'; diff --git a/tests/Feature/Auth/EmailVerificationTest.php b/tests/Feature/Auth/EmailVerificationTest.php index 397961eab..cc567fc28 100644 --- a/tests/Feature/Auth/EmailVerificationTest.php +++ b/tests/Feature/Auth/EmailVerificationTest.php @@ -45,6 +45,8 @@ public function test_email_is_not_verified_with_invalid_hash() { $user = User::factory()->unverified()->create(); + Event::fake(); + $verificationUrl = URL::temporarySignedRoute( 'verification.verify', now()->addMinutes(60), @@ -53,14 +55,15 @@ public function test_email_is_not_verified_with_invalid_hash() $this->actingAs($user)->get($verificationUrl); + Event::assertNotDispatched(Verified::class); $this->assertFalse($user->fresh()->hasVerifiedEmail()); } public function test_email_is_not_verified_with_invalid_user_id(): void { - $user = User::factory()->create([ - 'email_verified_at' => null, - ]); + $user = User::factory()->unverified()->create(); + + Event::fake(); $verificationUrl = URL::temporarySignedRoute( 'verification.verify', @@ -70,25 +73,25 @@ public function test_email_is_not_verified_with_invalid_user_id(): void $this->actingAs($user)->get($verificationUrl); + Event::assertNotDispatched(Verified::class); $this->assertFalse($user->fresh()->hasVerifiedEmail()); } public function test_verified_user_is_redirected_to_dashboard_from_verification_prompt(): void { - $user = User::factory()->create([ - 'email_verified_at' => now(), - ]); + $user = User::factory()->create(); + + Event::fake(); $response = $this->actingAs($user)->get(route('verification.notice')); + Event::assertNotDispatched(Verified::class); $response->assertRedirect(route('dashboard', absolute: false)); } public function test_already_verified_user_visiting_verification_link_is_redirected_without_firing_event_again(): void { - $user = User::factory()->create([ - 'email_verified_at' => now(), - ]); + $user = User::factory()->create(); Event::fake(); @@ -101,7 +104,7 @@ public function test_already_verified_user_visiting_verification_link_is_redirec $this->actingAs($user)->get($verificationUrl) ->assertRedirect(route('dashboard', absolute: false).'?verified=1'); - $this->assertTrue($user->fresh()->hasVerifiedEmail()); Event::assertNotDispatched(Verified::class); + $this->assertTrue($user->fresh()->hasVerifiedEmail()); } } diff --git a/tests/Feature/Auth/VerificationNotificationTest.php b/tests/Feature/Auth/VerificationNotificationTest.php index 34a81a81a..cfd8861f6 100644 --- a/tests/Feature/Auth/VerificationNotificationTest.php +++ b/tests/Feature/Auth/VerificationNotificationTest.php @@ -16,9 +16,7 @@ public function test_sends_verification_notification(): void { Notification::fake(); - $user = User::factory()->create([ - 'email_verified_at' => null, - ]); + $user = User::factory()->unverified()->create(); $this->actingAs($user) ->post(route('verification.send')) @@ -31,9 +29,7 @@ public function test_does_not_send_verification_notification_if_email_is_verifie { Notification::fake(); - $user = User::factory()->create([ - 'email_verified_at' => now(), - ]); + $user = User::factory()->create(); $this->actingAs($user) ->post(route('verification.send')) diff --git a/tests/Feature/DashboardTest.php b/tests/Feature/DashboardTest.php index 2cf1dc29d..10a967162 100644 --- a/tests/Feature/DashboardTest.php +++ b/tests/Feature/DashboardTest.php @@ -12,13 +12,16 @@ class DashboardTest extends TestCase public function test_guests_are_redirected_to_the_login_page() { - $this->get(route('dashboard'))->assertRedirect(route('login')); + $response = $this->get(route('dashboard')); + $response->assertRedirect(route('login')); } public function test_authenticated_users_can_visit_the_dashboard() { - $this->actingAs($user = User::factory()->create()); + $user = User::factory()->create(); + $this->actingAs($user); - $this->get(route('dashboard'))->assertOk(); + $response = $this->get(route('dashboard')); + $response->assertOk(); } } diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 000000000..77932df7d --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,18 @@ +get(route('home')); + + $response->assertOk(); + } +}