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();
+ }
+}